Excuse no wait for a move from a build not known, and count a module put back only once there is one (hq issue 318 review)
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer delivery to the same trunk took over its walk

This commit is contained in:
jochen
2026-10-08 15:49:07 +02:00
parent 41d6019fa0
commit a44dc01c65
4 changed files with 96 additions and 3 deletions
+57
View File
@@ -378,3 +378,60 @@ func TestAModuleItsSendChangedNothingOfIsLeftAsItWasAndRetried(t *testing.T) {
t.Fatalf("retried as %q: the plan is %s, app %+v", said, p.State, s)
}
}
// **In a plan's tier path, a broken module is put back at once too** (novox/hq issue 318 review): the node
// tools' witness reverts them on the first machine. Whether the gate is kept on them or on the module beside
// them, they are registered back and marked in the judging that finds them broken, the plan goes on judging
// the other module to its own pass, and only then fails.
func TestATierPutsABrokenModuleBackAtOnce(t *testing.T) {
for _, order := range [][]string{{broker.RuntimeModule, "app1"}, {"app1", broker.RuntimeModule}} {
t.Run("gate kept on "+order[0], func(t *testing.T) {
tm := aTierMesh(t, order...)
ctx := t.Context()
inv := tm.open.inventory
tierFacts := gatherGateFacts
var seen []string
gatherGateFacts = func(ctx context.Context, open *stores, component string) (gateFacts, error) {
f, err := tierFacts(ctx, open, component)
current, _ := open.inventory.CurrentBuilds(ctx)
seen = append(seen, current[broker.RuntimeModule].Commit)
if current[broker.RuntimeModule].Commit == "c2" {
f.rolledBack["anchor"] = []lease.Rollback{{Component: lease.ComponentNodeTools,
Outcome: lease.OutcomeRolledBack, From: "c2", To: "c1", At: time.Now(), Why: "the node tools did not answer"}}
}
return f, err
}
gateEvery, gateSettle, gateBound = 0, 100*time.Millisecond, 10*time.Second
for i := 0; i < 20 && len(seen) < 2; i++ {
advancePlans(ctx, tm.open)
}
if len(seen) < 2 || seen[0] != "c2" || seen[1] != "c1" {
t.Fatalf("the node tools' registered build at each judging: %v; want c2, then c1 at once", seen)
}
if failed, _ := inv.GateFailed(ctx, "build-"+broker.RuntimeModule+"-2"); !failed {
t.Fatal("the node tools' build is not marked failed at once")
}
if p := tm.plan(t); p.State == inventory.PlanFailed {
t.Fatalf("the plan failed at once: %s; want it judging app1 first", p.Note)
}
for deadline := time.Now().Add(5 * time.Second); time.Now().Before(deadline); {
advancePlans(ctx, tm.open)
if tm.plan(t).State == inventory.PlanFailed {
break
}
time.Sleep(20 * time.Millisecond)
}
p := tm.plan(t)
if p.State != inventory.PlanFailed {
t.Fatalf("the plan is %s: %s; want failed, for the node tools", p.State, p.Note)
}
if v, found, _ := inv.GateOf(ctx, "build-app1-2"); !found || v.Verdict != inventory.GatePassed {
t.Fatalf("app1's verdict: %+v; want its own pass (plan: %s)", v, p.Note)
}
if current, _ := inv.CurrentBuilds(ctx); current["app1"].Commit != "c2" || current[broker.RuntimeModule].Commit != "c1" {
t.Fatalf("registered app1 %s, node tools %s; want c2 and c1", current["app1"].Commit,
current[broker.RuntimeModule].Commit)
}
})
}
}