Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuang-Minh Nguyen <qmnguyen@gitlab.com>2023-12-06 10:05:57 +0300
committerQuang-Minh Nguyen <qmnguyen@gitlab.com>2023-12-08 08:18:03 +0300
commit6dbbea15a4d71c8d30ebdf4fff84082f85469782 (patch)
tree9d04e624363bfa9b5ce211a48ef86447447aee8f
parent615032a1b3a92c8369148cb27c6ea7d1d81c682f (diff)
Split tests related to alternate out of TestTransactionManager
-rw-r--r--internal/gitaly/storage/storagemgr/transaction_manager_alternate_test.go1495
-rw-r--r--internal/gitaly/storage/storagemgr/transaction_manager_test.go1475
2 files changed, 1496 insertions, 1474 deletions
diff --git a/internal/gitaly/storage/storagemgr/transaction_manager_alternate_test.go b/internal/gitaly/storage/storagemgr/transaction_manager_alternate_test.go
new file mode 100644
index 000000000..4583e5e63
--- /dev/null
+++ b/internal/gitaly/storage/storagemgr/transaction_manager_alternate_test.go
@@ -0,0 +1,1495 @@
+package storagemgr
+
+import (
+ "bytes"
+ "io/fs"
+ "path/filepath"
+ "testing"
+
+ "gitlab.com/gitlab-org/gitaly/v16/internal/git"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/git/gittest"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/config"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/helper/perm"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper"
+)
+
+func generateAlternateTests(t *testing.T, setup testTransactionSetup) []transactionTestCase {
+ umask := testhelper.Umask()
+
+ return []transactionTestCase{
+ {
+ desc: "repository is linked to alternate on creation",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ "member": {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/1/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ ),
+ "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ },
+ },
+ },
+ {
+ desc: "repository is linked to an alternate after creation",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ UpdateAlternate: &alternateUpdate{relativePath: "pool"},
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ "member": {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/1/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ ),
+ "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ },
+ },
+ },
+ {
+ desc: "repository is disconnected from alternate",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ CloseManager{},
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ // Transactions write objects always as packs into the repository. To test
+ // scenarios where repositories may have existing loose objects, manually
+ // unpack the objects to the repository.
+ gittest.ExecOpts(tb, cfg,
+ gittest.ExecConfig{Stdin: bytes.NewReader(setup.Commits.Second.Pack)},
+ "-C", filepath.Join(storagePath, "pool"), "unpack-objects",
+ )
+ },
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ UpdateAlternate: &alternateUpdate{},
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ },
+ "member": {
+ // The objects should have been copied over to the repository when it was
+ // disconnected from the alternate.
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/1/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ ),
+ "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ },
+ },
+ },
+ {
+ desc: "repository's alternate must be pointed to a git repository",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "repository",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ Alternate: "../..",
+ },
+ Commit{
+ TransactionID: 1,
+ ExpectedError: storage.InvalidGitDirectoryError{MissingEntry: "objects"},
+ },
+ },
+ expectedState: StateAssertion{
+ Repositories: RepositoryStates{},
+ },
+ },
+ {
+ desc: "repository's alternate must not point to repository itself",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "repository",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ Alternate: "../objects",
+ },
+ Commit{
+ TransactionID: 1,
+ ExpectedError: errAlternatePointsToSelf,
+ },
+ },
+ expectedState: StateAssertion{
+ Repositories: RepositoryStates{},
+ },
+ },
+ {
+ desc: "repository's alternate can't have an alternate itself",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "recursive-member",
+ ExpectedSnapshotLSN: 2,
+ },
+ CreateRepository{
+ TransactionID: 3,
+ Alternate: "../../member/objects",
+ },
+ Commit{
+ TransactionID: 3,
+ ExpectedError: errAlternateHasAlternate,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ Objects: []git.ObjectID{},
+ },
+ "member": {
+ Objects: []git.ObjectID{},
+ Alternate: "../../pool/objects",
+ },
+ },
+ },
+ },
+ {
+ desc: "repository can't be linked multiple times",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ UpdateAlternate: &alternateUpdate{relativePath: "pool"},
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 3,
+ },
+ Commit{
+ TransactionID: 4,
+ UpdateAlternate: &alternateUpdate{relativePath: "pool"},
+ ExpectedError: errAlternateAlreadyLinked,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ Objects: []git.ObjectID{},
+ },
+ "member": {
+ Objects: []git.ObjectID{},
+ Alternate: "../../pool/objects",
+ },
+ },
+ },
+ },
+ {
+ desc: "repository can't be linked concurrently multiple times",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ UpdateAlternate: &alternateUpdate{relativePath: "pool"},
+ },
+ Commit{
+ TransactionID: 4,
+ UpdateAlternate: &alternateUpdate{relativePath: "pool"},
+ ExpectedError: errAlternateAlreadyLinked,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ Objects: []git.ObjectID{},
+ },
+ "member": {
+ Objects: []git.ObjectID{},
+ Alternate: "../../pool/objects",
+ },
+ },
+ },
+ },
+ {
+ desc: "repository without an alternate can't be disconnected",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "repository",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "repository",
+ ExpectedSnapshotLSN: 1,
+ },
+ Commit{
+ TransactionID: 2,
+ UpdateAlternate: &alternateUpdate{},
+ ExpectedError: errNoAlternate,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "repository": {
+ Objects: []git.ObjectID{},
+ },
+ },
+ },
+ },
+ {
+ desc: "repository can't be disconnected concurrently multiple times",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ UpdateAlternate: &alternateUpdate{},
+ },
+ Commit{
+ TransactionID: 4,
+ UpdateAlternate: &alternateUpdate{},
+ ExpectedError: errNoAlternate,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ Objects: []git.ObjectID{},
+ },
+ "member": {
+ Objects: []git.ObjectID{},
+ },
+ },
+ },
+ },
+ {
+ desc: "reapplying alternate linking works",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ CloseManager{},
+ StartManager{
+ Hooks: testTransactionHooks{
+ BeforeStoreAppliedLSN: func(hookContext) {
+ panic(errSimulatedCrash)
+ },
+ },
+ ExpectedError: errSimulatedCrash,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ RepositoryAssertion{
+ TransactionID: 3,
+ Repositories: RepositoryStates{
+ "member": {
+ DefaultBranch: "refs/heads/main",
+ },
+ },
+ },
+ Commit{
+ TransactionID: 3,
+ UpdateAlternate: &alternateUpdate{relativePath: "pool"},
+ ExpectedError: ErrTransactionProcessingStopped,
+ },
+ AssertManager{
+ ExpectedError: errSimulatedCrash,
+ },
+ StartManager{},
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ Objects: []git.ObjectID{},
+ },
+ "member": {
+ Objects: []git.ObjectID{},
+ Alternate: "../../pool/objects",
+ },
+ },
+ },
+ },
+ {
+ desc: "reapplying alternate disconnection works",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ CloseManager{},
+ StartManager{
+ Hooks: testTransactionHooks{
+ BeforeStoreAppliedLSN: func(hookContext) {
+ panic(errSimulatedCrash)
+ },
+ },
+ ExpectedError: errSimulatedCrash,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ RepositoryAssertion{
+ TransactionID: 3,
+ Repositories: RepositoryStates{
+ "pool": {
+ DefaultBranch: "refs/heads/main",
+ },
+ "member": {
+ DefaultBranch: "refs/heads/main",
+ Alternate: "../../pool/objects",
+ },
+ },
+ },
+ Commit{
+ TransactionID: 3,
+ UpdateAlternate: &alternateUpdate{},
+ ExpectedError: ErrTransactionProcessingStopped,
+ },
+ AssertManager{
+ ExpectedError: errSimulatedCrash,
+ },
+ StartManager{},
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ Objects: []git.ObjectID{},
+ },
+ "member": {
+ Objects: []git.ObjectID{},
+ },
+ },
+ },
+ },
+ {
+ desc: "point reference to an object in an alternate",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ "member": {
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/1/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ ),
+ "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ },
+ },
+ },
+ {
+ desc: "point reference to new object with dependencies in an alternate",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID},
+ },
+ QuarantinedPacks: [][]byte{setup.Commits.Second.Pack},
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ "member": {
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/1/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ ),
+ "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ "/wal/3": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/3/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/3/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ // The pack should only contain the new object 'second' as the
+ // rest of the objects exist in the alternate. We're still including
+ // all unreachable objects in the logged pack until we can compute
+ // the pack files dependencies.
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ ),
+ "/wal/3/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ },
+ },
+ },
+ {
+ desc: "repository's alternate is automatically snapshotted",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ RepositoryAssertion{
+ TransactionID: 3,
+ Repositories: RepositoryStates{
+ "pool": {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ "member": {
+ DefaultBranch: "refs/heads/main",
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ },
+ Rollback{
+ TransactionID: 3,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ "member": {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/1/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ ),
+ "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ },
+ },
+ },
+ {
+ desc: "multiple repositories can be included in transaction's snapshot",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "repository-1",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ CustomHooks: validCustomHooks(t),
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "repository-2",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/branch": setup.Commits.Third.OID,
+ },
+ DefaultBranch: "refs/heads/branch",
+ Packs: [][]byte{
+ setup.Commits.First.Pack,
+ setup.Commits.Second.Pack,
+ setup.Commits.Third.Pack,
+ },
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "repository-3",
+ ExpectedSnapshotLSN: 2,
+ },
+ CreateRepository{
+ TransactionID: 3,
+ // Set repository-2 as repository-3's alternate to assert the
+ // snasphotted repositories' alternates are also included.
+ Alternate: "../../repository-2/objects",
+ },
+ Commit{
+ TransactionID: 3,
+ },
+ Begin{
+ TransactionID: 4,
+ // Create a repository that is not snapshotted to assert it's not included
+ // in the snapshot.
+ RelativePath: "repository-4",
+ ExpectedSnapshotLSN: 3,
+ },
+ CreateRepository{
+ TransactionID: 4,
+ },
+ Commit{
+ TransactionID: 4,
+ },
+ Begin{
+ TransactionID: 5,
+ RelativePath: "repository-1",
+ SnapshottedRelativePaths: []string{"repository-3"},
+ ExpectedSnapshotLSN: 4,
+ },
+ RepositoryAssertion{
+ TransactionID: 5,
+ Repositories: RepositoryStates{
+ "repository-1": {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ CustomHooks: testhelper.DirectoryState{
+ "/": {Mode: umask.Mask(fs.ModeDir | perm.PrivateDir)},
+ "/pre-receive": {
+ Mode: umask.Mask(fs.ModePerm),
+ Content: []byte("hook content"),
+ },
+ "/private-dir": {Mode: umask.Mask(fs.ModeDir | perm.PrivateDir)},
+ "/private-dir/private-file": {Mode: umask.Mask(perm.PrivateFile), Content: []byte("private content")},
+ },
+ },
+ "repository-2": {
+ DefaultBranch: "refs/heads/branch",
+ References: []git.Reference{
+ {Name: "refs/heads/branch", Target: setup.Commits.Third.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ },
+ },
+ "repository-3": {
+ DefaultBranch: "refs/heads/main",
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ },
+ Alternate: "../../repository-2/objects",
+ },
+ },
+ },
+ Rollback{
+ TransactionID: 5,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(4).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "repository-1": {
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ CustomHooks: testhelper.DirectoryState{
+ "/": {Mode: umask.Mask(fs.ModeDir | perm.PrivateDir)},
+ "/pre-receive": {
+ Mode: umask.Mask(fs.ModePerm),
+ Content: []byte("hook content"),
+ },
+ "/private-dir": {Mode: umask.Mask(fs.ModeDir | perm.PrivateDir)},
+ "/private-dir/private-file": {Mode: umask.Mask(perm.PrivateFile), Content: []byte("private content")},
+ },
+ },
+ "repository-2": {
+ DefaultBranch: "refs/heads/branch",
+ References: []git.Reference{
+ {Name: "refs/heads/branch", Target: setup.Commits.Third.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ },
+ },
+ "repository-3": {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ },
+ Alternate: "../../repository-2/objects",
+ },
+ "repository-4": {
+ Objects: []git.ObjectID{},
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/1/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ ),
+ "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ "/wal/2": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/2/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/2/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ },
+ ),
+ "/wal/2/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ },
+ },
+ },
+ {
+ desc: "additional repository is included in the snapshot explicitly and implicitly",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/branch": setup.Commits.Second.OID,
+ },
+ DefaultBranch: "refs/heads/branch",
+ Packs: [][]byte{
+ setup.Commits.First.Pack,
+ setup.Commits.Second.Pack,
+ },
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ // The pool is included explicitly here, and also implicitly through
+ // the alternate link of member.
+ SnapshottedRelativePaths: []string{"pool"},
+ ExpectedSnapshotLSN: 2,
+ },
+ RepositoryAssertion{
+ TransactionID: 3,
+ Repositories: RepositoryStates{
+ "pool": {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ "member": {
+ DefaultBranch: "refs/heads/branch",
+ References: []git.Reference{
+ {Name: "refs/heads/branch", Target: setup.Commits.Second.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ },
+ Rollback{
+ TransactionID: 3,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ "member": {
+ DefaultBranch: "refs/heads/branch",
+ References: []git.Reference{
+ {Name: "refs/heads/branch", Target: setup.Commits.Second.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/1/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ ),
+ "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ "/wal/2": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/2/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/2/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ ),
+ "/wal/2/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ },
+ },
+ },
+ {
+ desc: "target repository is included in the snapshot explicitly and implicitly",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/branch": setup.Commits.Second.OID,
+ },
+ DefaultBranch: "refs/heads/branch",
+ Packs: [][]byte{
+ setup.Commits.First.Pack,
+ setup.Commits.Second.Pack,
+ },
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ // The pool is targeted, and also implicitly included through
+ // the alternate link of member.
+ RelativePath: "pool",
+ SnapshottedRelativePaths: []string{"member"},
+ ExpectedSnapshotLSN: 2,
+ },
+ RepositoryAssertion{
+ TransactionID: 3,
+ Repositories: RepositoryStates{
+ "pool": {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ "member": {
+ DefaultBranch: "refs/heads/branch",
+ References: []git.Reference{
+ {Name: "refs/heads/branch", Target: setup.Commits.Second.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ },
+ Rollback{
+ TransactionID: 3,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ "member": {
+ DefaultBranch: "refs/heads/branch",
+ References: []git.Reference{
+ {Name: "refs/heads/branch", Target: setup.Commits.Second.OID.String()},
+ },
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/1/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ ),
+ "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ "/wal/2": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/2/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/2/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ ),
+ "/wal/2/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ },
+ },
+ },
+ {
+ desc: "non-git directories are not snapshotted",
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ // Try to snapshot the parent directory, which is no a valid Git directory.
+ RelativePath: filepath.Dir(setup.RelativePath),
+ ExpectedError: storage.InvalidGitDirectoryError{MissingEntry: "objects"},
+ },
+ },
+ },
+ }
+}
diff --git a/internal/gitaly/storage/storagemgr/transaction_manager_test.go b/internal/gitaly/storage/storagemgr/transaction_manager_test.go
index fe07b32c8..43cb6f2cb 100644
--- a/internal/gitaly/storage/storagemgr/transaction_manager_test.go
+++ b/internal/gitaly/storage/storagemgr/transaction_manager_test.go
@@ -26,7 +26,6 @@ import (
"gitlab.com/gitlab-org/gitaly/v16/internal/git/localrepo"
"gitlab.com/gitlab-org/gitaly/v16/internal/git/updateref"
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/config"
- "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage"
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/transaction"
"gitlab.com/gitlab-org/gitaly/v16/internal/grpc/backchannel"
"gitlab.com/gitlab-org/gitaly/v16/internal/helper/perm"
@@ -2222,1479 +2221,6 @@ func TestTransactionManager(t *testing.T) {
},
},
},
- {
- desc: "repository is linked to alternate on creation",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- References: map[git.ReferenceName]git.ObjectID{
- "refs/heads/main": setup.Commits.First.OID,
- },
- Packs: [][]byte{setup.Commits.First.Pack},
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- Alternate: "../../pool/objects",
- },
- Commit{
- TransactionID: 2,
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(2).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- },
- "member": {
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- Alternate: "../../pool/objects",
- },
- },
- Directory: testhelper.DirectoryState{
- "/": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/1/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- ),
- "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- },
- },
- },
- {
- desc: "repository is linked to an alternate after creation",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- References: map[git.ReferenceName]git.ObjectID{
- "refs/heads/main": setup.Commits.First.OID,
- },
- Packs: [][]byte{setup.Commits.First.Pack},
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- },
- Commit{
- TransactionID: 2,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "member",
- ExpectedSnapshotLSN: 2,
- },
- Commit{
- TransactionID: 3,
- UpdateAlternate: &alternateUpdate{relativePath: "pool"},
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(3).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- },
- "member": {
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- Alternate: "../../pool/objects",
- },
- },
- Directory: testhelper.DirectoryState{
- "/": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/1/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- ),
- "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- },
- },
- },
- {
- desc: "repository is disconnected from alternate",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- References: map[git.ReferenceName]git.ObjectID{
- "refs/heads/main": setup.Commits.First.OID,
- },
- Packs: [][]byte{setup.Commits.First.Pack},
- },
- Commit{
- TransactionID: 1,
- },
- CloseManager{},
- StartManager{
- ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
- // Transactions write objects always as packs into the repository. To test
- // scenarios where repositories may have existing loose objects, manually
- // unpack the objects to the repository.
- gittest.ExecOpts(tb, cfg,
- gittest.ExecConfig{Stdin: bytes.NewReader(setup.Commits.Second.Pack)},
- "-C", filepath.Join(storagePath, "pool"), "unpack-objects",
- )
- },
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- Alternate: "../../pool/objects",
- },
- Commit{
- TransactionID: 2,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "member",
- ExpectedSnapshotLSN: 2,
- },
- Commit{
- TransactionID: 3,
- UpdateAlternate: &alternateUpdate{},
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(3).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- },
- },
- "member": {
- // The objects should have been copied over to the repository when it was
- // disconnected from the alternate.
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- },
- },
- },
- Directory: testhelper.DirectoryState{
- "/": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/1/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- ),
- "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- },
- },
- },
- {
- desc: "repository's alternate must be pointed to a git repository",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "repository",
- },
- CreateRepository{
- TransactionID: 1,
- Alternate: "../..",
- },
- Commit{
- TransactionID: 1,
- ExpectedError: storage.InvalidGitDirectoryError{MissingEntry: "objects"},
- },
- },
- expectedState: StateAssertion{
- Repositories: RepositoryStates{},
- },
- },
- {
- desc: "repository's alternate must not point to repository itself",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "repository",
- },
- CreateRepository{
- TransactionID: 1,
- Alternate: "../objects",
- },
- Commit{
- TransactionID: 1,
- ExpectedError: errAlternatePointsToSelf,
- },
- },
- expectedState: StateAssertion{
- Repositories: RepositoryStates{},
- },
- },
- {
- desc: "repository's alternate can't have an alternate itself",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- Alternate: "../../pool/objects",
- },
- Commit{
- TransactionID: 2,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "recursive-member",
- ExpectedSnapshotLSN: 2,
- },
- CreateRepository{
- TransactionID: 3,
- Alternate: "../../member/objects",
- },
- Commit{
- TransactionID: 3,
- ExpectedError: errAlternateHasAlternate,
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(2).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- Objects: []git.ObjectID{},
- },
- "member": {
- Objects: []git.ObjectID{},
- Alternate: "../../pool/objects",
- },
- },
- },
- },
- {
- desc: "repository can't be linked multiple times",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- },
- Commit{
- TransactionID: 2,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "member",
- ExpectedSnapshotLSN: 2,
- },
- Commit{
- TransactionID: 3,
- UpdateAlternate: &alternateUpdate{relativePath: "pool"},
- },
- Begin{
- TransactionID: 4,
- RelativePath: "member",
- ExpectedSnapshotLSN: 3,
- },
- Commit{
- TransactionID: 4,
- UpdateAlternate: &alternateUpdate{relativePath: "pool"},
- ExpectedError: errAlternateAlreadyLinked,
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(3).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- Objects: []git.ObjectID{},
- },
- "member": {
- Objects: []git.ObjectID{},
- Alternate: "../../pool/objects",
- },
- },
- },
- },
- {
- desc: "repository can't be linked concurrently multiple times",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- },
- Commit{
- TransactionID: 2,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "member",
- ExpectedSnapshotLSN: 2,
- },
- Begin{
- TransactionID: 4,
- RelativePath: "member",
- ExpectedSnapshotLSN: 2,
- },
- Commit{
- TransactionID: 3,
- UpdateAlternate: &alternateUpdate{relativePath: "pool"},
- },
- Commit{
- TransactionID: 4,
- UpdateAlternate: &alternateUpdate{relativePath: "pool"},
- ExpectedError: errAlternateAlreadyLinked,
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(3).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- Objects: []git.ObjectID{},
- },
- "member": {
- Objects: []git.ObjectID{},
- Alternate: "../../pool/objects",
- },
- },
- },
- },
- {
- desc: "repository without an alternate can't be disconnected",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "repository",
- },
- CreateRepository{
- TransactionID: 1,
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "repository",
- ExpectedSnapshotLSN: 1,
- },
- Commit{
- TransactionID: 2,
- UpdateAlternate: &alternateUpdate{},
- ExpectedError: errNoAlternate,
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(1).toProto(),
- },
- Repositories: RepositoryStates{
- "repository": {
- Objects: []git.ObjectID{},
- },
- },
- },
- },
- {
- desc: "repository can't be disconnected concurrently multiple times",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- Alternate: "../../pool/objects",
- },
- Commit{
- TransactionID: 2,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "member",
- ExpectedSnapshotLSN: 2,
- },
- Begin{
- TransactionID: 4,
- RelativePath: "member",
- ExpectedSnapshotLSN: 2,
- },
- Commit{
- TransactionID: 3,
- UpdateAlternate: &alternateUpdate{},
- },
- Commit{
- TransactionID: 4,
- UpdateAlternate: &alternateUpdate{},
- ExpectedError: errNoAlternate,
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(3).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- Objects: []git.ObjectID{},
- },
- "member": {
- Objects: []git.ObjectID{},
- },
- },
- },
- },
- {
- desc: "reapplying alternate linking works",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- },
- Commit{
- TransactionID: 2,
- },
- CloseManager{},
- StartManager{
- Hooks: testTransactionHooks{
- BeforeStoreAppliedLSN: func(hookContext) {
- panic(errSimulatedCrash)
- },
- },
- ExpectedError: errSimulatedCrash,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "member",
- ExpectedSnapshotLSN: 2,
- },
- RepositoryAssertion{
- TransactionID: 3,
- Repositories: RepositoryStates{
- "member": {
- DefaultBranch: "refs/heads/main",
- },
- },
- },
- Commit{
- TransactionID: 3,
- UpdateAlternate: &alternateUpdate{relativePath: "pool"},
- ExpectedError: ErrTransactionProcessingStopped,
- },
- AssertManager{
- ExpectedError: errSimulatedCrash,
- },
- StartManager{},
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(3).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- Objects: []git.ObjectID{},
- },
- "member": {
- Objects: []git.ObjectID{},
- Alternate: "../../pool/objects",
- },
- },
- },
- },
- {
- desc: "reapplying alternate disconnection works",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- Alternate: "../../pool/objects",
- },
- Commit{
- TransactionID: 2,
- },
- CloseManager{},
- StartManager{
- Hooks: testTransactionHooks{
- BeforeStoreAppliedLSN: func(hookContext) {
- panic(errSimulatedCrash)
- },
- },
- ExpectedError: errSimulatedCrash,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "member",
- ExpectedSnapshotLSN: 2,
- },
- RepositoryAssertion{
- TransactionID: 3,
- Repositories: RepositoryStates{
- "pool": {
- DefaultBranch: "refs/heads/main",
- },
- "member": {
- DefaultBranch: "refs/heads/main",
- Alternate: "../../pool/objects",
- },
- },
- },
- Commit{
- TransactionID: 3,
- UpdateAlternate: &alternateUpdate{},
- ExpectedError: ErrTransactionProcessingStopped,
- },
- AssertManager{
- ExpectedError: errSimulatedCrash,
- },
- StartManager{},
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(3).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- Objects: []git.ObjectID{},
- },
- "member": {
- Objects: []git.ObjectID{},
- },
- },
- },
- },
- {
- desc: "point reference to an object in an alternate",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- References: map[git.ReferenceName]git.ObjectID{
- "refs/heads/main": setup.Commits.First.OID,
- },
- Packs: [][]byte{setup.Commits.First.Pack},
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- Alternate: "../../pool/objects",
- },
- Commit{
- TransactionID: 2,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "member",
- ExpectedSnapshotLSN: 2,
- },
- Commit{
- TransactionID: 3,
- ReferenceUpdates: ReferenceUpdates{
- "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
- },
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(3).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- },
- "member": {
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- Alternate: "../../pool/objects",
- },
- },
- Directory: testhelper.DirectoryState{
- "/": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/1/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- ),
- "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- },
- },
- },
- {
- desc: "point reference to new object with dependencies in an alternate",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- References: map[git.ReferenceName]git.ObjectID{
- "refs/heads/main": setup.Commits.First.OID,
- },
- Packs: [][]byte{setup.Commits.First.Pack},
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- Alternate: "../../pool/objects",
- },
- Commit{
- TransactionID: 2,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "member",
- ExpectedSnapshotLSN: 2,
- },
- Commit{
- TransactionID: 3,
- ReferenceUpdates: ReferenceUpdates{
- "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID},
- },
- QuarantinedPacks: [][]byte{setup.Commits.Second.Pack},
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(3).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- },
- "member": {
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- },
- Alternate: "../../pool/objects",
- },
- },
- Directory: testhelper.DirectoryState{
- "/": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/1/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- ),
- "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- "/wal/3": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/3/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/3/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- // The pack should only contain the new object 'second' as the
- // rest of the objects exist in the alternate. We're still including
- // all unreachable objects in the logged pack until we can compute
- // the pack files dependencies.
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- },
- ),
- "/wal/3/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- },
- },
- },
- {
- desc: "repository's alternate is automatically snapshotted",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- References: map[git.ReferenceName]git.ObjectID{
- "refs/heads/main": setup.Commits.First.OID,
- },
- Packs: [][]byte{setup.Commits.First.Pack},
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- Alternate: "../../pool/objects",
- },
- Commit{
- TransactionID: 2,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "member",
- ExpectedSnapshotLSN: 2,
- },
- RepositoryAssertion{
- TransactionID: 3,
- Repositories: RepositoryStates{
- "pool": {
- DefaultBranch: "refs/heads/main",
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- },
- "member": {
- DefaultBranch: "refs/heads/main",
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- Alternate: "../../pool/objects",
- },
- },
- },
- Rollback{
- TransactionID: 3,
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(2).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- },
- "member": {
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- Alternate: "../../pool/objects",
- },
- },
- Directory: testhelper.DirectoryState{
- "/": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/1/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- ),
- "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- },
- },
- },
- {
- desc: "multiple repositories can be included in transaction's snapshot",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "repository-1",
- },
- CreateRepository{
- TransactionID: 1,
- References: map[git.ReferenceName]git.ObjectID{
- "refs/heads/main": setup.Commits.First.OID,
- },
- Packs: [][]byte{setup.Commits.First.Pack},
- CustomHooks: validCustomHooks(t),
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "repository-2",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- References: map[git.ReferenceName]git.ObjectID{
- "refs/heads/branch": setup.Commits.Third.OID,
- },
- DefaultBranch: "refs/heads/branch",
- Packs: [][]byte{
- setup.Commits.First.Pack,
- setup.Commits.Second.Pack,
- setup.Commits.Third.Pack,
- },
- },
- Commit{
- TransactionID: 2,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "repository-3",
- ExpectedSnapshotLSN: 2,
- },
- CreateRepository{
- TransactionID: 3,
- // Set repository-2 as repository-3's alternate to assert the
- // snasphotted repositories' alternates are also included.
- Alternate: "../../repository-2/objects",
- },
- Commit{
- TransactionID: 3,
- },
- Begin{
- TransactionID: 4,
- // Create a repository that is not snapshotted to assert it's not included
- // in the snapshot.
- RelativePath: "repository-4",
- ExpectedSnapshotLSN: 3,
- },
- CreateRepository{
- TransactionID: 4,
- },
- Commit{
- TransactionID: 4,
- },
- Begin{
- TransactionID: 5,
- RelativePath: "repository-1",
- SnapshottedRelativePaths: []string{"repository-3"},
- ExpectedSnapshotLSN: 4,
- },
- RepositoryAssertion{
- TransactionID: 5,
- Repositories: RepositoryStates{
- "repository-1": {
- DefaultBranch: "refs/heads/main",
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- CustomHooks: testhelper.DirectoryState{
- "/": {Mode: umask.Mask(fs.ModeDir | perm.PrivateDir)},
- "/pre-receive": {
- Mode: umask.Mask(fs.ModePerm),
- Content: []byte("hook content"),
- },
- "/private-dir": {Mode: umask.Mask(fs.ModeDir | perm.PrivateDir)},
- "/private-dir/private-file": {Mode: umask.Mask(perm.PrivateFile), Content: []byte("private content")},
- },
- },
- "repository-2": {
- DefaultBranch: "refs/heads/branch",
- References: []git.Reference{
- {Name: "refs/heads/branch", Target: setup.Commits.Third.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- setup.Commits.Third.OID,
- },
- },
- "repository-3": {
- DefaultBranch: "refs/heads/main",
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- setup.Commits.Third.OID,
- },
- Alternate: "../../repository-2/objects",
- },
- },
- },
- Rollback{
- TransactionID: 5,
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(4).toProto(),
- },
- Repositories: RepositoryStates{
- "repository-1": {
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- CustomHooks: testhelper.DirectoryState{
- "/": {Mode: umask.Mask(fs.ModeDir | perm.PrivateDir)},
- "/pre-receive": {
- Mode: umask.Mask(fs.ModePerm),
- Content: []byte("hook content"),
- },
- "/private-dir": {Mode: umask.Mask(fs.ModeDir | perm.PrivateDir)},
- "/private-dir/private-file": {Mode: umask.Mask(perm.PrivateFile), Content: []byte("private content")},
- },
- },
- "repository-2": {
- DefaultBranch: "refs/heads/branch",
- References: []git.Reference{
- {Name: "refs/heads/branch", Target: setup.Commits.Third.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- setup.Commits.Third.OID,
- },
- },
- "repository-3": {
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- setup.Commits.Third.OID,
- },
- Alternate: "../../repository-2/objects",
- },
- "repository-4": {
- Objects: []git.ObjectID{},
- },
- },
- Directory: testhelper.DirectoryState{
- "/": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/1/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- ),
- "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- "/wal/2": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/2/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/2/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- setup.Commits.Third.OID,
- },
- ),
- "/wal/2/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- },
- },
- },
- {
- desc: "additional repository is included in the snapshot explicitly and implicitly",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- References: map[git.ReferenceName]git.ObjectID{
- "refs/heads/main": setup.Commits.First.OID,
- },
- Packs: [][]byte{setup.Commits.First.Pack},
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- References: map[git.ReferenceName]git.ObjectID{
- "refs/heads/branch": setup.Commits.Second.OID,
- },
- DefaultBranch: "refs/heads/branch",
- Packs: [][]byte{
- setup.Commits.First.Pack,
- setup.Commits.Second.Pack,
- },
- Alternate: "../../pool/objects",
- },
- Commit{
- TransactionID: 2,
- },
- Begin{
- TransactionID: 3,
- RelativePath: "member",
- // The pool is included explicitly here, and also implicitly through
- // the alternate link of member.
- SnapshottedRelativePaths: []string{"pool"},
- ExpectedSnapshotLSN: 2,
- },
- RepositoryAssertion{
- TransactionID: 3,
- Repositories: RepositoryStates{
- "pool": {
- DefaultBranch: "refs/heads/main",
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- },
- "member": {
- DefaultBranch: "refs/heads/branch",
- References: []git.Reference{
- {Name: "refs/heads/branch", Target: setup.Commits.Second.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- },
- Alternate: "../../pool/objects",
- },
- },
- },
- Rollback{
- TransactionID: 3,
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(2).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- },
- "member": {
- DefaultBranch: "refs/heads/branch",
- References: []git.Reference{
- {Name: "refs/heads/branch", Target: setup.Commits.Second.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- },
- Alternate: "../../pool/objects",
- },
- },
- Directory: testhelper.DirectoryState{
- "/": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/1/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- ),
- "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- "/wal/2": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/2/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/2/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- },
- ),
- "/wal/2/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- },
- },
- },
- {
- desc: "target repository is included in the snapshot explicitly and implicitly",
- steps: steps{
- RemoveRepository{},
- StartManager{},
- Begin{
- TransactionID: 1,
- RelativePath: "pool",
- },
- CreateRepository{
- TransactionID: 1,
- References: map[git.ReferenceName]git.ObjectID{
- "refs/heads/main": setup.Commits.First.OID,
- },
- Packs: [][]byte{setup.Commits.First.Pack},
- },
- Commit{
- TransactionID: 1,
- },
- Begin{
- TransactionID: 2,
- RelativePath: "member",
- ExpectedSnapshotLSN: 1,
- },
- CreateRepository{
- TransactionID: 2,
- References: map[git.ReferenceName]git.ObjectID{
- "refs/heads/branch": setup.Commits.Second.OID,
- },
- DefaultBranch: "refs/heads/branch",
- Packs: [][]byte{
- setup.Commits.First.Pack,
- setup.Commits.Second.Pack,
- },
- Alternate: "../../pool/objects",
- },
- Commit{
- TransactionID: 2,
- },
- Begin{
- TransactionID: 3,
- // The pool is targeted, and also implicitly included through
- // the alternate link of member.
- RelativePath: "pool",
- SnapshottedRelativePaths: []string{"member"},
- ExpectedSnapshotLSN: 2,
- },
- RepositoryAssertion{
- TransactionID: 3,
- Repositories: RepositoryStates{
- "pool": {
- DefaultBranch: "refs/heads/main",
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- },
- "member": {
- DefaultBranch: "refs/heads/branch",
- References: []git.Reference{
- {Name: "refs/heads/branch", Target: setup.Commits.Second.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- },
- Alternate: "../../pool/objects",
- },
- },
- },
- Rollback{
- TransactionID: 3,
- },
- },
- expectedState: StateAssertion{
- Database: DatabaseState{
- string(keyAppliedLSN(partitionID)): LSN(2).toProto(),
- },
- Repositories: RepositoryStates{
- "pool": {
- References: []git.Reference{
- {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- },
- "member": {
- DefaultBranch: "refs/heads/branch",
- References: []git.Reference{
- {Name: "refs/heads/branch", Target: setup.Commits.Second.OID.String()},
- },
- Objects: []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- },
- Alternate: "../../pool/objects",
- },
- },
- Directory: testhelper.DirectoryState{
- "/": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/1/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- },
- ),
- "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- "/wal/2": {Mode: fs.ModeDir | perm.PrivateDir},
- "/wal/2/objects.idx": indexFileDirectoryEntry(setup.Config),
- "/wal/2/objects.pack": packFileDirectoryEntry(
- setup.Config,
- []git.ObjectID{
- setup.ObjectHash.EmptyTreeOID,
- setup.Commits.First.OID,
- setup.Commits.Second.OID,
- },
- ),
- "/wal/2/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
- },
- },
- },
- {
- desc: "non-git directories are not snapshotted",
- steps: steps{
- StartManager{},
- Begin{
- TransactionID: 1,
- // Try to snapshot the parent directory, which is no a valid Git directory.
- RelativePath: filepath.Dir(relativePath),
- ExpectedError: storage.InvalidGitDirectoryError{MissingEntry: "objects"},
- },
- },
- },
}
subTests := [][]transactionTestCase{
@@ -3702,6 +2228,7 @@ func TestTransactionManager(t *testing.T) {
generateModifyReferencesTests(t, setup),
generateCreateRepositoryTests(t, setup),
generateDeleteRepositoryTests(t, setup),
+ generateAlternateTests(t, setup),
}
for _, subCases := range subTests {
testCases = append(testCases, subCases...)