diff options
author | Quang-Minh Nguyen <qmnguyen@gitlab.com> | 2023-12-06 10:13:03 +0300 |
---|---|---|
committer | Quang-Minh Nguyen <qmnguyen@gitlab.com> | 2023-12-08 08:18:04 +0300 |
commit | 190e94fa5f05b01325a7f2da0cf474f6b0e85ae0 (patch) | |
tree | 75cc7e28e7a7b0c3d4d357f22fcdf62a3ff3b2e2 | |
parent | 6dbbea15a4d71c8d30ebdf4fff84082f85469782 (diff) |
Split tests related to custom hooks out of TransactionManager
-rw-r--r-- | internal/gitaly/storage/storagemgr/transaction_manager_hook_test.go | 465 | ||||
-rw-r--r-- | internal/gitaly/storage/storagemgr/transaction_manager_test.go | 455 |
2 files changed, 466 insertions, 454 deletions
diff --git a/internal/gitaly/storage/storagemgr/transaction_manager_hook_test.go b/internal/gitaly/storage/storagemgr/transaction_manager_hook_test.go index 95fceadd7..f849f1c6f 100644 --- a/internal/gitaly/storage/storagemgr/transaction_manager_hook_test.go +++ b/internal/gitaly/storage/storagemgr/transaction_manager_hook_test.go @@ -1,6 +1,7 @@ package storagemgr import ( + "io/fs" "regexp" "runtime" "strings" @@ -8,6 +9,9 @@ import ( "github.com/dgraph-io/badger/v4" "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitaly/v16/internal/git" + "gitlab.com/gitlab-org/gitaly/v16/internal/helper/perm" + "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper" ) // hookFunc is a function that is executed at a specific point. It gets a hookContext that allows it to @@ -170,3 +174,464 @@ type testingHook struct { func (t testingHook) FailNow() { t.Fail() } + +func generateCustomHooksTests(t *testing.T, setup testTransactionSetup) []transactionTestCase { + umask := testhelper.Umask() + + return []transactionTestCase{ + { + desc: "set custom hooks successfully", + steps: steps{ + StartManager{}, + Begin{ + TransactionID: 1, + RelativePath: setup.RelativePath, + }, + Commit{ + TransactionID: 1, + CustomHooksUpdate: &CustomHooksUpdate{ + CustomHooksTAR: validCustomHooks(t), + }, + }, + Begin{ + TransactionID: 2, + RelativePath: setup.RelativePath, + ExpectedSnapshotLSN: 1, + }, + Commit{ + TransactionID: 2, + CustomHooksUpdate: &CustomHooksUpdate{}, + }, + }, + expectedState: StateAssertion{ + Database: DatabaseState{ + string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(), + }, + Directory: testhelper.DirectoryState{ + "/": {Mode: fs.ModeDir | perm.PrivateDir}, + "/wal": {Mode: fs.ModeDir | perm.PrivateDir}, + }, + Repositories: RepositoryStates{ + setup.RelativePath: { + CustomHooks: testhelper.DirectoryState{ + "/": {Mode: fs.ModeDir | perm.PrivateDir}, + }, + }, + }, + }, + }, + { + desc: "rejects invalid custom hooks", + steps: steps{ + StartManager{}, + Begin{ + TransactionID: 1, + RelativePath: setup.RelativePath, + }, + Commit{ + TransactionID: 1, + CustomHooksUpdate: &CustomHooksUpdate{ + CustomHooksTAR: []byte("corrupted tar"), + }, + ExpectedError: func(tb testing.TB, actualErr error) { + require.ErrorContains(tb, actualErr, "stage hooks: extract hooks: waiting for tar command completion: exit status") + }, + }, + }, + }, + { + desc: "reapplying custom hooks works", + steps: steps{ + StartManager{ + Hooks: testTransactionHooks{ + BeforeStoreAppliedLSN: func(hookContext) { + panic(errSimulatedCrash) + }, + }, + ExpectedError: errSimulatedCrash, + }, + Begin{ + TransactionID: 1, + RelativePath: setup.RelativePath, + }, + Commit{ + TransactionID: 1, + CustomHooksUpdate: &CustomHooksUpdate{ + CustomHooksTAR: validCustomHooks(t), + }, + ExpectedError: ErrTransactionProcessingStopped, + }, + AssertManager{ + ExpectedError: errSimulatedCrash, + }, + StartManager{}, + }, + expectedState: StateAssertion{ + Database: DatabaseState{ + string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(), + }, + Directory: testhelper.DirectoryState{ + "/": {Mode: fs.ModeDir | perm.PrivateDir}, + "/wal": {Mode: fs.ModeDir | perm.PrivateDir}, + }, + Repositories: RepositoryStates{ + setup.RelativePath: { + CustomHooks: testhelper.DirectoryState{ + "/": {Mode: fs.ModeDir | perm.PrivateDir}, + "/pre-receive": { + Mode: umask.Mask(fs.ModePerm), + Content: []byte("hook content"), + }, + "/private-dir": {Mode: fs.ModeDir | perm.PrivateDir}, + "/private-dir/private-file": {Mode: umask.Mask(perm.PrivateFile), Content: []byte("private content")}, + }, + }, + }, + }, + }, + { + desc: "hook index is correctly determined from log and disk", + steps: steps{ + StartManager{ + Hooks: testTransactionHooks{ + BeforeApplyLogEntry: func(hookContext) { + panic(errSimulatedCrash) + }, + }, + ExpectedError: errSimulatedCrash, + }, + Begin{ + TransactionID: 1, + RelativePath: setup.RelativePath, + }, + Commit{ + TransactionID: 1, + CustomHooksUpdate: &CustomHooksUpdate{ + CustomHooksTAR: validCustomHooks(t), + }, + ExpectedError: ErrTransactionProcessingStopped, + }, + AssertManager{ + ExpectedError: errSimulatedCrash, + }, + StartManager{}, + Begin{ + TransactionID: 2, + RelativePath: setup.RelativePath, + ExpectedSnapshotLSN: 1, + }, + Commit{ + TransactionID: 2, + CustomHooksUpdate: &CustomHooksUpdate{}, + }, + Begin{ + TransactionID: 3, + RelativePath: setup.RelativePath, + ExpectedSnapshotLSN: 2, + }, + CloseManager{}, + StartManager{}, + Begin{ + TransactionID: 4, + RelativePath: setup.RelativePath, + ExpectedSnapshotLSN: 2, + }, + Rollback{ + TransactionID: 4, + }, + }, + expectedState: StateAssertion{ + Database: DatabaseState{ + string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(), + }, + Directory: testhelper.DirectoryState{ + "/": {Mode: fs.ModeDir | perm.PrivateDir}, + "/wal": {Mode: fs.ModeDir | perm.PrivateDir}, + }, + Repositories: RepositoryStates{ + setup.RelativePath: { + CustomHooks: testhelper.DirectoryState{ + "/": {Mode: fs.ModeDir | perm.PrivateDir}, + }, + }, + }, + }, + }, + { + desc: "continues processing after reference verification failure", + steps: steps{ + StartManager{}, + Begin{ + TransactionID: 1, + RelativePath: setup.RelativePath, + }, + Commit{ + TransactionID: 1, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID}, + }, + ExpectedError: ReferenceVerificationError{ + ReferenceName: "refs/heads/main", + ExpectedOID: setup.Commits.First.OID, + ActualOID: setup.ObjectHash.ZeroOID, + }, + }, + Begin{ + TransactionID: 2, + RelativePath: setup.RelativePath, + }, + Commit{ + TransactionID: 2, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID}, + }, + }, + }, + expectedState: StateAssertion{ + Database: DatabaseState{ + string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(), + }, + Repositories: RepositoryStates{ + setup.RelativePath: { + DefaultBranch: "refs/heads/main", + References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()}}, + }, + }, + }, + }, + { + desc: "continues processing after a restart", + steps: steps{ + StartManager{}, + Begin{ + TransactionID: 1, + RelativePath: setup.RelativePath, + }, + Commit{ + TransactionID: 1, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID}, + }, + }, + AssertManager{}, + CloseManager{}, + StartManager{}, + Begin{ + TransactionID: 2, + RelativePath: setup.RelativePath, + ExpectedSnapshotLSN: 1, + }, + Commit{ + TransactionID: 2, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID}, + }, + }, + }, + expectedState: StateAssertion{ + Database: DatabaseState{ + string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(), + }, + Repositories: RepositoryStates{ + setup.RelativePath: { + DefaultBranch: "refs/heads/main", + References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()}}, + }, + }, + }, + }, + { + desc: "continues processing after restarting after a reference verification failure", + steps: steps{ + StartManager{}, + Begin{ + TransactionID: 1, + RelativePath: setup.RelativePath, + }, + Commit{ + TransactionID: 1, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID}, + }, + ExpectedError: ReferenceVerificationError{ + ReferenceName: "refs/heads/main", + ExpectedOID: setup.Commits.First.OID, + ActualOID: setup.ObjectHash.ZeroOID, + }, + }, + CloseManager{}, + StartManager{}, + Begin{ + TransactionID: 2, + RelativePath: setup.RelativePath, + }, + Commit{ + TransactionID: 2, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID}, + }, + }, + }, + expectedState: StateAssertion{ + Database: DatabaseState{ + string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(), + }, + Repositories: RepositoryStates{ + setup.RelativePath: { + DefaultBranch: "refs/heads/main", + References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()}}, + }, + }, + }, + }, + { + desc: "continues processing after failing to store log index", + steps: steps{ + StartManager{ + Hooks: testTransactionHooks{ + BeforeStoreAppliedLSN: func(hookCtx hookContext) { + panic(errSimulatedCrash) + }, + }, + ExpectedError: errSimulatedCrash, + }, + Begin{ + TransactionID: 1, + RelativePath: setup.RelativePath, + }, + Commit{ + TransactionID: 1, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID}, + }, + ExpectedError: ErrTransactionProcessingStopped, + }, + AssertManager{ + ExpectedError: errSimulatedCrash, + }, + StartManager{}, + Begin{ + TransactionID: 2, + RelativePath: setup.RelativePath, + ExpectedSnapshotLSN: 1, + }, + Commit{ + TransactionID: 2, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID}, + }, + }, + }, + expectedState: StateAssertion{ + Database: DatabaseState{ + string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(), + }, + Repositories: RepositoryStates{ + setup.RelativePath: { + DefaultBranch: "refs/heads/main", + References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()}}, + }, + }, + }, + }, + { + desc: "recovers from the write-ahead log on start up", + steps: steps{ + StartManager{ + Hooks: testTransactionHooks{ + BeforeApplyLogEntry: func(hookCtx hookContext) { + hookCtx.closeManager() + }, + }, + }, + Begin{ + TransactionID: 1, + RelativePath: setup.RelativePath, + }, + Commit{ + TransactionID: 1, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID}, + }, + ExpectedError: ErrTransactionProcessingStopped, + }, + AssertManager{}, + StartManager{}, + Begin{ + TransactionID: 2, + RelativePath: setup.RelativePath, + ExpectedSnapshotLSN: 1, + }, + Commit{ + TransactionID: 2, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID}, + }, + }, + }, + expectedState: StateAssertion{ + Database: DatabaseState{ + string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(), + }, + Repositories: RepositoryStates{ + setup.RelativePath: { + DefaultBranch: "refs/heads/main", + References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()}}, + }, + }, + }, + }, + { + desc: "reference verification fails after recovering logged writes", + steps: steps{ + StartManager{ + Hooks: testTransactionHooks{ + BeforeApplyLogEntry: func(hookCtx hookContext) { + hookCtx.closeManager() + }, + }, + }, + Begin{ + TransactionID: 1, + RelativePath: setup.RelativePath, + }, + Commit{ + TransactionID: 1, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID}, + }, + ExpectedError: ErrTransactionProcessingStopped, + }, + AssertManager{}, + StartManager{}, + Begin{ + TransactionID: 2, + RelativePath: setup.RelativePath, + ExpectedSnapshotLSN: 1, + }, + Commit{ + TransactionID: 2, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.Commits.Second.OID, NewOID: setup.Commits.First.OID}, + }, + ExpectedError: ReferenceVerificationError{ + ReferenceName: "refs/heads/main", + ExpectedOID: setup.Commits.Second.OID, + ActualOID: setup.Commits.First.OID, + }, + }, + }, + expectedState: StateAssertion{ + Database: DatabaseState{ + string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(), + }, + Repositories: RepositoryStates{ + setup.RelativePath: { + DefaultBranch: "refs/heads/main", + References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.First.OID.String()}}, + }, + }, + }, + }, + } +} diff --git a/internal/gitaly/storage/storagemgr/transaction_manager_test.go b/internal/gitaly/storage/storagemgr/transaction_manager_test.go index 43cb6f2cb..fb0bea1ea 100644 --- a/internal/gitaly/storage/storagemgr/transaction_manager_test.go +++ b/internal/gitaly/storage/storagemgr/transaction_manager_test.go @@ -250,460 +250,6 @@ func TestTransactionManager(t *testing.T) { testCases := []transactionTestCase{ { - desc: "set custom hooks successfully", - steps: steps{ - StartManager{}, - Begin{ - TransactionID: 1, - RelativePath: relativePath, - }, - Commit{ - TransactionID: 1, - CustomHooksUpdate: &CustomHooksUpdate{ - CustomHooksTAR: validCustomHooks(t), - }, - }, - Begin{ - TransactionID: 2, - RelativePath: relativePath, - ExpectedSnapshotLSN: 1, - }, - Commit{ - TransactionID: 2, - CustomHooksUpdate: &CustomHooksUpdate{}, - }, - }, - expectedState: StateAssertion{ - Database: DatabaseState{ - string(keyAppliedLSN(partitionID)): LSN(2).toProto(), - }, - Directory: testhelper.DirectoryState{ - "/": {Mode: fs.ModeDir | perm.PrivateDir}, - "/wal": {Mode: fs.ModeDir | perm.PrivateDir}, - }, - Repositories: RepositoryStates{ - relativePath: { - CustomHooks: testhelper.DirectoryState{ - "/": {Mode: fs.ModeDir | perm.PrivateDir}, - }, - }, - }, - }, - }, - { - desc: "rejects invalid custom hooks", - steps: steps{ - StartManager{}, - Begin{ - TransactionID: 1, - RelativePath: relativePath, - }, - Commit{ - TransactionID: 1, - CustomHooksUpdate: &CustomHooksUpdate{ - CustomHooksTAR: []byte("corrupted tar"), - }, - ExpectedError: func(tb testing.TB, actualErr error) { - require.ErrorContains(tb, actualErr, "stage hooks: extract hooks: waiting for tar command completion: exit status") - }, - }, - }, - }, - { - desc: "reapplying custom hooks works", - steps: steps{ - StartManager{ - Hooks: testTransactionHooks{ - BeforeStoreAppliedLSN: func(hookContext) { - panic(errSimulatedCrash) - }, - }, - ExpectedError: errSimulatedCrash, - }, - Begin{ - TransactionID: 1, - RelativePath: relativePath, - }, - Commit{ - TransactionID: 1, - CustomHooksUpdate: &CustomHooksUpdate{ - CustomHooksTAR: validCustomHooks(t), - }, - ExpectedError: ErrTransactionProcessingStopped, - }, - AssertManager{ - ExpectedError: errSimulatedCrash, - }, - StartManager{}, - }, - expectedState: StateAssertion{ - Database: DatabaseState{ - string(keyAppliedLSN(partitionID)): LSN(1).toProto(), - }, - Directory: testhelper.DirectoryState{ - "/": {Mode: fs.ModeDir | perm.PrivateDir}, - "/wal": {Mode: fs.ModeDir | perm.PrivateDir}, - }, - Repositories: RepositoryStates{ - relativePath: { - CustomHooks: testhelper.DirectoryState{ - "/": {Mode: fs.ModeDir | perm.PrivateDir}, - "/pre-receive": { - Mode: umask.Mask(fs.ModePerm), - Content: []byte("hook content"), - }, - "/private-dir": {Mode: fs.ModeDir | perm.PrivateDir}, - "/private-dir/private-file": {Mode: umask.Mask(perm.PrivateFile), Content: []byte("private content")}, - }, - }, - }, - }, - }, - { - desc: "hook index is correctly determined from log and disk", - steps: steps{ - StartManager{ - Hooks: testTransactionHooks{ - BeforeApplyLogEntry: func(hookContext) { - panic(errSimulatedCrash) - }, - }, - ExpectedError: errSimulatedCrash, - }, - Begin{ - TransactionID: 1, - RelativePath: relativePath, - }, - Commit{ - TransactionID: 1, - CustomHooksUpdate: &CustomHooksUpdate{ - CustomHooksTAR: validCustomHooks(t), - }, - ExpectedError: ErrTransactionProcessingStopped, - }, - AssertManager{ - ExpectedError: errSimulatedCrash, - }, - StartManager{}, - Begin{ - TransactionID: 2, - RelativePath: relativePath, - ExpectedSnapshotLSN: 1, - }, - Commit{ - TransactionID: 2, - CustomHooksUpdate: &CustomHooksUpdate{}, - }, - Begin{ - TransactionID: 3, - RelativePath: relativePath, - ExpectedSnapshotLSN: 2, - }, - CloseManager{}, - StartManager{}, - Begin{ - TransactionID: 4, - RelativePath: relativePath, - ExpectedSnapshotLSN: 2, - }, - Rollback{ - TransactionID: 4, - }, - }, - expectedState: StateAssertion{ - Database: DatabaseState{ - string(keyAppliedLSN(partitionID)): LSN(2).toProto(), - }, - Directory: testhelper.DirectoryState{ - "/": {Mode: fs.ModeDir | perm.PrivateDir}, - "/wal": {Mode: fs.ModeDir | perm.PrivateDir}, - }, - Repositories: RepositoryStates{ - relativePath: { - CustomHooks: testhelper.DirectoryState{ - "/": {Mode: fs.ModeDir | perm.PrivateDir}, - }, - }, - }, - }, - }, - { - desc: "continues processing after reference verification failure", - steps: steps{ - StartManager{}, - Begin{ - TransactionID: 1, - RelativePath: relativePath, - }, - Commit{ - TransactionID: 1, - ReferenceUpdates: ReferenceUpdates{ - "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID}, - }, - ExpectedError: ReferenceVerificationError{ - ReferenceName: "refs/heads/main", - ExpectedOID: setup.Commits.First.OID, - ActualOID: setup.ObjectHash.ZeroOID, - }, - }, - Begin{ - TransactionID: 2, - RelativePath: relativePath, - }, - Commit{ - TransactionID: 2, - ReferenceUpdates: ReferenceUpdates{ - "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID}, - }, - }, - }, - expectedState: StateAssertion{ - Database: DatabaseState{ - string(keyAppliedLSN(partitionID)): LSN(1).toProto(), - }, - Repositories: RepositoryStates{ - relativePath: { - DefaultBranch: "refs/heads/main", - References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()}}, - }, - }, - }, - }, - { - desc: "continues processing after a restart", - steps: steps{ - StartManager{}, - Begin{ - TransactionID: 1, - RelativePath: relativePath, - }, - Commit{ - TransactionID: 1, - ReferenceUpdates: ReferenceUpdates{ - "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID}, - }, - }, - AssertManager{}, - CloseManager{}, - StartManager{}, - Begin{ - TransactionID: 2, - RelativePath: relativePath, - ExpectedSnapshotLSN: 1, - }, - Commit{ - TransactionID: 2, - ReferenceUpdates: ReferenceUpdates{ - "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID}, - }, - }, - }, - expectedState: StateAssertion{ - Database: DatabaseState{ - string(keyAppliedLSN(partitionID)): LSN(2).toProto(), - }, - Repositories: RepositoryStates{ - relativePath: { - DefaultBranch: "refs/heads/main", - References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()}}, - }, - }, - }, - }, - { - desc: "continues processing after restarting after a reference verification failure", - steps: steps{ - StartManager{}, - Begin{ - TransactionID: 1, - RelativePath: relativePath, - }, - Commit{ - TransactionID: 1, - ReferenceUpdates: ReferenceUpdates{ - "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID}, - }, - ExpectedError: ReferenceVerificationError{ - ReferenceName: "refs/heads/main", - ExpectedOID: setup.Commits.First.OID, - ActualOID: setup.ObjectHash.ZeroOID, - }, - }, - CloseManager{}, - StartManager{}, - Begin{ - TransactionID: 2, - RelativePath: relativePath, - }, - Commit{ - TransactionID: 2, - ReferenceUpdates: ReferenceUpdates{ - "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID}, - }, - }, - }, - expectedState: StateAssertion{ - Database: DatabaseState{ - string(keyAppliedLSN(partitionID)): LSN(1).toProto(), - }, - Repositories: RepositoryStates{ - relativePath: { - DefaultBranch: "refs/heads/main", - References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()}}, - }, - }, - }, - }, - { - desc: "continues processing after failing to store log index", - steps: steps{ - StartManager{ - Hooks: testTransactionHooks{ - BeforeStoreAppliedLSN: func(hookCtx hookContext) { - panic(errSimulatedCrash) - }, - }, - ExpectedError: errSimulatedCrash, - }, - Begin{ - TransactionID: 1, - RelativePath: relativePath, - }, - Commit{ - TransactionID: 1, - ReferenceUpdates: ReferenceUpdates{ - "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID}, - }, - ExpectedError: ErrTransactionProcessingStopped, - }, - AssertManager{ - ExpectedError: errSimulatedCrash, - }, - StartManager{}, - Begin{ - TransactionID: 2, - RelativePath: relativePath, - ExpectedSnapshotLSN: 1, - }, - Commit{ - TransactionID: 2, - ReferenceUpdates: ReferenceUpdates{ - "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID}, - }, - }, - }, - expectedState: StateAssertion{ - Database: DatabaseState{ - string(keyAppliedLSN(partitionID)): LSN(2).toProto(), - }, - Repositories: RepositoryStates{ - relativePath: { - DefaultBranch: "refs/heads/main", - References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()}}, - }, - }, - }, - }, - { - desc: "recovers from the write-ahead log on start up", - steps: steps{ - StartManager{ - Hooks: testTransactionHooks{ - BeforeApplyLogEntry: func(hookCtx hookContext) { - hookCtx.closeManager() - }, - }, - }, - Begin{ - TransactionID: 1, - RelativePath: relativePath, - }, - Commit{ - TransactionID: 1, - ReferenceUpdates: ReferenceUpdates{ - "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID}, - }, - ExpectedError: ErrTransactionProcessingStopped, - }, - AssertManager{}, - StartManager{}, - Begin{ - TransactionID: 2, - RelativePath: relativePath, - ExpectedSnapshotLSN: 1, - }, - Commit{ - TransactionID: 2, - ReferenceUpdates: ReferenceUpdates{ - "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID}, - }, - }, - }, - expectedState: StateAssertion{ - Database: DatabaseState{ - string(keyAppliedLSN(partitionID)): LSN(2).toProto(), - }, - Repositories: RepositoryStates{ - relativePath: { - DefaultBranch: "refs/heads/main", - References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()}}, - }, - }, - }, - }, - { - desc: "reference verification fails after recovering logged writes", - steps: steps{ - StartManager{ - Hooks: testTransactionHooks{ - BeforeApplyLogEntry: func(hookCtx hookContext) { - hookCtx.closeManager() - }, - }, - }, - Begin{ - TransactionID: 1, - RelativePath: relativePath, - }, - Commit{ - TransactionID: 1, - ReferenceUpdates: ReferenceUpdates{ - "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID}, - }, - ExpectedError: ErrTransactionProcessingStopped, - }, - AssertManager{}, - StartManager{}, - Begin{ - TransactionID: 2, - RelativePath: relativePath, - ExpectedSnapshotLSN: 1, - }, - Commit{ - TransactionID: 2, - ReferenceUpdates: ReferenceUpdates{ - "refs/heads/main": {OldOID: setup.Commits.Second.OID, NewOID: setup.Commits.First.OID}, - }, - ExpectedError: ReferenceVerificationError{ - ReferenceName: "refs/heads/main", - ExpectedOID: setup.Commits.Second.OID, - ActualOID: setup.Commits.First.OID, - }, - }, - }, - expectedState: StateAssertion{ - Database: DatabaseState{ - string(keyAppliedLSN(partitionID)): LSN(1).toProto(), - }, - Repositories: RepositoryStates{ - relativePath: { - DefaultBranch: "refs/heads/main", - References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.First.OID.String()}}, - }, - }, - }, - }, - { desc: "begin returns if context is canceled before initialization", steps: steps{ Begin{ @@ -2229,6 +1775,7 @@ func TestTransactionManager(t *testing.T) { generateCreateRepositoryTests(t, setup), generateDeleteRepositoryTests(t, setup), generateAlternateTests(t, setup), + generateCustomHooksTests(t, setup), } for _, subCases := range subTests { testCases = append(testCases, subCases...) |