package index import ( "context" "strings" "testing" "git.novox.be/novox/mesh-sdk/go/delivery" ) const ( trunkNow = "5555555555555555555555555555555555555555" trunkOld = "4444444444444444444444444444444444444444" mergeSha = "6666666666666666666666666666666666666666" ) // A trunk of the catalogue with avahi (and the builder, a base and others) merged, the toolchain agreed, and the gate's source // naming the catalogue's trunk head: avahi has a fingerprint of the new form at tree-avahi. func previewBed(t *testing.T, report bool) (*Indexer, *memStore, *memStore) { t.Helper() ix, mods := newFormIndexer(t, builderModules(t)...) if report { reportToolchain(t, ix, "mesh-build-go", "novox", "go", "go1.27.1") } if err := ix.ApplySource(context.Background(), 5, delivery.Source{Group: "g", Heads: map[string]string{catalogueRepo: trunkNow}}); err != nil { t.Fatal(err) } return ix, mods, ix.Store.(*memStore) } // avahiOnTools is avahi's manifest as builderModules has it: standing on mesh-tools. func avahiOnTools(t *testing.T) string { return standingOn(t, catalogueManifest(t, "avahi"), "mesh-tools") } // avahiPull is the pull message of the catalogue's pull request 301 touching avahi, with its manifest. func avahiPull(t *testing.T, head string) delivery.Forge { t.Helper() f := manifestPull(t, 301, head, avahiOnTools(t)) f.Manifests = map[string]string{"modules/avahi": avahiOnTools(t)} return f } func avahiPreview(t *testing.T, trunk, head string) delivery.Preview { return delivery.Preview{Repository: catalogueRepo, Head: head, Trunk: trunk, Merge: mergeSha, Modules: map[string]delivery.PreviewModule{ "modules/avahi": {Tree: "tree-avahi", Manifest: delivery.ManifestDigest([]byte(avahiOnTools(t))), Locks: map[string]string{"go.sum": avahiGoSum}}}} } func applyPreview(t *testing.T, ix *Indexer, pv delivery.Preview) { t.Helper() key, err := delivery.PreviewKey(pv.Repository, pv.Head, pv.Trunk) if err != nil { t.Fatal(err) } data, err := delivery.EncodePreview(pv) if err != nil { t.Fatal(err) } if err := ix.ApplyPreview(context.Background(), key, data, false); err != nil { t.Fatal(err) } } func fpOf(t *testing.T, st *memStore) delivery.CandidateModule { t.Helper() c := candidateOf(t, st) if len(c.Pulls) != 1 { t.Fatalf("candidate = %+v", c) } return c.Pulls[0].Fps["avahi"] } func TestACandidatesModuleGetsTheFingerprintATrunkModuleOfTheSameTreeHas(t *testing.T) { // 1445/AC2 ix, mods, cands := previewBed(t, true) trunk, _ := moduleOf(t, mods, "avahi") if trunk.FP2 == "" { t.Fatalf("the trunk's avahi has no fingerprint of the new form: %+v", trunk) } if err := ix.Apply(context.Background(), avahiPull(t, h1)); err != nil { t.Fatal(err) } if got := fpOf(t, cands); got.None2 != NoneNoPreview { t.Fatalf("before the preview: %+v", got) } applyPreview(t, ix, avahiPreview(t, trunkNow, h1)) got := fpOf(t, cands) if got.FP2 != trunk.FP2 || got.None2 != "" || got.Manifest != delivery.ManifestDigest([]byte(avahiOnTools(t))) { t.Fatalf("candidate avahi = %+v, want the trunk's fingerprint %s", got, trunk.FP2) } if c := candidateOf(t, cands); c.FPSet != delivery.FingerprintSet(c.Pulls) || c.FPSet == "" { t.Errorf("fpset = %q", c.FPSet) } // Another tree gives another fingerprint. pv := avahiPreview(t, trunkNow, h1) m := pv.Modules["modules/avahi"] m.Tree = "tree-changed" pv.Modules["modules/avahi"] = m pv.Merge = "7777777777777777777777777777777777777777" applyPreview(t, ix, pv) if other := fpOf(t, cands); other.FP2 == "" || other.FP2 == trunk.FP2 { t.Errorf("a changed tree: %+v", other) } } func TestAnUnknownToolchainLeavesTheCandidatesModuleNamingIt(t *testing.T) { // 1445/AC2 ix, _, cands := previewBed(t, false) if err := ix.Apply(context.Background(), avahiPull(t, h1)); err != nil { t.Fatal(err) } applyPreview(t, ix, avahiPreview(t, trunkNow, h1)) if got := fpOf(t, cands); got.FP2 != "" || !strings.Contains(got.None2, delivery.ToolchainNoReport) { t.Errorf("candidate avahi = %+v, want none2 naming %q", got, delivery.ToolchainNoReport) } } func TestAPreviewMadeOntoAnOldTrunkIsOutOfDate(t *testing.T) { // 1445/AC3 ix, _, cands := previewBed(t, true) if err := ix.Apply(context.Background(), avahiPull(t, h1)); err != nil { t.Fatal(err) } applyPreview(t, ix, avahiPreview(t, trunkOld, h1)) if got := fpOf(t, cands); got.FP2 != "" || got.None2 != NonePreviewStale { t.Errorf("candidate avahi = %+v, want it out of date", got) } // A preview onto the trunk head now makes it good again, and the trunk moving again makes it stale. applyPreview(t, ix, avahiPreview(t, trunkNow, h1)) if got := fpOf(t, cands); got.FP2 == "" { t.Fatalf("after the right preview: %+v", got) } if err := ix.ApplySource(context.Background(), 6, delivery.Source{Group: "g", Heads: map[string]string{catalogueRepo: "8888888888888888888888888888888888888888"}}); err != nil { t.Fatal(err) } if got := fpOf(t, cands); got.None2 != NonePreviewStale { t.Errorf("after the trunk moved: %+v", got) } } func TestAPreviewHeardBeforeItsPullRequestGivesTheFingerprintWhenTheCandidateIsWritten(t *testing.T) { // 1445/AC2 ix, mods, cands := previewBed(t, true) applyPreview(t, ix, avahiPreview(t, trunkNow, h1)) if err := ix.Apply(context.Background(), avahiPull(t, h1)); err != nil { t.Fatal(err) } trunk, _ := moduleOf(t, mods, "avahi") if got := fpOf(t, cands); got.FP2 != trunk.FP2 { t.Errorf("candidate avahi = %+v, want %s", got, trunk.FP2) } // The same pull request at a new head has no preview of it yet. if err := ix.Apply(context.Background(), avahiPull(t, h2)); err != nil { t.Fatal(err) } if got := fpOf(t, cands); got.FP2 != "" || got.None2 != NoneNoPreview { t.Errorf("a new head: %+v, want no preview", got) } } func TestAPreviewNamingNoManifestOfTheModulesDirectoryGivesAReason(t *testing.T) { ix, _, cands := previewBed(t, true) if err := ix.Apply(context.Background(), avahiPull(t, h1)); err != nil { t.Fatal(err) } pv := avahiPreview(t, trunkNow, h1) pv.Modules = map[string]delivery.PreviewModule{"modules/other": pv.Modules["modules/avahi"]} applyPreview(t, ix, pv) if got := fpOf(t, cands); got.None2 != NonePreviewNoModule || !strings.Contains(NonePreviewNoModule, "manifest") { t.Errorf("candidate avahi = %+v", got) } } func TestTheSamePreviewAppliedAgainWritesNothing(t *testing.T) { ix, _, cands := previewBed(t, true) if err := ix.Apply(context.Background(), avahiPull(t, h1)); err != nil { t.Fatal(err) } applyPreview(t, ix, avahiPreview(t, trunkNow, h1)) w := cands.writes applyPreview(t, ix, avahiPreview(t, trunkNow, h1)) if cands.writes != w { t.Errorf("candidates written %d times for the same preview, want none", cands.writes-w) } } func TestAPreviewOfConflictsGivesTheCandidatesModuleTheReasonItDoesNotMerge(t *testing.T) { // 1445/AC5 ix, _, cands := previewBed(t, true) if err := ix.Apply(context.Background(), avahiPull(t, h1)); err != nil { t.Fatal(err) } applyPreview(t, ix, delivery.Preview{Repository: catalogueRepo, Head: h1, Trunk: trunkNow, Conflicts: []string{"modules/avahi/main.go", "go.mod"}}) got := fpOf(t, cands) if got.FP2 != "" || !strings.HasPrefix(got.None2, "does not merge") || !strings.Contains(got.None2, "go.mod, modules/avahi/main.go") && !strings.Contains(got.None2, "modules/avahi/main.go, go.mod") { t.Errorf("candidate avahi = %+v, want it not merging and naming the files", got) } }