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 fail: its merge-check.sh failed: FAIL github.com/novox/mesh-controller/cmd/mesh-controller 167.881s
mesh/delivery superseded: a newer head of the same pull request
A table of merges (two repositories, every edge kind, a diamond, a cycle, files no build reads) and a seeded property over 500 random catalogues pin what a merge moves and in which tiers, per ADR 0162 and ADR 0238 §3. The shared-repository rows document today's behaviour that issue 338 would change, once with hand edges and once with edges derived from the store. The cycle check fails on main: hasCycle reads a packages edge between two modules of the last tier as a cycle, so a plan with none is said to have one. Left failing, marked BUG, for the planner's fix.
132 lines
5.8 KiB
Go
132 lines
5.8 KiB
Go
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/catalogue"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// **The planner over edges the store derives**, not edges written by hand: modules registered, builds
|
|
// recorded with what they stood on and which repositories they read, the relation answered by
|
|
// inventory.Dependencies (dependenciesOf over the records), and the merge planned by reachOfMerge — the
|
|
// path a real merge takes, short of the bus.
|
|
//
|
|
// **The repository rows are CURRENT BEHAVIOUR, documented — not the rule the operator states**
|
|
// (novox/hq issue 338, and the decision pending on it): a build that read a repository gives its module a
|
|
// packages edge to every module built from that repository, and mergeCandidates moves it on any merge to
|
|
// that repository, whatever the files. So a change to C alone, or to a README, moves the module that
|
|
// packages C's repository. ADR 0238 §3 records exactly that today ("a repository a recipe names"); the
|
|
// expectations marked 338 change with that decision.
|
|
func TestASharedRepositoryIsPlannedFromTheRecordsAsItIsToday(t *testing.T) {
|
|
inv := inventory.ForTest(t)
|
|
ctx := t.Context()
|
|
asked := time.Now().Add(-time.Hour)
|
|
register := func(m catalogue.Manifest, repository, path string, against []string, read []inventory.ReadRepository) {
|
|
t.Helper()
|
|
if err := inv.RegisterModule(ctx, m, inventory.Source{Repository: repository, Seat: "git", Path: path,
|
|
Ref: "main", BuiltFrom: "old", Asked: asked}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.RecordBuild(ctx, inventory.Build{ID: "build-" + m.Module, Repository: repository, Ref: "main",
|
|
Module: m.Module, Commit: "old", On: "builder", Path: path, Against: against, Read: read, Asked: asked}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
controllerRead := []inventory.ReadRepository{{Repository: "novox/mesh-controller", Ref: "main"}}
|
|
agent := catalogue.Manifest{Module: "build-agent", Version: "1",
|
|
Claims: []catalogue.Claim{{Name: "node-build-agent", Scope: catalogue.ScopeNode}}}
|
|
|
|
// The shape of issue 338.
|
|
register(catalogue.Manifest{Module: "mesh-controller", Version: "1"}, "novox/mesh-controller", "", nil, nil)
|
|
register(agent, "novox/mesh-catalog", "modules/build-agent", nil, controllerRead)
|
|
register(catalogue.Manifest{Module: "route-proxy", Version: "1"}, "novox/mesh-catalog", "modules/route-proxy", nil, controllerRead)
|
|
register(catalogue.Manifest{Module: "gitea", Version: "1"}, "novox/mesh-catalog", "modules/gitea", nil, nil)
|
|
// A, B and C in one repository; D built against A's artifact, E declaring B, P packaging the repository.
|
|
for _, n := range []string{"a", "b", "c"} {
|
|
register(catalogue.Manifest{Module: n, Version: "1"}, "novox/one", "modules/"+n, nil, nil)
|
|
}
|
|
register(catalogue.Manifest{Module: "d", Version: "1"}, "novox/two", "d",
|
|
[]string{catalogue.ArtifactStoreScheme + "a/runtime@sha256:" + strings.Repeat("0", 64)}, nil)
|
|
register(catalogue.Manifest{Module: "e", Version: "1", Build: &catalogue.Build{
|
|
On: []catalogue.BuildsOn{{Arg: "BASE", Module: "b", Artifact: "runtime"}}}}, "novox/two", "e", nil, nil)
|
|
register(catalogue.Manifest{Module: "p", Version: "1"}, "novox/two", "p", nil,
|
|
[]inventory.ReadRepository{{Repository: "novox/one", Ref: "main"}})
|
|
|
|
entries, err := inv.Catalogued(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
read, err := inv.ReadRepositories(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
edges, err := inv.Dependencies(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// The edges the hand-written rows of TestASharedRepositoryMovesWhatPackagesItAsItDoesToday use are
|
|
// the ones derived here.
|
|
var shared []inventory.Edge
|
|
in338 := map[string]bool{"mesh-controller": true, "build-agent": true, "route-proxy": true, "gitea": true}
|
|
for _, e := range edges {
|
|
if in338[e.From] && in338[e.To] {
|
|
shared = append(shared, e)
|
|
}
|
|
}
|
|
if !reflect.DeepEqual(shared, sharedRepositoryEdges) {
|
|
t.Errorf("derived %v\nthe hand-written rows use %v", shared, sharedRepositoryEdges)
|
|
}
|
|
// Each kind derived from its record: built against (stands-on), build.on (declared), read (packages).
|
|
for _, want := range []inventory.Edge{
|
|
dep("d", inventory.EdgeStandsOn, "a"),
|
|
dep("e", inventory.EdgeDeclared, "b"),
|
|
dep("p", inventory.EdgePackages, "a"),
|
|
dep("p", inventory.EdgePackages, "b"),
|
|
dep("p", inventory.EdgePackages, "c"),
|
|
dep("d", inventory.EdgeBuiltBy, "build-agent"),
|
|
} {
|
|
found := false
|
|
for _, e := range edges {
|
|
found = found || e == want
|
|
}
|
|
if !found {
|
|
t.Errorf("no %s %s %s derived: %v", want.From, want.Kind, want.To, edges)
|
|
}
|
|
}
|
|
|
|
for _, c := range []struct {
|
|
what, repo string
|
|
paths []string
|
|
want string
|
|
issue338 bool
|
|
}{
|
|
{"A and B changed, C untouched: D after A, E after B; P packages their repository", "one",
|
|
[]string{"modules/a/x.go", "modules/b/x.go"}, "a,b,p | d,e", false},
|
|
{"C alone: C, and P, which packages C's repository", "one",
|
|
[]string{"modules/c/x.go"}, "c,p", true},
|
|
{"a README of the repository P packages: P moves, nothing built from it does", "one",
|
|
[]string{"README.md"}, "p", true},
|
|
{"the dependent's repository: D alone", "two", []string{"d/main.go"}, "d", false},
|
|
{"a README of the controller's repository: all three, three tiers", "mesh-controller",
|
|
[]string{"README.md"}, "mesh-controller | build-agent | route-proxy", true},
|
|
{"the route proxy's directory in the catalogue: it alone", "mesh-catalog",
|
|
[]string{"modules/route-proxy/module.json"}, "route-proxy", false},
|
|
{"the build agent's directory: it alone, nothing it builds", "mesh-catalog",
|
|
[]string{"modules/build-agent/module.json"}, "build-agent", false},
|
|
} {
|
|
_, got := planMerge(t, c.repo, c.paths, entries, read, edges)
|
|
if got != c.want {
|
|
tag := ""
|
|
if c.issue338 {
|
|
tag = " (current behaviour, issue 338)"
|
|
}
|
|
t.Errorf("%s: planned %q, wanted %q%s", c.what, got, c.want, tag)
|
|
}
|
|
}
|
|
}
|