package genselect import ( "context" "errors" "fmt" "testing" "github.com/carlosframework/platform/internal/hostclaim" ) // GenerationMax is the loss boundary of one generation in a restore // (platform-1e's ruling on #236): the highest TXID of that exact generation // among the restore plan's selected files. It never lists the bucket; a // generation the plan does not contain is 0, every write in it lost. func TestGenerationMax(t *testing.T) { ctx := context.Background() t.Run("the plan's own generation is Select's maximum", func(t *testing.T) { f := newFixture(t) f.file("g0", 1, 1, 14) f.file("g0", 0, 12, 14) plan, err := Select(ctx, f.store, f.req) if err != nil { t.Fatal(err) } got, err := GenerationMax(plan, f.req.Holder.Location, "g0") if err != nil || got != 14 || got != plan.MaxTXID { t.Fatalf("GenerationMax %d %v, Select %d", got, err, plan.MaxTXID) } if got, err := GenerationMax(plan, f.req.Holder.Location+"/", "g0"); err != nil || got != 14 { t.Fatalf("a trailing slash: %d %v", got, err) } }) t.Run("a generation the plan does not contain is 0", func(t *testing.T) { f := newFixture(t) f.file("g1", 0, 1, 99) // in the bucket, not in the restore plan, err := Select(ctx, f.store, f.req) if err != nil { t.Fatal(err) } for _, gen := range []string{"g1", "t2", hostclaim.RootGeneration, ""} { if got, err := GenerationMax(plan, f.req.Holder.Location, gen); err != nil || got != 0 { t.Fatalf("%q: %d %v", gen, got, err) } } }) t.Run("never a fresh listing", func(t *testing.T) { f := newFixture(t) plan, err := Select(ctx, f.store, f.req) if err != nil { t.Fatal(err) } f.file("g0", 0, 12, 40) // written after the selection if got, err := GenerationMax(plan, f.req.Holder.Location, "g0"); err != nil || got != 11 { t.Fatalf("%d %v", got, err) } }) t.Run("the legacy root spelling", func(t *testing.T) { f := newFixture(t) f.origin() f.file(hostclaim.RootGeneration, 9, 1, 7) f.file(hostclaim.RootGeneration, 0, 8, 8) plan, err := Select(ctx, f.store, f.req) if err != nil { t.Fatal(err) } if plan.Generation != hostclaim.RootGeneration { t.Fatalf("selected %q", plan.Generation) } for _, gen := range []string{hostclaim.RootGeneration, ""} { if got, err := GenerationMax(plan, f.req.Holder.Location, gen); err != nil || got != 8 { t.Fatalf("%q: %d %v", gen, got, err) } } if got, err := GenerationMax(plan, f.req.Holder.Location, "g0"); err != nil || got != 0 { t.Fatalf("a named generation under the origin: %d %v", got, err) } }) t.Run("a selected historical pinned version counts", func(t *testing.T) { f := newFixture(t) for _, key := range []string{f.pin.Key, f.req.Holder.Location + "/g-g0/0000/" + ltxName(11, 11)} { f.b.BareDelete("data", key) } plan, err := Select(ctx, f.store, f.req) if err != nil { t.Fatal(err) } if plan.Root == nil || plan.Root.VersionID != f.pin.VersionID || plan.MaxTXID != 11 { t.Fatalf("the selection is not the pinned historical root: %+v", plan) } if got, err := GenerationMax(plan, f.req.Holder.Location, "g0"); err != nil || got != 11 { t.Fatalf("%d %v", got, err) } }) t.Run("refusals", func(t *testing.T) { f := newFixture(t) plan, err := Select(ctx, f.store, f.req) if err != nil { t.Fatal(err) } if _, err := GenerationMax(nil, f.req.Holder.Location, "g0"); !errors.Is(err, ErrInput) { t.Fatalf("no plan: %v", err) } for name, in := range map[string][2]string{ "no location": {"", "g0"}, "a climbing path": {"instances/../h", "g0"}, "a nested gen": {f.req.Holder.Location, "g0/x"}, "a dot generation": {f.req.Holder.Location, ".."}, "an absolute path": {"/instances/h", "g0"}, "an empty element": {"instances//h", "g0"}, "a trailing double": {"instances/h//", "g0"}, } { if _, err := GenerationMax(plan, in[0], in[1]); !errors.Is(err, ErrInput) { t.Fatalf("%s: %v", name, err) } } lying := *plan lying.Files = append([]File(nil), plan.Files...) lying.Files[0].MaxTXID = 99 if _, err := GenerationMax(&lying, f.req.Holder.Location, "g0"); !errors.Is(err, ErrInput) { t.Fatalf("a file whose coordinates are not its key's: %v", err) } }) } func ltxName(min, max uint64) string { return fmt.Sprintf("%016x-%016x.ltx", min, max) }