package main import ( "strings" "testing" "github.com/novox/mesh-controller/internal/catalogue" "github.com/novox/mesh-controller/internal/inventory" "github.com/novox/mesh-controller/internal/link" ) // **One commit, one change plan** (novox/hq ADR 0238): the planner's reach, laid out machine by machine in // the build plan's order, with what is not an ordinary send said — the bus step, a module that waits for a // person, a provider whose consumers follow it. func TestAChangePlanSaysWhatEachMachineReceives(t *testing.T) { const catalogue_ = "http://forge.internal:20000/novox/mesh-catalog.git" entry := func(name, path string, on ...string) inventory.Entry { e := fromRepo(name, catalogue_, path) e.Source.BuiltFrom = "old" e.On = on return e } nats := entry("nats", "modules/nats", "anchor") nats.Manifest.Provides = []catalogue.Offer{{Name: "mesh-bus"}} gitea := entry("gitea", "modules/gitea", "anchor") held := entry("photos", "modules/photos", "anchor", "laptop") tools := fromRepo("node-tools", "http://forge.internal:20000/novox/mesh-tools.git", "node-tools") tools.On = []string{"anchor", "laptop"} entries := []inventory.Entry{nats, gitea, held, tools} edges := []inventory.Edge{{From: "node-tools", To: "nats", Kind: inventory.EdgeStandsOn}} policy := func(module string) (inventory.Upgrade, bool) { if module == "photos" { return inventory.Upgrade{RollOut: false, From: "a person"}, true } return inventory.Upgrade{RollOut: true}, true } plan := func(paths ...string) link.ChangePlan { m := link.SourceMoved{Owner: "novox", Repo: "mesh-catalog", Base: "main", Commit: "head", Paths: paths} return changePlanOf("novox/mesh-catalog", "main", "head", reachOfMerge(m, entries, nil, edges), entries, policy) } p := plan("modules/gitea/index.ts") if p.Summary != "builds gitea → anchor; no bus step" { t.Errorf("one module's change reads %q", p.Summary) } p = plan("modules/nats/Dockerfile", "modules/photos/x.js") if !strings.Contains(p.Summary, "needs a person's bus upgrade") || !strings.Contains(p.Summary, "2 wait(s) for a person") || !strings.Contains(p.Summary, "(+1 dependent(s))") { t.Errorf("the bus and a held module read %q", p.Summary) } got := map[string]string{} for _, m := range p.Machines { got[m.Machine] = strings.Join(m.Receives, ",") + "|" + strings.Join(m.Waits, ",") } // The bus first, what stands on it after: the build plan's order, per machine. if got["anchor"] != "nats,node-tools|photos" || got["laptop"] != "node-tools|photos" { t.Errorf("the deploy plan reads %v", got) } text := strings.Join(p.Steps, "\n") // Said before the merge (novox/hq issue 336): merging it holds every send to the bus's machine until a // person runs the bus's step, and the verb that does. for _, want := range []string{"merging this needs a person's bus upgrade", "nothing else is sent to anchor", "mesh_call mesh-controller.bus", "the same as the one running there", "photos waits for a person", "nats provides mesh-bus"} { if !strings.Contains(text, want) { t.Errorf("the steps do not say %q:\n%s", want, text) } } p = plan("merge-check.sh") if !strings.HasPrefix(p.Summary, "builds nothing") || len(p.Machines) != 0 || strings.Join(p.Unread, ",") != "merge-check.sh" { t.Errorf("a root file's plan reads %+v", p) } if !strings.Contains(planText(p), "read by no module's build: merge-check.sh") { t.Errorf("the plan's text does not say why nothing is built:\n%s", planText(p)) } }