From 63d06afc6c3cd6568fa9c56b1180c32d30348959 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 16 Oct 2016 16:41:35 +0200 Subject: [PATCH] Some more tests --- .../CQRS/Commands/CommandHandler.cs | 28 ++-- .../CQRS/Commands/ICommandBus.cs | 2 +- .../CQRS/Commands/InMemoryCommandBus.cs | 4 +- .../PropertyValue.cs | 5 - .../CQRS/Commands/CommandsHandlerTests.cs | 129 ++++++++++++++++++ .../CQRS/Commands/InMemoryCommandBusTests.cs | 116 ++++++++++++++++ .../CQRS/EnvelopeExtensionsTests.cs | 82 +++++++++++ .../PropertiesBagTests.cs | 18 +++ .../project.json | 5 + 9 files changed, 365 insertions(+), 24 deletions(-) create mode 100644 tests/PinkParrot.Infrastructure.Tests/CQRS/Commands/CommandsHandlerTests.cs create mode 100644 tests/PinkParrot.Infrastructure.Tests/CQRS/Commands/InMemoryCommandBusTests.cs create mode 100644 tests/PinkParrot.Infrastructure.Tests/CQRS/EnvelopeExtensionsTests.cs diff --git a/src/PinkParrot.Infrastructure/CQRS/Commands/CommandHandler.cs b/src/PinkParrot.Infrastructure/CQRS/Commands/CommandHandler.cs index d3f2db45b..30c0d0a7e 100644 --- a/src/PinkParrot.Infrastructure/CQRS/Commands/CommandHandler.cs +++ b/src/PinkParrot.Infrastructure/CQRS/Commands/CommandHandler.cs @@ -49,14 +49,11 @@ namespace PinkParrot.Infrastructure.CQRS.Commands protected Task CreateAsync(IAggregateCommand command, Action creator) { - Guard.NotNull(creator, nameof(creator)); - Guard.NotNull(command, nameof(command)); - - var domainObject = domainObjectFactory.CreateNew(command.AggregateId); - - creator(domainObject); - - return domainObjectRepository.SaveAsync(domainObject, command.AggregateId); + return CreateAsync(command, x => + { + creator(x); + return Task.FromResult(true); + }); } protected async Task UpdateAsync(IAggregateCommand command, Func updater) @@ -71,16 +68,13 @@ namespace PinkParrot.Infrastructure.CQRS.Commands await domainObjectRepository.SaveAsync(domainObject, Guid.NewGuid()); } - protected async Task UpdateAsync(IAggregateCommand command, Action updater) + protected Task UpdateAsync(IAggregateCommand command, Action updater) { - Guard.NotNull(updater, nameof(updater)); - Guard.NotNull(command, nameof(command)); - - var domainObject = await domainObjectRepository.GetByIdAsync(command.AggregateId); - - updater(domainObject); - - await domainObjectRepository.SaveAsync(domainObject, Guid.NewGuid()); + return UpdateAsync(command, x => + { + updater(x); + return Task.FromResult(true); + }); } public abstract Task HandleAsync(CommandContext context); diff --git a/src/PinkParrot.Infrastructure/CQRS/Commands/ICommandBus.cs b/src/PinkParrot.Infrastructure/CQRS/Commands/ICommandBus.cs index e486574fe..7832fa8f1 100644 --- a/src/PinkParrot.Infrastructure/CQRS/Commands/ICommandBus.cs +++ b/src/PinkParrot.Infrastructure/CQRS/Commands/ICommandBus.cs @@ -12,6 +12,6 @@ namespace PinkParrot.Infrastructure.CQRS.Commands { public interface ICommandBus { - Task PublishAsync(ICommand command); + Task PublishAsync(ICommand command); } } diff --git a/src/PinkParrot.Infrastructure/CQRS/Commands/InMemoryCommandBus.cs b/src/PinkParrot.Infrastructure/CQRS/Commands/InMemoryCommandBus.cs index fb66a2690..8409aaa17 100644 --- a/src/PinkParrot.Infrastructure/CQRS/Commands/InMemoryCommandBus.cs +++ b/src/PinkParrot.Infrastructure/CQRS/Commands/InMemoryCommandBus.cs @@ -23,7 +23,7 @@ namespace PinkParrot.Infrastructure.CQRS.Commands this.handlers = handlers; } - public async Task PublishAsync(ICommand command) + public async Task PublishAsync(ICommand command) { Guard.NotNull(command, nameof(command)); @@ -50,6 +50,8 @@ namespace PinkParrot.Infrastructure.CQRS.Commands { throw context.Exception; } + + return context; } } } \ No newline at end of file diff --git a/src/PinkParrot.Infrastructure/PropertyValue.cs b/src/PinkParrot.Infrastructure/PropertyValue.cs index fe841b728..7bcfbd2f0 100644 --- a/src/PinkParrot.Infrastructure/PropertyValue.cs +++ b/src/PinkParrot.Infrastructure/PropertyValue.cs @@ -58,11 +58,6 @@ namespace PinkParrot.Infrastructure { result = null; - if (binder.Type == typeof(object)) - { - result = rawValue; - } - Func parser; if (!Parsers.TryGetValue(binder.Type, out parser)) diff --git a/tests/PinkParrot.Infrastructure.Tests/CQRS/Commands/CommandsHandlerTests.cs b/tests/PinkParrot.Infrastructure.Tests/CQRS/Commands/CommandsHandlerTests.cs new file mode 100644 index 000000000..f9f6c8f94 --- /dev/null +++ b/tests/PinkParrot.Infrastructure.Tests/CQRS/Commands/CommandsHandlerTests.cs @@ -0,0 +1,129 @@ +// ========================================================================== +// CommandsHandlerTests.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using Moq; +using PinkParrot.Infrastructure.CQRS.Events; +using Xunit; + +namespace PinkParrot.Infrastructure.CQRS.Commands +{ + public class CommandsHandlerTests + { + private readonly Mock domainObjectFactory = new Mock(); + private readonly Mock domainObjectRepository = new Mock(); + private readonly TestCommandHandler sut; + private readonly Guid id = Guid.NewGuid(); + + private sealed class TestCommand : AggregateCommand + { + } + + private sealed class TestDomainObject : DomainObject + { + public TestDomainObject(Guid id, int version) : base(id, version) + { + } + + protected override void DispatchEvent(Envelope @event) + { + throw new NotImplementedException(); + } + } + + private sealed class TestCommandHandler : CommandHandler + { + public TestCommandHandler(IDomainObjectFactory domainObjectFactory, IDomainObjectRepository domainObjectRepository) + : base(domainObjectFactory, domainObjectRepository) + { + } + + public override Task HandleAsync(CommandContext context) + { + throw new NotImplementedException(); + } + + public IDomainObjectFactory TestFactory + { + get { return Factory; } + } + + public IDomainObjectRepository TestRepository + { + get { return Repository; } + } + + public Task CreateTestAsync(IAggregateCommand command, Action creator) + { + return CreateAsync(command, creator); + } + + public Task UpdateTestAsync(IAggregateCommand command, Action updater) + { + return UpdateAsync(command, updater); + } + } + + public CommandsHandlerTests() + { + sut = new TestCommandHandler(domainObjectFactory.Object, domainObjectRepository.Object); + } + + [Fact] + public void Should_provide_factory() + { + Assert.Equal(domainObjectFactory.Object, sut.TestFactory); + } + + [Fact] + public void Should_provide_repository() + { + Assert.Equal(domainObjectRepository.Object, sut.TestRepository); + } + + [Fact] + public async Task Should_retrieve_from_repository_and_update() + { + var command = new TestCommand { AggregateId = id }; + + var domainObject = new TestDomainObject(id, 123); + + domainObjectRepository.Setup(x => x.GetByIdAsync(id, int.MaxValue)).Returns(Task.FromResult(domainObject)).Verifiable(); + domainObjectRepository.Setup(x => x.SaveAsync(domainObject, It.IsAny())).Returns(Task.FromResult(true)).Verifiable(); + + var isCalled = false; + + await sut.UpdateTestAsync(command, x => isCalled = true); + + domainObjectRepository.VerifyAll(); + + Assert.True(isCalled); + } + + [Fact] + public async Task Should_create_with_factory_and_update() + { + var command = new TestCommand { AggregateId = id }; + + var domainObject = new TestDomainObject(id, 123); + + domainObjectFactory.Setup(x => x.CreateNew(typeof(TestDomainObject), id)).Returns(domainObject).Verifiable(); + domainObjectRepository.Setup(x => x.SaveAsync(domainObject, It.IsAny())).Returns(Task.FromResult(true)).Verifiable(); + + var isCalled = false; + + await sut.CreateTestAsync(command, x => isCalled = true); + + domainObjectFactory.VerifyAll(); + domainObjectRepository.VerifyAll(); + + Assert.True(isCalled); + } + } +} diff --git a/tests/PinkParrot.Infrastructure.Tests/CQRS/Commands/InMemoryCommandBusTests.cs b/tests/PinkParrot.Infrastructure.Tests/CQRS/Commands/InMemoryCommandBusTests.cs new file mode 100644 index 000000000..5b5e91811 --- /dev/null +++ b/tests/PinkParrot.Infrastructure.Tests/CQRS/Commands/InMemoryCommandBusTests.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace PinkParrot.Infrastructure.CQRS.Commands +{ + public class InMemoryCommandBusTests + { + private readonly TestCommand command = new TestCommand(); + + private sealed class TestCommand : ICommand + { + } + + private sealed class HandledHandler : ICommandHandler + { + public ICommand LastCommand; + + public Task HandleAsync(CommandContext context) + { + LastCommand = context.Command; + + return Task.FromResult(true); + } + } + + private sealed class NonHandledHandler : ICommandHandler + { + public ICommand LastCommand; + + public Task HandleAsync(CommandContext context) + { + LastCommand = context.Command; + + return Task.FromResult(false); + } + } + + private sealed class ThrowHandledHandler : ICommandHandler + { + public ICommand LastCommand; + + public Task HandleAsync(CommandContext context) + { + LastCommand = context.Command; + + throw new InvalidOperationException(); + } + } + + private sealed class AfterThrowHandler : ICommandHandler + { + public Exception LastException; + + public Task HandleAsync(CommandContext context) + { + LastException = context.Exception; + + return Task.FromResult(false); + } + } + + [Fact] + public async Task Should_not_set_handled_if_no_handler_registered() + { + var sut = new InMemoryCommandBus(new ICommandHandler[0]); + var ctx = await sut.PublishAsync(command); + + Assert.False(ctx.IsHandled); + } + + [Fact] + public async Task Should_not_set_succeeded_if_handler_returns_false() + { + var handler = new NonHandledHandler(); + + var sut = new InMemoryCommandBus(new ICommandHandler[] { handler }); + var ctx = await sut.PublishAsync(command); + + Assert.Equal(command, handler.LastCommand); + Assert.False(ctx.IsSucceeded); + Assert.False(ctx.IsHandled); + Assert.Null(ctx.Exception); + } + + [Fact] + public async Task Should_set_succeeded_if_handler_returns_true() + { + var handler = new HandledHandler(); + + var sut = new InMemoryCommandBus(new ICommandHandler[] { handler }); + var ctx = await sut.PublishAsync(command); + + Assert.Equal(command, handler.LastCommand); + Assert.True(ctx.IsSucceeded); + Assert.True(ctx.IsHandled); + Assert.Null(ctx.Exception); + } + + [Fact] + public async Task Should_throw_and_call_all_handlers_if_first_handler_fails() + { + var handler1 = new ThrowHandledHandler(); + var handler2 = new AfterThrowHandler(); + + var sut = new InMemoryCommandBus(new ICommandHandler[] { handler1, handler2 }); + + await Assert.ThrowsAsync(async () => await sut.PublishAsync(command)); + + Assert.Equal(command, handler1.LastCommand); + Assert.IsType(handler2.LastException); + } + } +} diff --git a/tests/PinkParrot.Infrastructure.Tests/CQRS/EnvelopeExtensionsTests.cs b/tests/PinkParrot.Infrastructure.Tests/CQRS/EnvelopeExtensionsTests.cs new file mode 100644 index 000000000..f9a8c23d0 --- /dev/null +++ b/tests/PinkParrot.Infrastructure.Tests/CQRS/EnvelopeExtensionsTests.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using NodaTime; +using Xunit; + +namespace PinkParrot.Infrastructure.CQRS +{ + public class EnvelopeExtensionsTests + { + private readonly Envelope sut = new Envelope(string.Empty); + private readonly CultureInfo culture = CultureInfo.InvariantCulture; + + [Fact] + public void Should_set_and_get_timestamp() + { + var timestamp = SystemClock.Instance.GetCurrentInstant(); + + sut.SetTimestamp(timestamp); + + Assert.Equal(timestamp, sut.Headers.Timestamp()); + Assert.Equal(timestamp, sut.Headers["Timestamp"].ToInstant(culture)); + } + + [Fact] + public void Should_set_and_get_commit_id() + { + var commitId = Guid.NewGuid(); + + sut.SetCommitId(commitId); + + Assert.Equal(commitId, sut.Headers.CommitId()); + Assert.Equal(commitId, sut.Headers["CommitId"].ToGuid(culture)); + } + + [Fact] + public void Should_set_and_get_event_id() + { + var commitId = Guid.NewGuid(); + + sut.SetEventId(commitId); + + Assert.Equal(commitId, sut.Headers.EventId()); + Assert.Equal(commitId, sut.Headers["EventId"].ToGuid(culture)); + } + + [Fact] + public void Should_set_and_get_aggregate_id() + { + var commitId = Guid.NewGuid(); + + sut.SetAggregateId(commitId); + + Assert.Equal(commitId, sut.Headers.AggregateId()); + Assert.Equal(commitId, sut.Headers["AggregateId"].ToGuid(culture)); + } + + [Fact] + public void Should_set_and_get_app_id() + { + var commitId = Guid.NewGuid(); + + sut.SetAppId(commitId); + + Assert.Equal(commitId, sut.Headers.AppId()); + Assert.Equal(commitId, sut.Headers["AppId"].ToGuid(culture)); + } + + [Fact] + public void Should_set_and_get_event_number() + { + const int eventNumber = 123; + + sut.SetEventNumber(eventNumber); + + Assert.Equal(eventNumber, sut.Headers.EventNumber()); + Assert.Equal(eventNumber, sut.Headers["EventNumber"].ToInt32(culture)); + } + } +} diff --git a/tests/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs b/tests/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs index 0a0ef6567..aa4d7a9ce 100644 --- a/tests/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs +++ b/tests/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs @@ -7,8 +7,10 @@ // ========================================================================== using System; +using System.Dynamic; using System.Globalization; using System.Linq; +using Microsoft.CSharp.RuntimeBinder; using Newtonsoft.Json; using NodaTime; using PinkParrot.Infrastructure.Json; @@ -181,6 +183,22 @@ namespace PinkParrot.Infrastructure Assert.Equal(0, (int)dynamicBag.Key); } + [Fact] + public void Should_throw_when_parsing_failed() + { + bag.Set("Key", "abc"); + + Assert.Throws(() => bag["Key"].ToInt32(CultureInfo.InvariantCulture)); + } + + [Fact] + public void Should_return_false_when_converter_does_not_exist() + { + bag.Set("Key", "abc"); + + Assert.Throws(() => (TimeSpan)dynamicBag.Key); + } + [Fact] public void Should_convert_string_to_numbers() { diff --git a/tests/PinkParrot.Infrastructure.Tests/project.json b/tests/PinkParrot.Infrastructure.Tests/project.json index 499886e3c..5a4f8da6b 100644 --- a/tests/PinkParrot.Infrastructure.Tests/project.json +++ b/tests/PinkParrot.Infrastructure.Tests/project.json @@ -8,6 +8,8 @@ }, "dependencies": { "dotnet-test-xunit": "2.2.0-preview2-build1029", + "Microsoft.DotNet.Watcher.Tools": "1.0.0-preview2-final", + "Moq": "4.6.38-alpha", "PinkParrot.Infrastructure": "1.0.0-*", "xunit": "2.2.0-beta3-build3402" }, @@ -25,5 +27,8 @@ "tooling": { "defaultNamespace": "PinkParrot.Core.Tests" }, + "tools": { + "Microsoft.DotNet.Watcher.Tools": "1.0.0-preview2-final" + }, "version": "1.0.0-*" } \ No newline at end of file