aboutsummaryrefslogtreecommitdiff
path: root/ref/store/files/transaction_names_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'ref/store/files/transaction_names_test.go')
-rw-r--r--ref/store/files/transaction_names_test.go188
1 files changed, 188 insertions, 0 deletions
diff --git a/ref/store/files/transaction_names_test.go b/ref/store/files/transaction_names_test.go
new file mode 100644
index 00000000..f23294e5
--- /dev/null
+++ b/ref/store/files/transaction_names_test.go
@@ -0,0 +1,188 @@
+package files_test
+
+import (
+ "testing"
+
+ "codeberg.org/lindenii/furgit/internal/testgit"
+ objectid "codeberg.org/lindenii/furgit/object/id"
+ "codeberg.org/lindenii/furgit/ref"
+ "codeberg.org/lindenii/furgit/ref/store"
+)
+
+func TestFilesTransactionValidateUpdateNames(t *testing.T) {
+ t.Parallel()
+
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
+ testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
+ _, _, commitID := testRepo.MakeCommit(t, "base")
+
+ store := openFilesStore(t, testRepo, algo)
+
+ tests := []struct {
+ name string
+ queue func(refstore.Transaction) error
+ wantErr bool
+ }{
+ {
+ name: "create refs/heads/main",
+ queue: func(tx refstore.Transaction) error {
+ return tx.Create("refs/heads/main", commitID)
+ },
+ },
+ {
+ name: "create foo/bar",
+ queue: func(tx refstore.Transaction) error {
+ return tx.Create("foo/bar", commitID)
+ },
+ },
+ {
+ name: "create FETCH_HEAD",
+ queue: func(tx refstore.Transaction) error {
+ return tx.Create("FETCH_HEAD", commitID)
+ },
+ wantErr: true,
+ },
+ {
+ name: "create MERGE_HEAD",
+ queue: func(tx refstore.Transaction) error {
+ return tx.Create("MERGE_HEAD", commitID)
+ },
+ wantErr: true,
+ },
+ {
+ name: "create bad refname",
+ queue: func(tx refstore.Transaction) error {
+ return tx.Create("refs/heads/.bad", commitID)
+ },
+ wantErr: true,
+ },
+ {
+ name: "verify unsafe delete-style name",
+ queue: func(tx refstore.Transaction) error {
+ return tx.Verify("foo/bar", commitID)
+ },
+ wantErr: true,
+ },
+ {
+ name: "verify pseudoref-style name",
+ queue: func(tx refstore.Transaction) error {
+ return tx.Verify("PSEUDOREF", commitID)
+ },
+ wantErr: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tx, err := store.BeginTransaction()
+ if err != nil {
+ t.Fatalf("BeginTransaction: %v", err)
+ }
+
+ err = tt.queue(tx)
+ if (err != nil) != tt.wantErr {
+ t.Fatalf("queue err=%v, wantErr=%v", err, tt.wantErr)
+ }
+
+ _ = tx.Abort()
+ })
+ }
+ })
+}
+
+func TestFilesTransactionSymbolicTargetRules(t *testing.T) {
+ t.Parallel()
+
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
+ testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
+ _, _, mainID := testRepo.MakeCommit(t, "main")
+ testRepo.UpdateRef(t, "refs/heads/main", mainID)
+ testRepo.UpdateRef(t, "ORIG_HEAD", mainID)
+
+ store := openFilesStore(t, testRepo, algo)
+
+ tests := []struct {
+ name string
+ queue func(refstore.Transaction) error
+ wantErr bool
+ }{
+ {
+ name: "head requires branch target",
+ queue: func(tx refstore.Transaction) error {
+ return tx.CreateSymbolic("HEAD", "foo")
+ },
+ wantErr: true,
+ },
+ {
+ name: "head accepts refs/heads target",
+ queue: func(tx refstore.Transaction) error {
+ return tx.CreateSymbolic("HEAD", "refs/heads/main")
+ },
+ },
+ {
+ name: "non-head allows top-level target",
+ queue: func(tx refstore.Transaction) error {
+ return tx.CreateSymbolic("refs/heads/top-level", "ORIG_HEAD")
+ },
+ },
+ {
+ name: "non-head rejects invalid target",
+ queue: func(tx refstore.Transaction) error {
+ return tx.CreateSymbolic("refs/heads/invalid", "foo..bar")
+ },
+ wantErr: true,
+ },
+ {
+ name: "non-head allows worktree target",
+ queue: func(tx refstore.Transaction) error {
+ return tx.CreateSymbolic("refs/heads/worktree-target", "worktrees/wt1/HEAD")
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tx, err := store.BeginTransaction()
+ if err != nil {
+ t.Fatalf("BeginTransaction: %v", err)
+ }
+
+ err = tt.queue(tx)
+ if (err != nil) != tt.wantErr {
+ t.Fatalf("queue err=%v, wantErr=%v", err, tt.wantErr)
+ }
+
+ _ = tx.Abort()
+ })
+ }
+
+ tx, err := store.BeginTransaction()
+ if err != nil {
+ t.Fatalf("BeginTransaction(final symbolic): %v", err)
+ }
+
+ err = tx.CreateSymbolic("refs/heads/top-level", "ORIG_HEAD")
+ if err != nil {
+ t.Fatalf("CreateSymbolic(top-level): %v", err)
+ }
+
+ err = tx.Commit()
+ if err != nil {
+ t.Fatalf("Commit(CreateSymbolic top-level): %v", err)
+ }
+
+ got, err := store.Resolve("refs/heads/top-level")
+ if err != nil {
+ t.Fatalf("Resolve(top-level): %v", err)
+ }
+
+ sym, ok := got.(ref.Symbolic)
+ if !ok {
+ t.Fatalf("Resolve(top-level) type = %T, want ref.Symbolic", got)
+ }
+
+ if sym.Target != "ORIG_HEAD" {
+ t.Fatalf("top-level target = %q, want %q", sym.Target, "ORIG_HEAD")
+ }
+ })
+}