package index import ( "context" "errors" "strings" "testing" "git.novox.be/novox/mesh-sdk/go/delivery" ) func twoModules(t *testing.T) (*Indexer, *memStore) { t.Helper() ix, mods := modulesIndexer(t) if err := ix.Apply(context.Background(), mergedMsg(catalogueRepo, map[string]string{"modules/a": "t", "modules/b": "t"})); err != nil { t.Fatal(err) } for _, k := range []string{"a", "b"} { if _, ok := mods.vals[k]; !ok { t.Fatalf("setup: modules.%s not written", k) } } return ix, mods } func TestAMergeThatRemovesAModuleDeletesItsKey(t *testing.T) { // 1900/AC1 ix, mods := twoModules(t) if err := ix.Apply(context.Background(), mergedMsg(catalogueRepo, map[string]string{"modules/a": "t2"})); err != nil { t.Fatal(err) } if _, ok := mods.vals["b"]; ok { t.Error("modules.b is still held after a merge whose trunk has no module b") } if _, ok := mods.vals["a"]; !ok { t.Error("modules.a, which the trunk still holds, was deleted") } } func TestAMergeLeavesTheModulesOfAnotherRepository(t *testing.T) { // 1900/AC1 (scope) ix, mods := twoModules(t) if err := ix.Apply(context.Background(), mergedMsg("novox/mesh-delivery", map[string]string{"modules/c": "t"})); err != nil { t.Fatal(err) } if _, ok := mods.vals["a"]; !ok { t.Error("a merge of another repository deleted modules.a") } if _, ok := mods.vals["b"]; !ok { t.Error("a merge of another repository deleted modules.b") } } func TestAMergeWithoutManifestsDeletesNothing(t *testing.T) { // 1900/AC1: older writers send none, which does not say the trunk is empty ix, mods := twoModules(t) f := mergedMsg(catalogueRepo, map[string]string{"modules/a": "t2"}) f.Manifests = nil if err := ix.Apply(context.Background(), f); err != nil { t.Fatal(err) } if _, ok := mods.vals["b"]; !ok { t.Error("a merge message with no manifests deleted modules.b") } } func TestAPathWithoutAModuleJSONIsNeverAModule(t *testing.T) { // 1900/AC2: testdata such as fixture-build ix, mods := twoModules(t) f := mergedMsg(catalogueRepo, map[string]string{"modules/a": "t2"}) f.Trees["testdata/fixture-build"] = "t" // a directory in the tree whose module.json the message does not carry if err := ix.Apply(context.Background(), f); err != nil { t.Fatal(err) } if _, ok := mods.vals["fixture-build"]; ok { t.Error("modules.fixture-build written for a path without a module.json") } } func TestStartingDeletesTheKeysTheTrunkDoesNotHold(t *testing.T) { // 1900/AC3 ix, mods := twoModules(t) ctx := context.Background() if err := ix.ApplySource(ctx, 3, sourceOf(catalogueRepo, merge)); err != nil { t.Fatal(err) } var asked string ix.Trunk = func(_ context.Context, repository, sha string) ([]string, error) { asked = repository + "@" + sha return []string{"modules/a"}, nil } ix.Contains = func(context.Context, string, string, string) (bool, error) { return true, nil } if err := ix.Reconcile(ctx); err != nil { t.Fatal(err) } if asked != catalogueRepo+"@"+merge { t.Errorf("the trunk was asked as %q, want the source's head of the repository", asked) } if _, ok := mods.vals["b"]; ok { t.Error("modules.b is still held though the trunk holds no module at modules/b") } if _, ok := mods.vals["a"]; !ok { t.Error("modules.a was deleted") } } func TestStartingDeletesNothingWhenTheTrunkCannotBeRead(t *testing.T) { // 1900/AC3: failure is loud, never a mass delete ix, mods := twoModules(t) ctx := context.Background() if err := ix.ApplySource(ctx, 3, sourceOf(catalogueRepo, merge)); err != nil { t.Fatal(err) } for name, trunk := range map[string]func(context.Context, string, string) ([]string, error){ "an error": func(context.Context, string, string) ([]string, error) { return nil, errors.New("forge down") }, "no module at all": func(context.Context, string, string) ([]string, error) { return nil, nil }, } { ix.Trunk = trunk _ = ix.Reconcile(ctx) if len(mods.vals) != 2 { t.Errorf("%s: %d keys held, want 2 untouched", name, len(mods.vals)) } } _ = delivery.RootPath } func TestAKeyWrittenAheadOfTheSourceHeadStaysAndOneWithoutAPathIsSkipped(t *testing.T) { // 1900/AC3 (review): the source lags a merge already applied ix, mods := twoModules(t) // written at the merge commit C ctx := context.Background() const older = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" if err := ix.ApplySource(ctx, 3, sourceOf(catalogueRepo, older)); err != nil { t.Fatal(err) } mods.put("nopath", mustEncode(t, delivery.Module{Repository: catalogueRepo, Commit: older, None2: "x", Manifest: strings.Repeat("a", 64)})) ix.Trunk = func(context.Context, string, string) ([]string, error) { return []string{"modules/a"}, nil } ix.Contains = func(_ context.Context, _, commit, in string) (bool, error) { return commit == in, nil } // C is not in B if err := ix.Reconcile(ctx); err != nil { t.Fatal(err) } for _, k := range []string{"a", "b", "nopath"} { if _, ok := mods.vals[k]; !ok { t.Errorf("modules.%s was deleted", k) } } ix.Contains = func(context.Context, string, string, string) (bool, error) { return false, errors.New("forge down") } _ = ix.Reconcile(ctx) if _, ok := mods.vals["b"]; !ok { t.Error("modules.b deleted though whether its commit is in the head cannot be read") } }