aboutsummaryrefslogtreecommitdiff
path: root/refstore/loose/loose_test.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-02-21 11:25:50 +0800
committerGravatar Runxi Yu2026-02-21 11:25:50 +0800
commit5682de102bdd28741d0b7e371e8ee9bbd003d045 (patch)
tree9ce4b8c704c4a5d8b5f0f9537e19a2638e1ff871 /refstore/loose/loose_test.go
parenttestgit: Add ref-related functions (diff)
signatureNo signature
refstore/loose: Add loose refs implementation
Diffstat (limited to 'refstore/loose/loose_test.go')
-rw-r--r--refstore/loose/loose_test.go149
1 files changed, 149 insertions, 0 deletions
diff --git a/refstore/loose/loose_test.go b/refstore/loose/loose_test.go
new file mode 100644
index 00000000..b56e40ac
--- /dev/null
+++ b/refstore/loose/loose_test.go
@@ -0,0 +1,149 @@
+package loose_test
+
+import (
+ "errors"
+ "os"
+ "path/filepath"
+ "slices"
+ "testing"
+
+ "codeberg.org/lindenii/furgit/internal/testgit"
+ "codeberg.org/lindenii/furgit/objectid"
+ "codeberg.org/lindenii/furgit/ref"
+ "codeberg.org/lindenii/furgit/refstore"
+ "codeberg.org/lindenii/furgit/refstore/loose"
+)
+
+func openLooseStore(t *testing.T, repoPath string, algo objectid.Algorithm) *loose.Store {
+ t.Helper()
+ root, err := os.OpenRoot(repoPath)
+ if err != nil {
+ t.Fatalf("OpenRoot(%q): %v", repoPath, err)
+ }
+ t.Cleanup(func() { _ = root.Close() })
+
+ store, err := loose.New(root, algo)
+ if err != nil {
+ t.Fatalf("loose.New: %v", err)
+ }
+ return store
+}
+
+func TestLooseResolveAndResolveFully(t *testing.T) {
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
+ testRepo := testgit.NewBareRepo(t, algo)
+ _, _, commitID := testRepo.MakeCommit(t, "loose refs commit")
+ testRepo.UpdateRef(t, "refs/heads/main", commitID)
+ testRepo.SymbolicRef(t, "HEAD", "refs/heads/main")
+
+ store := openLooseStore(t, testRepo.Dir(), algo)
+
+ resolvedHead, err := store.Resolve("HEAD")
+ if err != nil {
+ t.Fatalf("Resolve(HEAD): %v", err)
+ }
+ headSym, ok := resolvedHead.(ref.Symbolic)
+ if !ok {
+ t.Fatalf("Resolve(HEAD) type = %T, want ref.Symbolic", resolvedHead)
+ }
+ if headSym.Target != "refs/heads/main" {
+ t.Fatalf("Resolve(HEAD) target = %q, want %q", headSym.Target, "refs/heads/main")
+ }
+
+ resolvedMain, err := store.Resolve("refs/heads/main")
+ if err != nil {
+ t.Fatalf("Resolve(refs/heads/main): %v", err)
+ }
+ mainDet, ok := resolvedMain.(ref.Detached)
+ if !ok {
+ t.Fatalf("Resolve(main) type = %T, want ref.Detached", resolvedMain)
+ }
+ if mainDet.ID != commitID {
+ t.Fatalf("Resolve(main) id = %s, want %s", mainDet.ID, commitID)
+ }
+
+ fullHead, err := store.ResolveFully("HEAD")
+ if err != nil {
+ t.Fatalf("ResolveFully(HEAD): %v", err)
+ }
+ if fullHead.ID != commitID {
+ t.Fatalf("ResolveFully(HEAD) id = %s, want %s", fullHead.ID, commitID)
+ }
+
+ if _, err := store.Resolve("refs/heads/does-not-exist"); !errors.Is(err, refstore.ErrReferenceNotFound) {
+ t.Fatalf("Resolve(not-found) error = %v", err)
+ }
+ })
+}
+
+func TestLooseResolveFullyCycle(t *testing.T) {
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
+ testRepo := testgit.NewBareRepo(t, algo)
+ testRepo.SymbolicRef(t, "refs/heads/a", "refs/heads/b")
+ testRepo.SymbolicRef(t, "refs/heads/b", "refs/heads/a")
+
+ store := openLooseStore(t, testRepo.Dir(), algo)
+ if _, err := store.ResolveFully("refs/heads/a"); err == nil {
+ t.Fatalf("ResolveFully(cycle) expected error")
+ }
+ })
+}
+
+func TestLooseListPattern(t *testing.T) {
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
+ testRepo := testgit.NewBareRepo(t, algo)
+ _, _, commitID := testRepo.MakeCommit(t, "list refs commit")
+ testRepo.UpdateRef(t, "refs/heads/main", commitID)
+ testRepo.UpdateRef(t, "refs/heads/feature", commitID)
+ testRepo.UpdateRef(t, "refs/tags/v1.0.0", commitID)
+ testRepo.SymbolicRef(t, "HEAD", "refs/heads/main")
+
+ store := openLooseStore(t, testRepo.Dir(), algo)
+
+ allRefs, err := store.List("")
+ if err != nil {
+ t.Fatalf("List(\"\"): %v", err)
+ }
+ allNames := make([]string, 0, len(allRefs))
+ for _, entry := range allRefs {
+ allNames = append(allNames, entry.Name())
+ }
+ slices.Sort(allNames)
+ wantAll := []string{"HEAD", "refs/heads/feature", "refs/heads/main", "refs/tags/v1.0.0"}
+ if !slices.Equal(allNames, wantAll) {
+ t.Fatalf("List(\"\") names = %v, want %v", allNames, wantAll)
+ }
+
+ headRefs, err := store.List("refs/heads/*")
+ if err != nil {
+ t.Fatalf("List(refs/heads/*): %v", err)
+ }
+ headNames := make([]string, 0, len(headRefs))
+ for _, entry := range headRefs {
+ headNames = append(headNames, entry.Name())
+ }
+ slices.Sort(headNames)
+ wantHeads := []string{"refs/heads/feature", "refs/heads/main"}
+ if !slices.Equal(headNames, wantHeads) {
+ t.Fatalf("List(refs/heads/*) names = %v, want %v", headNames, wantHeads)
+ }
+ })
+}
+
+func TestLooseMalformedDetachedRef(t *testing.T) {
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
+ testRepo := testgit.NewBareRepo(t, algo)
+ refPath := filepath.Join(testRepo.Dir(), "refs", "heads", "bad")
+ if err := os.MkdirAll(filepath.Dir(refPath), 0o755); err != nil {
+ t.Fatalf("MkdirAll: %v", err)
+ }
+ if err := os.WriteFile(refPath, []byte("not-a-hash\n"), 0o644); err != nil {
+ t.Fatalf("WriteFile: %v", err)
+ }
+
+ store := openLooseStore(t, testRepo.Dir(), algo)
+ if _, err := store.Resolve("refs/heads/bad"); err == nil {
+ t.Fatalf("Resolve(malformed) expected error")
+ }
+ })
+}