Files
mesh-catalog/modules/mesh-delivery/cmd/mesh-delivery/issue474_test.go
T
jschoubben 9d0f93bba8
mesh/merge-gate pass: builds mesh-delivery → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery covered after its failure: a later walk that contains it was delivered: plan-1791711936761655786 (novox/mesh-catalog at 83c64dcd into main,…
mesh-delivery: hold a stranded stop for a later merge on its way, and keep it loud (review, hq issue 474)
A stop is final, so a later merge whose queued walk would carry the modules
now holds it off; a stopped one stays in stalled until a later delivered merge
carries them. A walk counts only modules it sent, and an old walk record is read
again rather than taken as building nothing.
2026-10-11 11:38:08 +02:00

212 lines
8.9 KiB
Go

package main
import (
"strings"
"testing"
"time"
)
// novox/hq issue 474: a merge batched behind a later one of its branch (ADR 0276) whose walk ended done before
// any word was asked — the controller judged the batch's files moved nothing it holds, "nothing to walk" — was
// left published, waiting for "its walk's start", which a done walk never has. Its walk's record settles it: it
// is delivered when that walk moved its modules, covered when a later delivered merge carried them (ADR 0270),
// and otherwise stopped, saying why — never left waiting.
//
// The shape is the night's own (2026-10-11): mesh-controller #215 (merged as fbc7bc97, its check's plan moving
// build-agent) and #217 (merged as 8305e4e3 a minute later, moving nothing) in one batch, walked as
// plan-1791681624611167730 at 8305e4e3: done, no tier, still saying it awaits mesh-delivery.
const (
ctl474 = "novox/mesh-controller"
head215 = "d2e9dc61590f950b"
merge215 = "fbc7bc976b3c115b"
head217 = "68fbd99e894443ce"
merge217 = "8305e4e3d126594d"
head219 = "ef551fdfb6b29d4a"
merge219 = "0a2e58c0704ccc09"
batchWalk = "plan-1791681624611167730"
walk219 = "plan-1791686195314774150"
nothingNote = "moved nothing the mesh holds: nothing to walk"
)
var at215 = time.Date(2026, 10, 11, 1, 20, 19, 0, time.UTC)
// theNight is #215 and #217 checked and merged, and their batch's walk done with no tier, as the controller said it.
func theNight(t *testing.T) *world {
w := newWorldOver(t, newMemStore(), newFakeController(), newFakeForge(), at215.Add(-time.Minute))
p215 := pr(ctl474, 215, head215, "fix/431-make-check-runs-what-the-gate-runs", "Makefile")
p217 := pr(ctl474, 217, head217, "fix/432-a-test-reads-what-it-carries", "cmd/mesh-controller/check_test.go")
w.h.PullUpdated(p215)
w.h.PullUpdated(p217)
w.h.Checked(verdictMoving(ctl474, 215, head215, "build-agent"))
w.h.Checked(verdictMoving(ctl474, 217, head217))
w.later(time.Minute)
w.h.PullMerged(mergedAt(p215, merge215, at215))
w.later(time.Minute)
w.h.PullMerged(mergedAt(p217, merge217, at215.Add(56*time.Second)))
w.later(90 * time.Second)
cut := w.now
w.h.WalkMoved(w.ctl.put(Walk{ID: batchWalk, Repository: ctl474, Branch: "main", Commit: merge217, Created: cut,
Updated: cut, State: walkDone, Tiers: [][]string{}, Modules: map[string]*WalkModule{}, Note: nothingNote,
Delivery: &WalkDelivery{Awaits: "mesh-delivery", Merges: []WalkMerge{
{Repository: ctl474, Commit: merge215, Carried: merge217}, {Repository: ctl474, Commit: merge217}}}}))
w.settleAll()
return w
}
// The live delivery: its walk ended without moving build-agent, and a later merge (#219) delivered build-agent
// everywhere from main holding its merge — covered, from the walk's record and the forge's word, never waiting.
func TestAMergeWhoseBatchWalkEndedIsCoveredByALaterDeliveredMerge(t *testing.T) {
w := theNight(t)
id := IDOf(ctl474, head215)
if s := w.state(IDOf(ctl474, head217)); s != Delivered {
t.Fatalf("#217, which moves nothing, is %s", s)
}
w.forge.contains[merge215+" in "+merge219] = true
deliverLater(w, ctl474, 219, "main", head219, merge219, at215.Add(76*time.Minute), walkDone, "build-agent")
w.later(2 * time.Minute)
w.settleAll()
d := w.delivery(id)
if d.State != Covered {
t.Fatalf("#215, its walk %s done with nothing moved and build-agent delivered later by #219, is %s, waiting "+
"for %q", batchWalk, d.State, w.h.waitsFor(d))
}
if why := d.Transitions[len(d.Transitions)-1].Why; !strings.Contains(why, IDOf(ctl474, head219)) {
t.Fatalf("covered, but not by #219: %q", why)
}
if len(w.h.Stalled()) != 0 {
t.Fatalf("stalled still lists %+v", w.h.Stalled())
}
}
// With nothing later to carry its modules, it is stopped once the cover test says so and the walk ended past the
// grace — saying which walk ended without moving what — and not before. Stopped, it stays loud: `stalled` lists it
// until a later delivered merge carries its modules.
func TestAMergeWhoseBatchWalkEndedAndNothingCoversItIsStoppedSayingWhy(t *testing.T) {
w := theNight(t)
id := IDOf(ctl474, head215)
w.later(5 * time.Minute)
w.settleAll()
if s := w.state(id); s != Published {
t.Fatalf("within the grace it is %s", s)
}
w.later(strandedAfter)
w.settleAll()
d := w.delivery(id)
if d.State != Stopped {
t.Fatalf("its walk done without build-agent and nothing later carrying it, it is %s, waiting for %q", d.State,
w.h.waitsFor(d))
}
why := d.Transitions[len(d.Transitions)-1].Why
for _, want := range []string{"never reached the machines", batchWalk, "build-agent", nothingNote} {
if !strings.Contains(why, want) {
t.Fatalf("its stop says %q, not %q", why, want)
}
}
lines := w.h.Stalled()
if len(lines) != 1 || lines[0].ID != id || !strings.Contains(lines[0].Says, "never reached the machines") {
t.Fatalf("stopped, it is not listed loudly: %+v", lines)
}
// A later merge that delivers build-agent ends the listing; the stop itself stays, final.
w.forge.contains[merge215+" in "+merge219] = true
deliverLater(w, ctl474, 219, "main", head219, merge219, w.now, walkDone, "build-agent")
w.later(2 * time.Minute)
w.settleAll()
if d := w.delivery(id); d.State != Stopped || d.CoveredBy == "" {
t.Fatalf("after #219 it is %s, covered by %q", d.State, d.CoveredBy)
}
if lines := w.h.Stalled(); len(lines) != 0 {
t.Fatalf("stalled still lists %+v", lines)
}
}
// A later merge whose walk is still queued, and whose plan moves what this one's walk left unbuilt, holds the stop
// off: a stop is final, and that merge may yet cover it (review of issue 474).
func TestAMergeWhoseBatchWalkEndedWaitsForALaterMergeOnItsWay(t *testing.T) {
w := theNight(t)
id := IDOf(ctl474, head215)
p := pr(ctl474, 219, head219, "fix/334", "internal/builder/builder.go")
w.h.PullUpdated(p)
w.h.Checked(verdictMoving(ctl474, 219, head219, "build-agent", "mesh-controller"))
w.later(3 * time.Minute)
w.h.PullMerged(mergedAt(p, merge219, w.now))
w.settleAll()
w.later(strandedAfter)
w.settleAll()
if d := w.delivery(id); d.State != Published {
t.Fatalf("with #219 on its way it is %s (%s)", d.State, d.Stranded)
}
// #219's walk delivers build-agent: covered.
w.forge.contains[merge215+" in "+merge219] = true
walk := aWalk(walk219, ctl474, merge219, true)
walk.Modules = map[string]*WalkModule{"build-agent": {}, "mesh-controller": {}}
w.h.WalkMoved(w.ctl.put(walk))
w.settleAll()
walk = w.ctl.walk(walk219)
sent := w.now
for m := range walk.Modules {
walk.Modules[m] = &WalkModule{State: "built", SentAt: &sent}
}
walk.State = walkDone
w.h.WalkMoved(w.ctl.put(walk))
w.later(2 * time.Minute)
w.settleAll()
if s := w.state(IDOf(ctl474, head219)); s != Delivered {
t.Fatalf("#219 is %s", s)
}
if d := w.delivery(id); d.State != Covered {
t.Fatalf("after #219 delivered build-agent it is %s, waiting for %q", d.State, w.h.waitsFor(d))
}
}
// A walk that ended before its word built nothing: a module its record names with why it was not sent is not read
// as delivered. A merge whose plan moves nothing, answered by such a walk, is delivered.
func TestAWalkThatEndedBeforeItsWordBuiltNothing(t *testing.T) {
for _, moves := range [][]string{{"build-agent"}, nil} {
w := newWorldOver(t, newMemStore(), newFakeController(), newFakeForge(), at215.Add(-time.Minute))
p := pr(ctl474, 215, head215, "fix/x", "cmd/mesh-builder/main.go")
w.h.PullUpdated(p)
w.h.Checked(verdictMoving(ctl474, 215, head215, moves...))
w.later(time.Minute)
w.h.PullMerged(mergedAt(p, merge215, at215))
w.later(2 * time.Minute)
w.h.WalkMoved(w.ctl.put(Walk{ID: batchWalk, Repository: ctl474, Branch: "main", Commit: merge215, Created: w.now,
Updated: w.now, State: walkDone, Tiers: [][]string{{"build-agent"}},
Modules: map[string]*WalkModule{"build-agent": {Why: "not sent: its walk never started"}},
Delivery: &WalkDelivery{Awaits: "mesh-delivery", Merges: []WalkMerge{{Repository: ctl474, Commit: merge215}}}}))
w.settleAll()
d := w.delivery(IDOf(ctl474, head215))
want := Published
if len(moves) == 0 {
want = Delivered
}
if d.State != want {
t.Fatalf("moving %v: it is %s, waiting for %q, not %s", moves, d.State, w.h.waitsFor(d), want)
}
}
}
// A walk record kept before it named the modules it built, with tiers, is not read as building nothing: the walk is
// asked for again, and what it answers settles the delivery.
func TestAnOldWalkRecordIsAskedForAgain(t *testing.T) {
w := theNight(t)
id := IDOf(ctl474, head215)
w.h.mu.Lock()
d := w.h.deliveries[id]
d.Walk.Moved, d.Walk.MovedSaid, d.Walk.Tiers = nil, false, 1
w.h.mu.Unlock()
if got := w.h.waitsFor(w.delivery(id)); !strings.Contains(got, "walk's record") {
t.Fatalf("an old record waits for %q", got)
}
w.later(time.Minute)
w.settleAll()
if d := w.delivery(id); !d.Walk.MovedSaid || d.Walk.Tiers != 0 {
t.Fatalf("the walk was not read again: %+v", d.Walk)
}
w.later(strandedAfter)
w.settleAll()
if s := w.state(id); s != Stopped {
t.Fatalf("read again, it is %s", s)
}
}