128 lines
4.6 KiB
Go
128 lines
4.6 KiB
Go
//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)
|
|
}
|
|
}
|