protected virtual void generateDepositAction(int depositValue) { var transaction = new TransactionRecord("Banking", _instanceCount++); var step = new TransactionStep( "Player", "Deposited funds", "Balance updated", depositValue ); transaction.addStep(step); _ledger.appendTransaction(transaction); }
protected virtual void generateWithdrawAction(int withdrawValue) { var transaction = new TransactionRecord("Banking", _instanceCount++); var step = new TransactionStep( "Player", "Withdrew funds", "Balance zeroed", -withdrawValue ); transaction.addStep(step); _ledger.appendTransaction(transaction); }
public void WhenRunRecoveredTransaction_ShouldProcessStepsProperly() { // Arrange object transactionData = new object(); List <string> runStepActions = new List <string>(); List <string> runUndoActions = new List <string>(); List <string> runPostActions = new List <string>(); Mock <ITransactionStorage <object> > storageMock = new Mock <ITransactionStorage <object> >(); Mock <ITransactionData <object> > recoveredData = new Mock <ITransactionData <object> >(); recoveredData.SetupGet(x => x.CurrentStepIndex) .Returns(2); recoveredData.SetupGet(x => x.Data) .Returns(transactionData); recoveredData.SetupGet(x => x.SessionId) .Returns(Guid.NewGuid()); recoveredData.SetupGet(x => x.StartTimestamp) .Returns(DateTime.Now); storageMock.Setup(x => x.RecoverTransaction()) .Returns(recoveredData.Object); ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; options.TransactionStorageCreator = context => storageMock.Object; }); for (int i = 0; i < 6; ++i) { string index = i.ToString(); TransactionStep <string, object> step = new TransactionStep <string, object>() { Id = index, Settings = StepSettings.None }; step.StepAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); }; step.UndoAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); }; step.PostAction = (data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); info.CurrentStepId.Should().Be(index); }; target.Add(step); } ITransactionResult <object> result = null; /* * 0: * 1: * 2: recovered step * 3: * 4: * 5: */ // Act target.Run(settings => { settings.Data = transactionData; settings.Mode = RunMode.RecoverAndUndoAndRun; settings.TransactionResultCallback = callbackResult => result = callbackResult; }); // Assert result.Data.Should().BeSameAs(transactionData); result.Errors.ShouldAllBeEquivalentTo(new Exception[0]); result.Recovered.Should().BeTrue(); result.Result.Should().Be(ResultType.Success); runStepActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4", "5" }); runUndoActions.ShouldAllBeEquivalentTo(new string[] { "2", "1", "0" }); runPostActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4", "5" }); storageMock.AssertStorageOperations(new AssertStorageOperationsContext <string, object>() { SessionStartedTimes = Times.Never(), TransactionData = transactionData, Transaction = target, ExpectedStepsOrder = runStepActions, ExpectedUndoOrder = runUndoActions }); }
public void WhenRunRecoveredTransactionWithCurrentStepAsFirstStepWithExecutor_ShouldProcessStepsProperly() { // Arrange object transactionData = new object(); List <string> runStepActions = new List <string>(); List <string> runUndoActions = new List <string>(); List <string> runPostActions = new List <string>(); List <int> stepActionThreadId = new List <int>(); List <int> undoActionThreadId = new List <int>(); List <int> postActionThreadId = new List <int>(); int transactionCallbackThreadId = 0; int transactionRunThreadId = Thread.CurrentThread.ManagedThreadId; Dictionary <int, TestExecutor> stepExecutors = new Dictionary <int, TestExecutor>() { { 0, null }, { 1, null }, { 2, new TestExecutor() { ShouldRun = true } }, { 3, new TestExecutor() { ShouldRun = true } }, { 4, new TestExecutor() { ShouldRun = true } }, { 5, null }, { 6, null } }; Dictionary <int, TestExecutor> undoExecutors = new Dictionary <int, TestExecutor>() { { 0, new TestExecutor() { ShouldRun = true } }, { 1, null }, { 2, new TestExecutor() { ShouldRun = true } }, { 3, null }, { 4, new TestExecutor() { ShouldRun = true } }, { 5, null }, { 6, null } }; Dictionary <int, TestExecutor> postExecutors = new Dictionary <int, TestExecutor>() { { 0, null }, { 1, null }, { 2, null }, { 3, new TestExecutor() { ShouldRun = true } }, { 4, null }, { 5, null }, { 6, null } }; TestExecutor callBackExecutor = new TestExecutor() { ShouldRun = true }; Mock <ITransactionStorage <object> > storageMock = new Mock <ITransactionStorage <object> >(); Mock <ITransactionData <object> > recoveredData = new Mock <ITransactionData <object> >(); recoveredData.SetupGet(x => x.CurrentStepIndex) .Returns(0); recoveredData.SetupGet(x => x.Data) .Returns(transactionData); recoveredData.SetupGet(x => x.SessionId) .Returns(Guid.NewGuid()); recoveredData.SetupGet(x => x.StartTimestamp) .Returns(DateTime.Now); storageMock.Setup(x => x.RecoverTransaction()) .Returns(recoveredData.Object); ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; options.TransactionStorageCreator = context => storageMock.Object; }); for (int i = 0; i < 7; ++i) { string index = i.ToString(); TransactionStep <string, object> step = new TransactionStep <string, object>() { Id = index, StepActionExecutor = stepExecutors[i], UndoActionExecutor = undoExecutors[i], PostActionExecutor = postExecutors[i], Settings = StepSettings.None }; step.StepAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); stepActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); }; step.UndoAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); undoActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); }; step.PostAction = (data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); postActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); info.CurrentStepId.Should().Be(index); }; target.Add(step); } ITransactionResult <object> result = null; /* * 0: recovered step, executor for other thread (undo action) * 1: * 2: executor for other thread (step action), executor for other thread (undo action) * 3: executor for other thread (step action), executor for other thread (post action) * 4: executor for other thread (step action), executor for other thread (undo action) * 5: * 6: */ // Act using (ManualResetEvent transactionEndResetEvent = new ManualResetEvent(false)) { target.Run(settings => { settings.Data = transactionData; settings.Mode = RunMode.RecoverAndUndoAndRun; settings.TransactionResultCallback = callbackResult => { transactionCallbackThreadId = Thread.CurrentThread.ManagedThreadId; result = callbackResult; transactionEndResetEvent.Set(); }; settings.TransactionResultCallbackExecutor = callBackExecutor; }); transactionEndResetEvent.WaitOne(); } // Assert callBackExecutor.Dispose(); foreach (TestExecutor executor in stepExecutors.Values.Concat(postExecutors.Values).Where(x => x != null)) { executor.Dispose(); } result.Data.Should().BeSameAs(transactionData); result.Errors.ShouldAllBeEquivalentTo(new Exception[0]); result.Recovered.Should().BeTrue(); result.Result.Should().Be(ResultType.Success); callBackExecutor.Verify(Times.Once, Times.Once); foreach (TestExecutor executor in stepExecutors.Values.Concat(postExecutors.Values).Where(x => x != null)) { executor.Verify(Times.Once, Times.Once); } runStepActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4", "5", "6" }); runUndoActions.ShouldAllBeEquivalentTo(new string[] { "0" }); runPostActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4", "5", "6" }); stepActionThreadId.ShouldAllBeEquivalentTo(new int[] { /*step 0*/ undoExecutors[0].ThreadId, /*step 1*/ undoExecutors[0].ThreadId, /*step 2*/ stepExecutors[2].ThreadId, /*step 3*/ stepExecutors[3].ThreadId, /*step 4*/ stepExecutors[4].ThreadId, /*step 5*/ stepExecutors[4].ThreadId, /*step 6*/ stepExecutors[4].ThreadId }); undoActionThreadId.ShouldAllBeEquivalentTo(new int[] { undoExecutors[0].ThreadId }); postActionThreadId.ShouldAllBeEquivalentTo(new int[] { /*step 0*/ stepExecutors[4].ThreadId, /*step 1*/ stepExecutors[4].ThreadId, /*step 2*/ stepExecutors[4].ThreadId, /*step 3*/ postExecutors[3].ThreadId, /*step 4*/ postExecutors[3].ThreadId, /*step 5*/ postExecutors[3].ThreadId, /*step 6*/ postExecutors[3].ThreadId }); transactionCallbackThreadId.Should().Be(callBackExecutor.ThreadId); storageMock.AssertStorageOperations(new AssertStorageOperationsContext <string, object>() { SessionStartedTimes = Times.Never(), TransactionData = transactionData, Transaction = target, ExpectedStepsOrder = runStepActions, ExpectedUndoOrder = runUndoActions }); }
public async Task WhenRunTransactionWithStepExecutorsWithSameExecutorForAllActions_ShouldRunAllStepsProperly() { // Arrange object transactionData = new object(); List <string> runStepActions = new List <string>(); List <string> runUndoActions = new List <string>(); List <string> runPostActions = new List <string>(); List <int> stepActionThreadId = new List <int>(); List <int> postActionThreadId = new List <int>(); int transactionCallbackThreadId = 0; Dictionary <int, TestExecutor> stepExecutors = new Dictionary <int, TestExecutor>() { { 0, null }, { 1, new TestExecutor() { ShouldRun = true } }, { 2, null }, { 3, new TestExecutor() { ShouldRun = true } }, { 4, new TestExecutor() { ShouldRun = true } }, { 5, null }, { 6, null } }; Dictionary <int, TestExecutor> postExecutors = new Dictionary <int, TestExecutor>() { { 0, null }, { 1, null }, { 2, new TestExecutor() { ShouldRun = true } }, { 3, new TestExecutor() { ShouldRun = true } }, { 4, null }, { 5, null }, { 6, null } }; int transactionRunThreadId = Thread.CurrentThread.ManagedThreadId; int step1StepActionThreadId = stepExecutors[1].ThreadId; int step2PostActionThreadId = postExecutors[2].ThreadId; int step3StepActionThreadId = stepExecutors[3].ThreadId; int step3PostActionThreadId = postExecutors[3].ThreadId; int step4StepActionThreadId = stepExecutors[4].ThreadId; ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; }); for (int i = 0; i < 7; ++i) { string index = i.ToString(); TransactionStep <string, object> step = new TransactionStep <string, object>() { Id = index, StepActionExecutor = stepExecutors[i] == null ? null : stepExecutors[i], PostActionExecutor = postExecutors[i] == null ? null : postExecutors[i], Settings = i == 1 ? StepSettings.SameExecutorForAllActions : StepSettings.None }; if (i == 3 || i == 4) { step.AsyncStepAction = async(data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); stepActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); await Task.CompletedTask; }; } else { step.StepAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); stepActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); }; } if (i % 4 == 0) { step.AsyncUndoAction = async(data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); await Task.CompletedTask; }; } else { step.UndoAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); }; } if (i == 1 || i == 2) { step.AsyncPostAction = async(data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); postActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); info.CurrentStepId.Should().Be(index); await Task.CompletedTask; }; } else { step.PostAction = (data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); postActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); info.CurrentStepId.Should().Be(index); }; } target.Add(step); } ITransactionResult <object> transactionCallbackResult = null; /* * 0: * 1: executor for other thread (step action), SameExecutorForAllActions * 2: executor for other thread (post action) * 3: executor for other thread (step action), executor for other thread (post action) * 4: executor for other thread (step action) * 5: * 6: */ // Act ITransactionResult <object> result = await target.Run(settings => { settings.Data = transactionData; settings.Mode = RunMode.Run; settings.TransactionResultCallback = callbackResult => { transactionCallbackThreadId = Thread.CurrentThread.ManagedThreadId; transactionCallbackResult = callbackResult; }; }); // Assert foreach (TestExecutor executor in stepExecutors.Values.Concat(postExecutors.Values).Where(x => x != null)) { executor.Dispose(); } result.Should().BeSameAs(transactionCallbackResult); result.Data.Should().BeSameAs(transactionData); result.Errors.ShouldAllBeEquivalentTo(new Exception[0]); result.Recovered.Should().BeFalse(); result.Result.Should().Be(ResultType.Success); TestExecutor sharedExecutor = stepExecutors[1]; sharedExecutor.Verify(Times.Exactly(2), Times.Exactly(2)); foreach (TestExecutor executor in stepExecutors.Values.Concat(postExecutors.Values).Where(x => x != null && x != sharedExecutor)) { executor.Verify(Times.Once, Times.Once); } runStepActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4", "5", "6" }); runUndoActions.ShouldAllBeEquivalentTo(new string[0]); runPostActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4", "5", "6" }); stepActionThreadId.ShouldAllBeEquivalentTo(new int[] { /*step 0*/ transactionRunThreadId, /*step 1*/ step1StepActionThreadId, /*step 2*/ step1StepActionThreadId, /*step 3*/ step3StepActionThreadId, /*step 4*/ step4StepActionThreadId, /*step 5*/ step4StepActionThreadId, /*step 6*/ step4StepActionThreadId }); postActionThreadId.ShouldAllBeEquivalentTo(new int[] { /*step 0*/ step4StepActionThreadId, /*step 1*/ step1StepActionThreadId, /*step 2*/ step2PostActionThreadId, /*step 3*/ step3PostActionThreadId, /*step 4*/ step3PostActionThreadId, /*step 5*/ step3PostActionThreadId, /*step 6*/ step3PostActionThreadId }); transactionCallbackThreadId.Should().Be(step3PostActionThreadId); }
public async Task WhenRunTransaction_ShouldRunAllStepsProperly() { // Arrange object transactionData = new object(); List <string> runStepActions = new List <string>(); List <string> runUndoActions = new List <string>(); List <string> runPostActions = new List <string>(); Mock <ITransactionStorage <object> > storageMock = new Mock <ITransactionStorage <object> >(); ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; options.TransactionStorageCreator = partContext => storageMock.Object; }); for (int i = 0; i < 5; ++i) { string index = i.ToString(); TransactionStep <string, object> step = new TransactionStep <string, object>() { Id = index }; if (i % 3 == 0) { step.AsyncStepAction = async(data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); await Task.CompletedTask; }; } else { step.StepAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); }; } if (i % 4 == 0) { step.AsyncUndoAction = async(data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); await Task.CompletedTask; }; } else { step.UndoAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); }; } if (i % 2 == 0) { step.AsyncPostAction = async(data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); info.CurrentStepId.Should().Be(index); await Task.CompletedTask; }; } else { step.PostAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runPostActions.Add(index); }; } target.Add(step); } ITransactionResult <object> transactionCallbackResult = null; /* * 0: * 1: * 2: * 3: * 4: */ // Act ITransactionResult <object> result = await target.Run(settings => { settings.Data = transactionData; settings.Mode = RunMode.Run; settings.TransactionResultCallback = callbackResult => transactionCallbackResult = callbackResult; }); // Assert result.Should().BeSameAs(transactionCallbackResult); result.Data.Should().BeSameAs(transactionData); result.Errors.ShouldAllBeEquivalentTo(new Exception[0]); result.Recovered.Should().BeFalse(); result.Result.Should().Be(ResultType.Success); runStepActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4" }); runUndoActions.ShouldAllBeEquivalentTo(new string[0]); runPostActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4" }); storageMock.AssertStorageOperations(new AssertStorageOperationsContext <string, object>() { TransactionData = transactionData, Transaction = target, ExpectedStepsOrder = runStepActions, ExpectedUndoOrder = runUndoActions }); }
public Schedule Parse(string input) { // todo: this method handles both tokenization and parsing. split into two. var stepsInput = input .Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries) .Where(i => !string.IsNullOrWhiteSpace(i)); var steps = new List <TransactionStep>(); var transactions = new Dictionary <Object, Transaction>(); var targets = new List <object>(); foreach (var stepInput in stepsInput) { var trimmedInput = stepInput.Trim(); if (trimmedInput.Length <= 4) { throw new FormatException("Invalid step format: " + stepInput); } var operation = trimmedInput[0]; var openParen = trimmedInput.IndexOf('('); var transaction = trimmedInput.Substring(1, openParen - 1); if (!transactions.ContainsKey(transaction)) { transactions.Add(transaction, new Transaction { ID = transaction }); } var start = openParen + 1; var target = trimmedInput.Substring(start, trimmedInput.LastIndexOf(')') - start); if (!targets.Any(t => t.Equals(target))) { targets.Add(target); } var step = new TransactionStep() { Transaction = transactions[transaction], Operation = 'w'.Equals(char.ToLower(operation)) ? Operation.Write : Operation.Read, Target = targets.First(t => Equals(t, target)) }; transactions[transaction].Steps.Add(step); steps.Add(step); } return(new Schedule() { Steps = steps.ToArray() }); }
/// <summary> /// Метод, откатывающий транзакцию. /// </summary> /// <param name="step"></param> public abstract void Rollback(TransactionStep step);
public override void Rollback(TransactionStep step) { step.Rollback(); }
public void WhenGoForwardWithComparerWithCompareFailure_ShouldFailTransaction() { // Arrange object transactionData = new object(); List <string> runStepActions = new List <string>(); List <string> runUndoActions = new List <string>(); List <string> runPostActions = new List <string>(); Mock <ITransactionStorage <object> > storageMock = new Mock <ITransactionStorage <object> >(); ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; options.TransactionStorageCreator = partContext => storageMock.Object; }); for (int i = 0; i < 5; ++i) { string index = "A" + i.ToString(); TransactionStep <string, object> step = new TransactionStep <string, object>() { Id = index }; step.StepAction = (data, info) => { if (info.CurrentStepId == "A1") { info.GoForward("a4", StringComparer.Ordinal); } data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); }; step.UndoAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); }; step.PostAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runPostActions.Add(index); }; target.Add(step); } /* * 0: * 1: go forward to step 4 * 2: * 3: * 4: */ // Act ITransactionResult <object> result = null; target.Run(settings => { settings.Data = transactionData; settings.Mode = RunMode.Run; settings.TransactionResultCallback = callbackResult => result = callbackResult; }); // Assert result.Data.Should().BeSameAs(transactionData); result.Errors.Count().Should().Be(1); result.Errors.First().Message.Contains("Could not move forward to a step with id 'a4' as the step does not exist.").Should().BeTrue(); result.Recovered.Should().BeFalse(); result.Result.Should().Be(ResultType.Failed); runStepActions.ShouldAllBeEquivalentTo(new string[] { "A0", "A1" }); runUndoActions.ShouldAllBeEquivalentTo(new string[] { "A1", "A0" }); runPostActions.ShouldAllBeEquivalentTo(new string[0]); storageMock.AssertStorageOperations(new AssertStorageOperationsContext <string, object>() { TransactionData = transactionData, Transaction = target, ExpectedStepsOrder = runStepActions, ExpectedUndoOrder = runUndoActions }); }
public void WhenGoForwardWithoutComparer_ShouldMoveToStepProperly() { // Arrange object transactionData = new object(); List <string> runStepActions = new List <string>(); List <string> runUndoActions = new List <string>(); List <string> runPostActions = new List <string>(); Mock <ITransactionStorage <object> > storageMock = new Mock <ITransactionStorage <object> >(); ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; options.TransactionStorageCreator = partContext => storageMock.Object; }); for (int i = 0; i < 5; ++i) { string index = i.ToString(); TransactionStep <string, object> step = new TransactionStep <string, object>() { Id = index }; step.StepAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); if (info.CurrentStepId == "1") { info.GoForward("4"); } runStepActions.Add(index); }; step.UndoAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); }; step.PostAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runPostActions.Add(index); }; target.Add(step); } /* * 0: * 1: go forward to step 4 * 2: * 3: * 4: */ // Act ITransactionResult <object> result = null; target.Run(settings => { settings.Data = transactionData; settings.Mode = RunMode.Run; settings.TransactionResultCallback = callbackResult => result = callbackResult; }); // Assert result.Data.Should().BeSameAs(transactionData); result.Errors.ShouldAllBeEquivalentTo(new Exception[0]); result.Recovered.Should().BeFalse(); result.Result.Should().Be(ResultType.Success); runStepActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "4" }); runUndoActions.ShouldAllBeEquivalentTo(new string[0]); runPostActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4" }); storageMock.AssertStorageOperations(new AssertStorageOperationsContext <string, object>() { TransactionData = transactionData, Transaction = target, ExpectedStepsOrder = runStepActions, ExpectedUndoOrder = runUndoActions }); }
public async Task WhenRunRecoveredTransactionWittCurrentStepWithUndoOnRecoverSettingAndPreviousStepsWithUndoOnRecover_ShouldUndoTheSteps() { // Arrange object transactionData = new object(); List <string> runStepActions = new List <string>(); List <string> runUndoActions = new List <string>(); List <string> runPostActions = new List <string>(); Mock <ITransactionStorage <object> > storageMock = new Mock <ITransactionStorage <object> >(); Mock <ITransactionData <object> > recoveredData = new Mock <ITransactionData <object> >(); recoveredData.SetupGet(x => x.CurrentStepIndex) .Returns(3); recoveredData.SetupGet(x => x.Data) .Returns(transactionData); recoveredData.SetupGet(x => x.SessionId) .Returns(Guid.NewGuid()); recoveredData.SetupGet(x => x.StartTimestamp) .Returns(DateTime.Now); storageMock.Setup(x => x.RecoverTransaction()) .Returns(Task.FromResult <ITransactionData <object> >(recoveredData.Object)); ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; options.TransactionStorageCreator = context => storageMock.Object; }); for (int i = 0; i < 6; ++i) { string index = i.ToString(); TransactionStep <string, object> step = new TransactionStep <string, object>() { Id = index, Settings = i == 1 || i == 2 || i == 3 ? StepSettings.UndoOnRecover : StepSettings.None }; if (i % 3 == 0) { step.AsyncStepAction = async(data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); await Task.CompletedTask; }; } else { step.StepAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); }; } if (i % 2 == 0) { step.AsyncUndoAction = async(data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); await Task.CompletedTask; }; } else { step.UndoAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); }; } if (i % 3 == 0) { step.AsyncPostAction = async(data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); info.CurrentStepId.Should().Be(index); await Task.CompletedTask; }; } else { step.PostAction = (data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); info.CurrentStepId.Should().Be(index); }; } target.Add(step); } ITransactionResult <object> transactionCallbackResult = null; /* * 0: * 1: undo on recover * 2: undo on recover * 3: recovered step, undo on recover * 4: * 5: */ // Act ITransactionResult <object> result = await target.Run(settings => { settings.Data = transactionData; settings.Mode = RunMode.RecoverAndContinue; settings.TransactionResultCallback = callbackResult => transactionCallbackResult = callbackResult; }); // Assert result.Should().BeSameAs(transactionCallbackResult); result.Data.Should().BeSameAs(transactionData); result.Errors.ShouldAllBeEquivalentTo(new Exception[0]); result.Recovered.Should().BeTrue(); result.Result.Should().Be(ResultType.Success); runStepActions.ShouldAllBeEquivalentTo(new string[] { "1", "2", "3", "4", "5" }); runUndoActions.ShouldAllBeEquivalentTo(new string[] { "3", "2", "1" }); runPostActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4", "5" }); storageMock.AssertStorageOperations(new AssertStorageOperationsContext <string, object>() { SessionStartedTimes = Times.Never(), TransactionData = transactionData, Transaction = target, ExpectedStepsOrder = runStepActions, ExpectedUndoOrder = runUndoActions }); }
public async Task WhenCancellTransaction_ShouldCancelProperly() { // Arrange object transactionData = new object(); List <string> runStepActions = new List <string>(); List <string> runUndoActions = new List <string>(); List <string> runPostActions = new List <string>(); ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; }); for (int i = 0; i < 5; ++i) { string index = i.ToString(); TransactionStep <string, object> step = new TransactionStep <string, object>() { Id = index }; if (i % 3 == 0) { step.AsyncStepAction = async(data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); if (index == "3") { info.Cancel(); } await Task.CompletedTask; }; } else { step.StepAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); }; } if (i % 4 == 0) { step.AsyncUndoAction = async(data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); await Task.CompletedTask; }; } else { step.UndoAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); }; } if (i % 2 == 0) { step.AsyncPostAction = async(data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); info.CurrentStepId.Should().Be(index); await Task.CompletedTask; }; } else { step.PostAction = (data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); info.CurrentStepId.Should().Be(index); }; } target.Add(step); } ITransactionResult <object> transactionCallbackResult = null; /* * 0: * 1: * 2: * 3: cancel * 4: */ // Act ITransactionResult <object> result = await target.Run(settings => { settings.Data = transactionData; settings.Mode = RunMode.Run; settings.TransactionResultCallback = callbackResult => transactionCallbackResult = callbackResult; }); // Assert result.Should().BeSameAs(transactionCallbackResult); result.Data.Should().BeSameAs(transactionData); result.Errors.ShouldAllBeEquivalentTo(new Exception[0]); result.Recovered.Should().BeFalse(); result.Result.Should().Be(ResultType.Cancelled); runStepActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3" }); runUndoActions.ShouldAllBeEquivalentTo(new string[] { "3", "2", "1", "0" }); runPostActions.ShouldAllBeEquivalentTo(new string[0]); }
public void WhenRunTransactionAndErrorOccurredStepAction_ShouldRunUndoSteps() { // Arrange object transactionData = new object(); List <string> runStepActions = new List <string>(); List <string> runUndoActions = new List <string>(); List <string> runPostActions = new List <string>(); Exception testException = new Exception("test exception"); Mock <ITransactionStorage <object> > storageMock = new Mock <ITransactionStorage <object> >(); ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; options.TransactionStorageCreator = partContext => storageMock.Object; }); for (int i = 0; i < 6; ++i) { string index = i.ToString(); TransactionStep <string, object> step = new TransactionStep <string, object>() { Id = index }; step.StepAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); if (index == "3") { throw testException; } }; step.UndoAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); }; step.PostAction = (data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); info.CurrentStepId.Should().Be(index); }; target.Add(step); } ITransactionResult <object> result = null; /* * 0: * 1: * 2: * 3: error * 4: * 5: */ // Act target.Run(settings => { settings.Data = transactionData; settings.Mode = RunMode.Run; settings.TransactionResultCallback = callbackResult => result = callbackResult; }); // Assert result.Data.Should().BeSameAs(transactionData); result.Errors.ShouldAllBeEquivalentTo(new Exception[] { testException }); result.Recovered.Should().BeFalse(); result.Result.Should().Be(ResultType.Failed); runStepActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3" }); runUndoActions.ShouldAllBeEquivalentTo(new string[] { "3", "2", "1", "0" }); runPostActions.ShouldAllBeEquivalentTo(new string[0]); storageMock.AssertStorageOperations(new AssertStorageOperationsContext <string, object>() { TransactionData = transactionData, Transaction = target, ExpectedStepsOrder = runStepActions, ExpectedUndoOrder = runUndoActions }); }
public abstract void Commit(TransactionStep step);
public void WhenGoForwardWithComparerWithCompareFailureWithStepExecutors_ShouldFailTransaction() { // Arrange object transactionData = new object(); List <string> runStepActions = new List <string>(); List <string> runUndoActions = new List <string>(); List <string> runPostActions = new List <string>(); List <int> stepActionThreadId = new List <int>(); List <int> postActionThreadId = new List <int>(); int transactionCallbackThreadId = 0; Dictionary <int, TestExecutor> stepExecutors = new Dictionary <int, TestExecutor>() { { 0, null }, { 1, new TestExecutor() { ShouldRun = true } }, { 2, new TestExecutor() { ShouldRun = true } }, { 3, new TestExecutor() { ShouldRun = true } }, { 4, new TestExecutor() { ShouldRun = true } }, { 5, null }, { 6, null } }; Dictionary <int, TestExecutor> postExecutors = new Dictionary <int, TestExecutor>() { { 0, null }, { 1, null }, { 2, new TestExecutor() { ShouldRun = true } }, { 3, new TestExecutor() { ShouldRun = true } }, { 4, null }, { 5, null }, { 6, null } }; int transactionRunThreadId = Thread.CurrentThread.ManagedThreadId; TestExecutor callBackExecutor = new TestExecutor() { ShouldRun = true }; ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; }); for (int i = 0; i < 7; ++i) { string index = "B" + i.ToString(); TransactionStep <string, object> step = new TransactionStep <string, object>() { Id = index, StepActionExecutor = stepExecutors[i] == null ? null : stepExecutors[i], PostActionExecutor = postExecutors[i] == null ? null : postExecutors[i] }; step.StepAction = (data, info) => { if (info.CurrentStepId == "B2") { info.GoForward("b4", StringComparer.Ordinal); } data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); stepActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); }; step.UndoAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); }; step.PostAction = (data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); postActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); info.CurrentStepId.Should().Be(index); }; target.Add(step); } /* * 0: * 1: executor for other thread (step action) * 2: executor for other thread (step action), executor for other thread (post action), go forward to step 4 * 3: executor for other thread (step action), executor for other thread (post action) * 4: executor for other thread (step action) * 5: * 6: */ // Act ITransactionResult <object> result = null; using (ManualResetEvent transactionEndResetEvent = new ManualResetEvent(false)) { target.Run(settings => { settings.Data = transactionData; settings.Mode = RunMode.Run; settings.TransactionResultCallback = callbackResult => { transactionCallbackThreadId = Thread.CurrentThread.ManagedThreadId; result = callbackResult; transactionEndResetEvent.Set(); }; settings.TransactionResultCallbackExecutor = callBackExecutor; }); transactionEndResetEvent.WaitOne(); } // Assert callBackExecutor.Dispose(); foreach (TestExecutor executor in stepExecutors.Values.Concat(postExecutors.Values).Where(x => x != null)) { executor.Dispose(); } result.Data.Should().BeSameAs(transactionData); result.Errors.Count().Should().Be(1); result.Errors.First().Message.Contains("Could not move forward to a step with id 'b4' as the step does not exist.").Should().BeTrue(); result.Recovered.Should().BeFalse(); result.Result.Should().Be(ResultType.Failed); runStepActions.ShouldAllBeEquivalentTo(new string[] { "B0", "B1", "B2" }); runUndoActions.ShouldAllBeEquivalentTo(new string[] { "B2", "B1", "B0" }); runPostActions.ShouldAllBeEquivalentTo(new string[0]); stepActionThreadId.ShouldAllBeEquivalentTo(new int[] { /*step 0*/ transactionRunThreadId, /*step 1*/ stepExecutors[1].ThreadId, /*step 2*/ stepExecutors[2].ThreadId }); transactionCallbackThreadId.Should().Be(callBackExecutor.ThreadId); }
public void WhenRunTransactionWithStepExecutors_ShouldRunAllStepsProperly() { // Arrange object transactionData = new object(); List <string> runStepActions = new List <string>(); List <string> runUndoActions = new List <string>(); List <string> runPostActions = new List <string>(); List <int> stepActionThreadId = new List <int>(); List <int> postActionThreadId = new List <int>(); int transactionCallbackThreadId = 0; Dictionary <int, TestExecutor> stepExecutors = new Dictionary <int, TestExecutor>() { { 0, null }, { 1, new TestExecutor() { ShouldRun = true } }, { 2, null }, { 3, new TestExecutor() { ShouldRun = true } }, { 4, new TestExecutor() { ShouldRun = true } }, { 5, null }, { 6, null } }; Dictionary <int, TestExecutor> postExecutors = new Dictionary <int, TestExecutor>() { { 0, null }, { 1, null }, { 2, new TestExecutor() { ShouldRun = true } }, { 3, new TestExecutor() { ShouldRun = true } }, { 4, null }, { 5, null }, { 6, null } }; int transactionRunThreadId = Thread.CurrentThread.ManagedThreadId; int step1StepActionThreadId = stepExecutors[1].ThreadId; int step2PostActionThreadId = postExecutors[2].ThreadId; int step3StepActionThreadId = stepExecutors[3].ThreadId; int step3PostActionThreadId = postExecutors[3].ThreadId; int step4StepActionThreadId = stepExecutors[4].ThreadId; ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; }); for (int i = 0; i < 7; ++i) { string index = i.ToString(); TransactionStep <string, object> step = new TransactionStep <string, object>() { Id = index, StepActionExecutor = stepExecutors[i] == null ? null : stepExecutors[i], PostActionExecutor = postExecutors[i] == null ? null : postExecutors[i] }; step.StepAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); stepActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); }; step.UndoAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); }; step.PostAction = (data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); postActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); info.CurrentStepId.Should().Be(index); }; target.Add(step); } ITransactionResult <object> result = null; /* * 0: * 1: executor for other thread (step action) * 2: executor for other thread (post action) * 3: executor for other thread (step action), executor for other thread (post action) * 4: executor for other thread (step action) * 5: * 6: */ // Act using (ManualResetEvent transactionEndResetEvent = new ManualResetEvent(false)) { target.Run(settings => { settings.Data = transactionData; settings.Mode = RunMode.Run; settings.TransactionResultCallback = callbackResult => { transactionCallbackThreadId = Thread.CurrentThread.ManagedThreadId; result = callbackResult; transactionEndResetEvent.Set(); }; }); transactionEndResetEvent.WaitOne(); } // Assert foreach (TestExecutor executor in stepExecutors.Values.Concat(postExecutors.Values).Where(x => x != null)) { executor.Dispose(); } result.Data.Should().BeSameAs(transactionData); result.Errors.ShouldAllBeEquivalentTo(new Exception[0]); result.Recovered.Should().BeFalse(); result.Result.Should().Be(ResultType.Success); foreach (TestExecutor executor in stepExecutors.Values.Concat(postExecutors.Values).Where(x => x != null)) { executor.Verify(Times.Once, Times.Once); } runStepActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4", "5", "6" }); runUndoActions.ShouldAllBeEquivalentTo(new string[0]); runPostActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4", "5", "6" }); stepActionThreadId.ShouldAllBeEquivalentTo(new int[] { /*step 0*/ transactionRunThreadId, /*step 1*/ step1StepActionThreadId, /*step 2*/ step1StepActionThreadId, /*step 3*/ step3StepActionThreadId, /*step 4*/ step4StepActionThreadId, /*step 5*/ step4StepActionThreadId, /*step 6*/ step4StepActionThreadId }); postActionThreadId.ShouldAllBeEquivalentTo(new int[] { /*step 0*/ step4StepActionThreadId, /*step 1*/ step4StepActionThreadId, /*step 2*/ step2PostActionThreadId, /*step 3*/ step3PostActionThreadId, /*step 4*/ step3PostActionThreadId, /*step 5*/ step3PostActionThreadId, /*step 6*/ step3PostActionThreadId }); transactionCallbackThreadId.Should().Be(step3PostActionThreadId); }
public override void Commit(TransactionStep step) { step.Commit(); }
public async Task WhenGoForwardWithComparerWithCompareSuccessWithStepExecutors_ShouldMoveToStepProperly() { // Arrange object transactionData = new object(); List <string> runStepActions = new List <string>(); List <string> runUndoActions = new List <string>(); List <string> runPostActions = new List <string>(); List <int> stepActionThreadId = new List <int>(); List <int> postActionThreadId = new List <int>(); int transactionCallbackThreadId = 0; Dictionary <int, TestExecutor> stepExecutors = new Dictionary <int, TestExecutor>() { { 0, null }, { 1, new TestExecutor() { ShouldRun = true } }, { 2, new TestExecutor() { ShouldRun = true } }, { 3, new TestExecutor() { ShouldRun = true } }, { 4, new TestExecutor() { ShouldRun = true } }, { 5, null }, { 6, null } }; Dictionary <int, TestExecutor> postExecutors = new Dictionary <int, TestExecutor>() { { 0, null }, { 1, null }, { 2, new TestExecutor() { ShouldRun = true } }, { 3, new TestExecutor() { ShouldRun = true } }, { 4, null }, { 5, null }, { 6, null } }; int transactionRunThreadId = Thread.CurrentThread.ManagedThreadId; TestExecutor callBackExecutor = new TestExecutor() { ShouldRun = true }; ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; }); for (int i = 0; i < 7; ++i) { string index = "B" + i.ToString(); TransactionStep <string, object> step = new TransactionStep <string, object>() { Id = index, StepActionExecutor = stepExecutors[i] == null ? null : stepExecutors[i], PostActionExecutor = postExecutors[i] == null ? null : postExecutors[i] }; if (i != 2) { step.AsyncStepAction = async(data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); stepActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); await Task.CompletedTask; }; } else { step.StepAction = (data, info) => { info.GoForward("b4", StringComparer.OrdinalIgnoreCase); data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runStepActions.Add(index); stepActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); }; } if (i % 4 == 0) { step.AsyncUndoAction = async(data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); await Task.CompletedTask; }; } else { step.UndoAction = (data, info) => { data.Should().BeSameAs(transactionData); info.CurrentStepId.Should().Be(index); runUndoActions.Add(index); }; } if (i == 1 || i == 2) { step.AsyncPostAction = async(data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); postActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); info.CurrentStepId.Should().Be(index); await Task.CompletedTask; }; } else { step.PostAction = (data, info) => { data.Should().BeSameAs(transactionData); runPostActions.Add(index); postActionThreadId.Add(Thread.CurrentThread.ManagedThreadId); info.CurrentStepId.Should().Be(index); }; } target.Add(step); } ITransactionResult <object> transactionCallbackResult = null; /* * 0: * 1: executor for other thread (step action) * 2: executor for other thread (step action), executor for other thread (post action), go forward to step 4 * 3: executor for other thread (step action), executor for other thread (post action) * 4: executor for other thread (step action) * 5: * 6: */ // Act ITransactionResult <object> result = await target.Run(settings => { settings.Data = transactionData; settings.Mode = RunMode.Run; settings.TransactionResultCallback = callbackResult => { transactionCallbackThreadId = Thread.CurrentThread.ManagedThreadId; transactionCallbackResult = callbackResult; }; settings.TransactionResultCallbackExecutor = callBackExecutor; }); // Assert callBackExecutor.Dispose(); foreach (TestExecutor executor in stepExecutors.Values.Concat(postExecutors.Values).Where(x => x != null)) { executor.Dispose(); } result.Should().BeSameAs(transactionCallbackResult); result.Data.Should().BeSameAs(transactionData); result.Errors.ShouldAllBeEquivalentTo(new Exception[0]); result.Recovered.Should().BeFalse(); result.Result.Should().Be(ResultType.Success); callBackExecutor.Verify(Times.Once, Times.Once); foreach (TestExecutor executor in stepExecutors.Values.Concat(postExecutors.Values).Where(x => x != null && x != stepExecutors[3])) { executor.Verify(Times.Once, Times.Once); } stepExecutors[3].Verify(Times.Never, Times.Never); runStepActions.ShouldAllBeEquivalentTo(new string[] { "B0", "B1", "B2", "B4", "B5", "B6" }); runUndoActions.ShouldAllBeEquivalentTo(new string[0]); runPostActions.ShouldAllBeEquivalentTo(new string[] { "B0", "B1", "B2", "B3", "B4", "B5", "B6" }); stepActionThreadId.ShouldAllBeEquivalentTo(new int[] { /*step 0*/ transactionRunThreadId, /*step 1*/ stepExecutors[1].ThreadId, /*step 2*/ stepExecutors[2].ThreadId, /*step 4*/ stepExecutors[4].ThreadId, /*step 5*/ stepExecutors[4].ThreadId, /*step 6*/ stepExecutors[4].ThreadId }); postActionThreadId.ShouldAllBeEquivalentTo(new int[] { /*step 0*/ stepExecutors[4].ThreadId, /*step 1*/ stepExecutors[4].ThreadId, /*step 2*/ postExecutors[2].ThreadId, /*step 3*/ postExecutors[3].ThreadId, /*step 4*/ postExecutors[3].ThreadId, /*step 5*/ postExecutors[3].ThreadId, /*step 6*/ postExecutors[3].ThreadId }); transactionCallbackThreadId.Should().Be(callBackExecutor.ThreadId); }