Files
mesh-controller/cmd/mesh-controller/replay336_test.go
T
jochen 9a7ae131cb
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 head of the same pull request
TestReplay336 reads the bus call in its mesh form
The previous commit named the upgrade as a mesh call, and the replay still looked for the command-line
words; it now asserts the seat and the upgrade, as any wording of the call says them.
2026-10-09 03:08:21 +02:00

93 lines
3.8 KiB
Go

package main
import (
"context"
"strings"
"testing"
"time"
"github.com/novox/mesh-controller/internal/catalogue"
"github.com/novox/mesh-controller/internal/conditions"
"github.com/novox/mesh-controller/internal/inventory"
)
// TestReplay336 replays novox/hq issue 336 (2026-10-08): a catalogue merge built a new bus build for the
// control-node; from then on every send to that machine was refused for the bus's planned step, which only a
// person starts, and for 28 minutes nothing but the walks' notes said so. The outcome asserted: the first
// watchdog tick after the first refusal opens a condition for the operator that names what waits and the verb
// that ends it, and it clears once the bus's machine runs the new build. Written with only what the controller
// had before its fix — the stores, register, assign, a plan saved and advanced, the watchdogs' gathering and
// seeing — so it is laid over the commit before.
func TestReplay336(t *testing.T) {
open := aMesh(t)
ctx := t.Context()
inv := open.inventory
bus := catalogue.Manifest{Module: "nats", Version: "2", Provides: []catalogue.Offer{{Name: "mesh-bus"}}}
if err := inv.RegisterModule(ctx, bus, inventory.Source{Repository: "novox/mesh-catalog", Seat: "git",
Path: "modules/nats", BuiltFrom: "32307bd1", Head: "32307bd1"}); err != nil {
t.Fatal(err)
}
register(t, open, catalogue.Manifest{Module: "engine", Version: "1"})
for _, m := range []string{"nats", "engine"} {
if _, err := inv.Assign(ctx, "anchor", m); err != nil {
t.Fatal(err)
}
}
// The control-node runs the bus build it was last sent; the mesh holds a newer one, which waits for its step.
if err := inv.RecordSent(ctx, nodeID(t, open, "anchor"), "d-anchor", map[string]string{"nats": "88135ad0"}); err != nil {
t.Fatal(err)
}
// A walk of the engine, built, whose tier sends to the control-node.
now := time.Now().UTC()
plan := inventory.Plan{ID: "plan-engine", Repository: "novox/mesh-host", Commit: "e1e1e1e1", Created: now,
State: inventory.PlanBuilding, Tiers: [][]string{{"engine"}},
Modules: map[string]*inventory.PlanModule{"engine": {State: "built", BuiltAt: &now, Commit: "e1e1e1e1", Build: "b"}}}
if err := inv.SavePlan(ctx, &plan); err != nil {
t.Fatal(err)
}
advancePlans(ctx, open)
p, err := inv.PlanByID(ctx, "plan-engine")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(p.Note, errBusWaits.Error()) {
t.Fatalf("the walk's send to the bus's machine was not refused for the bus: %s %q", p.State, p.Note)
}
store := conditions.NewInMemory()
k := conditions.NewKeeper(ctx, conditions.Options{Store: store, History: store, Teller: &conditions.Told{}})
defer k.Close(context.Background())
w := &watchdogs{open: open, keeper: k, started: now.Add(-time.Hour)}
waiting := func() []conditions.Condition {
t.Helper()
w.see(ctx, w.gather(ctx))
all, err := k.Open(ctx)
if err != nil {
t.Fatal(err)
}
var out []conditions.Condition
for _, c := range all {
if strings.HasPrefix(c.Key, "bus.") && c.Resolver == conditions.ResolverOperator {
out = append(out, c)
}
}
return out
}
got := waiting()
if len(got) != 1 {
t.Fatalf("the first tick after the refusal told the operator nothing about the bus's step: %d condition(s)", len(got))
}
for _, want := range []string{"anchor", "engine", "mesh-controller.bus", "upgrade", "32307bd1"} {
if !strings.Contains(got[0].Summary, want) {
t.Errorf("the condition does not say %q: %s", want, got[0].Summary)
}
}
// The step taken: the control-node is sent the new bus build, and the wait is over.
if err := inv.RecordSent(ctx, nodeID(t, open, "anchor"), "d-anchor-2", map[string]string{"nats": "32307bd1"}); err != nil {
t.Fatal(err)
}
if got := waiting(); len(got) != 0 {
t.Fatalf("the bus's machine runs the new build, and the operator is still asked: %+v", got)
}
}