-
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 #47 from Noblix/main
Fix bug related to required version
- Loading branch information
Showing
9 changed files
with
343 additions
and
34 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
290 changes: 290 additions & 0 deletions
290
test/Atc.Cosmos.EventStore.Cqrs.Tests/Commands/StateProjectorTests.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,290 @@ | ||
using Atc.Cosmos.EventStore.Cqrs.Commands; | ||
using Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; | ||
using Atc.Cosmos.EventStore.Streams; | ||
using Atc.Test; | ||
using AutoFixture.AutoNSubstitute; | ||
using AutoFixture.Xunit2; | ||
using FluentAssertions; | ||
using NSubstitute; | ||
using Xunit; | ||
|
||
namespace Atc.Cosmos.EventStore.Cqrs.Tests.Commands; | ||
|
||
public class StateProjectorTests | ||
{ | ||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Check_If_Handler_Consumes_Events( | ||
[Frozen] ICommandHandlerMetadata<MockCommand> handlerMetadata, | ||
StateProjector<MockCommand> sut, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
CancellationToken cancellationToken) | ||
{ | ||
await sut.ProjectAsync(command, handler, cancellationToken); | ||
|
||
handlerMetadata.Received().IsNotConsumingEvents(); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Read_Metadata_For_Handler_With_No_Consumes( | ||
[Frozen] ICommandHandlerMetadata<MockCommand> handlerMetadata, | ||
[Frozen] IEventStoreClient eventStore, | ||
StateProjector<MockCommand> sut, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
CancellationToken cancellationToken) | ||
{ | ||
handlerMetadata | ||
.IsNotConsumingEvents() | ||
.ReturnsForAnyArgs(true); | ||
|
||
await sut.ProjectAsync(command, handler, cancellationToken); | ||
|
||
await eventStore.Received() | ||
.GetStreamInfoAsync( | ||
command.GetEventStreamId().Value, | ||
cancellationToken); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Validate_Metadata_For_Handler_With_No_Consumes( | ||
[Frozen] ICommandHandlerMetadata<MockCommand> handlerMetadata, | ||
[Frozen] IEventStoreClient eventStore, | ||
[Frozen, Substitute] IStreamReadValidator validator, | ||
StateProjector<MockCommand> sut, | ||
StreamMetadata metadata, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
CancellationToken cancellationToken) | ||
{ | ||
handlerMetadata | ||
.IsNotConsumingEvents() | ||
.ReturnsForAnyArgs(true); | ||
eventStore | ||
.GetStreamInfoAsync(default, default) | ||
.ReturnsForAnyArgs(metadata); | ||
|
||
await sut.ProjectAsync(command, handler, cancellationToken); | ||
|
||
validator | ||
.Received(1) | ||
.Validate( | ||
metadata, | ||
command.RequiredVersion!.Value.Value); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Default_To_Any_Version_When_Validating_Metadata( | ||
[Frozen] ICommandHandlerMetadata<MockCommand> handlerMetadata, | ||
[Frozen] IEventStoreClient eventStore, | ||
[Frozen, Substitute] IStreamReadValidator validator, | ||
StateProjector<MockCommand> sut, | ||
StreamMetadata metadata, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
CancellationToken cancellationToken) | ||
{ | ||
command = command with { RequiredVersion = null }; | ||
handlerMetadata | ||
.IsNotConsumingEvents() | ||
.ReturnsForAnyArgs(true); | ||
eventStore | ||
.GetStreamInfoAsync(default, default) | ||
.ReturnsForAnyArgs(metadata); | ||
|
||
await sut.ProjectAsync(command, handler, cancellationToken); | ||
|
||
validator | ||
.Received(1) | ||
.Validate( | ||
metadata, | ||
StreamVersion.Any); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Return_State_With_Version_From_Metadata_For_Handler_With_No_Consumes( | ||
[Frozen] ICommandHandlerMetadata<MockCommand> handlerMetadata, | ||
[Frozen] IEventStoreClient eventStore, | ||
StateProjector<MockCommand> sut, | ||
StreamMetadata metadata, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
CancellationToken cancellationToken) | ||
{ | ||
handlerMetadata | ||
.IsNotConsumingEvents() | ||
.ReturnsForAnyArgs(true); | ||
eventStore | ||
.GetStreamInfoAsync(default, default) | ||
.ReturnsForAnyArgs(metadata); | ||
|
||
var result = await sut.ProjectAsync(command, handler, cancellationToken); | ||
|
||
result.Should() | ||
.BeEquivalentTo( | ||
new Cqrs.Commands.StreamState() | ||
{ | ||
Id = command.GetEventStreamId().Value, | ||
Version = metadata.Version, | ||
}); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Not_Read_Stream_From_Event_Store_For_Handler_With_No_Consumes( | ||
[Frozen] ICommandHandlerMetadata<MockCommand> handlerMetadata, | ||
[Frozen] IEventStoreClient eventStore, | ||
StateProjector<MockCommand> sut, | ||
StreamMetadata metadata, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
CancellationToken cancellationToken) | ||
{ | ||
handlerMetadata | ||
.IsNotConsumingEvents() | ||
.ReturnsForAnyArgs(true); | ||
eventStore | ||
.GetStreamInfoAsync(default, default) | ||
.ReturnsForAnyArgs(metadata); | ||
|
||
await sut.ProjectAsync(command, handler, cancellationToken); | ||
|
||
_ = eventStore | ||
.DidNotReceiveWithAnyArgs() | ||
.ReadFromStreamAsync( | ||
default, | ||
default, | ||
default, | ||
default); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Read_Stream_From_Event_Store_When_Handler_Consumes( | ||
[Frozen] ICommandHandlerMetadata<MockCommand> handlerMetadata, | ||
[Frozen] IEventStoreClient eventStore, | ||
StateProjector<MockCommand> sut, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
CancellationToken cancellationToken) | ||
{ | ||
handlerMetadata | ||
.IsNotConsumingEvents() | ||
.ReturnsForAnyArgs(false); | ||
|
||
await sut.ProjectAsync(command, handler, cancellationToken); | ||
|
||
_ = eventStore.Received() | ||
.ReadFromStreamAsync( | ||
command.GetEventStreamId().Value, | ||
command.RequiredVersion!.Value.Value, | ||
filter: null, | ||
cancellationToken); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Default_To_Any_Version_When_Reading_Event_Store( | ||
[Frozen] ICommandHandlerMetadata<MockCommand> handlerMetadata, | ||
[Frozen] IEventStoreClient eventStore, | ||
StateProjector<MockCommand> sut, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
CancellationToken cancellationToken) | ||
{ | ||
command = command with { RequiredVersion = null }; | ||
handlerMetadata | ||
.IsNotConsumingEvents() | ||
.ReturnsForAnyArgs(false); | ||
|
||
await sut.ProjectAsync(command, handler, cancellationToken); | ||
|
||
_ = eventStore.Received() | ||
.ReadFromStreamAsync( | ||
command.GetEventStreamId().Value, | ||
StreamVersion.Any, | ||
filter: null, | ||
cancellationToken); | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Consume_Events_Found_In_Stream( | ||
[Frozen] ICommandHandlerMetadata<MockCommand> handlerMetadata, | ||
[Frozen] IEventStoreClient eventStore, | ||
StateProjector<MockCommand> sut, | ||
ICollection<MockEvent> events, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
CancellationToken cancellationToken) | ||
{ | ||
command = command with { RequiredVersion = null }; | ||
handlerMetadata | ||
.IsNotConsumingEvents() | ||
.ReturnsForAnyArgs(false); | ||
eventStore | ||
.ReadFromStreamAsync( | ||
default, | ||
default, | ||
default, | ||
default) | ||
.ReturnsForAnyArgs(ToAsyncEnumerable(events)); | ||
|
||
await sut.ProjectAsync(command, handler, cancellationToken); | ||
|
||
foreach (var evt in events) | ||
{ | ||
await handlerMetadata | ||
.Received() | ||
.ConsumeAsync( | ||
evt, | ||
handler, | ||
cancellationToken); | ||
} | ||
} | ||
|
||
[Theory, AutoNSubstituteData] | ||
internal async Task Should_Return_State_With_Version_From_Last_Event_When_Handler_Consumes( | ||
[Frozen] ICommandHandlerMetadata<MockCommand> handlerMetadata, | ||
[Frozen] IEventStoreClient eventStore, | ||
StateProjector<MockCommand> sut, | ||
ICollection<MockEvent> events, | ||
MockEvent lastEvent, | ||
MockEventMetadata lastEventMetadata, | ||
MockCommand command, | ||
ICommandHandler<MockCommand> handler, | ||
CancellationToken cancellationToken) | ||
{ | ||
handlerMetadata | ||
.IsNotConsumingEvents() | ||
.ReturnsForAnyArgs(false); | ||
lastEvent.Metadata = lastEventMetadata; | ||
events.Add(lastEvent); | ||
eventStore | ||
.ReadFromStreamAsync( | ||
default, | ||
default, | ||
default, | ||
default) | ||
.ReturnsForAnyArgs(ToAsyncEnumerable(events)); | ||
|
||
var result = await sut.ProjectAsync( | ||
command, | ||
handler, | ||
cancellationToken); | ||
|
||
result.Should() | ||
.BeEquivalentTo( | ||
new Cqrs.Commands.StreamState() | ||
{ | ||
Id = command.GetEventStreamId().Value, | ||
Version = lastEventMetadata.Version, | ||
}); | ||
} | ||
|
||
#pragma warning disable CS1998 // Mark method as async | ||
private static async IAsyncEnumerable<T> ToAsyncEnumerable<T>(IEnumerable<T> enumerable) | ||
#pragma warning restore CS1998 // Mark method as async | ||
{ | ||
foreach (var item in enumerable) | ||
{ | ||
yield return item; | ||
} | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/MockCommand.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,19 @@ | ||
namespace Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; | ||
|
||
public record MockCommand : ICommand | ||
{ | ||
private readonly EventStreamId eventStreamId = new(Guid.NewGuid().ToString()); | ||
|
||
public string CommandId { get; set; } | ||
|
||
public string? CorrelationId { get; set; } | ||
|
||
public EventStreamVersion? RequiredVersion { get; set; } | ||
|
||
public OnConflict Behavior { get; set; } | ||
|
||
public int BehaviorCount { get; set; } | ||
|
||
public EventStreamId GetEventStreamId() | ||
=> eventStreamId; | ||
} |
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
Oops, something went wrong.