One commit, one plan: a commit off the trunk — a pull request's head, a branch built by hand, a rebuild or replay of one — is for checking. The build seat reads from its clone which branches hold the commit, and the controller records and never registers a build whose commit is not on the branch the module follows (the repository's default for a new one), so nothing off the trunk can be sent. A pull request's check now carries its change plan, computed by the planner: what a merge would build in which order, what each machine would receive, and what is not an ordinary send — the bus step, a module waiting for a person, a provider's consumers.
75 lines
3.1 KiB
Go
75 lines
3.1 KiB
Go
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, "a bus step") || !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")
|
|
for _, want := range []string{"a planned bus step", "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))
|
|
}
|
|
}
|