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.
93 lines
3.8 KiB
Go
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)
|
|
}
|
|
}
|