Files
mesh-catalog/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/issue1792_test.go
T
jschoubben d2faada61b
mesh/merge-gate head d2faada61bf3 merged into trunk 379317589488 — pass: builds mesh-delivery-gate → novox; no bus step; every machine composes with the ch…
mesh/repo-check head d2faada61bf3 merged into trunk 379317589488 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
Clear a member's own-ask mark when its own verdict arrives, so a later pending status is asked again (issue 1792)
2026-10-21 23:16:35 +02:00

121 lines
4.9 KiB
Go

package main
import "testing"
const gHk2 = "f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4"
// Issue 1792: a sibling's new head put a member's own mesh/repo-check back to pending ("out of date: judged beside ..."),
// and nothing asked it again. The composed check passed, the group stayed held ("unasked") on the member's pending
// status, and the operator's Approve was never asked. The group's hold on a member's own pending repository check asks
// that check, once for the head, with no composed check (ADR 0382 §2: each member's own check stays required).
func TestAGroupHeldBeforeItsAskOnAMembersOwnPendingRepoCheckAsksThatCheckOnce(t *testing.T) {
w := newWorld(t)
w.op.auto = false
w.h.PullUpdated(pr(gCtl, 1, gHc, "feat/g", "internal/a.go"))
w.h.PullUpdated(pr(gCat, 2, gHk, "feat/g", "modules/app/module.json"))
w.h.Checked(judged(gCtl, 1, gHc, "pass", gTrunk0))
w.h.Checked(judged(gCat, 2, gHk, "pass", gTrunk0))
w.settleAll()
g := w.h.groups["feat/g"]
setChecks(w, gCat, gHk, "success", "success")
setChecks(w, gCtl, gHc, "success", "pending")
asked := len(w.ctl.checks)
w.h.Checked(CheckedEvent{Group: "feat/g", ID: g.Check.Asked, Verdict: "pass", Summary: "composed",
Members: []struct {
Owner string `json:"owner"`
Repo string `json:"repo"`
Commit string `json:"commit"`
Trunk string `json:"trunk,omitempty"`
}{{Owner: "novox", Repo: "mesh-controller", Commit: gHc, Trunk: gTrunk0[:12]},
{Owner: "novox", Repo: "mesh-catalog", Commit: gHk, Trunk: gTrunk0[:12]}}})
w.settleAll()
w.later(time30s)
w.settleAll()
own, composed := ownChecks(w, asked)
if len(composed) != 0 || len(own) != 1 || own[0] != ":"+IDOf(gCtl, gHc) {
t.Fatalf("own checks %v, composed %v; wanted the member's own check asked once", own, composed)
}
if w.op.count() != 0 {
t.Fatalf("the operator was asked with a required status pending: %d", w.op.count())
}
}
func composedPassAt(w *world, hc, hk string) {
g := w.h.groups["feat/g"]
w.h.Checked(CheckedEvent{Group: "feat/g", ID: g.Check.Asked, Verdict: "pass", Summary: "composed",
Members: []struct {
Owner string `json:"owner"`
Repo string `json:"repo"`
Commit string `json:"commit"`
Trunk string `json:"trunk,omitempty"`
}{{Owner: "novox", Repo: "mesh-controller", Commit: hc, Trunk: gTrunk0[:12]},
{Owner: "novox", Repo: "mesh-catalog", Commit: hk, Trunk: gTrunk0[:12]}}})
}
// heldOnOwnPending is the group of issue 1792 after its first own ask: the member's own check asked, once.
func heldOnOwnPending(t *testing.T) *world {
w := newWorld(t)
w.op.auto = false
w.h.PullUpdated(pr(gCtl, 1, gHc, "feat/g", "internal/a.go"))
w.h.PullUpdated(pr(gCat, 2, gHk, "feat/g", "modules/app/module.json"))
w.h.Checked(judged(gCtl, 1, gHc, "pass", gTrunk0))
w.h.Checked(judged(gCat, 2, gHk, "pass", gTrunk0))
w.settleAll()
setChecks(w, gCat, gHk, "success", "success")
setChecks(w, gCtl, gHc, "success", "pending")
asked := len(w.ctl.checks)
composedPassAt(w, gHc, gHk)
w.settleAll()
w.later(time30s)
w.settleAll()
if own, _ := ownChecks(w, asked); len(own) != 1 {
t.Fatalf("the first own ask: %v", own)
}
return w
}
// A second sibling push puts the member's repo-check out of date again after its own check answered: asked again.
func TestAMembersOwnCheckIsAskedAgainWhenASecondSiblingHeadPutsItOutOfDate(t *testing.T) {
w := heldOnOwnPending(t)
w.h.Checked(judged(gCtl, 1, gHc, "pass", gTrunk0)) // its own check answered
setChecks(w, gCtl, gHc, "success", "success")
w.settleAll()
w.h.PullUpdated(pr(gCat, 2, gHk2, "feat/g", "modules/app/module.json"))
w.h.Checked(judged(gCat, 2, gHk2, "pass", gTrunk0))
w.settleAll()
setChecks(w, gCat, gHk2, "success", "success")
setChecks(w, gCtl, gHc, "success", "pending")
asked := len(w.ctl.checks)
composedPassAt(w, gHc, gHk2)
w.settleAll()
w.later(time30s)
w.settleAll()
own, composed := ownChecks(w, asked)
if len(composed) != 0 || len(own) != 1 || own[0] != ":"+IDOf(gCtl, gHc) {
t.Fatalf("own %v composed %v; wanted the member's own check asked again, once", own, composed)
}
}
// An own ask before the Approve, answered, and then a merge refused for the member's own pending status at the same head:
// asked again (the one ask is per answer, not per head's life).
func TestAMergeRefusedForOwnPendingAfterAnAnsweredOwnAskAsksItAgain(t *testing.T) {
w := heldOnOwnPending(t)
w.h.Checked(judged(gCtl, 1, gHc, "pass", gTrunk0))
setChecks(w, gCtl, gHc, "success", "success")
w.later(time30s)
w.settleAll()
if len(w.op.open()) != 1 {
t.Fatalf("the group is not asked once its statuses are green: %d", w.op.count())
}
w.forge.refuseRepo = map[string]error{gCtl: ownPending}
w.op.answer(w.op.open()[0], OptionApprove)
w.settleAll()
w.h.PullMerged(merged(gCat, 2, gHk, gT1))
asked := len(w.ctl.checks)
w.settleAll()
own, composed := ownChecks(w, asked)
if len(composed) != 0 || len(own) != 1 || own[0] != ":"+IDOf(gCtl, gHc) {
t.Fatalf("own %v composed %v; wanted the member's own check asked again after the refusal", own, composed)
}
}