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.
177 lines
7.5 KiB
Go
177 lines
7.5 KiB
Go
package inventory
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-controller/internal/catalogue"
|
|
)
|
|
|
|
// The catalogue's one dependency relation (novox/hq ADR 0162): four kinds of edge from one call.
|
|
func TestDependenciesAreOneRelationWithTheirKinds(t *testing.T) {
|
|
entry := func(name, repository string) Entry {
|
|
return Entry{Manifest: catalogue.Manifest{Module: name}, Source: Source{Repository: repository}}
|
|
}
|
|
builder := entry("builder", "http://forge/novox/mesh-catalog.git")
|
|
builder.Manifest.Claims = []catalogue.Claim{{Name: "node-build-agent", Scope: catalogue.ScopeNode}}
|
|
plugin := entry("shop-plugin", "http://forge/novox/mesh-catalog.git")
|
|
plugin.Manifest.Build = &catalogue.Build{On: []catalogue.BuildsOn{{Arg: "BASE", Module: "shop"}}}
|
|
entries := []Entry{
|
|
entry("mesh-tools", "http://forge/novox/mesh-tools.git"),
|
|
entry("shop", "http://forge/novox/mesh-catalog.git"),
|
|
plugin,
|
|
builder,
|
|
entry("mesh-controller", "http://forge/novox/mesh-controller.git"),
|
|
entry("route-proxy", "http://forge/novox/mesh-catalog.git"),
|
|
{Manifest: catalogue.Manifest{Module: "hand-made"}},
|
|
}
|
|
against := map[string][]string{
|
|
"shop": {catalogue.ArtifactStoreScheme + "mesh-tools/runtime@sha256:a"},
|
|
"builder": {catalogue.ArtifactStoreScheme + "mesh-tools/runtime@sha256:a"},
|
|
}
|
|
read := map[string][]ReadRepository{
|
|
"route-proxy": {{Repository: "http://forge/novox/mesh-controller.git", Ref: "main"}},
|
|
}
|
|
got := dependenciesOf(entries, against, read)
|
|
has := func(from, to, kind string) bool {
|
|
for _, e := range got {
|
|
if e == (Edge{From: from, To: to, Kind: kind}) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
for _, want := range []Edge{
|
|
{"shop", "mesh-tools", EdgeStandsOn},
|
|
{"builder", "mesh-tools", EdgeStandsOn},
|
|
{"shop-plugin", "shop", EdgeDeclared},
|
|
{"shop", "builder", EdgeBuiltBy},
|
|
{"mesh-controller", "builder", EdgeBuiltBy},
|
|
{"mesh-tools", "builder", EdgeBuiltBy},
|
|
} {
|
|
if !has(want.From, want.To, want.Kind) {
|
|
t.Errorf("missing %+v in %+v", want, got)
|
|
}
|
|
}
|
|
// Sharing a repository is no dependency (novox/hq ADR 0267 rule 4): what a build read draws no edge.
|
|
if has("route-proxy", "mesh-controller", EdgePackages) {
|
|
t.Error("a packages edge was drawn from what a build read")
|
|
}
|
|
if has("builder", "builder", EdgeBuiltBy) {
|
|
t.Error("the builder is not built by itself")
|
|
}
|
|
for _, e := range got {
|
|
if e.From == "hand-made" {
|
|
t.Errorf("a module with no source depends on nothing: %+v", e)
|
|
}
|
|
}
|
|
}
|
|
|
|
// novox/hq 04-ISSUES/211: a bundle stands on the toolchain it is compiled in, so a merge moving both
|
|
// builds the toolchain first — read from the manifest, before any build recorded it.
|
|
func TestABundleStandsOnTheToolchainItIsCompiledIn(t *testing.T) {
|
|
entries := []Entry{
|
|
{Manifest: catalogue.Manifest{Module: "mesh-tools"}, Source: Source{Repository: "novox/mesh-tools"}},
|
|
{Manifest: catalogue.Manifest{Module: "mesh-tools-go"}, Source: Source{Repository: "novox/mesh-tools-go"}},
|
|
{Manifest: catalogue.Manifest{Module: "node-tools", Build: &catalogue.Build{Artifacts: []catalogue.Artifact{
|
|
{Name: "runtime", Kind: catalogue.ArtifactBundle, Language: "go", System: "arch", From: "cmd/node-tools"}}}},
|
|
Source: Source{Repository: "novox/mesh-tools"}},
|
|
{Manifest: catalogue.Manifest{Module: "nftables", Build: &catalogue.Build{Artifacts: []catalogue.Artifact{
|
|
{Name: "tools", Kind: catalogue.ArtifactBundle, Language: "typescript", Entrypoints: []string{"tools/index.js"}}}}},
|
|
Source: Source{Repository: "novox/mesh-catalog"}},
|
|
{Manifest: catalogue.Manifest{Module: "photos", Build: &catalogue.Build{Artifacts: []catalogue.Artifact{
|
|
{Name: "server", Kind: catalogue.ArtifactImage, From: "Dockerfile"}}}},
|
|
Source: Source{Repository: "novox/photos"}},
|
|
}
|
|
edges := dependenciesOf(entries, nil, nil)
|
|
has := func(from, to string) bool {
|
|
for _, e := range edges {
|
|
if e.From == from && e.To == to && e.Kind == EdgeStandsOn {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
if !has("nftables", "mesh-tools") {
|
|
t.Errorf("a TypeScript bundle does not stand on the TypeScript toolchain: %v", edges)
|
|
}
|
|
if !has("node-tools", "mesh-tools-go") {
|
|
t.Errorf("a Go bundle does not stand on the Go toolchain: %v", edges)
|
|
}
|
|
for _, e := range edges {
|
|
if e.From == "photos" && e.Kind == EdgeStandsOn {
|
|
t.Errorf("an image stands on a toolchain it is not compiled in: %v", e)
|
|
}
|
|
}
|
|
}
|
|
|
|
// novox/hq 04-ISSUES/212: a toolchain standing on the SDK's package is planned after the SDK, so a
|
|
// release of the SDK rebuilds the toolchain, and every bundle compiled in it after that.
|
|
func TestAToolchainStandingOnTheSDKFollowsIt(t *testing.T) {
|
|
entries := []Entry{
|
|
{Manifest: catalogue.Manifest{Module: "mesh-sdk"}},
|
|
{Manifest: catalogue.Manifest{Module: "mesh-tools", Build: &catalogue.Build{
|
|
On: []catalogue.BuildsOn{{Arg: "MESH_SDK", Module: "mesh-sdk", Artifact: "lib"}}}}},
|
|
}
|
|
edges := dependenciesOf(entries, nil, nil)
|
|
found := false
|
|
for _, e := range edges {
|
|
if e.From == "mesh-tools" && e.To == "mesh-sdk" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("no edge from the toolchain to the SDK: %v", edges)
|
|
}
|
|
}
|
|
|
|
// novox/hq issue 278: nothing depends on the build agent but by being built by it. A plan is widened
|
|
// only along the other kinds (reachableFrom), so this is what keeps a change to the build agent — its
|
|
// manifest or its program — from rebuilding what it builds: the relation holds no edge to it that could.
|
|
func TestNothingStandsOnTheBuildAgentOnlyIsBuiltByIt(t *testing.T) {
|
|
const catalogueRepo = "http://forge/novox/mesh-catalog.git"
|
|
agent := Entry{Manifest: catalogue.Manifest{Module: "build-agent",
|
|
Claims: []catalogue.Claim{{Name: "node-build-agent", Scope: catalogue.ScopeNode}}},
|
|
Source: Source{Repository: catalogueRepo, Path: "modules/build-agent"}}
|
|
bundle := Entry{Manifest: catalogue.Manifest{Module: "redis", Build: &catalogue.Build{
|
|
Artifacts: []catalogue.Artifact{{Name: "code", Kind: catalogue.ArtifactBundle, Language: "typescript"}}}},
|
|
Source: Source{Repository: catalogueRepo, Path: "modules/redis"}}
|
|
entries := []Entry{
|
|
agent, bundle,
|
|
{Manifest: catalogue.Manifest{Module: "postgres"}, Source: Source{Repository: catalogueRepo, Path: "modules/postgres"}},
|
|
{Manifest: catalogue.Manifest{Module: "mesh-tools"}, Source: Source{Repository: "http://forge/novox/mesh-tools.git"}},
|
|
{Manifest: catalogue.Manifest{Module: TheControlPlane}, Source: Source{Repository: "http://forge/novox/mesh-controller.git"}},
|
|
}
|
|
// A module packaging another repository depends on what is built from it, never on the agent.
|
|
read := map[string][]ReadRepository{"postgres": {{Repository: "http://forge/novox/mesh-tools.git"}}}
|
|
builtBy := 0
|
|
for _, e := range dependenciesOf(entries, nil, read) {
|
|
if e.To != "build-agent" {
|
|
continue
|
|
}
|
|
if e.Kind != EdgeBuiltBy {
|
|
t.Errorf("%s depends on the build agent as %s: a change to the agent would rebuild it", e.From, e.Kind)
|
|
}
|
|
builtBy++
|
|
}
|
|
if builtBy != 4 {
|
|
t.Errorf("everything source-built is built by the agent: %d edges", builtBy)
|
|
}
|
|
}
|
|
|
|
// A copy of an upstream image is no module's artifact (novox/hq ADR 0257), even when a module happens to
|
|
// carry the name its repository starts with.
|
|
func TestACopyOfAnUpstreamImageIsNoEdge(t *testing.T) {
|
|
entries := []Entry{
|
|
{Manifest: catalogue.Manifest{Module: "route-proxy"}},
|
|
{Manifest: catalogue.Manifest{Module: "upstream"}},
|
|
}
|
|
against := map[string][]string{
|
|
"route-proxy": {catalogue.ArtifactStoreScheme + "upstream/docker.io/library/golang@sha256:a"},
|
|
}
|
|
for _, e := range dependenciesOf(entries, against, nil) {
|
|
if e.From == "route-proxy" && e.To == "upstream" {
|
|
t.Fatalf("a copy of an upstream image was read as a module's artifact: %+v", e)
|
|
}
|
|
}
|
|
}
|