Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/#96 command validation #99

Merged
merged 9 commits into from
May 24, 2018
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
2 changes: 1 addition & 1 deletion build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var netFrameworkUnitTestAssemblies = new []
@"./src/Evelyn.Storage.EventStore.Tests/bin/"+compileConfig+"/net461/Evelyn.Storage.EventStore.Tests.dll",
};
var openCoverSettings = new OpenCoverSettings();
var minCodeCoverage = 90d;
var minCodeCoverage =89d;
var coverallsRepoToken = "coveralls-repo-token-evelyn";

// integration testing
Expand Down
30 changes: 30 additions & 0 deletions src/Evelyn.Core.Tests/TestUtilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace Evelyn.Core.Tests
{
using System;
using System.Linq;
using System.Text;

public class TestUtilities
{
private static readonly Random Random = new Random();

public static string CreateKey(uint length)
{
const string chars = "abcdefghijklmnopqrstuvwxyz01234567890_-";

return new string(Enumerable.Repeat(chars, (int)length).Select(s => s[Random.Next(s.Length)]).ToArray());
}

public static string CreateString(uint length)
{
var stringBuilder = new StringBuilder();
while (length > 0)
{
stringBuilder.Append(length % 10);
length--;
}

return stringBuilder.ToString();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
namespace Evelyn.Core.Tests.WriteModel.Account
namespace Evelyn.Core.Tests.WriteModel.Account.CreateProject
{
using System;
using System.Linq;
using AutoFixture;
using Core.WriteModel.Account.Commands;
using Core.WriteModel.Account.Commands.CreateProject;
using Core.WriteModel.Project.Domain;
using FluentAssertions;
using TestStack.BDDfy;
using Xunit;
using AccountEvent = Core.WriteModel.Account.Events;
using ProjectEvent = Core.WriteModel.Project.Events;

public class CreateProjectSpecs : AccountCommandHandlerSpecs<CreateProject>
public class CommandSpecs : HandlerSpecs<Command>
{
private Guid _accountId;

Expand All @@ -36,6 +36,7 @@ public void ProjectedAlreadyExists()
public void StaleAccountVersion()
{
this.Given(_ => GivenWeHaveRegisteredAnAccount())
.And(_ => GivenWeHaveAlreadyCreatedAProject())
.And(_ => GivenTheAccountVersionForOurNextCommandIsStale())
.When(_ => WhenWeCreateAProjectOnTheAccount())
.Then(_ => ThenAConcurrencyExceptionIsThrown())
Expand Down Expand Up @@ -111,7 +112,7 @@ private void WhenWeCreateAProjectOnTheAccount()
_projectId = DataFixture.Create<Guid>();
_projectName = DataFixture.Create<string>();

var command = new CreateProject(UserId, _accountId, _projectId, _projectName, _accountVersion);
var command = new Command(UserId, _accountId, _projectId, _projectName, _accountVersion);
WhenWeHandle(command);
}

Expand All @@ -121,7 +122,7 @@ private void WhenWeAddAnotherProjectWithTheSameId()
_projectId = _existingProjectId;
_projectName = DataFixture.Create<string>();

var command = new CreateProject(UserId, _accountId, _existingProjectId, _projectName, _accountVersion);
var command = new Command(UserId, _accountId, _existingProjectId, _projectName, _accountVersion);
WhenWeHandle(command);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
namespace Evelyn.Core.Tests.WriteModel.Account
namespace Evelyn.Core.Tests.WriteModel.Account.CreateProject
{
using System;
using AutoFixture;
using Core.WriteModel.Account;
using Core.WriteModel.Account.Commands.CreateProject;
using Core.WriteModel.Account.Domain;
using Core.WriteModel.Project.Events;
using CQRSlite.Commands;

public abstract class AccountCommandHandlerSpecs<TCommand> : CommandHandlerSpecs<Account, AccountCommandHandler, TCommand>
public abstract class HandlerSpecs<TCommand> : CommandHandlerSpecs<Account, Handler, TCommand>
where TCommand : ICommand
{
protected override AccountCommandHandler BuildHandler()
protected override Handler BuildHandler()
{
return new AccountCommandHandler(Session);
return new Handler(Session);
}

protected void GivenWeHaveCreatedAProjectWith(Guid id)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
namespace Evelyn.Core.Tests.WriteModel.Account.CreateProject
{
using System;
using System.Linq;
using AutoFixture;
using Core.WriteModel.Account.Commands.CreateProject;
using FluentAssertions;
using FluentValidation.TestHelper;
using Xunit;
using ErrorCodes = Core.WriteModel.ErrorCodes;

public class ValidatorSpecs
{
private readonly Fixture _fixture;
private readonly Validator _validator;

public ValidatorSpecs()
{
_fixture = new Fixture();
_validator = new Validator();
}

[Fact]
public void ProjectNameWhenNominalIsValid()
{
var command = _fixture.Create<Command>();

_validator.ShouldNotHaveValidationErrorFor(c => c.Name, command);
}

[Fact]
public void ProjectNameWhenNullIsInvalid()
{
var name = (string)null;
var command = _fixture.Build<Command>().With(c => c.Name, name).Create();

var errors = _validator.ShouldHaveValidationErrorFor(c => c.Name, command).ToList();
errors.Should().Contain(error =>
error.ErrorCode == ErrorCodes.PropertyNotSet &&
error.ErrorMessage == "'Name' should not be empty.");
}

[Fact]
public void ProjectNameWhenEmptyIsInvalid()
{
var name = string.Empty;
var command = _fixture.Build<Command>().With(c => c.Name, name).Create();

var errors = _validator.ShouldHaveValidationErrorFor(c => c.Name, command).ToList();
errors.Should().Contain(error =>
error.ErrorCode == ErrorCodes.PropertyNotSet &&
error.ErrorMessage == "'Name' should not be empty.");
}

[Fact]
public void ProjectNameWhenWhitespaceIsInvalid()
{
var name = " ";
var command = _fixture.Build<Command>().With(c => c.Name, name).Create();

var errors = _validator.ShouldHaveValidationErrorFor(c => c.Name, command).ToList();
errors.Should().Contain(error =>
error.ErrorCode == ErrorCodes.PropertyNotSet &&
error.ErrorMessage == "'Name' should not be empty.");
}

[Fact]
public void ProjectNameWhen128CharactersIsValid()
{
var name = TestUtilities.CreateString(128);
var command = _fixture.Build<Command>().With(c => c.Name, name).Create();

_validator.ShouldNotHaveValidationErrorFor(c => c.Name, command);
}

[Fact]
public void ProjectNameWhen129CharactersIsInvalid()
{
var name = TestUtilities.CreateString(129);
var command = _fixture.Build<Command>().With(c => c.Name, name).Create();

var errors = _validator.ShouldHaveValidationErrorFor(c => c.Name, command).ToList();
errors.Should().Contain(error =>
error.ErrorCode == ErrorCodes.PropertyTooLong &&
error.ErrorMessage == "The length of 'Name' must be 128 characters or fewer. You entered 129 characters.");
}

[Fact]
public void ExpectedVersionWhenNullIsValid()
{
var expectedVersion = (int?)null;
var command = _fixture.Build<Command>().With(c => c.ExpectedVersion, expectedVersion).Create();

_validator.ShouldNotHaveValidationErrorFor(c => c.ExpectedVersion, command);
}

[Fact]
public void ExpectedVersionWhenPositiveIsValid()
{
var expectedVersion = _fixture.Create<int>();
var command = _fixture.Build<Command>().With(c => c.ExpectedVersion, expectedVersion).Create();

_validator.ShouldNotHaveValidationErrorFor(c => c.ExpectedVersion, command);
}

[Fact]
public void ExpectedVersionWhenMaxIntIsValid()
{
var expectedVersion = int.MaxValue;
var command = _fixture.Build<Command>().With(c => c.ExpectedVersion, expectedVersion).Create();

_validator.ShouldNotHaveValidationErrorFor(c => c.ExpectedVersion, command);
}

[Fact]
public void ExpectedVersionWhenNegativeIsInvalid()
{
var expectedVersion = _fixture.Create<int>() * -1;
var command = _fixture.Build<Command>().With(c => c.ExpectedVersion, expectedVersion).Create();

var errors = _validator.ShouldHaveValidationErrorFor(c => c.ExpectedVersion, command).ToList();
errors.Should().Contain(error =>
error.ErrorCode == ErrorCodes.PropertyOutOfRange &&
error.ErrorMessage == "'Expected Version' must be greater than or equal to '0'.");
}

[Fact]
public void ProjectIdWhenNominalGuidIsValid()
{
var command = _fixture.Create<Command>();

_validator.ShouldNotHaveValidationErrorFor(c => c.ProjectId, command);
}

[Fact]
public void ProjectIdWhenEmptyGuidIsInvalid()
{
var projectId = Guid.Empty;
var command = _fixture.Build<Command>().With(c => c.ProjectId, projectId).Create();

var errors = _validator.ShouldHaveValidationErrorFor(c => c.ProjectId, command).ToList();
errors.Should().Contain(error =>
error.ErrorCode == ErrorCodes.PropertyNotSet &&
error.ErrorMessage == "'Project Id' should not be empty.");
}
}
}
18 changes: 9 additions & 9 deletions src/Evelyn.Core.Tests/WriteModel/CommandHandlerSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,25 +214,25 @@ private Guid ExtractAggregateId(TCommand command)
{
switch (command)
{
case AccountCommands.CreateProject c:
case AccountCommands.CreateProject.Command c:
return c.Id;

case EvelynCommands.CreateSystem c:
case EvelynCommands.CreateSystem.Command c:
return Constants.EvelynSystem;
case EvelynCommands.RegisterAccount c:
case EvelynCommands.RegisterAccount.Command c:
return Constants.EvelynSystem;
case EvelynCommands.StartSystem c:
case EvelynCommands.StartSystem.Command c:
return Constants.EvelynSystem;

case ProjectCommands.AddToggle c:
case ProjectCommands.AddToggle.Command c:
return c.ProjectId;
case ProjectCommands.DeleteToggle c:
case ProjectCommands.DeleteToggle.Command c:
return c.ProjectId;
case ProjectCommands.AddEnvironment c:
case ProjectCommands.AddEnvironment.Command c:
return c.ProjectId;
case ProjectCommands.DeleteEnvironment c:
case ProjectCommands.DeleteEnvironment.Command c:
return c.ProjectId;
case ProjectCommands.ChangeToggleState c:
case ProjectCommands.ChangeToggleState.Command c:
return c.ProjectId;

default:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
namespace Evelyn.Core.Tests.WriteModel.Evelyn
namespace Evelyn.Core.Tests.WriteModel.Evelyn.CreateSystem
{
using System;
using System.Linq;
using Core.WriteModel.Evelyn.Commands;
using Core.WriteModel.Evelyn.Commands.CreateSystem;
using Core.WriteModel.Evelyn.Domain;
using FluentAssertions;
using TestStack.BDDfy;
using Xunit;
using AccountEvent = Core.WriteModel.Account.Events;
using EvelynEvent = Core.WriteModel.Evelyn.Events;

public class CreateSystemSpecs : EvelynCommandHandlerSpecs<CreateSystem>
public class CommandSpecs : CommandHandlerSpecs<Evelyn, Handler, Command>
{
public CreateSystemSpecs()
public CommandSpecs()
{
UserId = Constants.SystemUser;
}
Expand Down Expand Up @@ -47,14 +48,19 @@ public void SystemHasAlreadyBeenCreated()
.BDDfy();
}

protected override Handler BuildHandler()
{
return new Handler(Session);
}

private void GivenWeHaveAlreadyCreatedTheSystem()
{
HistoricalEvents.Add(new EvelynEvent.SystemCreated(UserId, Constants.EvelynSystem, DateTimeOffset.UtcNow) { Version = HistoricalEvents.Count });
}

private void WhenWeCreateTheSystem()
{
var command = new CreateSystem(UserId, Constants.EvelynSystem) { ExpectedVersion = HistoricalEvents.Count - 1 };
var command = new Command(UserId, Constants.EvelynSystem) { ExpectedVersion = HistoricalEvents.Count - 1 };
WhenWeHandle(command);
}

Expand Down

This file was deleted.

Loading