public void Throws_Exception_On_Unregistered_Event() { // Arrange var register = TransitionRegisterFactory.StartRegistrationFor <TransactionExecutionAggregate, TransactionExecutionState>(); register.GetCurrentStateWith(a => a.State); register.From(TransactionExecutionState.Started) .On <SourceAddressLockedEvent>() .HandleTransition((a, e) => a.OnSourceAddressLocked()); var core = register.Build(); var aggregate = TransactionExecutionAggregate.Start ( Guid.NewGuid(), Guid.NewGuid(), 0, "", new[] { new TransactionOutputValueType("", 0) }, "", "", "", false ); // Act / Assert Assert.Throws <UnexpectedEventException>(() => { core.Switch(aggregate, new TransactionBuiltEvent()); }); }
public void Throws_Exception_On_Transition_Configuration_Conflict() { var register = TransitionRegisterFactory.StartRegistrationFor <TransactionExecutionAggregate, TransactionExecutionState>(); register.From(TransactionExecutionState.Started) .On <SourceAddressLockedEvent>() .HandleTransition((a, e) => a.OnSourceAddressLocked()); Assert.Throws <ArgumentException>(() => { register.In(TransactionExecutionState.Started) .Ignore <SourceAddressLockedEvent>(); }); }
public void Can_Ignore_Events() { // Arrange var register = TransitionRegisterFactory.StartRegistrationFor <TransactionExecutionAggregate, TransactionExecutionState>(); register.GetCurrentStateWith(a => a.State); register.From(TransactionExecutionState.Started) .On <SourceAddressLockedEvent>() .HandleTransition((a, e) => a.OnSourceAddressLocked()); register.In(TransactionExecutionState.Started) .Ignore <TransactionExecutionStartedEvent>(); register.In(TransactionExecutionState.SourceAddressLocked) .Ignore <TransactionExecutionStartedEvent>() .Ignore <SourceAddressLockedEvent>(); var core = register.Build(); var aggregate = TransactionExecutionAggregate.Start ( Guid.NewGuid(), Guid.NewGuid(), 0, "", new[] { new TransactionOutputValueType("", 0) }, "", "", "", false ); // Act var result1 = core.Switch(aggregate, new TransactionExecutionStartedEvent()); var result2 = core.Switch(aggregate, new SourceAddressLockedEvent()); var result3 = core.Switch(aggregate, new TransactionExecutionStartedEvent()); var result4 = core.Switch(aggregate, new SourceAddressLockedEvent()); // Assert Assert.False(result1); Assert.True(result2); Assert.False(result3); Assert.False(result4); }
public void Can_Proceed_Valid_Transaction_Multiple_Register() { // Arrange var register = TransitionRegisterFactory.StartRegistrationFor <TransactionExecutionAggregate, TransactionExecutionState>(); register.GetCurrentStateWith(a => a.State); register.From(TransactionExecutionState.SourceAddressLocked, outputs => { outputs.On <TransactionBuiltEvent>() .HandleTransition((a, e) => a.OnBuilt(e.FromAddressContext, e.TransactionContext)); outputs.On <TransactionExecutionFailedEvent>() .HandleTransition((a, e) => a.OnBuildingFailed(e.ErrorCode, e.Error)); }); var core = register.Build(); var aggregate1 = TransactionExecutionAggregate.Start ( Guid.NewGuid(), Guid.NewGuid(), 0, "", new[] { new TransactionOutputValueType("", 0) }, "", "", "", false ); aggregate1.OnSourceAddressLocked(); var aggregate2 = TransactionExecutionAggregate.Start ( Guid.NewGuid(), Guid.NewGuid(), 0, "", new[] { new TransactionOutputValueType("", 0) }, "", "", "", false ); aggregate2.OnSourceAddressLocked(); // Act var result1 = core.Switch(aggregate1, new TransactionBuiltEvent()); var result2 = core.Switch(aggregate2, new TransactionExecutionFailedEvent { ErrorCode = TransactionExecutionResult.UnknownError }); // Assert Assert.True(result1); Assert.Equal(TransactionExecutionState.Built, aggregate1.State); Assert.True(result2); Assert.Equal(TransactionExecutionState.BuildingFailed, aggregate2.State); }
public void Check_Transition_Preconditions() { // Arrange var register = TransitionRegisterFactory.StartRegistrationFor <TransactionExecutionAggregate, TransactionExecutionState>(); register.GetCurrentStateWith(a => a.State); register.From(TransactionExecutionState.Started) .On <SourceAddressLockedEvent>() .WithPrecondition((a, e) => a.IncludeFee, (a, e) => "Include fee should be enabled") .WithPrecondition((a, e) => a.Outputs.Single().Amount > 0, (a, e) => "Amount should be positive number") .HandleTransition((a, e) => a.OnSourceAddressLocked()); var core = register.Build(); var aggregate1 = TransactionExecutionAggregate.Start ( Guid.NewGuid(), Guid.NewGuid(), 0, "", new[] { new TransactionOutputValueType("", 0) }, "", "", "", false ); var aggregate2 = TransactionExecutionAggregate.Start ( Guid.NewGuid(), Guid.NewGuid(), 0, "", new[] { new TransactionOutputValueType("", 1) }, "", "", "", true ); // Act Assert.Throws <UnexpectedEventException>(() => { try { core.Switch(aggregate1, new SourceAddressLockedEvent()); } catch (Exception e) { _output.WriteLine(e.ToString()); throw; } }); var result21 = core.Switch(aggregate2, new SourceAddressLockedEvent()); // Assert Assert.Equal(TransactionExecutionState.Started, aggregate1.State); Assert.True(result21); Assert.Equal(TransactionExecutionState.SourceAddressLocked, aggregate2.State); }
public void Can_Ignore_Already_Registered_Transition_With_Additional_Conditions() { // Arrange var register = TransitionRegisterFactory.StartRegistrationFor <TransactionExecutionAggregate, TransactionExecutionState>(); register.GetCurrentStateWith(a => a.State); register.From(TransactionExecutionState.Started) .On <SourceAddressLockedEvent>() .HandleTransition((a, e) => a.OnSourceAddressLocked()); register.In(TransactionExecutionState.Started) .Ignore <SourceAddressLockedEvent>((a, e) => a.IncludeFee) .Ignore <TransactionExecutionStartedEvent>(); var core = register.Build(); var aggregate1 = TransactionExecutionAggregate.Start ( Guid.NewGuid(), Guid.NewGuid(), 0, "", new[] { new TransactionOutputValueType("", 0) }, "", "", "", false ); var aggregate2 = TransactionExecutionAggregate.Start ( Guid.NewGuid(), Guid.NewGuid(), 0, "", new[] { new TransactionOutputValueType("", 0) }, "", "", "", true ); // Act var result11 = core.Switch(aggregate1, new TransactionExecutionStartedEvent()); var result12 = core.Switch(aggregate1, new SourceAddressLockedEvent()); var result21 = core.Switch(aggregate2, new TransactionExecutionStartedEvent()); var result22 = core.Switch(aggregate2, new SourceAddressLockedEvent()); // Assert Assert.False(result11); Assert.True(result12); Assert.Equal(TransactionExecutionState.SourceAddressLocked, aggregate1.State); Assert.False(result21); Assert.False(result22); Assert.Equal(TransactionExecutionState.Started, aggregate2.State); }
public static IStateSwitcher <TransactionExecutionAggregate> Build() { var register = TransitionRegisterFactory.StartRegistrationFor <TransactionExecutionAggregate, TransactionExecutionState>(); register.GetCurrentStateWith(aggregate => aggregate.State); register.From(TransactionExecutionState.Started) .On <SourceAddressLockedEvent>() .HandleTransition((a, e) => a.OnSourceAddressLocked()); register.From(TransactionExecutionState.SourceAddressLocked, outputs => { outputs.On <TransactionBuiltEvent>() .HandleTransition((a, e) => a.OnBuilt(e.FromAddressContext, e.TransactionContext)); outputs.On <TransactionExecutionFailedEvent>() .HandleTransition((a, e) => a.OnBuildingFailed(e.ErrorCode, e.Error)); }); register.From(TransactionExecutionState.Built) .On <TransactionSignedEvent>() .HandleTransition((a, e) => a.OnSigned(e.SignedTransaction)); register.From(TransactionExecutionState.Signed, outputs => { outputs.On <TransactionBroadcastedEvent>() .HandleTransition((a, e) => a.OnBroadcasted()); outputs.On <TransactionExecutionFailedEvent>() .HandleTransition((a, e) => a.OnBroadcastingFailed(e.ErrorCode, e.Error)); outputs.On <TransactionExecutionRepeatRequestedEvent>() .HandleTransition((a, e) => a.OnBroadcastingFailed(e.ErrorCode, e.Error)); }); register.From(TransactionExecutionState.Broadcasted) .On <SourceAddressLockReleasedEvent>() .HandleTransition((a, e) => a.OnWaitingForEndingStarted()); register.From(TransactionExecutionState.WaitingForEnding, outputs => { outputs.On <TransactionExecutionCompletedEvent>() .HandleTransition((a, e) => a.OnCompleted( e.TransactionOutputs? .Select(o => o.ToDomain()) .ToArray(), e.TransactionBlock, e.TransactionFee, e.TransactionHash)); outputs.On <TransactionExecutionFailedEvent>() .HandleTransition((a, e) => a.OnWaitingForEndingFailed(e.ErrorCode, e.Error)); outputs.On <TransactionExecutionRepeatRequestedEvent>() .HandleTransition((a, e) => a.OnWaitingForEndingFailed(e.ErrorCode, e.Error)); }); register.From(TransactionExecutionState.Completed) .On <BroadcastedTransactionClearedEvent>() .HandleTransition((a, e) => a.OnCleared()); register.From(TransactionExecutionState.WaitingForEndingFailed) .On <BroadcastedTransactionClearedEvent>() .HandleTransition((a, e) => a.OnCleared()); register.From(TransactionExecutionState.BuildingFailed) .On <SourceAddressLockReleasedEvent>() .HandleTransition((a, e) => a.OnSourceAddressLockReleased()); register.From(TransactionExecutionState.BroadcastingFailed) .On <SourceAddressLockReleasedEvent>() .HandleTransition((a, e) => a.OnSourceAddressLockReleased()); register.From(TransactionExecutionState.SourceAddressReleased) .On <BroadcastedTransactionClearedEvent>() .HandleTransition((a, e) => a.OnCleared()); // Ignore events which already processed and possibly could be retried due to infrastructure failures register.In(TransactionExecutionState.Started) .Ignore <TransactionExecutionStartedEvent>(); register.In(TransactionExecutionState.SourceAddressLocked) .Ignore <TransactionExecutionStartedEvent>() .Ignore <SourceAddressLockedEvent>(); register.In(TransactionExecutionState.Built) .Ignore <TransactionExecutionStartedEvent>() .Ignore <SourceAddressLockedEvent>() .Ignore <TransactionBuiltEvent>(); register.In(TransactionExecutionState.BuildingFailed) .Ignore <TransactionExecutionStartedEvent>() .Ignore <SourceAddressLockedEvent>() .Ignore <TransactionExecutionFailedEvent>(); register.In(TransactionExecutionState.Signed) .Ignore <TransactionExecutionStartedEvent>() .Ignore <SourceAddressLockedEvent>() .Ignore <TransactionBuiltEvent>() .Ignore <TransactionSignedEvent>(); register.In(TransactionExecutionState.Broadcasted) .Ignore <TransactionExecutionStartedEvent>() .Ignore <SourceAddressLockedEvent>() .Ignore <TransactionBuiltEvent>() .Ignore <TransactionSignedEvent>() .Ignore <TransactionBroadcastedEvent>(); register.In(TransactionExecutionState.BroadcastingFailed) .Ignore <TransactionExecutionStartedEvent>() .Ignore <SourceAddressLockedEvent>() .Ignore <TransactionBuiltEvent>() .Ignore <TransactionSignedEvent>() .Ignore <TransactionExecutionFailedEvent>() .Ignore <TransactionExecutionRepeatRequestedEvent>(); register.In(TransactionExecutionState.WaitingForEnding) .Ignore <TransactionExecutionStartedEvent>() .Ignore <SourceAddressLockedEvent>() .Ignore <TransactionBuiltEvent>() .Ignore <TransactionSignedEvent>() .Ignore <TransactionBroadcastedEvent>() .Ignore <SourceAddressLockReleasedEvent>(); register.In(TransactionExecutionState.SourceAddressReleased) .Ignore <TransactionExecutionStartedEvent>() .Ignore <SourceAddressLockedEvent>() .Ignore <TransactionBuiltEvent>() .Ignore <TransactionSignedEvent>() .Ignore <TransactionExecutionFailedEvent>() .Ignore <TransactionExecutionRepeatRequestedEvent>() .Ignore <SourceAddressLockReleasedEvent>(); register.In(TransactionExecutionState.Completed) .Ignore <TransactionExecutionStartedEvent>() .Ignore <SourceAddressLockedEvent>() .Ignore <TransactionBuiltEvent>() .Ignore <TransactionSignedEvent>() .Ignore <TransactionBroadcastedEvent>() .Ignore <SourceAddressLockReleasedEvent>() .Ignore <TransactionExecutionCompletedEvent>(); register.In(TransactionExecutionState.WaitingForEndingFailed) .Ignore <TransactionExecutionStartedEvent>() .Ignore <SourceAddressLockedEvent>() .Ignore <TransactionBuiltEvent>() .Ignore <TransactionSignedEvent>() .Ignore <TransactionBroadcastedEvent>() .Ignore <SourceAddressLockReleasedEvent>() .Ignore <TransactionExecutionFailedEvent>() .Ignore <TransactionExecutionRepeatRequestedEvent>(); register.In(TransactionExecutionState.Cleared) .Ignore <TransactionExecutionStartedEvent>() .Ignore <SourceAddressLockedEvent>() .Ignore <TransactionBuiltEvent>() .Ignore <TransactionSignedEvent>() .Ignore <TransactionBroadcastedEvent>() .Ignore <SourceAddressLockReleasedEvent>() .Ignore <TransactionExecutionCompletedEvent>() .Ignore <TransactionExecutionFailedEvent>() .Ignore <TransactionExecutionRepeatRequestedEvent>() .Ignore <BroadcastedTransactionClearedEvent>(); return(register.Build()); }
public static IStateSwitcher <OperationExecutionAggregate> Build() { var register = TransitionRegisterFactory.StartRegistrationFor <OperationExecutionAggregate, OperationExecutionState>(); register.GetCurrentStateWith(aggregate => aggregate.State); register.From(OperationExecutionState.Started) .On <ActiveTransactionIdGeneratedEvent>() .WithPrecondition((a, e) => e.TransactionNumber == 1, (a, e) => "Transaction number should be 1") .WithPrecondition((a, e) => a.ActiveTransactionNumber == 0, (a, e) => "Active transaction number should be 0") .WithPrecondition((a, e) => a.ActiveTransactionId == null, (a, e) => "Active transaction should be null") .HandleTransition((a, e) => a.OnActiveTransactionIdGenerated(e.TransactionId, e.TransactionNumber)); register.From(OperationExecutionState.ActiveTransactionIdGenerated) .On <TransactionExecutionStartedEvent>() .WithPrecondition((a, e) => a.ActiveTransactionNumber == e.TransactionNumber, (a, e) => $"Unexpected transaction number. Active transaction number is [{a.ActiveTransactionNumber}]") .HandleTransition((a, e) => a.OnTransactionExecutionStarted()); register.From(OperationExecutionState.TransactionExecutionInProgress, outputs => { outputs.On <TransactionExecutionRepeatRequestedEvent>() .WithPrecondition((a, e) => a.ActiveTransactionNumber == e.TransactionNumber, (a, e) => $"Unexpected transaction number. Active transaction number is [{a.ActiveTransactionNumber}]") .HandleTransition((a, e) => a.OnTransactionExecutionRepeatRequested( e.Error)); outputs.On <TransactionExecutionCompletedEvent>() .WithPrecondition((a, e) => a.ActiveTransactionNumber == e.TransactionNumber, (a, e) => $"Unexpected transaction number. Active transaction number is [{a.ActiveTransactionNumber}]") .HandleTransition((a, e) => a.OnTransactionExecutionCompleted( e.TransactionOutputs? .Select(o => o.ToDomain()) .ToArray(), e.TransactionBlock, e.TransactionFee, e.TransactionHash)); outputs.On <TransactionExecutionFailedEvent>() .WithPrecondition((a, e) => a.ActiveTransactionNumber == e.TransactionNumber, (a, e) => $"Unexpected transaction number. Active transaction number is [{a.ActiveTransactionNumber}]") .WithPrecondition((a, e) => e.ErrorCode != TransactionExecutionResult.Completed, (a, e) => $"Error code should be not {TransactionExecutionResult.Completed}") .HandleTransition((a, e) => a.OnTransactionExecutionFailed( e.ErrorCode.MapToOperationExecutionResult(), e.Error)); }); register.From(OperationExecutionState.TransactionExecutionRepeatRequested) .On <ActiveTransactionClearedEvent>() .WithPrecondition((a, e) => a.ActiveTransactionNumber == e.TransactionNumber, (a, e) => $"Unexpected transaction number. Active transaction number is [{a.ActiveTransactionNumber}]") .WithPrecondition((a, e) => a.ActiveTransactionId != null, (a, e) => "Active transaction should be not null") .HandleTransition((a, e) => a.OnActiveTransactionCleared()); register.From(OperationExecutionState.ActiveTransactionCleared, outputs => { outputs.On <ActiveTransactionIdGeneratedEvent>() .WithPrecondition((a, e) => e.TransactionNumber == a.ActiveTransactionNumber + 1, (a, e) => $"Transaction number should be active transaction number [{a.ActiveTransactionNumber}] + 1") .WithPrecondition((a, e) => a.ActiveTransactionId == null, (a, e) => "Active transaction should be null") .HandleTransition((a, e) => a.OnActiveTransactionIdGenerated(e.TransactionId, e.TransactionNumber)); outputs.On <TransactionReBuildingRejectedEvent>() .WithPrecondition((a, e) => a.ActiveTransactionId == null, (a, e) => "Active transaction should be null") .HandleTransition((a, e) => a.OnTransactionExecutionFailed(OperationExecutionResult.RebuildingRejected, "Rebuilding rejected")); }); register.From(OperationExecutionState.Completed) .On <OperationExecutionCompletedEvent>() .HandleTransition((a, e) => a.OnNotifiedAboutEnding()); register.From(OperationExecutionState.Completed) .On <OneToManyOperationExecutionCompletedEvent>() .HandleTransition((a, e) => a.OnNotifiedAboutEnding()); register.From(OperationExecutionState.Failed) .On <OperationExecutionFailedEvent>() .HandleTransition((a, e) => a.OnNotifiedAboutEnding()); // Ignore events which already processed and possibly could be retried due to infrastructure failures register.In(OperationExecutionState.Started) .Ignore <OperationExecutionStartedEvent>(); register.In(OperationExecutionState.ActiveTransactionIdGenerated) .Ignore <OperationExecutionStartedEvent>() .Ignore <ActiveTransactionIdGeneratedEvent>((a, e) => a.ActiveTransactionNumber >= e.TransactionNumber) .Ignore <TransactionReBuildingRejectedEvent>() .Ignore <TransactionExecutionStartedEvent>((a, e) => a.ActiveTransactionNumber > e.TransactionNumber) .Ignore <TransactionExecutionRepeatRequestedEvent>((a, e) => a.ActiveTransactionNumber > e.TransactionNumber) .Ignore <ActiveTransactionClearedEvent>((a, e) => a.ActiveTransactionNumber > e.TransactionNumber); register.In(OperationExecutionState.TransactionExecutionInProgress) .Ignore <OperationExecutionStartedEvent>() .Ignore <ActiveTransactionIdGeneratedEvent>((a, e) => a.ActiveTransactionNumber >= e.TransactionNumber) .Ignore <TransactionReBuildingRejectedEvent>() .Ignore <TransactionExecutionStartedEvent>((a, e) => a.ActiveTransactionNumber >= e.TransactionNumber) .Ignore <TransactionExecutionRepeatRequestedEvent>((a, e) => a.ActiveTransactionNumber > e.TransactionNumber) .Ignore <ActiveTransactionClearedEvent>((a, e) => a.ActiveTransactionNumber > e.TransactionNumber); register.In(OperationExecutionState.TransactionExecutionRepeatRequested) .Ignore <OperationExecutionStartedEvent>() .Ignore <ActiveTransactionIdGeneratedEvent>((a, e) => a.ActiveTransactionNumber >= e.TransactionNumber) .Ignore <TransactionReBuildingRejectedEvent>() .Ignore <TransactionExecutionStartedEvent>((a, e) => a.ActiveTransactionNumber >= e.TransactionNumber) .Ignore <TransactionExecutionRepeatRequestedEvent>((a, e) => a.ActiveTransactionNumber >= e.TransactionNumber) .Ignore <ActiveTransactionClearedEvent>((a, e) => a.ActiveTransactionNumber > e.TransactionNumber); register.In(OperationExecutionState.ActiveTransactionCleared) .Ignore <OperationExecutionStartedEvent>() .Ignore <ActiveTransactionIdGeneratedEvent>((a, e) => a.ActiveTransactionNumber >= e.TransactionNumber) .Ignore <TransactionExecutionStartedEvent>((a, e) => a.ActiveTransactionNumber >= e.TransactionNumber) .Ignore <TransactionExecutionRepeatRequestedEvent>((a, e) => a.ActiveTransactionNumber >= e.TransactionNumber) .Ignore <ActiveTransactionClearedEvent>((a, e) => a.ActiveTransactionNumber >= e.TransactionNumber); register.In(OperationExecutionState.Completed) .Ignore <OperationExecutionStartedEvent>() .Ignore <ActiveTransactionIdGeneratedEvent>() .Ignore <TransactionReBuildingRejectedEvent>() .Ignore <TransactionExecutionStartedEvent>() .Ignore <TransactionExecutionRepeatRequestedEvent>() .Ignore <ActiveTransactionClearedEvent>() .Ignore <TransactionExecutionCompletedEvent>(); register.In(OperationExecutionState.Failed) .Ignore <OperationExecutionStartedEvent>() .Ignore <ActiveTransactionIdGeneratedEvent>() .Ignore <TransactionReBuildingRejectedEvent>() .Ignore <TransactionExecutionStartedEvent>() .Ignore <TransactionExecutionRepeatRequestedEvent>() .Ignore <ActiveTransactionClearedEvent>() .Ignore <TransactionExecutionFailedEvent>(); register.In(OperationExecutionState.NotifiedAboutEnding) .Ignore <OperationExecutionStartedEvent>() .Ignore <ActiveTransactionIdGeneratedEvent>() .Ignore <TransactionReBuildingRejectedEvent>() .Ignore <TransactionExecutionStartedEvent>() .Ignore <TransactionExecutionRepeatRequestedEvent>() .Ignore <ActiveTransactionClearedEvent>() .Ignore <TransactionExecutionCompletedEvent>() .Ignore <TransactionExecutionFailedEvent>() .Ignore <OperationExecutionCompletedEvent>() .Ignore <OperationExecutionFailedEvent>() .Ignore <OneToManyOperationExecutionCompletedEvent>(); return(register.Build()); }