Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions samples/EasyWay.Samples/Commands/SampleCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal sealed class SampleCommandHandler : CommandHandler<SampleCommand>

private readonly SampleDomainService _domainService;

private readonly IConcurrencyConflictValidator _concurrencyTokenValidator;
private readonly ConcurrencyConflictValidator _concurrencyTokenValidator;

private readonly IEnumerable<ISamplePolicy> _policies;

Expand All @@ -24,7 +24,7 @@ public SampleCommandHandler(
ISampleAggragateRootRepository repository,
SampleAggregateRootFactory factory,
SampleDomainService domainService,
IConcurrencyConflictValidator concurrencyTokenValidator,
ConcurrencyConflictValidator concurrencyTokenValidator,
IEnumerable<ISamplePolicy> policies,
IUserContext userContext)
{
Expand Down
43 changes: 43 additions & 0 deletions source/EasyWay/ConcurrencyConflictValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using EasyWay.Internals;
using EasyWay.Internals.Commands.ConcurrencyConflict;

namespace EasyWay
{
public sealed class ConcurrencyConflictValidator
{
internal ConcurrencyConflictValidator() { }

public void Validate<TCommand>(AggregateRoot aggregateRoot, TCommand command)
where TCommand : Command, IWithConcurrencyToken
{
if (aggregateRoot.ConcurrencyToken == command.ConcurrencyToken)
{
return;
}

var message = $"{aggregateRoot.GetType().Name} with id {aggregateRoot.Id} has different concurrency token ({aggregateRoot.ConcurrencyToken}) that command ({command.ConcurrencyToken})";

throw new ConcurrencyException(message);
}

public void Validate<T>(AggregateRoot aggregateRoot, Command<T> command)
where T : OperationResult
{
var commandWithConcurrencyToken = command as IWithConcurrencyToken;

if (commandWithConcurrencyToken is null)
{
throw new CommandWihtoutConcurrencyTokenException(command.GetType());
}

if (aggregateRoot.ConcurrencyToken == commandWithConcurrencyToken.ConcurrencyToken)
{
return;
}

var message = $"{aggregateRoot.GetType().Name} with id {aggregateRoot.Id} has different concurrency token ({aggregateRoot.ConcurrencyToken}) that command ({commandWithConcurrencyToken.ConcurrencyToken})";

throw new ConcurrencyException(message);
}
}
}
8 changes: 0 additions & 8 deletions source/EasyWay/IConcurrencyConflictValidator.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace EasyWay.Internals.Commands.ConcurrencyConflict
{
internal sealed class CommandWihtoutConcurrencyTokenException : EasyWayException
{
internal CommandWihtoutConcurrencyTokenException(Type commandType)
: base($"Add {nameof(IWithConcurrencyToken)} interface to {commandType.Name}"){ }
}
}
18 changes: 0 additions & 18 deletions source/EasyWay/Internals/Commands/ConcurrencyConflictValidator.cs

This file was deleted.

2 changes: 1 addition & 1 deletion source/EasyWay/Internals/Commands/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal static IServiceCollection AddCommands(

services.AddScoped<IUnitOfWorkCommandHandler, UnitOfWorkCommandHandler>();

services.AddSingleton<IConcurrencyConflictValidator, ConcurrencyConflictValidator>();
services.AddSingleton(new ConcurrencyConflictValidator());

return services;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using EasyWay.Internals;
using EasyWay.Internals.Commands;
using EasyWay.Internals.GuidGenerators;

namespace EasyWay.Tests.Internals.Commands
{
public sealed class ConcurrencyConflictValidatorTests
public sealed class ConcurrencyConflictValidatorCommandTests
{
private readonly IConcurrencyConflictValidator _validator = new ConcurrencyConflictValidator();
private readonly ConcurrencyConflictValidator _validator = new ConcurrencyConflictValidator();

internal sealed class TestAggragate : AggregateRoot;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using EasyWay.Internals.GuidGenerators;
using EasyWay.Internals;
using EasyWay.Internals.Commands.ConcurrencyConflict;

namespace EasyWay.Tests.Internals.Commands
{
public sealed class ConcurrencyConflictValidatorCommandWithResultTests
{
private readonly ConcurrencyConflictValidator _validator = new ConcurrencyConflictValidator();

internal sealed class TestAggragate : AggregateRoot;

internal sealed class CommandResult : OperationResult;

internal sealed class TestClassWithResult : Command<CommandResult>, IWithConcurrencyToken
{
public short ConcurrencyToken { get; }

public TestClassWithResult(short concurrencyToken)
{
ConcurrencyToken = concurrencyToken;
}
}

internal sealed class TestClassWithoutConcurrencyToken : Command<CommandResult>;

[Fact(DisplayName = $"When concurrency tokens are different validator should throw {nameof(ConcurrencyException)}")]
public void WhenConcurrencyTokensAreDifferent()
{
// Arrange
var id = new Guid("56bc4845-b8c4-4c33-a20d-67e14a486f90");

GuidGenerator.Set(id);

var aggregateRoot = new TestAggragate();

var commandWithToken = new TestClassWithResult(128);

var expectedMessage = $"{typeof(TestAggragate).Name} with id {id} has different concurrency token ({aggregateRoot.ConcurrencyToken}) that command ({commandWithToken.ConcurrencyToken})";

// Assert
var ex = Assert.Throws<ConcurrencyException>(() =>
{
// Act
_validator.Validate(aggregateRoot, commandWithToken);
});

Assert.Equal(expectedMessage, ex.Message);

GuidGenerator.Reset();
}

[Fact(DisplayName = $"When concurrency tokens are the same validator should not throw exception")]
public void WhenConcurrencyTokensAreTheSame()
{
// Arrange
var aggregateRoot = new TestAggragate();

var commandWithToken = new TestClassWithResult(0);

// Act & Assert
_validator.Validate(aggregateRoot, commandWithToken);
}

[Fact(DisplayName = $"When command with result don't implement {nameof(IWithConcurrencyToken)} interface")]
public void WhenCommandIsWithoutConcurrencyToken()
{
// Arrange
var aggregateRoot = new TestAggragate();

var commandWithToken = new TestClassWithoutConcurrencyToken();

// Act & Assert

// Assert
var ex = Assert.Throws<CommandWihtoutConcurrencyTokenException>(() =>
{
// Act
_validator.Validate(aggregateRoot, commandWithToken);
});

string expectedMessage = $"Add {nameof(IWithConcurrencyToken)} interface to {nameof(TestClassWithoutConcurrencyToken)}";

Assert.Equal(expectedMessage, ex.Message);
}
}
}
Loading