public void WhenAddStepAfterTransactionRun_ShouldThrowException() { // Arrange ITransaction <string, object> target = new TransactionFactory().Create <string, object>(options => { options.TransactionInfo.Name = "test transaction"; }); target.Add(new TransactionStep <string, object>() { Id = "1", StepAction = (data, info) => { } }); target.Run(settings => { settings.Mode = RunMode.Run; }); // Act target.Add(new TransactionStep <string, object>() { Id = "1", StepAction = (data, info) => { } }); // Assert Assert.Fail("An exception is expected."); }
public void WhenInsertStepsBeforeIdWithComparer_ShouldInsertProperly() { List <string> runStepActions = new List <string>(); 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 == 3 ? i.ToString() + " " : i.ToString(); target.Add(new TransactionStep <string, object>() { Id = index, StepAction = (data, info) => runStepActions.Add(index) }); } Mock <IEqualityComparer <string> > comparer = new Mock <IEqualityComparer <string> >(); comparer.Setup(x => x.Equals(It.IsAny <string>(), It.IsAny <string>())) .Returns((string a, string b) => a.Trim() == b.Trim()); // Act target.InsertBefore("3", new TransactionStep <string, object>[] { new TransactionStep <string, object>() { Id = "inserted 1", StepAction = (data, info) => runStepActions.Add("inserted 1") }, new TransactionStep <string, object>() { Id = "inserted 2", StepAction = (data, info) => runStepActions.Add("inserted 2") } }, comparer.Object); ITransactionResult <object> result = null; target.Run(settings => { settings.TransactionResultCallback = x => result = x; }); // Assert runStepActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "inserted 1", "inserted 2", "3 ", "4", "5", "6" }); }
public void WhenInsertStepsAfterIdWithoutComparer_ShouldInsertProperly() { List <string> runStepActions = new List <string>(); 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(); target.Add(new TransactionStep <string, object>() { Id = index, StepAction = (data, info) => runStepActions.Add(index) }); } // Act target.InsertAfter("5", new TransactionStep <string, object>[] { new TransactionStep <string, object>() { Id = "inserted 1", StepAction = (data, info) => runStepActions.Add("inserted 1") }, new TransactionStep <string, object>() { Id = "inserted 2", StepAction = (data, info) => runStepActions.Add("inserted 2") }, new TransactionStep <string, object>() { Id = "inserted 3", StepAction = (data, info) => runStepActions.Add("inserted 3") } }); ITransactionResult <object> result = null; target.Run(settings => { settings.TransactionResultCallback = x => result = x; }); // Assert runStepActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "3", "4", "5", "inserted 1", "inserted 2", "inserted 3", "6" }); }
public async Task WhenInsertStepsAtIndex_ShouldInsertProperly() { List <string> runStepActions = new List <string>(); 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(); target.Add(new TransactionStep <string, object>() { Id = index, StepAction = (data, info) => runStepActions.Add(index) }); } // Act target.InsertAtIndex(3, new TransactionStep <string, object>[] { new TransactionStep <string, object>() { Id = "inserted 1", StepAction = (data, info) => runStepActions.Add("inserted 1") }, new TransactionStep <string, object>() { Id = "inserted 2", StepAction = (data, info) => runStepActions.Add("inserted 2") } }); ITransactionResult <object> result = await target.Run(settings => { }); // Assert runStepActions.ShouldAllBeEquivalentTo(new string[] { "0", "1", "2", "inserted 1", "inserted 2", "3", "4", "5", "6" }); }
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 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 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 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 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); }