aboutsummaryrefslogtreecommitdiff
path: root/object/store/packed/quarantine_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'object/store/packed/quarantine_test.go')
-rw-r--r--object/store/packed/quarantine_test.go215
1 files changed, 215 insertions, 0 deletions
diff --git a/object/store/packed/quarantine_test.go b/object/store/packed/quarantine_test.go
new file mode 100644
index 00000000..036da535
--- /dev/null
+++ b/object/store/packed/quarantine_test.go
@@ -0,0 +1,215 @@
+package packed_test
+
+import (
+ "bytes"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "codeberg.org/lindenii/furgit/internal/testgit"
+ objectid "codeberg.org/lindenii/furgit/object/id"
+ objectstore "codeberg.org/lindenii/furgit/object/store"
+ "codeberg.org/lindenii/furgit/object/store/packed"
+ objecttype "codeberg.org/lindenii/furgit/object/type"
+)
+
+func fixturePath(t *testing.T, algo objectid.Algorithm, name string) string {
+ t.Helper()
+
+ return filepath.Join("internal", "ingest", "testdata", "fixtures", algo.String(), name)
+}
+
+func fixtureBytes(t *testing.T, algo objectid.Algorithm, name string) []byte {
+ t.Helper()
+
+ path := fixturePath(t, algo, name)
+ dir := filepath.Dir(path)
+ base := filepath.Base(path)
+
+ root, err := os.OpenRoot(dir)
+ if err != nil {
+ t.Fatalf("open fixture root %q: %v", dir, err)
+ }
+
+ defer func() {
+ err := root.Close()
+ if err != nil {
+ t.Fatalf("close fixture root %q: %v", dir, err)
+ }
+ }()
+
+ data, err := root.ReadFile(base)
+ if err != nil {
+ t.Fatalf("read fixture %q: %v", base, err)
+ }
+
+ return data
+}
+
+func fixtureMetadata(t *testing.T, algo objectid.Algorithm) map[string]string {
+ t.Helper()
+
+ data := fixtureBytes(t, algo, "METADATA.txt")
+ out := make(map[string]string)
+
+ for line := range strings.SplitSeq(strings.TrimSpace(string(data)), "\n") {
+ line = strings.TrimSpace(line)
+ if line == "" {
+ continue
+ }
+
+ key, value, ok := strings.Cut(line, "=")
+ if !ok {
+ t.Fatalf("invalid fixture metadata line %q", line)
+ }
+
+ out[strings.TrimSpace(key)] = strings.TrimSpace(value)
+ }
+
+ return out
+}
+
+func fixtureOID(t *testing.T, algo objectid.Algorithm, key string) objectid.ObjectID {
+ t.Helper()
+
+ meta := fixtureMetadata(t, algo)
+
+ hex, ok := meta[key]
+ if !ok {
+ t.Fatalf("missing fixture metadata key %q", key)
+ }
+
+ id, err := objectid.ParseHex(algo, hex)
+ if err != nil {
+ t.Fatalf("parse fixture metadata oid %q: %v", hex, err)
+ }
+
+ return id
+}
+
+func TestPackQuarantinePromotePublishesWrittenObjects(t *testing.T) {
+ t.Parallel()
+
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
+ head := fixtureOID(t, algo, "head")
+ packBytes := fixtureBytes(t, algo, "nonthin.pack")
+
+ repo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
+ packRoot := repo.OpenPackRoot(t)
+
+ store, err := packed.New(packRoot, algo, packed.Options{WriteRev: true})
+ if err != nil {
+ t.Fatalf("packed.New: %v", err)
+ }
+
+ defer func() {
+ err := store.Close()
+ if err != nil {
+ t.Fatalf("store.Close: %v", err)
+ }
+ }()
+
+ quarantiner, ok := any(store).(objectstore.PackQuarantiner)
+ if !ok {
+ t.Fatal("packed store does not implement PackQuarantiner")
+ }
+
+ quarantine, err := quarantiner.BeginPackQuarantine(objectstore.PackQuarantineOptions{})
+ if err != nil {
+ t.Fatalf("BeginPackQuarantine: %v", err)
+ }
+
+ err = quarantine.WritePack(bytes.NewReader(packBytes), objectstore.PackWriteOptions{RequireTrailingEOF: true})
+ if err != nil {
+ t.Fatalf("quarantine.WritePack: %v", err)
+ }
+
+ ty, _, err := quarantine.ReadHeader(head)
+ if err != nil {
+ t.Fatalf("quarantine.ReadHeader: %v", err)
+ }
+
+ if ty != objecttype.TypeCommit {
+ t.Fatalf("quarantine.ReadHeader type = %v, want commit", ty)
+ }
+
+ _, _, err = store.ReadHeader(head)
+ if err == nil {
+ t.Fatal("store.ReadHeader unexpectedly saw quarantined object before promote")
+ }
+
+ err = quarantine.Promote()
+ if err != nil {
+ t.Fatalf("quarantine.Promote: %v", err)
+ }
+
+ err = store.Refresh()
+ if err != nil {
+ t.Fatalf("store.Refresh: %v", err)
+ }
+
+ ty, _, err = store.ReadHeader(head)
+ if err != nil {
+ t.Fatalf("store.ReadHeader after promote: %v", err)
+ }
+
+ if ty != objecttype.TypeCommit {
+ t.Fatalf("store.ReadHeader type = %v, want commit", ty)
+ }
+ })
+}
+
+func TestPackQuarantineDiscardDropsWrittenObjects(t *testing.T) {
+ t.Parallel()
+
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
+ head := fixtureOID(t, algo, "head")
+ packBytes := fixtureBytes(t, algo, "nonthin.pack")
+
+ repo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
+ packRoot := repo.OpenPackRoot(t)
+
+ store, err := packed.New(packRoot, algo, packed.Options{WriteRev: true})
+ if err != nil {
+ t.Fatalf("packed.New: %v", err)
+ }
+
+ defer func() {
+ err := store.Close()
+ if err != nil {
+ t.Fatalf("store.Close: %v", err)
+ }
+ }()
+
+ quarantiner, ok := any(store).(objectstore.PackQuarantiner)
+ if !ok {
+ t.Fatalf("expected objectstore.PackQuarantiner")
+ }
+
+ quarantine, err := quarantiner.BeginPackQuarantine(objectstore.PackQuarantineOptions{})
+ if err != nil {
+ t.Fatalf("BeginPackQuarantine: %v", err)
+ }
+
+ err = quarantine.WritePack(bytes.NewReader(packBytes), objectstore.PackWriteOptions{RequireTrailingEOF: true})
+ if err != nil {
+ t.Fatalf("quarantine.WritePack: %v", err)
+ }
+
+ err = quarantine.Discard()
+ if err != nil {
+ t.Fatalf("quarantine.Discard: %v", err)
+ }
+
+ err = store.Refresh()
+ if err != nil {
+ t.Fatalf("store.Refresh: %v", err)
+ }
+
+ _, _, err = store.ReadHeader(head)
+ if err == nil {
+ t.Fatal("store.ReadHeader unexpectedly saw discarded object")
+ }
+ })
+}