//go:build integration package main import ( "fmt" "slices" "strings" "testing" "time" "git.novox.be/novox/mesh-sdk/go/catalogue" "github.com/novox/mesh-controller/internal/inventory" ) func TestRecordingACopyTakesWhatTheStoreHoldsAndDeletesNothing(t *testing.T) { inv := inventory.ForTest(t) held := ref("route-proxy", "on-go_base", 1) absent := ref("route-proxy", "on-go_base", 2) known := ref("nats", "on-nats_base", 3) notACopy := ref("route-proxy", "server", 4) if err := inv.RecordMirrored(t.Context(), "b1", "", []string{known}); err != nil { t.Fatal(err) } store, asked := heldStore(t, fmt.Sprintf("%064x", 1)) given := []string{held, absent, known, notACopy} mirrored, err := inv.Mirrored(t.Context()) if err != nil { t.Fatal(err) } // A dry run says, and records nothing. dry, err := recordMirrors(t.Context(), inv, store, given, mirrored, "", false) if err != nil { t.Fatal(err) } if !dry.DryRun || !slices.Equal(dry.Recorded, []string{held}) || !slices.Equal(dry.AlreadyRecorded, []string{known}) || len(dry.Refused) != 2 || dry.Refused[absent] == "" || dry.Refused[notACopy] == "" { t.Fatalf("a dry run answered %+v", dry) } if !strings.Contains(dry.Then, "would become eligible") || !strings.Contains(dry.Then, "next sweep") { t.Fatalf("a dry run did not say what recording does: %q", dry.Then) } if again, _ := inv.Mirrored(t.Context()); again[held] { t.Fatal("a dry run recorded a copy") } // A real one records what the store holds, with the person's why, and the sweep may now decide. real, err := recordMirrors(t.Context(), inv, store, given, mirrored, "copied before copies were recorded", true) if err != nil { t.Fatal(err) } if !slices.Equal(real.Recorded, []string{held}) { t.Fatalf("recorded %v", real.Recorded) } left, err := inv.ToCollect(t.Context()) if err != nil { t.Fatal(err) } if !slices.Contains(left, held) { t.Fatalf("a recorded copy no kept build stood on is not offered to collect: %v", left) } for _, r := range *asked { if !strings.HasPrefix(r, "HEAD ") { t.Fatalf("recording a copy asked the store %s", r) } } } func TestAConfirmedCollectLetsAnIndexGoWithItsOwnPlatformsOnly(t *testing.T) { inv := inventory.ForTest(t) _, eligible, reg := twoCopies(t, inv) store := reg.serve(t) references, err := inv.ToCollect(t.Context()) if err != nil { t.Fatal(err) } if !slices.Equal(references, []string{eligible}) { t.Fatalf("offered %v, want the failed build's copy", references) } a := runCollect(t.Context(), inv, store, references, nil, true, sweepBounds{most: 10, budget: 5 * time.Second, platforms: true}) if !slices.Equal(a.LetGo, []string{eligible}) || a.Stopped != "" { t.Fatalf("let go %v, stopped %q", a.LetGo, a.Stopped) } if !slices.Equal(reg.deleted, []string{copyDigest(13), copyDigest(2)}) { t.Fatalf("deleted %v; want its own platform, then the index", reg.deleted) } } func TestTheSweepAfterABuildLeavesAnIndexForAConfirmedCollect(t *testing.T) { inv := inventory.ForTest(t) _, eligible, reg := twoCopies(t, inv) reg.held[copyDigest(5)] = true image := catalogue.ArtifactStoreScheme + "upstream/docker.io/library/golang@" + copyDigest(5) store := reg.serve(t) r := sweep(t.Context(), inv, store, []string{eligible, image}, nil, afterBuild) if r.Indexes != 1 || !slices.Equal(r.LetGo, []string{image}) || !slices.Equal(reg.deleted, []string{copyDigest(5)}) { t.Fatalf("after a build: %d indexes left, let go %v, deleted %v; want the index left and the image gone", r.Indexes, r.LetGo, reg.deleted) } left, err := inv.ToCollect(t.Context()) if err != nil { t.Fatal(err) } if !slices.Equal(left, []string{eligible}) { t.Fatalf("after the sweep the records offer %v; want the index still eligible", left) } a := runCollect(t.Context(), inv, store, left, nil, true, sweepBounds{most: 10, budget: 5 * time.Second, platforms: true}) if !slices.Equal(a.LetGo, []string{eligible}) || !slices.Equal(reg.deleted, []string{copyDigest(5), copyDigest(13), copyDigest(2)}) { t.Fatalf("a confirmed collect let go %v, deleted %v; want the index with its own platform", a.LetGo, reg.deleted) } } func TestASpareListThatCannotBeReadStopsTheSweepBeforeAnyDelete(t *testing.T) { inv := inventory.ForTest(t) _, eligible, reg := twoCopies(t, inv) reg.fail[copyDigest(1)] = true store := reg.serve(t) a := runCollect(t.Context(), inv, store, []string{eligible}, nil, true, sweepBounds{most: 10, budget: 5 * time.Second, platforms: true}) if len(a.LetGo) != 0 || len(reg.deleted) != 0 || !strings.Contains(a.Stopped, "nothing was let go") { t.Fatalf("let go %v, deleted %v, stopped %q", a.LetGo, reg.deleted, a.Stopped) } }