Every merge to the controller's repository planned the controller, the build seat's holder and the route proxy in three gated tiers, whatever it changed (issue 338). The planner now maps a merge's files onto the build source each module's newest trunk build said: a README moves nothing, the controller's command the controller alone, the proxy's program the proxy alone. A module with none said, or one an open plan has yet to build, is read whole as before. Sharing a repository draws no packages edge any more, and one recorded before neither widens nor orders a plan.
152 lines
7.2 KiB
Go
152 lines
7.2 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.
|
|
//
|
|
// **A shared repository moves only what a change's files are in the build source of** (novox/hq ADR 0267,
|
|
// issues 338 and 363): each build records the build source it said, and a merge is mapped onto those of the
|
|
// newest builds. A build that said none (P below, as every build before ADR 0267) is read as before: P moves
|
|
// on any merge to the repository it packages, though through no edge. The rows tagged 338 held the opposite
|
|
// until ADR 0267 was built.
|
|
func TestASharedRepositoryIsPlannedFromTheRecordedBuildSources(t *testing.T) {
|
|
inv := inventory.ForTest(t)
|
|
ctx := t.Context()
|
|
asked := time.Now().Add(-time.Hour)
|
|
sourcesOf := map[string][]inventory.BuildSource{}
|
|
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,
|
|
Sources: sourcesOf[m.Module]}); 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, each build saying its build source (ADR 0267).
|
|
gomod := []string{"go.mod", "go.sum"}
|
|
sourcesOf["mesh-controller"] = []inventory.BuildSource{{Paths: append([]string{"module.json", "cmd/mesh-controller/",
|
|
"internal/conditions/", "internal/broker/"}, gomod...)}}
|
|
sourcesOf["build-agent"] = []inventory.BuildSource{
|
|
{Paths: []string{"modules/build-agent/Dockerfile", "modules/build-agent/module.json"}},
|
|
{Repository: "novox/mesh-controller", Ref: "main", Paths: append([]string{"cmd/mesh-builder/", "internal/broker/"}, gomod...)}}
|
|
sourcesOf["route-proxy"] = []inventory.BuildSource{
|
|
{Paths: []string{"modules/route-proxy/Dockerfile", "modules/route-proxy/module.json"}},
|
|
{Repository: "novox/mesh-controller", Ref: "main", Paths: append([]string{"examples/route-proxy/", "internal/broker/"}, gomod...)}}
|
|
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); a read draws none.
|
|
for _, e := range edges {
|
|
if e.Kind == inventory.EdgePackages {
|
|
t.Errorf("a packages edge was drawn (ADR 0267 rule 4): %v", e)
|
|
}
|
|
}
|
|
for _, want := range []inventory.Edge{
|
|
dep("d", inventory.EdgeStandsOn, "a"),
|
|
dep("e", inventory.EdgeDeclared, "b"),
|
|
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, which said no build source, reads all", "one",
|
|
[]string{"modules/a/x.go", "modules/b/x.go"}, "a,b,p | d,e", false},
|
|
{"C alone: C, and P, read whole as before; P after nothing", "one",
|
|
[]string{"modules/c/x.go"}, "c,p", false},
|
|
{"a README of the repository P packages: P, read whole as before", "one",
|
|
[]string{"README.md"}, "p", false},
|
|
{"the dependent's repository: D alone", "two", []string{"d/main.go"}, "d", false},
|
|
{"a README of the controller's repository: no module", "mesh-controller",
|
|
[]string{"README.md"}, "", true},
|
|
{"the controller's own command: the controller alone", "mesh-controller",
|
|
[]string{"cmd/mesh-controller/main.go"}, "mesh-controller", true},
|
|
{"a package only the controller builds from: the controller alone", "mesh-controller",
|
|
[]string{"internal/conditions/condition.go"}, "mesh-controller", true},
|
|
{"the route proxy's program: the route proxy alone", "mesh-controller",
|
|
[]string{"examples/route-proxy/main.go"}, "route-proxy", true},
|
|
{"a package all three build from: all three", "mesh-controller",
|
|
[]string{"internal/broker/broker.go"}, "mesh-controller | build-agent | route-proxy", false},
|
|
{"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 = " (issue 338, flipped by ADR 0267)"
|
|
}
|
|
t.Errorf("%s: planned %q, wanted %q%s", c.what, got, c.want, tag)
|
|
}
|
|
}
|
|
}
|