169 lines
6.2 KiB
Go
169 lines
6.2 KiB
Go
package index
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
|
|
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
|
|
)
|
|
|
|
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)
|
|
// DLV-I9 (design 25 § 12.2): the module leaving the trunk is said as an empty manifest-accepted, under an id naming it.
|
|
var said []meshcatalog.ManifestAcceptedEvent
|
|
var ids []string
|
|
ix.SayAccepted = func(_ context.Context, a meshcatalog.ManifestAcceptedEvent, id string) error {
|
|
said, ids = append(said, a), append(ids, id)
|
|
return nil
|
|
}
|
|
defer func() {
|
|
var left []string
|
|
for i, a := range said {
|
|
if a.Digest == "" {
|
|
left = append(left, a.Module)
|
|
// Join item 3: the id is the event's own (acceptedEventID), naming the module and the
|
|
// modules state's revision of the value that left, so two leaves of one module are two events.
|
|
if a.BusState == nil || len(a.BusState) != 0 || a.Revision == 0 || ids[i] != acceptedEventID(a) || !strings.Contains(ids[i], "-left-b-") {
|
|
t.Errorf("the empty manifest-accepted = %+v, id %s", a, ids[i])
|
|
}
|
|
}
|
|
}
|
|
if strings.Join(left, ",") != "b" {
|
|
t.Errorf("said empty for %v, want b alone", left)
|
|
}
|
|
}()
|
|
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))
|
|
}
|
|
}
|
|
_ = meshcatalogclient.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, meshcatalog.ModulesByModuleValue{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")
|
|
}
|
|
}
|