package index import ( "context" "encoding/json" "strings" "testing" "git.novox.be/novox/mesh-sdk/go/delivery" ) const merge = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // record is a build record as the controller writes it (mesh-sdk go/delivery/testdata/build-record-build-agent.json is // the captured shape): SourceInputs under its Go field names, no json keys. inputs is that object's JSON. func record(t *testing.T, id, module, repo, path, ref, state, at, inputs string) (string, []byte) { t.Helper() raw := `{"id":"` + id + `","state":"` + state + `","repository":"` + repo + `","ref":"` + ref + `","module":"` + module + `","commit":"7a74cee83f88e6585a5511c3cc53bdf0f5303d20","path":"` + path + `","at":"` + at + `"` if inputs != "" { raw += `,"source-fingerprint":"src1:ignored","source-inputs":` + inputs } raw += `}` var check delivery.BuildRecord if err := json.Unmarshal([]byte(raw), &check); err != nil { t.Fatal(err) } return delivery.BuildKey(module, id), []byte(raw) } func modulesIndexer(t *testing.T, recs ...[2]any) (*Indexer, *memStore) { t.Helper() mods := newMem() ix := &Indexer{Store: newMem(), Modules: mods, Builds: NewBuilds()} for _, r := range recs { ix.Builds.Put(r[0].(string), r[1].([]byte)) } return ix, mods } func rec(t *testing.T, id, module, repo, path, ref, state, at, inputs string) [2]any { k, v := record(t, id, module, repo, path, ref, state, at, inputs) return [2]any{k, v} } func mergedMsg(repo string, trees map[string]string) delivery.Forge { return delivery.Forge{V: delivery.Version, Kind: delivery.ForgeMerged, Repository: repo, Branch: "feat/x", Number: 12, Head: h1, Merge: merge, Root: "r0", Trees: trees} } func moduleOf(t *testing.T, st *memStore, key string) (delivery.Module, bool) { t.Helper() v, ok := st.vals[key] if !ok { return delivery.Module{}, false } m, err := delivery.DecodeModule(v) if err != nil { t.Fatal(err) } return m, true } const aInputs = `{"Module":"a","Tree":"oldtree","Bases":["registry.example/base@sha256:aaa"],"Contexts":null,"Toolchains":{"code":"go sha256:ccc 0123456789abcdef"},"Unpinned":""}` func TestModuleFingerprintFromTheMergeTreeAndTheRecordedInputs(t *testing.T) { // 1239/AC1 ix, mods := modulesIndexer(t, rec(t, "build-1", "a", "novox/mesh-delivery", "modules/a", "main", "built", "2026-10-19T09:00:00Z", aInputs)) if err := ix.Apply(context.Background(), mergedMsg("novox/mesh-delivery", map[string]string{"modules/a": "newtree", "modules/b": "other"})); err != nil { t.Fatal(err) } want, none := delivery.SourceFingerprint(delivery.SourceInputs{Module: "a", Tree: "newtree", Bases: []string{"registry.example/base@sha256:aaa"}, Toolchains: map[string]string{"code": "go sha256:ccc 0123456789abcdef"}}) if none != "" { t.Fatal(none) } m, ok := moduleOf(t, mods, "a") if !ok || m.FP != want || m.None != "" || m.Repository != "novox/mesh-delivery" || m.Commit != merge || m.Src != 0 { t.Errorf("module = %+v (%v), want fp %s", m, ok, want) } if _, ok := mods.vals["b"]; ok { t.Error("a key was written for a directory no build names") } } func TestMergeOfTheRecordedTreeReproducesTheRecordedFingerprint(t *testing.T) { // 1239/AC2 // mailu, build-1792391134620131370: mesh-catalog at b4527d9091e972f28cb365623d9280045e7741a3, dir modules/mailu. Its // inputs and recorded fingerprint are the live ones mesh-sdk go/delivery/fingerprint_test.go TestFingerprintLiveBuilds // captured (its tree is `git rev-parse :modules/mailu`, its bases from the build's [bases] log line). const tree = "cfc6fa8ff5fc1e220132e51ade653b9d3f387b3e" const recorded = "src1:539f2e64000d436c572ef53b40e524012455945f8c5a1c6bb173ee0707e88ef4" inputs := `{"Module":"mailu","Tree":"` + tree + `","Bases":["sha256:25f3cfeaceca14921366af4d1240b56457ef46273bdb508c7b0e8f469f6fd228"],"Contexts":null,` + `"Toolchains":{"code":"typescript sha256:4b81682df5665c1f461d3f720a3a0fd7593f4aa71e4fc272dbfdc4bbd235b489 20125bd590041690"},"Unpinned":""}` ix, mods := modulesIndexer(t, rec(t, "build-1792391134620131370", "mailu", "novox/mesh-catalog", "modules/mailu", "main", "built", "2026-10-19T09:00:00Z", inputs)) if err := ix.Apply(context.Background(), mergedMsg("novox/mesh-catalog", map[string]string{"modules/mailu": tree})); err != nil { t.Fatal(err) } if m, ok := moduleOf(t, mods, "mailu"); !ok || m.FP != recorded { t.Errorf("module = %+v (%v), want fp %s", m, ok, recorded) } } func TestANarrowContextHasNoFingerprint(t *testing.T) { // 1239/AC3, 1209/AC1 // A Go program's build source as the builder records it (build-agent's record, mesh-sdk go/delivery/testdata): // the context "server" is "narrow:" and a hash. inputs := `{"Module":"build-agent","Tree":"11c0b4f90cae0d6e4ef9cc48cbfdf9f6dbe52bca","Bases":["sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc"],"Contexts":{"server":"narrow:d51ca744fd11af59d616c858f76c27b3e58e16e11fbdc0c720eb9d7d19d55fac"},"Toolchains":null,"Unpinned":""}` ix, mods := modulesIndexer(t, rec(t, "build-1", "build-agent", "novox/mesh-controller", "modules/build-agent", "main", "built", "2026-10-19T09:00:00Z", inputs)) if err := ix.Apply(context.Background(), mergedMsg("novox/mesh-controller", map[string]string{"modules/build-agent": "newtree"})); err != nil { t.Fatal(err) } m, ok := moduleOf(t, mods, "build-agent") if !ok || m.FP != "" || !strings.Contains(m.None, "Go program") || !strings.Contains(m.None, "release 4") { t.Errorf("module = %+v (%v), want none naming the Go program's build source and release 4", m, ok) } } func TestUnpinnedBuildHasNoFingerprint(t *testing.T) { // 1239/AC4 inputs := `{"Module":"a","Tree":"t","Bases":null,"Contexts":null,"Toolchains":null,"Unpinned":"the build resolves packages when built"}` ix, mods := modulesIndexer(t, rec(t, "build-1", "a", "novox/mesh-delivery", "modules/a", "main", "built", "2026-10-19T09:00:00Z", inputs)) _ = ix.Apply(context.Background(), mergedMsg("novox/mesh-delivery", map[string]string{"modules/a": "newtree"})) if m, ok := moduleOf(t, mods, "a"); !ok || m.FP != "" || m.None != "the build resolves packages when built" { t.Errorf("module = %+v (%v)", m, ok) } } func TestARootTreeContextHasNoFingerprint(t *testing.T) { // 1239/AC5 inputs := `{"Module":"a","Tree":"t","Bases":null,"Contexts":{"web":"f00dcafef00dcafef00dcafef00dcafef00dcafe"},"Toolchains":null,"Unpinned":""}` ix, mods := modulesIndexer(t, rec(t, "build-1", "a", "novox/mesh-delivery", "modules/a", "main", "built", "2026-10-19T09:00:00Z", inputs)) _ = ix.Apply(context.Background(), mergedMsg("novox/mesh-delivery", map[string]string{"modules/a": "newtree"})) m, ok := moduleOf(t, mods, "a") if !ok || m.FP != "" || !strings.Contains(m.None, "root tree") || !strings.Contains(m.None, "release 4") { t.Errorf("module = %+v (%v)", m, ok) } } func TestDirectoryGoneFromTheMergeHasNoTree(t *testing.T) { // 1239/AC6 ix, mods := modulesIndexer(t, rec(t, "build-1", "a", "novox/mesh-delivery", "modules/a", "main", "built", "2026-10-19T09:00:00Z", aInputs)) _ = ix.Apply(context.Background(), mergedMsg("novox/mesh-delivery", map[string]string{"modules/other": "t"})) if m, ok := moduleOf(t, mods, "a"); !ok || m.FP != "" || m.None != "no tree" { t.Errorf("module = %+v (%v)", m, ok) } } func TestAModuleWithNoSuccessfulTrunkBuildHasNone(t *testing.T) { // 1239/AC7 ix, mods := modulesIndexer(t, rec(t, "build-1", "failed-only", "novox/mesh-delivery", "modules/f", "main", "failed", "2026-10-19T09:00:00Z", aInputs), rec(t, "build-2", "branch-only", "novox/mesh-delivery", "modules/g", "feat/x", "built", "2026-10-19T09:00:00Z", aInputs), rec(t, "build-3", "no-inputs", "novox/mesh-delivery", "modules/h", "main", "built", "2026-10-19T09:00:00Z", ""), ) _ = ix.Apply(context.Background(), mergedMsg("novox/mesh-delivery", map[string]string{"modules/f": "t", "modules/g": "t", "modules/h": "t"})) for _, name := range []string{"failed-only", "branch-only", "no-inputs"} { if m, ok := moduleOf(t, mods, name); !ok || m.FP != "" || !strings.Contains(m.None, "no successful trunk build") { t.Errorf("%s: module = %+v (%v)", name, m, ok) } } } func TestTheNewestSuccessfulTrunkBuildCounts(t *testing.T) { // 1239 (the newest key is only a hint) older := strings.Replace(aInputs, `"Bases":["registry.example/base@sha256:aaa"]`, `"Bases":["registry.example/base@sha256:old"]`, 1) ix, mods := modulesIndexer(t, rec(t, "build-1", "a", "novox/mesh-delivery", "modules/a", "main", "built", "2026-10-19T09:00:00Z", older), rec(t, "build-2", "a", "novox/mesh-delivery", "modules/a", "main", "built", "2026-10-19T10:00:00Z", aInputs), rec(t, "build-3", "a", "novox/mesh-delivery", "modules/a", "main", "failed", "2026-10-19T11:00:00Z", older), rec(t, "build-4", "a", "novox/mesh-delivery", "modules/a", "feat/x", "built", "2026-10-19T12:00:00Z", older), ) _ = ix.Apply(context.Background(), mergedMsg("novox/mesh-delivery", map[string]string{"modules/a": "newtree"})) want, _ := delivery.SourceFingerprint(delivery.SourceInputs{Module: "a", Tree: "newtree", Bases: []string{"registry.example/base@sha256:aaa"}, Toolchains: map[string]string{"code": "go sha256:ccc 0123456789abcdef"}}) if m, _ := moduleOf(t, mods, "a"); m.FP != want { t.Errorf("fp = %s, want the 10:00 trunk build's %s", m.FP, want) } } func TestModulesOfOtherRepositoriesAreNotWritten(t *testing.T) { // 1239/AC8 ix, mods := modulesIndexer(t, rec(t, "build-1", "a", "novox/mesh-delivery", "modules/a", "main", "built", "2026-10-19T09:00:00Z", aInputs), rec(t, "build-2", "c", "novox/mesh-catalog", "modules/c", "main", "built", "2026-10-19T09:00:00Z", strings.Replace(aInputs, `"Module":"a"`, `"Module":"c"`, 1))) _ = ix.Apply(context.Background(), mergedMsg("novox/mesh-delivery", map[string]string{"modules/a": "t", "modules/c": "t"})) if _, ok := mods.vals["c"]; ok { t.Error("the key of a module of another repository was written") } if _, ok := mods.vals["a"]; !ok { t.Error("the key of the merged repository's module was not written") } } func TestAnUnchangedModuleWritesNothing(t *testing.T) { // 1239 (unchanged value writes nothing) ix, mods := modulesIndexer(t, rec(t, "build-1", "a", "novox/mesh-delivery", "modules/a", "main", "built", "2026-10-19T09:00:00Z", aInputs)) m := mergedMsg("novox/mesh-delivery", map[string]string{"modules/a": "newtree"}) _ = ix.Apply(context.Background(), m) rev, writes := mods.revs["a"], mods.writes _ = ix.Apply(context.Background(), m) if mods.revs["a"] != rev || mods.writes != writes { t.Errorf("the same merge wrote again (%d writes, was %d)", mods.writes, writes) } } func TestMergedMessageRemovesThePullFromItsGroupToo(t *testing.T) { // 1238/AC5 with 1239 (both effects of one message) ix, _ := modulesIndexer(t, rec(t, "build-1", "a", "novox/mesh-delivery", "modules/a", "main", "built", "2026-10-19T09:00:00Z", aInputs)) cands := ix.Store.(*memStore) _ = ix.Apply(context.Background(), pull("novox/mesh-delivery", 12, "feat/x", h1, "modules/a")) _ = ix.Apply(context.Background(), mergedMsg("novox/mesh-delivery", map[string]string{"modules/a": "newtree"})) if _, ok := cands.vals["feat/x"]; ok { t.Error("the merged pull request is still a candidate") } }