package main import ( "strings" "testing" "time" "github.com/novox/mesh-controller/internal/catalogue" "github.com/novox/mesh-controller/internal/inventory" "github.com/novox/mesh-controller/internal/link" ) // entry is a module as the catalogue holds it: built, so its manifest carries no `build` any more. func entry(module string, _ ...string) inventory.Entry { return inventory.Entry{Manifest: catalogue.Manifest{Module: module}} } // stoodOn is what each module's newest build recorded it was handed. func stoodOn(edges map[string][]string) map[string][]string { out := map[string][]string{} for module, bases := range edges { for _, b := range bases { out[module] = append(out[module], catalogue.ArtifactStoreScheme+b+"/runtime@sha256:"+strings.Repeat("0", 64)) } } return out } // A module built before the module it stands on is built against the old one and reports success // (novox/hq 04-ISSUES/131). So bases come first, however the set arrived. func TestBasesAreBuiltBeforeWhatStandsOnThem(t *testing.T) { in := []inventory.Entry{entry("app"), entry("runtime"), entry("other"), entry("base")} edges := stoodOn(map[string][]string{"app": {"runtime"}, "runtime": {"base"}}) got := orderByBases(in, edges) pos := map[string]int{} for i, e := range got { pos[e.Manifest.Module] = i } if !(pos["base"] < pos["runtime"] && pos["runtime"] < pos["app"]) { t.Fatalf("bases not first: %v", pos) } if len(got) != 4 { t.Fatalf("an entry was lost or doubled: %d", len(got)) } // A base outside the set is not waited for: it is not being rebuilt. got = orderByBases([]inventory.Entry{entry("app")}, stoodOn(map[string][]string{"app": {"elsewhere"}})) if len(got) != 1 { t.Fatalf("a dependency outside the set changed the set: %v", got) } } // A module registered from its manifest and never built still names its bases there; once built, // the recorded edge is what says so. Both are read, and a module never stands on itself. func TestWhatStandsOnAModuleIsReadFromItsBuildOrItsManifest(t *testing.T) { built := entry("gitea") edges := stoodOn(map[string][]string{"gitea": {"mesh-tools"}}) if !standsOnModule(built, "mesh-tools", edges) { t.Fatal("a recorded edge was not read") } if standsOnModule(built, "gitea", edges) || standsOnModule(built, "postgres", edges) { t.Fatal("an edge was invented") } fresh := inventory.Entry{Manifest: catalogue.Manifest{Module: "plex", Build: &catalogue.Build{ On: []catalogue.BuildsOn{{Arg: "RUNTIME_BASE", Module: "mesh-tools", Artifact: "runtime"}}, }}} if !standsOnModule(fresh, "mesh-tools", nil) { t.Fatal("a manifest's own base was not read") } } // A merge names a repository the way the forge does; a source is recorded the way a build was // asked for. The two meet on owner/repo and branch, whichever form the record took. func TestAMergeMatchesTheSourcesBuiltFromIt(t *testing.T) { m := link.SourceMoved{Owner: "novox", Repo: "mesh-controller", Base: "main", CloneURL: "http://forge.internal:20000/novox/mesh-controller.git"} for _, s := range []inventory.Source{ {Repository: "http://forge.internal:20000/novox/mesh-controller.git", Ref: "main"}, {Repository: "novox/mesh-controller", Seat: "git", Ref: ""}, {Repository: "https://elsewhere.example/novox/mesh-controller", Ref: "main"}, } { if !sourceIs(s, m) { t.Errorf("%+v was not matched by the merge", s) } } for _, s := range []inventory.Source{ {Repository: "novox/mesh-host", Seat: "git"}, {Repository: "http://forge.internal:20000/novox/mesh-controller.git", Ref: "release"}, } { if sourceIs(s, m) { t.Errorf("%+v was matched by a merge that is not its", s) } } } // A merge made before the source was last seen is history: it does not move the source, and a // merge that says nothing about when it was made is taken as news. func TestAMergeOlderThanTheLastLookIsHistory(t *testing.T) { seen := time.Date(2026, 9, 28, 3, 0, 0, 0, time.UTC) if !isHistory("2026-09-28T02:00:00Z", seen) { t.Fatal("an older merge was taken as news") } if isHistory("2026-09-28T04:00:00Z", seen) { t.Fatal("a newer merge was taken as history") } if isHistory("", seen) || isHistory("2026-09-28T02:00:00Z", time.Time{}) { t.Fatal("a merge or a source with no time on it was refused") } } // A module as the catalogue holds it: built from a repository, at a directory inside it. func fromRepo(module, repository, path string) inventory.Entry { return inventory.Entry{ Manifest: catalogue.Manifest{Module: module}, Source: inventory.Source{Repository: repository, Path: path, Ref: "main"}, } } // A merge rebuilds the modules whose own directories it changed, and everything when what it changed // is shared. One repository holding many modules is the ordinary case here, and rebuilding all of // them for a change to one is what exhausted a registry's pull limit the first night this ran. func TestAMergeRebuildsTheModulesItChanged(t *testing.T) { const repo = "http://forge.internal:20000/novox/mesh-catalog.git" gitea := fromRepo("gitea", repo, "modules/gitea") keycloak := fromRepo("keycloak", repo, "modules/keycloak") known := []inventory.Entry{gitea, keycloak, fromRepo("plex", repo, "modules/plex")} candidates := []inventory.Entry{gitea, keycloak} merge := func(paths []string, truncated bool) link.SourceMoved { return link.SourceMoved{Owner: "novox", Repo: "mesh-catalog", Base: "main", Paths: paths, PathsTruncated: truncated} } named := func(entries []inventory.Entry) string { var names []string for _, e := range entries { names = append(names, e.Manifest.Module) } return strings.Join(names, ",") } for _, c := range []struct { what string m link.SourceMoved want string }{ {"one module's own files", merge([]string{"modules/gitea/index.ts", "modules/gitea/client.ts"}, false), "gitea"}, {"two modules' files", merge([]string{"modules/gitea/index.ts", "modules/keycloak/module.json"}, false), "gitea,keycloak"}, {"a file they share", merge([]string{"tsconfig.json"}, false), "gitea,keycloak"}, {"a module the mesh does not hold", merge([]string{"modules/plex/index.ts"}, false), ""}, {"nothing said about the files", merge(nil, false), "gitea,keycloak"}, {"more files than were listed", merge([]string{"modules/gitea/index.ts"}, true), "gitea,keycloak"}, } { if got := named(whatTheMergeTouched(candidates, known, c.m)); got != c.want { t.Errorf("%s: rebuilt %q, wanted %q", c.what, got, c.want) } } } // A module whose recipe packages source from another repository is affected when that repository // moves — the manifest the mesh keeps says nothing about it, so the record of what the build read is // the only thing that can say so. func TestAModuleIsAffectedByTheRepositoryItPackages(t *testing.T) { m := link.SourceMoved{Owner: "novox", Repo: "mesh-controller", Base: "main", CloneURL: "http://forge.internal:20000/novox/mesh-controller.git"} for _, read := range [][]inventory.ReadRepository{ {{Repository: "http://forge.internal:20000/novox/mesh-controller.git", Ref: "main"}}, {{Repository: "novox/mesh-controller"}}, {{Repository: "https://elsewhere.example/novox/other"}, {Repository: "novox/mesh-controller.git", Ref: "main"}}, } { if !readsFrom(read, m) { t.Errorf("%+v was not matched by the merge", read) } } for _, read := range [][]inventory.ReadRepository{ nil, {{Repository: "novox/mesh-host", Ref: "main"}}, {{Repository: "novox/mesh-controller", Ref: "release"}}, } { if readsFrom(read, m) { t.Errorf("%+v was matched by a merge that is not its", read) } } } // What a module handed over by hand records about where it came from, and what is refused. func TestWhatAHandedOverModuleRecordsAboutItsSource(t *testing.T) { // The whole location: a repository on the mesh's own forge, the directory inside it, the branch // and the commit the manifest was read at. from, err := whereItComesFrom("novox/mesh-catalog", "main", "c0ffee", "modules/gitea", true) if err != nil { t.Fatal(err) } if from.Path != "modules/gitea" || from.Seat != "git" || from.Repository != "novox/mesh-catalog" { t.Fatalf("the source records as %+v", from) } // A manifest with no provenance at all is legitimate: fixing something in a hurry. if from, err := whereItComesFrom("", "", "", "", false); err != nil || from != (inventory.Source{}) { t.Fatalf("a manifest handed over with no provenance was refused: %+v, %v", from, err) } for _, c := range []struct { what string repository, ref, commit, path string self bool }{ {what: "a source with no commit", repository: "novox/mesh-catalog", commit: ""}, {what: "a commit with no source", commit: "c0ffee"}, {what: "a directory inside nothing", path: "modules/gitea"}, {what: "a forge holding nothing", self: true}, {what: "an address given as a path on the forge", repository: "http://forge.internal:20000/novox/x.git", commit: "c0ffee", self: true}, } { if _, err := whereItComesFrom(c.repository, c.ref, c.commit, c.path, c.self); err == nil { t.Errorf("%s was recorded as a source", c.what) } } }