Artifact 2e056dcb6d0d6be47f752475594c7826a581accdfe92fae6548ac9341dc3d5b7:
- File
repo_compact_test.go
— part of check-in
[774f579bfa]
at
2026-10-21 11:44:51
on branch feat/submanifest-reader
— feat(sparse): expose narrow-then-compact as Repo.CompactStore and repo cone compact
The public entry point refuses a dense store, as zit's pack --drop-elided does, and drops exactly the blobs that only out-of-cone F cards name. The test computes that set from the held artifacts, then verifies the store and recovers every dropped payload by widening through SetConeContext. (user: [email protected], size: 6470) [annotate] [blame] [check-ins using] [more...]
package libfossil import ( "context" "errors" "maps" "os" "path/filepath" "slices" "testing" "github.com/danmestas/go-libfossil/internal/content" "github.com/danmestas/go-libfossil/internal/deck" "github.com/danmestas/go-libfossil/internal/fsltype" "github.com/danmestas/go-libfossil/internal/manifest" ) type compactFixture struct { source, target *Repo parent int64 peer Transport files map[string]string } // The tree has out-of-cone leaves under both a spine node and an out-of-cone // interior, and one blob named both inside and outside the cone. func newCompactFixture(t *testing.T) compactFixture { t.Helper() source, err := Create(filepath.Join(t.TempDir(), "source.fossil"), CreateOpts{User: "test"}) if err != nil { t.Fatal(err) } t.Cleanup(func() { source.Close() }) for key, value := range map[string]string{ manifest.SettingEnableSubmanifests: "on", manifest.SettingSubmanifestK: "1", } { if err := source.inner.SetConfig(key, value); err != nil { t.Fatal(err) } } f := compactFixture{source: source, files: map[string]string{ "src/a": "inside\n", "src/shared": "shared\n", "zzz/shared": "shared\n", "zzz/b": "outside\n", "zzz/deep/c": "deep\n", "yyy/d": "y\n", "yyy/e/f": "yf\n", }} var files []FileToCommit for _, name := range slices.Sorted(maps.Keys(f.files)) { files = append(files, FileToCommit{Name: name, Content: []byte(f.files[name])}) } rid, _, err := source.Commit(CommitOpts{Files: files, User: "test"}) if err != nil { t.Fatal(err) } f.peer = TransportFunc(func(ctx context.Context, payload []byte) ([]byte, error) { return source.HandleSync(ctx, payload) }) f.target, _, err = Clone(context.Background(), filepath.Join(t.TempDir(), "target.fossil"), f.peer, CloneOpts{PullPhantoms: true}) if err != nil { t.Fatal(err) } t.Cleanup(func() { f.target.Close() }) f.parent = rid return f } // compactHeld lists every artifact holding as uuid:size, in rid order. func compactHeld(t *testing.T, r *Repo) []string { t.Helper() rows, err := r.DB().Query("SELECT uuid, size FROM blob ORDER BY rid") if err != nil { t.Fatal(err) } defer rows.Close() var out []string for rows.Next() { var uuid string var size int64 if err := rows.Scan(&uuid, &size); err != nil { t.Fatal(err) } if size >= 0 { out = append(out, uuid) } } if err := rows.Err(); err != nil { t.Fatal(err) } return out } // compactZitDrops is zit's narrow-then-compact rule computed from the held // artifacts, not from the compactor: every structural artifact stays, and a // blob goes only when every F card naming it is out of the cone. func compactZitDrops(t *testing.T, r *Repo, c *Cone) map[string]bool { t.Helper() inside, outside := map[string]bool{}, map[string]bool{} for _, uuid := range compactHeld(t, r) { rid, ok := content.AvailableByUUID(r.DB(), uuid) if !ok { t.Fatalf("held artifact %s is unavailable", uuid) } data, err := content.Expand(r.DB(), rid) if err != nil { t.Fatal(err) } d, err := deck.Parse(data) if err != nil { continue } for _, card := range d.F { if c.Covers(card.Name) { inside[card.UUID] = true } else if card.UUID != "" { outside[card.UUID] = true } } } for uuid := range inside { delete(outside, uuid) } return outside } func TestRepoCompactStore(t *testing.T) { f := newCompactFixture(t) ctx := context.Background() dense := compactHeld(t, f.target) if _, err := f.target.CompactStore(ctx); !errors.Is(err, ErrNoStoreCone) { t.Fatalf("dense compaction = %v, want ErrNoStoreCone", err) } narrow, err := NewCone([]string{"src"}, nil) if err != nil { t.Fatal(err) } if err := f.target.SetStoreCone(narrow); err != nil { t.Fatal(err) } if got := compactHeld(t, f.target); !slices.Equal(got, dense) { t.Fatal("narrowing changed holdings") } drops := compactZitDrops(t, f.target, narrow) if len(drops) != 4 { t.Fatalf("fixture has %d out-of-cone-only blobs, want 4", len(drops)) } n, err := f.target.CompactStore(ctx) if err != nil { t.Fatal(err) } var want []string for _, uuid := range dense { if !drops[uuid] { want = append(want, uuid) } } if got := compactHeld(t, f.target); n != len(drops) || !slices.Equal(got, want) { t.Fatalf("compacted %d; held %v, want %v", n, got, want) } compactElided(t, f.target, drops) if err := f.target.Verify(); err != nil { t.Fatalf("verify after compaction: %v", err) } if n, err := f.target.CompactStore(ctx); err != nil || n != 0 { t.Fatalf("repeat compaction = %d, %v", n, err) } compactWiden(t, f, narrow) } func compactElided(t *testing.T, r *Repo, drops map[string]bool) { t.Helper() a, err := content.LoadAbsences(r.DB()) if err != nil { t.Fatal(err) } if a.Count(content.Elided) != len(drops) || a.Count(content.Transit) != 0 { t.Fatalf("absences elided=%d transit=%d, want %d and 0", a.Count(content.Elided), a.Count(content.Transit), len(drops)) } for uuid := range drops { rid, ok := content.AvailableByUUID(r.DB(), uuid) if ok { t.Fatalf("dropped artifact %s is still available", uuid) } var phantom fsltype.FslID if err := r.DB().QueryRow("SELECT b.rid FROM blob b JOIN phantom p ON p.rid=b.rid WHERE b.uuid=?", uuid).Scan(&phantom); err != nil || a.Class(phantom) != content.Elided { t.Fatalf("dropped artifact %s (%d) is not an elided phantom: %v", uuid, rid, err) } } } // Widening through the public checkout recovers every compacted payload. func compactWiden(t *testing.T, f compactFixture, narrow *Cone) { t.Helper() co, err := f.target.CreateCheckout(t.TempDir(), CheckoutCreateOpts{Cone: narrow}) if err != nil { t.Fatal(err) } t.Cleanup(func() { co.Close() }) if err := co.Extract(f.parent, ExtractOpts{}); err != nil { t.Fatal(err) } if _, err := co.SetConeContext(context.Background(), nil, RequiredContentOpts{Peer: f.peer}); err != nil { t.Fatal(err) } for name, text := range f.files { data, err := os.ReadFile(filepath.Join(co.Dir(), name)) if err != nil || string(data) != text { t.Fatalf("widened %s = %q, %v", name, data, err) } } if err := f.target.Verify(); err != nil { t.Fatalf("verify after widening: %v", err) } a, err := content.LoadAbsences(f.target.DB()) if err != nil { t.Fatal(err) } if a.Count(content.Elided) != 0 || a.Count(content.Transit) != 0 { t.Fatalf("after widening elided=%d transit=%d", a.Count(content.Elided), a.Count(content.Transit)) } }