aboutsummaryrefslogtreecommitdiff
path: root/ref/store/memory/store_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'ref/store/memory/store_test.go')
-rw-r--r--ref/store/memory/store_test.go282
1 files changed, 282 insertions, 0 deletions
diff --git a/ref/store/memory/store_test.go b/ref/store/memory/store_test.go
new file mode 100644
index 00000000..d8735805
--- /dev/null
+++ b/ref/store/memory/store_test.go
@@ -0,0 +1,282 @@
+package memory_test
+
+import (
+ "errors"
+ "testing"
+
+ "codeberg.org/lindenii/furgit/internal/testgit"
+ objectid "codeberg.org/lindenii/furgit/object/id"
+ "codeberg.org/lindenii/furgit/ref"
+ refstore "codeberg.org/lindenii/furgit/ref/store"
+ "codeberg.org/lindenii/furgit/ref/store/memory"
+)
+
+// Unlike the public ResolveToDetached,
+// this one does not resolve symbolic refs.
+func resolveDetached(t *testing.T, store *memory.Store, name string) ref.Detached {
+ t.Helper()
+
+ resolved, err := store.Resolve(name)
+ if err != nil {
+ t.Fatalf("Resolve(%q): %v", name, err)
+ }
+
+ detached, ok := resolved.(ref.Detached)
+ if !ok {
+ t.Fatalf("Resolve(%q) = %T, want ref.Detached", name, resolved)
+ }
+
+ return detached
+}
+
+func TestReadListAndResolveSymbolic(t *testing.T) {
+ t.Parallel()
+
+ //nolint:thelper
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
+ t.Parallel()
+
+ store, err := memory.New(algo)
+ if err != nil {
+ t.Fatalf("memory.New: %v", err)
+ }
+
+ mainID := algo.Sum([]byte("main"))
+ tx, err := store.BeginTransaction()
+ if err != nil {
+ t.Fatalf("BeginTransaction: %v", err)
+ }
+
+ err = tx.Create("refs/heads/main", mainID)
+ if err != nil {
+ t.Fatalf("Create(main): %v", err)
+ }
+
+ err = tx.CreateSymbolic("HEAD", "refs/heads/main")
+ if err != nil {
+ t.Fatalf("CreateSymbolic(HEAD): %v", err)
+ }
+
+ err = tx.Commit()
+ if err != nil {
+ t.Fatalf("Commit seed refs: %v", err)
+ }
+
+ head, err := store.Resolve("HEAD")
+ if err != nil {
+ t.Fatalf("Resolve(HEAD): %v", err)
+ }
+
+ symbolic, ok := head.(ref.Symbolic)
+ if !ok {
+ t.Fatalf("Resolve(HEAD) = %T, want ref.Symbolic", head)
+ }
+
+ if symbolic.Target != "refs/heads/main" {
+ t.Fatalf("HEAD target = %q, want refs/heads/main", symbolic.Target)
+ }
+
+ detached, err := store.ResolveToDetached("HEAD")
+ if err != nil {
+ t.Fatalf("ResolveToDetached(HEAD): %v", err)
+ }
+
+ if detached.ID != mainID {
+ t.Fatalf("ResolveToDetached(HEAD) ID = %v, want %v", detached.ID, mainID)
+ }
+
+ listed, err := store.List("")
+ if err != nil {
+ t.Fatalf("List: %v", err)
+ }
+
+ if len(listed) != 2 || listed[0].Name() != "HEAD" || listed[1].Name() != "refs/heads/main" {
+ t.Fatalf("List names = %v, want [HEAD refs/heads/main]", listed)
+ }
+ })
+}
+
+func TestTransactionRejectLeavesStoreUnchanged(t *testing.T) {
+ t.Parallel()
+
+ //nolint:thelper
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
+ t.Parallel()
+
+ store, err := memory.New(algo)
+ if err != nil {
+ t.Fatalf("memory.New: %v", err)
+ }
+
+ mainID := algo.Sum([]byte("main"))
+ devID := algo.Sum([]byte("dev"))
+ nextID := algo.Sum([]byte("next"))
+ wrongOld := algo.Sum([]byte("wrong"))
+
+ tx, err := store.BeginTransaction()
+ if err != nil {
+ t.Fatalf("BeginTransaction: %v", err)
+ }
+
+ err = tx.Create("refs/heads/main", mainID)
+ if err != nil {
+ t.Fatalf("Create(main): %v", err)
+ }
+
+ err = tx.Create("refs/heads/dev", devID)
+ if err != nil {
+ t.Fatalf("Create(dev): %v", err)
+ }
+
+ err = tx.Commit()
+ if err != nil {
+ t.Fatalf("Commit seed refs: %v", err)
+ }
+
+ tx, err = store.BeginTransaction()
+ if err != nil {
+ t.Fatalf("BeginTransaction: %v", err)
+ }
+
+ err = tx.Update("refs/heads/main", nextID, mainID)
+ if err != nil {
+ t.Fatalf("Update(main): %v", err)
+ }
+
+ err = tx.Update("refs/heads/dev", nextID, wrongOld)
+ if err != nil {
+ t.Fatalf("Update(dev): %v", err)
+ }
+
+ err = tx.Commit()
+ if err == nil {
+ t.Fatalf("Commit succeeded, want incorrect old value error")
+ }
+
+ var oldValueErr *refstore.IncorrectOldValueError
+ if !errors.As(err, &oldValueErr) {
+ t.Fatalf("Commit error = %T %v, want IncorrectOldValueError", err, err)
+ }
+
+ if got := resolveDetached(t, store, "refs/heads/main").ID; got != mainID {
+ t.Fatalf("main after rejected transaction = %v, want %v", got, mainID)
+ }
+
+ if got := resolveDetached(t, store, "refs/heads/dev").ID; got != devID {
+ t.Fatalf("dev after rejected transaction = %v, want %v", got, devID)
+ }
+ })
+}
+
+func TestBatchRejectsDuplicateResolvedTargetAndAppliesRemainder(t *testing.T) {
+ t.Parallel()
+
+ //nolint:thelper
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
+ t.Parallel()
+
+ store, err := memory.New(algo)
+ if err != nil {
+ t.Fatalf("memory.New: %v", err)
+ }
+
+ mainID := algo.Sum([]byte("main"))
+ devID := algo.Sum([]byte("dev"))
+ nextMainID := algo.Sum([]byte("next-main"))
+ nextDevID := algo.Sum([]byte("next-dev"))
+ aliasID := algo.Sum([]byte("alias"))
+
+ tx, err := store.BeginTransaction()
+ if err != nil {
+ t.Fatalf("BeginTransaction: %v", err)
+ }
+
+ err = tx.Create("refs/heads/main", mainID)
+ if err != nil {
+ t.Fatalf("Create(main): %v", err)
+ }
+
+ err = tx.Create("refs/heads/dev", devID)
+ if err != nil {
+ t.Fatalf("Create(dev): %v", err)
+ }
+
+ err = tx.CreateSymbolic("refs/heads/alias", "refs/heads/main")
+ if err != nil {
+ t.Fatalf("CreateSymbolic(alias): %v", err)
+ }
+
+ err = tx.Commit()
+ if err != nil {
+ t.Fatalf("Commit seed refs: %v", err)
+ }
+
+ batch, err := store.BeginBatch()
+ if err != nil {
+ t.Fatalf("BeginBatch: %v", err)
+ }
+
+ err = batch.Update("refs/heads/main", nextMainID, mainID)
+ if err != nil {
+ t.Fatalf("Update(main): %v", err)
+ }
+
+ err = batch.Update("refs/heads/alias", aliasID, mainID)
+ if err != nil {
+ t.Fatalf("Update(alias): %v", err)
+ }
+
+ err = batch.Update("refs/heads/dev", nextDevID, devID)
+ if err != nil {
+ t.Fatalf("Update(dev): %v", err)
+ }
+
+ results, err := batch.Apply()
+ if err != nil {
+ t.Fatalf("Apply: %v", err)
+ }
+
+ if len(results) != 3 {
+ t.Fatalf("len(results) = %d, want 3", len(results))
+ }
+
+ if results[0].Status != refstore.BatchStatusApplied {
+ t.Fatalf("results[0].Status = %v, want applied", results[0].Status)
+ }
+
+ if results[1].Status != refstore.BatchStatusRejected {
+ t.Fatalf("results[1].Status = %v, want rejected", results[1].Status)
+ }
+
+ var duplicateErr *refstore.DuplicateUpdateError
+ if !errors.As(results[1].Error, &duplicateErr) {
+ t.Fatalf("results[1].Error = %T %v, want DuplicateUpdateError", results[1].Error, results[1].Error)
+ }
+
+ if results[2].Status != refstore.BatchStatusApplied {
+ t.Fatalf("results[2].Status = %v, want applied", results[2].Status)
+ }
+
+ if got := resolveDetached(t, store, "refs/heads/main").ID; got != nextMainID {
+ t.Fatalf("main after batch = %v, want %v", got, nextMainID)
+ }
+
+ if got := resolveDetached(t, store, "refs/heads/dev").ID; got != nextDevID {
+ t.Fatalf("dev after batch = %v, want %v", got, nextDevID)
+ }
+
+ resolved, err := store.Resolve("refs/heads/alias")
+ if err != nil {
+ t.Fatalf("Resolve(alias): %v", err)
+ }
+
+ symbolic, ok := resolved.(ref.Symbolic)
+ if !ok {
+ t.Fatalf("Resolve(alias) = %T, want ref.Symbolic", resolved)
+ }
+
+ if symbolic.Target != "refs/heads/main" {
+ t.Fatalf("alias target = %q, want refs/heads/main", symbolic.Target)
+ }
+ })
+}