public async Task 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; 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 = 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 == 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) * 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; }; 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)) { 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(callBackExecutor.ThreadId); }
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 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] }; 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.Ordinal); 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.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); }