mesh/merge-gate head 9f072c110c14 merged into trunk 60cc9ec363df — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 9f072c110c14 merged into trunk 60cc9ec363df — pass: its merge-check.sh passed
mesh/delivery delivered
A rebuild that landed while a walk waited for mesh-delivery's word was taken as the walk's build of that module; once let go, the walk asked a tier only when every module of it was unasked, so it never asked the rest and stood still. A walk now takes only the outcomes of builds it asked, and none while it waits; a rebuild does not join a waiting walk and says it runs beside it; and a tier asked in part is asked the rest, including a module held built that the walk never asked for, which is somebody else's build and not sent as the walk's.
123 lines
5.2 KiB
Go
123 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// novox/hq issue 754: a walk waiting for its delivery's word takes no build's outcome, nor does a walk take one for
|
|
// a module it never asked; asked under an id, it takes that id's alone.
|
|
func TestAWalkTakesOnlyTheOutcomesOfWhatItAsked(t *testing.T) {
|
|
asked := time.Date(2026, 10, 14, 23, 30, 0, 0, time.UTC)
|
|
before := asked.Add(-time.Minute)
|
|
waiting := inventory.Plan{Delivery: &inventory.PlanDelivery{Awaits: "mesh-delivery"}}
|
|
letGo := inventory.Plan{Delivery: &inventory.PlanDelivery{Awaits: "mesh-delivery", Go: &asked}}
|
|
own := &inventory.PlanModule{State: "asked", AskedAt: &asked, Build: "build-own"}
|
|
old := &inventory.PlanModule{State: "asked", AskedAt: &asked}
|
|
for _, c := range []struct {
|
|
what string
|
|
p inventory.Plan
|
|
state *inventory.PlanModule
|
|
at time.Time
|
|
id string
|
|
takes bool
|
|
}{
|
|
{"a rebuild while the walk waits", waiting, nil, asked, "build-rebuild", false},
|
|
{"its own id while it waits", waiting, own, asked, "build-own", false},
|
|
{"a module it never asked", letGo, nil, asked, "build-rebuild", false},
|
|
{"a module it never asked, state kept empty", letGo, &inventory.PlanModule{}, asked, "build-rebuild", false},
|
|
{"another id", letGo, own, asked, "build-rebuild", false},
|
|
{"its own id", letGo, own, asked, "build-own", true},
|
|
{"a plan from before ids, asked after it", letGo, old, asked.Add(time.Second), "x", true},
|
|
{"a plan from before ids, asked before it", letGo, old, before, "x", false},
|
|
{"a plan with no delivery, its own id", inventory.Plan{}, own, asked, "build-own", true},
|
|
} {
|
|
if got := takesOutcome(c.p, c.state, c.at, c.id); got != c.takes {
|
|
t.Errorf("%s: takes %v, want %v", c.what, got, c.takes)
|
|
}
|
|
}
|
|
}
|
|
|
|
// novox/hq issue 754: a tier partly asked asks the rest; a module held built that the plan never asked — the live
|
|
// shape, a hand rebuild taken in during the wait, built after the plan was made — is asked again; one asked is kept.
|
|
func TestATierPartlyAskedAsksTheRest(t *testing.T) {
|
|
created := time.Date(2026, 10, 14, 23, 30, 0, 0, time.UTC)
|
|
earlier, later := created.Add(-time.Hour), created.Add(7*time.Minute)
|
|
p := inventory.Plan{Created: created, Tiers: [][]string{{"build-agent", "gitea", "route-proxy", "stale", "kept", "own"}},
|
|
Modules: map[string]*inventory.PlanModule{
|
|
"gitea": {},
|
|
"route-proxy": {State: "built", BuiltAt: &later, Commit: "c51357f8"},
|
|
"stale": {State: "built", BuiltAt: &earlier},
|
|
"kept": {State: "asked", AskedAt: &created, Build: "build-kept"},
|
|
"own": {State: "built", AskedAt: &created, BuiltAt: &later, Build: "build-own"},
|
|
}}
|
|
if got, want := unaskedOf(p), []string{"build-agent", "gitea", "route-proxy", "stale"}; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("unasked %v, want %v", got, want)
|
|
}
|
|
if !askedInTier(p) {
|
|
t.Fatal("a tier with a module asked under an id reads as never asked")
|
|
}
|
|
p.Modules["kept"], p.Modules["own"] = nil, nil
|
|
if askedInTier(p) {
|
|
t.Fatal("a tier the plan never asked reads as asked")
|
|
}
|
|
}
|
|
|
|
// novox/hq issue 754, as seen on 2026-10-14: a rebuild lands while the walk waits for its word; the walk is let go;
|
|
// it asks every module of its tier, and completes it once those builds come in.
|
|
func TestARebuildInTheWaitIsNotTheWalksBuild(t *testing.T) {
|
|
open := aMesh(t)
|
|
ctx := t.Context()
|
|
asked := asksRecorded(t)
|
|
twoTiers(t, open)
|
|
created := time.Now().UTC().Add(-10 * time.Minute)
|
|
plan := inventory.Plan{ID: "plan-754", Repository: "novox/a", Branch: "main", Commit: "c0ffee", Created: created,
|
|
State: inventory.PlanBuilding, Tiers: [][]string{{"a", "b"}}, Modules: map[string]*inventory.PlanModule{},
|
|
Delivery: &inventory.PlanDelivery{Awaits: "mesh-delivery"}}
|
|
if err := open.inventory.SavePlan(ctx, &plan); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// A rebuild of a, beside the waiting walk: not joined, and its outcome not taken.
|
|
joined, said, err := joinAPlan(ctx, open, "a")
|
|
if err != nil || joined || said == "" {
|
|
t.Fatalf("a rebuild joined a waiting walk: %v %q %v", joined, said, err)
|
|
}
|
|
planBuilt(ctx, open, "a", "c0ffee", "", time.Now().UTC(), "build-rebuild")
|
|
p, _ := open.inventory.PlanByID(ctx, plan.ID)
|
|
if s := p.Modules["a"]; s != nil && s.State != "" {
|
|
t.Fatalf("the waiting walk took a rebuild as its build: %+v", s)
|
|
}
|
|
if len(*asked) != 0 {
|
|
t.Fatalf("a waiting walk asked %v", *asked)
|
|
}
|
|
|
|
// Let go: both are asked, and their outcomes complete the tier.
|
|
if _, err := letGo(ctx, open.inventory, plan.ID, "operator", "test"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
advancePlans(ctx, open)
|
|
p, _ = open.inventory.PlanByID(ctx, plan.ID)
|
|
for _, m := range []string{"a", "b"} {
|
|
if s := p.Modules[m]; s == nil || s.State != "asked" || s.Build == "" {
|
|
t.Fatalf("let go, %s is %+v", m, s)
|
|
}
|
|
}
|
|
if len(*asked) != 2 {
|
|
t.Fatalf("asked %v", *asked)
|
|
}
|
|
for _, m := range []string{"a", "b"} {
|
|
id := p.Modules[m].Build
|
|
at, _ := link.BuildAskedAt(id)
|
|
planBuilt(ctx, open, m, "c0ffee", "", at, id)
|
|
}
|
|
advancePlans(ctx, open)
|
|
if p, _ = open.inventory.PlanByID(ctx, plan.ID); p.Open() && p.Tier == 0 {
|
|
t.Fatalf("the walk did not get past its tier: %s at %d, %q", p.State, p.Tier, p.Note)
|
|
}
|
|
}
|