212 lines
8.9 KiB
Go
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)
|
|
}
|
|
}
|