diff options
Diffstat (limited to 'ref/store/files/transaction_names_test.go')
| -rw-r--r-- | ref/store/files/transaction_names_test.go | 188 |
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") + } + }) +} |
