-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #55 from atc-net/feature/command-handler-context-s…
…treamversion Extend CommandContext with the streams current StreamVersion
- Loading branch information
Showing
8 changed files
with
222 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
180 changes: 180 additions & 0 deletions
180
test/Atc.Cosmos.EventStore.Cqrs.Tests/Commands/CommandProcessorTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
using System.Collections.ObjectModel; | ||
using Atc.Cosmos.EventStore.Cqrs.Commands; | ||
using Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; | ||
using Atc.Test; | ||
using AutoFixture.Xunit2; | ||
using FluentAssertions; | ||
using NSubstitute; | ||
using Xunit; | ||
|
||
namespace Atc.Cosmos.EventStore.Cqrs.Tests.Commands; | ||
|
||
public class CommandProcessorTests | ||
{ | ||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Exeute_State_Projector( | ||
[Frozen] ICommandHandlerFactory commandHandlerFactory, | ||
[Frozen] IStateProjector<MockCommand> stateProjector, | ||
CommandProcessor<MockCommand> sut, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
Atc.Cosmos.EventStore.Cqrs.Commands.StreamState streamState, | ||
CancellationToken cancellationToken) | ||
{ | ||
commandHandlerFactory.Create<MockCommand>().Returns(handler); | ||
stateProjector.ProjectAsync(command, handler, cancellationToken).Returns(streamState); | ||
|
||
await sut.ExecuteAsync(command, cancellationToken); | ||
|
||
await stateProjector.Received(1).ProjectAsync(command, handler, cancellationToken); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Execute_Command( | ||
[Frozen] ICommandHandlerFactory commandHandlerFactory, | ||
[Frozen] IStateProjector<MockCommand> stateProjector, | ||
CommandProcessor<MockCommand> sut, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
Atc.Cosmos.EventStore.Cqrs.Commands.StreamState streamState, | ||
CancellationToken cancellationToken) | ||
{ | ||
commandHandlerFactory.Create<MockCommand>().Returns(handler); | ||
stateProjector.ProjectAsync(command, handler, cancellationToken).Returns(streamState); | ||
|
||
await sut.ExecuteAsync(command, cancellationToken); | ||
|
||
await handler.Received(1).ExecuteAsync(command, Arg.Any<CommandContext>(), cancellationToken); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Set_Command_Context_StreamVersion( | ||
[Frozen] ICommandHandlerFactory commandHandlerFactory, | ||
[Frozen] IStateProjector<MockCommand> stateProjector, | ||
CommandProcessor<MockCommand> sut, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
Atc.Cosmos.EventStore.Cqrs.Commands.StreamState streamState, | ||
CancellationToken cancellationToken) | ||
{ | ||
commandHandlerFactory.Create<MockCommand>().Returns(handler); | ||
stateProjector.ProjectAsync(command, handler, cancellationToken).Returns(streamState); | ||
|
||
await sut.ExecuteAsync(command, cancellationToken); | ||
|
||
var commandContext = handler.ReceivedCallWithArgument<CommandContext>(); | ||
commandContext.StreamVersion.Should().Be(streamState.Version); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Return_NotModified_When_Command_Emits_No_Events( | ||
[Frozen] ICommandHandlerFactory commandHandlerFactory, | ||
[Frozen] IStateProjector<MockCommand> stateProjector, | ||
CommandProcessor<MockCommand> sut, | ||
MockCommand command, | ||
MockCommandHandler handler, | ||
Atc.Cosmos.EventStore.Cqrs.Commands.StreamState streamState, | ||
CancellationToken cancellationToken) | ||
{ | ||
commandHandlerFactory.Create<MockCommand>().Returns(handler); | ||
stateProjector.ProjectAsync(command, handler, cancellationToken).Returns(streamState); | ||
|
||
var result = await sut.ExecuteAsync(command, cancellationToken); | ||
|
||
result.Result.Should().Be(ResultType.NotModified); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Write_ResposeObject_To_CommandResult_When_Command_Emits_No_Events( | ||
[Frozen] ICommandHandlerFactory commandHandlerFactory, | ||
[Frozen] IStateProjector<MockCommand> stateProjector, | ||
CommandProcessor<MockCommand> sut, | ||
MockCommand command, | ||
MockCommandHandler handler, | ||
object responseObject, | ||
Atc.Cosmos.EventStore.Cqrs.Commands.StreamState streamState, | ||
CancellationToken cancellationToken) | ||
{ | ||
commandHandlerFactory.Create<MockCommand>().Returns(handler); | ||
stateProjector.ProjectAsync(command, handler, cancellationToken).Returns(streamState); | ||
handler.ResponseObject = responseObject; | ||
|
||
var result = await sut.ExecuteAsync(command, cancellationToken); | ||
|
||
result.Response.Should().Be(responseObject); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Call_StateWriter_With_Events__When_Command_Emits_Events( | ||
[Frozen] ICommandHandlerFactory commandHandlerFactory, | ||
[Frozen] IStateProjector<MockCommand> stateProjector, | ||
[Frozen] IStateWriter<MockCommand> stateWriter, | ||
CommandProcessor<MockCommand> sut, | ||
MockCommand command, | ||
CommandResult commandResult, | ||
MockCommandHandler handler, | ||
MockEvent[] events, | ||
Atc.Cosmos.EventStore.Cqrs.Commands.StreamState streamState, | ||
CancellationToken cancellationToken) | ||
{ | ||
commandHandlerFactory.Create<MockCommand>().Returns(handler); | ||
stateProjector.ProjectAsync(command, handler, cancellationToken).Returns(streamState); | ||
stateWriter.WriteEventAsync(command, events, cancellationToken).ReturnsForAnyArgs(commandResult); | ||
handler.AddEventsToEmit(events); | ||
|
||
await sut.ExecuteAsync(command, cancellationToken); | ||
|
||
await stateWriter.Received(1).WriteEventAsync(command, Arg.Any<IReadOnlyCollection<object>>(), cancellationToken); | ||
var writtenEvents = stateWriter.ReceivedCallWithArgument<IReadOnlyCollection<object>>(); | ||
writtenEvents.Should().HaveSameCount(events); | ||
writtenEvents.AsEnumerable().Should().BeEquivalentTo(events); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Return_Changed_When_Command_Emits_Events( | ||
[Frozen] ICommandHandlerFactory commandHandlerFactory, | ||
[Frozen] IStateProjector<MockCommand> stateProjector, | ||
[Frozen] IStateWriter<MockCommand> stateWriter, | ||
CommandProcessor<MockCommand> sut, | ||
MockCommand command, | ||
MockCommandHandler handler, | ||
CommandResult commandResult, | ||
MockEvent[] events, | ||
Atc.Cosmos.EventStore.Cqrs.Commands.StreamState streamState, | ||
CancellationToken cancellationToken) | ||
{ | ||
commandHandlerFactory.Create<MockCommand>().Returns(handler); | ||
stateProjector.ProjectAsync(command, handler, cancellationToken).Returns(streamState); | ||
stateWriter.WriteEventAsync(command, events, cancellationToken).ReturnsForAnyArgs(commandResult); | ||
handler.AddEventsToEmit(events); | ||
|
||
var result = await sut.ExecuteAsync(command, cancellationToken); | ||
|
||
result.Result.Should().Be(ResultType.Changed); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Write_ResposeObject_To_CommandResult_When_Command_Emits_Events( | ||
[Frozen] ICommandHandlerFactory commandHandlerFactory, | ||
[Frozen] IStateProjector<MockCommand> stateProjector, | ||
[Frozen] IStateWriter<MockCommand> stateWriter, | ||
CommandProcessor<MockCommand> sut, | ||
MockCommand command, | ||
MockCommandHandler handler, | ||
CommandResult commandResult, | ||
MockEvent[] events, | ||
object responseObject, | ||
Atc.Cosmos.EventStore.Cqrs.Commands.StreamState streamState, | ||
CancellationToken cancellationToken) | ||
{ | ||
commandHandlerFactory.Create<MockCommand>().Returns(handler); | ||
stateProjector.ProjectAsync(command, handler, cancellationToken).Returns(streamState); | ||
stateWriter.WriteEventAsync(command, events, cancellationToken).ReturnsForAnyArgs(commandResult); | ||
handler.AddEventsToEmit(events); | ||
handler.ResponseObject = responseObject; | ||
|
||
var result = await sut.ExecuteAsync(command, cancellationToken); | ||
|
||
result.Response.Should().Be(responseObject); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/MockCommandHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
namespace Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; | ||
|
||
public class MockCommandHandler : ICommandHandler<MockCommand> | ||
{ | ||
private List<IEvent> events = new(); | ||
|
||
public object ResponseObject { get; set; } = null; | ||
|
||
public void AddEventsToEmit(params IEvent[] eventsToEmit) | ||
{ | ||
events = events.Concat(eventsToEmit).ToList(); | ||
} | ||
|
||
public ValueTask ExecuteAsync( | ||
MockCommand command, | ||
ICommandContext context, | ||
CancellationToken cancellationToken) | ||
{ | ||
foreach (var evt in events) | ||
{ | ||
context.AddEvent(evt); | ||
} | ||
|
||
context.ResponseObject = ResponseObject; | ||
|
||
return default; | ||
} | ||
} |