Browse Source

Event flow improved

pull/1/head
Sebastian 9 years ago
parent
commit
3708ed76c7
  1. 29
      src/Squidex.Events/EventExtensions.cs
  2. 5
      src/Squidex.Events/Schemas/FieldAdded.cs
  3. 4
      src/Squidex.Events/Schemas/FieldDeleted.cs
  4. 4
      src/Squidex.Events/Schemas/FieldDisabled.cs
  5. 4
      src/Squidex.Events/Schemas/FieldEnabled.cs
  6. 9
      src/Squidex.Events/Schemas/FieldEvent.cs
  7. 4
      src/Squidex.Events/Schemas/FieldHidden.cs
  8. 4
      src/Squidex.Events/Schemas/FieldShown.cs
  9. 5
      src/Squidex.Events/Schemas/FieldUpdated.cs
  10. 3
      src/Squidex.Events/Schemas/SchemaCreated.cs
  11. 88
      src/Squidex.Infrastructure/CQRS/Commands/AggregateHandler.cs
  12. 82
      src/Squidex.Infrastructure/CQRS/Commands/CommandHandler.cs
  13. 22
      src/Squidex.Infrastructure/CQRS/Commands/CommandingExtensions.cs
  14. 20
      src/Squidex.Infrastructure/CQRS/Commands/IAggregateHandler.cs
  15. 4
      src/Squidex.Infrastructure/CQRS/Commands/IDomainObjectRepository.cs
  16. 10
      src/Squidex.Infrastructure/CQRS/CommonHeaders.cs
  17. 14
      src/Squidex.Infrastructure/CQRS/DomainObject.cs
  18. 38
      src/Squidex.Infrastructure/CQRS/Envelope.cs
  19. 8
      src/Squidex.Infrastructure/CQRS/EnvelopeExtensions.cs
  20. 37
      src/Squidex.Infrastructure/CQRS/EnvelopeFactory.cs
  21. 11
      src/Squidex.Infrastructure/CQRS/EventStore/EventStoreDomainObjectRepository.cs
  22. 29
      src/Squidex.Infrastructure/CQRS/Events/EnrichWithAggregateIdProcessor.cs
  23. 29
      src/Squidex.Infrastructure/CQRS/Events/EnrichWithUserProcessor.cs
  24. 18
      src/Squidex.Infrastructure/CQRS/Events/IEventProcessor.cs
  25. 1
      src/Squidex.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs
  26. 1
      src/Squidex.Store.MongoDb/Utils/EntityMapper.cs
  27. 3
      src/Squidex.Write/AppAggregateCommand.cs
  28. 3
      src/Squidex.Write/AppCommand.cs
  29. 57
      src/Squidex.Write/Apps/AppCommandHandler.cs
  30. 32
      src/Squidex.Write/EnrichWithAppIdProcessor.cs
  31. 15
      src/Squidex.Write/Schemas/Commands/CreateSchema.cs
  32. 46
      src/Squidex.Write/Schemas/SchemaCommandHandler.cs
  33. 10
      src/Squidex.Write/Schemas/SchemaDomainObject.cs
  34. 11
      src/Squidex.Write/SquidexCommand.cs
  35. 14
      src/Squidex/Config/Domain/WriteModule.cs
  36. 209
      tests/Squidex.Infrastructure.Tests/CQRS/Commands/AggregateHandlerTests.cs
  37. 4
      tests/Squidex.Infrastructure.Tests/CQRS/Commands/CommandContextTests.cs
  38. 129
      tests/Squidex.Infrastructure.Tests/CQRS/Commands/CommandsHandlerTests.cs
  39. 10
      tests/Squidex.Infrastructure.Tests/CQRS/Commands/EnrichWithTimestampHandlerTests.cs
  40. 4
      tests/Squidex.Infrastructure.Tests/CQRS/Commands/InMemoryCommandBusTests.cs
  41. 10
      tests/Squidex.Infrastructure.Tests/CQRS/DomainObjectTest.cs
  42. 10
      tests/Squidex.Infrastructure.Tests/CQRS/EnvelopeExtensionsTests.cs
  43. 20
      tests/Squidex.Infrastructure.Tests/CQRS/EventStore/DefaultNameResolverTests.cs
  44. 13
      tests/Squidex.Infrastructure.Tests/CQRS/EventStore/EventStoreFormatterTests.cs
  45. 56
      tests/Squidex.Infrastructure.Tests/CQRS/Events/EnrichWithAggregateIdProcessorTests.cs
  46. 55
      tests/Squidex.Infrastructure.Tests/CQRS/Events/EnrichWithUserProcessorTests.cs
  47. 144
      tests/Squidex.Infrastructure.Tests/DispatchingTests.cs
  48. 8
      tests/Squidex.Infrastructure.Tests/GuardTests.cs
  49. 12
      tests/Squidex.Infrastructure.Tests/Reflection/ReflectionExtensionTests.cs
  50. 22
      tests/Squidex.Infrastructure.Tests/Reflection/SimpleMapperTests.cs
  51. 7
      tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs
  52. 58
      tests/Squidex.Write.Tests/EnrichWithAppIdProcessorTests.cs
  53. 5
      tests/Squidex.Write.Tests/Schemas/SchemaCommandHandlerTests.cs
  54. 2
      tests/Squidex.Write.Tests/Schemas/SchemaDomainObjectTests.cs
  55. 62
      tests/Squidex.Write.Tests/Utils/HandlerTestBase.cs

29
src/Squidex.Events/EventExtensions.cs

@ -0,0 +1,29 @@
// ==========================================================================
// EventExtensions.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Globalization;
using Squidex.Infrastructure.CQRS;
namespace Squidex.Events
{
public static class EventExtensions
{
public static Guid AppId(this EnvelopeHeaders headers)
{
return headers["AppId"].ToGuid(CultureInfo.InvariantCulture);
}
public static Envelope<T> SetAppId<T>(this Envelope<T> envelope, Guid value) where T : class
{
envelope.Headers.Set("AppId", value);
return envelope;
}
}
}

5
src/Squidex.Events/Schemas/FieldAdded.cs

@ -8,15 +8,12 @@
using Squidex.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Events.Schemas
{
[TypeName("FieldAddedEvent")]
public class FieldAdded : IEvent
public class FieldAdded : FieldEvent
{
public long FieldId { get; set; }
public string Name { get; set; }
public FieldProperties Properties { get; set; }

4
src/Squidex.Events/Schemas/FieldDeleted.cs

@ -7,13 +7,11 @@
// ==========================================================================
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Events.Schemas
{
[TypeName("FieldDeletedEvent")]
public class FieldDeleted : IEvent
public class FieldDeleted : FieldEvent
{
public long FieldId { get; set; }
}
}

4
src/Squidex.Events/Schemas/FieldDisabled.cs

@ -7,13 +7,11 @@
// ==========================================================================
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Events.Schemas
{
[TypeName("FieldDisabledEvent")]
public class FieldDisabled : IEvent
public class FieldDisabled : FieldEvent
{
public long FieldId { get; set; }
}
}

4
src/Squidex.Events/Schemas/FieldEnabled.cs

@ -7,13 +7,11 @@
// ==========================================================================
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Events.Schemas
{
[TypeName("FieldEnabledEvent")]
public class FieldEnabled : IEvent
public class FieldEnabled : FieldEvent
{
public long FieldId { get; set; }
}
}

9
src/Squidex.Events/AppEvent.cs → src/Squidex.Events/Schemas/FieldEvent.cs

@ -1,18 +1,17 @@
// ==========================================================================
// AppEvent.cs
// FieldEvent.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Events
namespace Squidex.Events.Schemas
{
public class AppEvent : IEvent
public abstract class FieldEvent : IEvent
{
public Guid AppId { get; set; }
public long FieldId { get; set; }
}
}

4
src/Squidex.Events/Schemas/FieldHidden.cs

@ -7,13 +7,11 @@
// ==========================================================================
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Events.Schemas
{
[TypeName("FieldHiddenEvent")]
public class FieldHidden : IEvent
public class FieldHidden : FieldEvent
{
public long FieldId { get; set; }
}
}

4
src/Squidex.Events/Schemas/FieldShown.cs

@ -7,13 +7,11 @@
// ==========================================================================
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Events.Schemas
{
[TypeName("FieldShownEvent")]
public class FieldShown : IEvent
public class FieldShown : FieldEvent
{
public long FieldId { get; set; }
}
}

5
src/Squidex.Events/Schemas/FieldUpdated.cs

@ -8,15 +8,12 @@
using Squidex.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Events.Schemas
{
[TypeName("FieldUpdatedEvent")]
public class FieldUpdated : IEvent
public class FieldUpdated : FieldEvent
{
public long FieldId { get; set; }
public FieldProperties Properties { get; set; }
}
}

3
src/Squidex.Events/Schemas/SchemaCreated.cs

@ -8,11 +8,12 @@
using Squidex.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Events.Schemas
{
[TypeName("SchemaCreatedEvent")]
public class SchemaCreated : AppEvent
public class SchemaCreated : IEvent
{
public string Name { get; set; }

88
src/Squidex.Infrastructure/CQRS/Commands/AggregateHandler.cs

@ -0,0 +1,88 @@
// ==========================================================================
// AggregateHandler.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Infrastructure.CQRS.Commands
{
public sealed class AggregateHandler : IAggregateHandler
{
private readonly IDomainObjectRepository domainObjectRepository;
private readonly IDomainObjectFactory domainObjectFactory;
private readonly IEnumerable<IEventProcessor> eventProcessors;
public IDomainObjectRepository Repository
{
get { return domainObjectRepository; }
}
public IDomainObjectFactory Factory
{
get { return domainObjectFactory; }
}
public AggregateHandler(
IDomainObjectFactory domainObjectFactory,
IDomainObjectRepository domainObjectRepository,
IEnumerable<IEventProcessor> eventProcessors)
{
Guard.NotNull(eventProcessors, nameof(eventProcessors));
Guard.NotNull(domainObjectFactory, nameof(domainObjectFactory));
Guard.NotNull(domainObjectRepository, nameof(domainObjectRepository));
this.domainObjectFactory = domainObjectFactory;
this.domainObjectRepository = domainObjectRepository;
this.eventProcessors = eventProcessors;
}
public async Task CreateAsync<T>(IAggregateCommand command, Func<T, Task> creator) where T : class, IAggregate
{
Guard.NotNull(creator, nameof(creator));
Guard.NotNull(command, nameof(command));
var aggregate = domainObjectFactory.CreateNew<T>(command.AggregateId);
await creator(aggregate);
await Save(command, aggregate);
}
public async Task UpdateAsync<T>(IAggregateCommand command, Func<T, Task> updater) where T : class, IAggregate
{
Guard.NotNull(updater, nameof(updater));
Guard.NotNull(command, nameof(command));
var aggregate = await domainObjectRepository.GetByIdAsync<T>(command.AggregateId);
await updater(aggregate);
await Save(command, aggregate);
}
private async Task Save(ICommand command, IAggregate aggregate)
{
var events = aggregate.GetUncomittedEvents();
foreach (var @event in events)
{
foreach (var eventProcessor in eventProcessors)
{
await eventProcessor.ProcessEventAsync(@event, aggregate, command);
}
}
await domainObjectRepository.SaveAsync(aggregate, events, Guid.NewGuid());
aggregate.ClearUncommittedEvents();
}
}
}

82
src/Squidex.Infrastructure/CQRS/Commands/CommandHandler.cs

@ -1,82 +0,0 @@
// ==========================================================================
// CommandHandler.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
namespace Squidex.Infrastructure.CQRS.Commands
{
public abstract class CommandHandler<T> : ICommandHandler where T : class, IAggregate
{
private readonly IDomainObjectRepository domainObjectRepository;
private readonly IDomainObjectFactory domainObjectFactory;
protected IDomainObjectRepository Repository
{
get { return domainObjectRepository; }
}
protected IDomainObjectFactory Factory
{
get { return domainObjectFactory; }
}
protected CommandHandler(IDomainObjectFactory domainObjectFactory, IDomainObjectRepository domainObjectRepository)
{
Guard.NotNull(domainObjectFactory, nameof(domainObjectFactory));
Guard.NotNull(domainObjectRepository, nameof(domainObjectRepository));
this.domainObjectFactory = domainObjectFactory;
this.domainObjectRepository = domainObjectRepository;
}
protected async Task CreateAsync(IAggregateCommand command, Func<T, Task> creator)
{
Guard.NotNull(creator, nameof(creator));
Guard.NotNull(command, nameof(command));
var domainObject = domainObjectFactory.CreateNew<T>(command.AggregateId);
await creator(domainObject);
await domainObjectRepository.SaveAsync(domainObject, Guid.NewGuid());
}
protected Task CreateAsync(IAggregateCommand command, Action<T> creator)
{
return CreateAsync(command, x =>
{
creator(x);
return Task.FromResult(true);
});
}
protected async Task UpdateAsync(IAggregateCommand command, Func<T, Task> updater)
{
Guard.NotNull(updater, nameof(updater));
Guard.NotNull(command, nameof(command));
var domainObject = await domainObjectRepository.GetByIdAsync<T>(command.AggregateId);
await updater(domainObject);
await domainObjectRepository.SaveAsync(domainObject, Guid.NewGuid());
}
protected Task UpdateAsync(IAggregateCommand command, Action<T> updater)
{
return UpdateAsync(command, x =>
{
updater(x);
return Task.FromResult(true);
});
}
public abstract Task<bool> HandleAsync(CommandContext context);
}
}

22
src/Squidex.Infrastructure/CQRS/Commands/CommandingExtensions.cs

@ -7,6 +7,8 @@
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Infrastructure.CQRS.Commands
{
@ -16,5 +18,25 @@ namespace Squidex.Infrastructure.CQRS.Commands
{
return (T)factory.CreateNew(typeof(T), id);
}
public static Task CreateAsync<T>(this IAggregateHandler handler, IAggregateCommand command, Action<T> creator) where T : class, IAggregate
{
return handler.CreateAsync<T>(command, x =>
{
creator(x);
return TaskHelper.Done;
});
}
public static Task UpdateAsync<T>(this IAggregateHandler handler, IAggregateCommand command, Action<T> creator) where T : class, IAggregate
{
return handler.UpdateAsync<T>(command, x =>
{
creator(x);
return TaskHelper.Done;
});
}
}
}

20
src/Squidex.Infrastructure/CQRS/Commands/IAggregateHandler.cs

@ -0,0 +1,20 @@
// ==========================================================================
// IAggregateHandler.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
namespace Squidex.Infrastructure.CQRS.Commands
{
public interface IAggregateHandler
{
Task CreateAsync<T>(IAggregateCommand command, Func<T, Task> creator) where T : class, IAggregate;
Task UpdateAsync<T>(IAggregateCommand command, Func<T, Task> updater) where T : class, IAggregate;
}
}

4
src/Squidex.Infrastructure/CQRS/Commands/IDomainObjectRepository.cs

@ -7,7 +7,9 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Infrastructure.CQRS.Commands
{
@ -15,6 +17,6 @@ namespace Squidex.Infrastructure.CQRS.Commands
{
Task<TDomainObject> GetByIdAsync<TDomainObject>(Guid id, int version = int.MaxValue) where TDomainObject : class, IAggregate;
Task SaveAsync(IAggregate domainObject, Guid commitId);
Task SaveAsync(IAggregate domainObject, ICollection<Envelope<IEvent>> events, Guid commitId);
}
}

10
src/Squidex.Infrastructure/CQRS/CommonHeaders.cs

@ -5,15 +5,21 @@
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Infrastructure.CQRS
{
public sealed class CommonHeaders
{
public const string AggregateId = "AggregateId";
public const string CommitId = "CommitId";
public const string Timestamp = "Timestamp";
public const string AppId = "AppId";
public const string EventId = "EventId";
public const string EventNumber = "EventNumber";
public const string Timestamp = "Timestamp";
public const string User = "User";
}
}

14
src/Squidex.Infrastructure/CQRS/DomainObject.cs

@ -40,25 +40,23 @@ namespace Squidex.Infrastructure.CQRS
protected abstract void DispatchEvent(Envelope<IEvent> @event);
protected void RaiseEvent(IEvent @event)
private void ApplyEventCore(Envelope<IEvent> @event)
{
RaiseEvent(EnvelopeFactory.ForEvent(@event, this));
DispatchEvent(@event); version++;
}
private void ApplyEventCore(Envelope<IEvent> @event)
protected void RaiseEvent(IEvent @event)
{
DispatchEvent(@event); version++;
RaiseEvent(Envelope<IEvent>.Create(@event));
}
protected void RaiseEvent<TEvent>(Envelope<TEvent> @event) where TEvent : class, IEvent
{
Guard.NotNull(@event, nameof(@event));
var envelopeToAdd = @event.To<IEvent>();
uncomittedEvents.Add(envelopeToAdd);
uncomittedEvents.Add(@event.To<IEvent>());
ApplyEventCore(envelopeToAdd);
ApplyEventCore(@event.To<IEvent>());
}
void IAggregate.ApplyEvent(Envelope<IEvent> @event)

38
src/Squidex.Infrastructure/CQRS/Envelope.cs

@ -5,6 +5,10 @@
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using NodaTime;
namespace Squidex.Infrastructure.CQRS
{
public class Envelope<TPayload> where TPayload : class
@ -14,18 +18,22 @@ namespace Squidex.Infrastructure.CQRS
public EnvelopeHeaders Headers
{
get
{
return headers;
}
get { return headers; }
}
public TPayload Payload
{
get
{
return payload;
}
get { return payload; }
}
public Envelope(TPayload payload)
: this(payload, new EnvelopeHeaders())
{
}
public Envelope(TPayload payload, PropertiesBag bag)
: this(payload, new EnvelopeHeaders(bag))
{
}
public Envelope(TPayload payload, EnvelopeHeaders headers)
@ -37,14 +45,16 @@ namespace Squidex.Infrastructure.CQRS
this.headers = headers;
}
public Envelope(TPayload payload)
: this(payload, new EnvelopeHeaders())
public static Envelope<TPayload> Create(TPayload payload)
{
}
var eventId = Guid.NewGuid();
public Envelope(TPayload payload, PropertiesBag bag)
: this(payload, new EnvelopeHeaders(bag))
{
var envelope =
new Envelope<TPayload>(payload)
.SetEventId(eventId)
.SetTimestamp(SystemClock.Instance.GetCurrentInstant());
return envelope;
}
public Envelope<TOther> To<TOther>() where TOther : class

8
src/Squidex.Infrastructure/CQRS/EnvelopeExtensions.cs

@ -62,14 +62,14 @@ namespace Squidex.Infrastructure.CQRS
return envelope;
}
public static Guid AppId(this EnvelopeHeaders headers)
public static UserToken User(this EnvelopeHeaders headers)
{
return headers[CommonHeaders.AppId].ToGuid(CultureInfo.InvariantCulture);
return UserToken.Parse(headers[CommonHeaders.User].ToString());
}
public static Envelope<T> SetAppId<T>(this Envelope<T> envelope, Guid value) where T : class
public static Envelope<T> SetUser<T>(this Envelope<T> envelope, UserToken value) where T : class
{
envelope.Headers.Set(CommonHeaders.AppId, value);
envelope.Headers.Set(CommonHeaders.User, value.ToString());
return envelope;
}

37
src/Squidex.Infrastructure/CQRS/EnvelopeFactory.cs

@ -1,37 +0,0 @@
// ==========================================================================
// EnvelopeFactory.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using NodaTime;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Infrastructure.CQRS
{
public static class EnvelopeFactory
{
public static Envelope<IEvent> ForEvent(IEvent @event, IAggregate aggregate)
{
var eventId = Guid.NewGuid();
var envelope =
new Envelope<IEvent>(@event)
.SetAggregateId(aggregate.Id)
.SetEventId(eventId)
.SetTimestamp(SystemClock.Instance.GetCurrentInstant());
var appAggregate = aggregate as IAppAggregate;
if (appAggregate != null)
{
envelope = envelope.SetAppId(appAggregate.AppId);
}
return envelope;
}
}
}

11
src/Squidex.Infrastructure/CQRS/EventStore/EventStoreDomainObjectRepository.cs

@ -14,6 +14,7 @@ using System.Threading.Tasks;
using EventStore.ClientAPI;
using EventStore.ClientAPI.SystemData;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.CQRS.Events;
// ReSharper disable RedundantAssignment
// ReSharper disable ConvertIfStatementToSwitchStatement
@ -98,19 +99,17 @@ namespace Squidex.Infrastructure.CQRS.EventStore
return domainObject;
}
public async Task SaveAsync(IAggregate domainObject, Guid commitId)
public async Task SaveAsync(IAggregate domainObject, ICollection<Envelope<IEvent>> events, Guid commitId)
{
Guard.NotNull(domainObject, nameof(domainObject));
var streamName = nameResolver.GetStreamName(domainObject.GetType(), domainObject.Id);
var newEvents = domainObject.GetUncomittedEvents();
var versionCurrent = domainObject.Version;
var versionPrevious = versionCurrent - newEvents.Count;
var versionExpected = versionPrevious == 0 ? ExpectedVersion.NoStream : versionPrevious - 1;
var versionBefore = versionCurrent - events.Count;
var versionExpected = versionBefore == 0 ? ExpectedVersion.NoStream : versionBefore - 1;
var eventsToSave = newEvents.Select(x => formatter.ToEventData(x, commitId)).ToList();
var eventsToSave = events.Select(x => formatter.ToEventData(x, commitId)).ToList();
await InsertEventsAsync(streamName, versionExpected, eventsToSave);

29
src/Squidex.Infrastructure/CQRS/Events/EnrichWithAggregateIdProcessor.cs

@ -0,0 +1,29 @@
// ==========================================================================
// EnrichWithAggregateIdProcessor.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Infrastructure.CQRS.Events
{
public sealed class EnrichWithAggregateIdProcessor : IEventProcessor
{
public Task ProcessEventAsync(Envelope<IEvent> @event, IAggregate aggregate, ICommand command)
{
var aggregateCommand = command as IAggregateCommand;
if (aggregateCommand != null)
{
@event.SetAggregateId(aggregateCommand.AggregateId);
}
return TaskHelper.Done;
}
}
}

29
src/Squidex.Infrastructure/CQRS/Events/EnrichWithUserProcessor.cs

@ -0,0 +1,29 @@
// ==========================================================================
// EnrichWithUserProcessor.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Infrastructure.CQRS.Events
{
public sealed class EnrichWithUserProcessor : IEventProcessor
{
public Task ProcessEventAsync(Envelope<IEvent> @event, IAggregate aggregate, ICommand command)
{
var userCommand = command as IUserCommand;
if (userCommand != null)
{
@event.SetUser(userCommand.User);
}
return TaskHelper.Done;
}
}
}

18
src/Squidex.Infrastructure/CQRS/Events/IEventProcessor.cs

@ -0,0 +1,18 @@
// ==========================================================================
// IEventProcessor.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Infrastructure.CQRS.Commands;
namespace Squidex.Infrastructure.CQRS.Events
{
public interface IEventProcessor
{
Task ProcessEventAsync(Envelope<IEvent> @event, IAggregate aggregate, ICommand command);
}
}

1
src/Squidex.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs

@ -15,6 +15,7 @@ using Squidex.Infrastructure.CQRS;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Read.Schemas.Repositories;
using Squidex.Read.Utils;
using Squidex.Events;
// ReSharper disable InvertIf

1
src/Squidex.Store.MongoDb/Utils/EntityMapper.cs

@ -11,6 +11,7 @@ using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json.Linq;
using Squidex.Events;
using Squidex.Infrastructure.CQRS;
using Squidex.Read;

3
src/Squidex.Write/AppAggregateCommand.cs

@ -7,11 +7,10 @@
// ==========================================================================
using System;
using Squidex.Infrastructure.CQRS.Commands;
namespace Squidex.Write
{
public class AppAggregateCommand : AggregateCommand, IAppCommand
public class AppAggregateCommand : SquidexCommand, IAppCommand
{
Guid IAppCommand.AppId
{

3
src/Squidex.Write/AppCommand.cs

@ -7,11 +7,10 @@
// ==========================================================================
using System;
using Squidex.Infrastructure.CQRS.Commands;
namespace Squidex.Write
{
public abstract class AppCommand : AggregateCommand, IAppCommand
public abstract class AppCommand : SquidexCommand, IAppCommand
{
public Guid AppId { get; set; }
}

57
src/Squidex.Write/Apps/AppCommandHandler.cs

@ -16,68 +16,71 @@ using Squidex.Write.Apps.Commands;
namespace Squidex.Write.Apps
{
public class AppCommandHandler : CommandHandler<AppDomainObject>
public class AppCommandHandler : ICommandHandler
{
private readonly IAggregateHandler handler;
private readonly IAppRepository appRepository;
private readonly IUserRepository userRepository;
private readonly ClientKeyGenerator keyGenerator;
public AppCommandHandler(
IDomainObjectFactory domainObjectFactory,
IDomainObjectRepository domainObjectRepository,
IUserRepository userRepository,
IAggregateHandler handler,
IAppRepository appRepository,
IUserRepository userRepository,
ClientKeyGenerator keyGenerator)
: base(domainObjectFactory, domainObjectRepository)
{
Guard.NotNull(handler, nameof(handler));
Guard.NotNull(keyGenerator, nameof(keyGenerator));
Guard.NotNull(appRepository, nameof(appRepository));
Guard.NotNull(userRepository, nameof(userRepository));
this.handler = handler;
this.keyGenerator = keyGenerator;
this.appRepository = appRepository;
this.userRepository = userRepository;
}
protected Task On(CreateApp command, CommandContext context)
protected async Task On(CreateApp command, CommandContext context)
{
return CreateAsync(command, async x =>
if (await appRepository.FindAppByNameAsync(command.Name) != null)
{
if (await appRepository.FindAppByNameAsync(command.Name) != null)
{
var error = new ValidationError($"A app with name '{command.Name}' already exists", nameof(CreateApp.Name));
var error =
new ValidationError($"A app with name '{command.Name}' already exists",
nameof(CreateApp.Name));
throw new ValidationException("Cannot create a new app", error);
}
throw new ValidationException("Cannot create a new app", error);
}
await handler.CreateAsync<AppDomainObject>(command, x =>
{
x.Create(command);
context.Succeed(command.AggregateId);
});
}
protected Task On(AssignContributor command, CommandContext context)
protected async Task On(AssignContributor command, CommandContext context)
{
return UpdateAsync(command, async x =>
if (await userRepository.FindUserByIdAsync(command.ContributorId) == null)
{
if (await userRepository.FindUserByIdAsync(command.ContributorId) == null)
{
var error = new ValidationError($"Cannot find contributor '{command.ContributorId ?? "UNKNOWN"}'", nameof(AssignContributor.ContributorId));
var error =
new ValidationError($"Cannot find contributor '{command.ContributorId ?? "UNKNOWN"}'",
nameof(AssignContributor.ContributorId));
throw new ValidationException("Cannot assign contributor to app", error);
}
throw new ValidationException("Cannot assign contributor to app", error);
}
await handler.UpdateAsync<AppDomainObject>(command, x =>
{
x.AssignContributor(command);
});
}
protected Task On(AttachClient command, CommandContext context)
{
return UpdateAsync(command, x =>
return handler.UpdateAsync<AppDomainObject>(command, x =>
{
var clientKey = keyGenerator.GenerateKey();
x.AttachClient(command, clientKey);
x.AttachClient(command, keyGenerator.GenerateKey());
context.Succeed(x.Clients[command.ClientName]);
});
@ -85,20 +88,20 @@ namespace Squidex.Write.Apps
protected Task On(RemoveContributor command, CommandContext context)
{
return UpdateAsync(command, x => x.RemoveContributor(command));
return handler.UpdateAsync<AppDomainObject>(command, x => x.RemoveContributor(command));
}
protected Task On(RevokeClient command, CommandContext context)
{
return UpdateAsync(command, x => x.RevokeClient(command));
return handler.UpdateAsync<AppDomainObject>(command, x => x.RevokeClient(command));
}
protected Task On(ConfigureLanguages command, CommandContext context)
{
return UpdateAsync(command, x => x.ConfigureLanguages(command));
return handler.UpdateAsync<AppDomainObject>(command, x => x.ConfigureLanguages(command));
}
public override Task<bool> HandleAsync(CommandContext context)
public Task<bool> HandleAsync(CommandContext context)
{
return context.IsHandled ? Task.FromResult(false) : this.DispatchActionAsync(context.Command, context);
}

32
src/Squidex.Write/EnrichWithAppIdProcessor.cs

@ -0,0 +1,32 @@
// ==========================================================================
// EnrichWithAppIdProcessor.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Events;
using Squidex.Infrastructure.CQRS;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Write
{
public sealed class EnrichWithAppIdProcessor : IEventProcessor
{
public Task ProcessEventAsync(Envelope<IEvent> @event, IAggregate aggregate, ICommand command)
{
var appCommand = command as IAppCommand;
if (appCommand != null)
{
@event.SetAppId(appCommand.AppId);
}
return TaskHelper.Done;
}
}
}

15
src/Squidex.Write/Schemas/Commands/CreateSchema.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Core.Schemas;
using Squidex.Infrastructure;
@ -16,15 +17,23 @@ namespace Squidex.Write.Schemas.Commands
{
private SchemaProperties properties;
public string Name { get; set; }
public SchemaProperties Properties
{
get
{
return properties ?? (properties = new SchemaProperties());
}
set { properties = value; }
set
{
properties = value;
}
}
public string Name { get; set; }
public CreateSchema()
{
AggregateId = Guid.NewGuid();
}
public void Validate(IList<ValidationError> errors)

46
src/Squidex.Write/Schemas/SchemaCommandHandler.cs

@ -16,28 +16,32 @@ using Squidex.Write.Schemas.Commands;
namespace Squidex.Write.Schemas
{
public class SchemaCommandHandler : CommandHandler<SchemaDomainObject>
public class SchemaCommandHandler : ICommandHandler
{
private readonly ISchemaProvider schemaProvider;
private readonly ISchemaProvider schemas;
private readonly IAggregateHandler handler;
public SchemaCommandHandler(
IDomainObjectFactory domainObjectFactory,
IDomainObjectRepository domainObjectRepository,
ISchemaProvider schemaProvider)
: base(domainObjectFactory, domainObjectRepository)
public SchemaCommandHandler(IAggregateHandler handler, ISchemaProvider schemas)
{
this.schemaProvider = schemaProvider;
Guard.NotNull(handler, nameof(handler));
Guard.NotNull(schemas, nameof(schemas));
this.handler = handler;
this.schemas = schemas;
}
protected async Task On(CreateSchema command, CommandContext context)
{
if (await schemaProvider.FindSchemaIdByNameAsync(command.AppId, command.Name) != null)
if (await schemas.FindSchemaIdByNameAsync(command.AppId, command.Name) != null)
{
var error = new ValidationError($"A schema with name '{command.Name}' already exists", "DisplayName");
var error =
new ValidationError($"A schema with name '{command.Name}' already exists", "DisplayName",
nameof(CreateSchema.Name));
throw new ValidationException("Cannot create a new schema", error);
}
await CreateAsync(command, s =>
await handler.CreateAsync<SchemaDomainObject>(command, s =>
{
s.Create(command);
@ -47,7 +51,7 @@ namespace Squidex.Write.Schemas
protected Task On(AddField command, CommandContext context)
{
return UpdateAsync(command, s =>
return handler.UpdateAsync<SchemaDomainObject>(command, s =>
{
s.AddField(command);
@ -57,45 +61,45 @@ namespace Squidex.Write.Schemas
protected Task On(DeleteSchema command, CommandContext context)
{
return UpdateAsync(command, s => s.Delete(command));
return handler.UpdateAsync<SchemaDomainObject>(command, s => s.Delete(command));
}
protected Task On(DeleteField command, CommandContext context)
{
return UpdateAsync(command, s => s.DeleteField(command));
return handler.UpdateAsync<SchemaDomainObject>(command, s => s.DeleteField(command));
}
protected Task On(DisableField command, CommandContext context)
{
return UpdateAsync(command, s => s.DisableField(command));
return handler.UpdateAsync<SchemaDomainObject>(command, s => s.DisableField(command));
}
protected Task On(EnableField command, CommandContext context)
{
return UpdateAsync(command, s => s.EnableField(command));
return handler.UpdateAsync<SchemaDomainObject>(command, s => s.EnableField(command));
}
protected Task On(HideField command, CommandContext context)
{
return UpdateAsync(command, s => s.HideField(command));
return handler.UpdateAsync<SchemaDomainObject>(command, s => s.HideField(command));
}
protected Task On(ShowField command, CommandContext context)
{
return UpdateAsync(command, s => s.ShowField(command));
return handler.UpdateAsync<SchemaDomainObject>(command, s => s.ShowField(command));
}
protected Task On(UpdateSchema command, CommandContext context)
{
return UpdateAsync(command, s => s.Update(command));
return handler.UpdateAsync<SchemaDomainObject>(command, s => s.Update(command));
}
protected Task On(UpdateField command, CommandContext context)
{
return UpdateAsync(command, s => { s.UpdateField(command); });
return handler.UpdateAsync<SchemaDomainObject>(command, s => { s.UpdateField(command); });
}
public override Task<bool> HandleAsync(CommandContext context)
public Task<bool> HandleAsync(CommandContext context)
{
return context.IsHandled ? Task.FromResult(false) : this.DispatchActionAsync(context.Command, context);
}

10
src/Squidex.Write/Schemas/SchemaDomainObject.cs

@ -18,10 +18,9 @@ using Squidex.Write.Schemas.Commands;
namespace Squidex.Write.Schemas
{
public class SchemaDomainObject : DomainObject, IAppAggregate
public class SchemaDomainObject : DomainObject
{
private readonly FieldRegistry registry;
private Guid appId;
private bool isDeleted;
private long totalFields;
private Schema schema;
@ -31,11 +30,6 @@ namespace Squidex.Write.Schemas
get { return schema; }
}
public Guid AppId
{
get { return appId; }
}
public bool IsDeleted
{
get { return isDeleted; }
@ -58,8 +52,6 @@ namespace Squidex.Write.Schemas
public void On(SchemaCreated @event)
{
appId = @event.AppId;
schema = Schema.Create(@event.Name, @event.Properties);
}

11
src/Squidex.Infrastructure/CQRS/IAppAggregate.cs → src/Squidex.Write/SquidexCommand.cs

@ -1,17 +1,18 @@
// ==========================================================================
// IAppAggregate.cs
// SquidexCommand.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands;
namespace Squidex.Infrastructure.CQRS
namespace Squidex.Write
{
public interface IAppAggregate : IAggregate
public abstract class SquidexCommand : AggregateCommand, IUserCommand
{
Guid AppId { get; }
public UserToken User { get; set; }
}
}

14
src/Squidex/Config/Domain/WriteModule.cs

@ -7,7 +7,9 @@
// ==========================================================================
using Autofac;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Pipeline.CommandHandlers;
using Squidex.Write;
using Squidex.Write.Apps;
using Squidex.Write.Schemas;
@ -33,6 +35,18 @@ namespace Squidex.Config.Domain
.As<ICommandHandler>()
.SingleInstance();
builder.RegisterType<EnrichWithAppIdProcessor>()
.As<IEventProcessor>()
.SingleInstance();
builder.RegisterType<EnrichWithAggregateIdProcessor>()
.As<IEventProcessor>()
.SingleInstance();
builder.RegisterType<EnrichWithUserHandler>()
.As<IEventProcessor>()
.SingleInstance();
builder.RegisterType<AppCommandHandler>()
.As<ICommandHandler>()
.SingleInstance();

209
tests/Squidex.Infrastructure.Tests/CQRS/Commands/AggregateHandlerTests.cs

@ -0,0 +1,209 @@
// ==========================================================================
// AggregateHandlerTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Moq;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Tasks;
using Xunit;
namespace Squidex.Infrastructure.CQRS.Commands
{
public class AggregateHandlerTests
{
private sealed class MyEvent : IEvent
{
}
private sealed class MyCommand : IAggregateCommand
{
public Guid AggregateId { get; set; }
}
private sealed class MyDomainObject : DomainObject
{
public MyDomainObject(Guid id, int version)
: base(id, version)
{
}
public MyDomainObject RaiseNewEvent(Envelope<IEvent> @event)
{
RaiseEvent(@event);
return this;
}
protected override void DispatchEvent(Envelope<IEvent> @event)
{
}
}
private readonly Mock<IDomainObjectFactory> factory = new Mock<IDomainObjectFactory>();
private readonly Mock<IDomainObjectRepository> repository = new Mock<IDomainObjectRepository>();
private readonly Mock<IEventProcessor> processor1 = new Mock<IEventProcessor>();
private readonly Mock<IEventProcessor> processor2 = new Mock<IEventProcessor>();
private readonly Envelope<IEvent> event1 = new Envelope<IEvent>(new MyEvent());
private readonly Envelope<IEvent> event2 = new Envelope<IEvent>(new MyEvent());
private readonly MyCommand command;
private readonly AggregateHandler sut;
private readonly MyDomainObject domainObject;
public AggregateHandlerTests()
{
var processors = new[] { processor1.Object, processor2.Object };
sut = new AggregateHandler(factory.Object, repository.Object, processors);
domainObject =
new MyDomainObject(Guid.NewGuid(), 1)
.RaiseNewEvent(event1)
.RaiseNewEvent(event2);
command = new MyCommand { AggregateId = domainObject.Id };
}
[Fact]
public void Should_provide_access_to_factory()
{
Assert.Equal(factory.Object, sut.Factory);
}
[Fact]
public void Should_provide_access_to_repository()
{
Assert.Equal(repository.Object, sut.Repository);
}
[Fact]
public async Task Create_async_should_create_domain_object_and_save()
{
factory.Setup(x => x.CreateNew(typeof(MyDomainObject), domainObject.Id))
.Returns(domainObject)
.Verifiable();
await TestFlowAsync(async () =>
{
MyDomainObject passedDomainObject = null;
await sut.CreateAsync<MyDomainObject>(command, x =>
{
passedDomainObject = x;
return TaskHelper.Done;
});
Assert.Equal(domainObject, passedDomainObject);
});
factory.VerifyAll();
}
[Fact]
public async Task Create_sync_should_create_domain_object_and_save()
{
factory.Setup(x => x.CreateNew(typeof(MyDomainObject), domainObject.Id))
.Returns(domainObject)
.Verifiable();
await TestFlowAsync(async () =>
{
MyDomainObject passedDomainObject = null;
await sut.CreateAsync<MyDomainObject>(command, x =>
{
passedDomainObject = x;
});
Assert.Equal(domainObject, passedDomainObject);
});
factory.VerifyAll();
}
[Fact]
public async Task Update_async_should_create_domain_object_and_save()
{
repository.Setup(x => x.GetByIdAsync<MyDomainObject>(command.AggregateId, int.MaxValue))
.Returns(Task.FromResult(domainObject))
.Verifiable();
await TestFlowAsync(async () =>
{
MyDomainObject passedDomainObject = null;
await sut.UpdateAsync<MyDomainObject>(command, x =>
{
passedDomainObject = x;
return TaskHelper.Done;
});
Assert.Equal(domainObject, passedDomainObject);
});
}
[Fact]
public async Task Update_sync_should_create_domain_object_and_save()
{
repository.Setup(x => x.GetByIdAsync<MyDomainObject>(command.AggregateId, int.MaxValue))
.Returns(Task.FromResult(domainObject))
.Verifiable();
await TestFlowAsync(async () =>
{
MyDomainObject passedDomainObject = null;
await sut.UpdateAsync<MyDomainObject>(command, x =>
{
passedDomainObject = x;
});
Assert.Equal(domainObject, passedDomainObject);
});
}
private async Task TestFlowAsync(Func<Task> action)
{
repository.Setup(x => x.SaveAsync(domainObject,
It.IsAny<ICollection<Envelope<IEvent>>>(),
It.IsAny<Guid>()))
.Returns(TaskHelper.Done)
.Verifiable();
processor1.Setup(x => x.ProcessEventAsync(
It.Is<Envelope<IEvent>>(y => y.Payload == event1.Payload), domainObject, command))
.Returns(TaskHelper.Done)
.Verifiable();
processor2.Setup(x => x.ProcessEventAsync(
It.Is<Envelope<IEvent>>(y => y.Payload == event1.Payload), domainObject, command))
.Returns(TaskHelper.Done)
.Verifiable();
processor1.Setup(x => x.ProcessEventAsync(
It.Is<Envelope<IEvent>>(y => y.Payload == event2.Payload), domainObject, command))
.Returns(TaskHelper.Done)
.Verifiable();
processor2.Setup(x => x.ProcessEventAsync(
It.Is<Envelope<IEvent>>(y => y.Payload == event2.Payload), domainObject, command))
.Returns(TaskHelper.Done)
.Verifiable();
await action();
processor1.VerifyAll();
processor2.VerifyAll();
repository.VerifyAll();
}
}
}

4
tests/Squidex.Infrastructure.Tests/CQRS/Commands/CommandContextTests.cs

@ -13,9 +13,9 @@ namespace Squidex.Infrastructure.CQRS.Commands
{
public class CommandContextTests
{
private readonly MockupCommand command = new MockupCommand();
private readonly MyCommand command = new MyCommand();
private sealed class MockupCommand : ICommand
private sealed class MyCommand : ICommand
{
}

129
tests/Squidex.Infrastructure.Tests/CQRS/Commands/CommandsHandlerTests.cs

@ -1,129 +0,0 @@
// ==========================================================================
// CommandsHandlerTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Moq;
using Squidex.Infrastructure.CQRS.Events;
using Xunit;
namespace Squidex.Infrastructure.CQRS.Commands
{
public class CommandsHandlerTests
{
private readonly Mock<IDomainObjectFactory> domainObjectFactory = new Mock<IDomainObjectFactory>();
private readonly Mock<IDomainObjectRepository> domainObjectRepository = new Mock<IDomainObjectRepository>();
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<IEvent> @event)
{
throw new NotImplementedException();
}
}
private sealed class TestCommandHandler : CommandHandler<TestDomainObject>
{
public TestCommandHandler(IDomainObjectFactory domainObjectFactory, IDomainObjectRepository domainObjectRepository)
: base(domainObjectFactory, domainObjectRepository)
{
}
public override Task<bool> HandleAsync(CommandContext context)
{
throw new NotImplementedException();
}
public IDomainObjectFactory TestFactory
{
get { return Factory; }
}
public IDomainObjectRepository TestRepository
{
get { return Repository; }
}
public Task CreateTestAsync(IAggregateCommand command, Action<TestDomainObject> creator)
{
return CreateAsync(command, creator);
}
public Task UpdateTestAsync(IAggregateCommand command, Action<TestDomainObject> 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<TestDomainObject>(id, int.MaxValue)).Returns(Task.FromResult(domainObject)).Verifiable();
domainObjectRepository.Setup(x => x.SaveAsync(domainObject, It.IsAny<Guid>())).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<Guid>())).Returns(Task.FromResult(true)).Verifiable();
var isCalled = false;
await sut.CreateTestAsync(command, x => isCalled = true);
domainObjectFactory.VerifyAll();
domainObjectRepository.VerifyAll();
Assert.True(isCalled);
}
}
}

10
tests/Squidex.Infrastructure.Tests/CQRS/Commands/EnrichWithTimestampHandlerTests.cs

@ -14,11 +14,11 @@ namespace Squidex.Infrastructure.CQRS.Commands
{
public sealed class EnrichWithTimestampHandlerTests
{
private sealed class NormalCommand : AggregateCommand
private sealed class MyNormalCommand : AggregateCommand
{
}
private sealed class TimestampCommand : AggregateCommand, ITimestampCommand
private sealed class MyTimestampCommand : AggregateCommand, ITimestampCommand
{
public DateTime Timestamp { get; set; }
}
@ -29,7 +29,7 @@ namespace Squidex.Infrastructure.CQRS.Commands
var utc = DateTime.Today;
var sut = new EnrichWithTimestampHandler(() => utc);
var command = new TimestampCommand();
var command = new MyTimestampCommand();
var result = await sut.HandleAsync(new CommandContext(command));
@ -43,7 +43,7 @@ namespace Squidex.Infrastructure.CQRS.Commands
var now = DateTime.UtcNow;
var sut = new EnrichWithTimestampHandler();
var command = new TimestampCommand();
var command = new MyTimestampCommand();
var result = await sut.HandleAsync(new CommandContext(command));
@ -57,7 +57,7 @@ namespace Squidex.Infrastructure.CQRS.Commands
var utc = DateTime.Today;
var sut = new EnrichWithTimestampHandler(() => utc);
var result = await sut.HandleAsync(new CommandContext(new NormalCommand()));
var result = await sut.HandleAsync(new CommandContext(new MyNormalCommand()));
Assert.False(result);
}

4
tests/Squidex.Infrastructure.Tests/CQRS/Commands/InMemoryCommandBusTests.cs

@ -14,9 +14,9 @@ namespace Squidex.Infrastructure.CQRS.Commands
{
public class InMemoryCommandBusTests
{
private readonly TestCommand command = new TestCommand();
private readonly MyCommand command = new MyCommand();
private sealed class TestCommand : ICommand
private sealed class MyCommand : ICommand
{
}

10
tests/Squidex.Infrastructure.Tests/CQRS/DomainObjectTest.cs

@ -17,7 +17,7 @@ namespace Squidex.Infrastructure.CQRS
{
public class DomainObjectTest
{
private sealed class Event : IEvent
private sealed class MyEvent : IEvent
{
}
@ -52,8 +52,8 @@ namespace Squidex.Infrastructure.CQRS
[Fact]
public void Should_add_event_to_uncommitted_events_and_increase_version_when_raised()
{
var event1 = new Event();
var event2 = new Event();
var event1 = new MyEvent();
var event2 = new MyEvent();
var sut = new UserDomainObject(Guid.NewGuid(), 10);
@ -74,8 +74,8 @@ namespace Squidex.Infrastructure.CQRS
[Fact]
public void Should_not_add_event_to_uncommitted_events_and_increase_version_when_raised()
{
var event1 = new Event();
var event2 = new Event();
var event1 = new MyEvent();
var event2 = new MyEvent();
var sut = new UserDomainObject(Guid.NewGuid(), 10);

10
tests/Squidex.Infrastructure.Tests/CQRS/EnvelopeExtensionsTests.cs

@ -63,14 +63,14 @@ namespace Squidex.Infrastructure.CQRS
}
[Fact]
public void Should_set_and_get_app_id()
public void Should_set_and_get_user()
{
var commitId = Guid.NewGuid();
var user = new UserToken("subject", "123");
sut.SetAppId(commitId);
sut.SetUser(user);
Assert.Equal(commitId, sut.Headers.AppId());
Assert.Equal(commitId, sut.Headers["AppId"].ToGuid(culture));
Assert.Equal(user, sut.Headers.User());
Assert.Equal(user, UserToken.Parse(sut.Headers["User"].ToString()));
}
[Fact]

20
tests/Squidex.Infrastructure.Tests/CQRS/EventStore/DefaultNameResolverTests.cs

@ -14,9 +14,9 @@ namespace Squidex.Infrastructure.CQRS.EventStore
{
public class DefaultNameResolverTests
{
private sealed class User : DomainObject
private sealed class MyUser : DomainObject
{
public User(Guid id, int version)
public MyUser(Guid id, int version)
: base(id, version)
{
}
@ -26,9 +26,9 @@ namespace Squidex.Infrastructure.CQRS.EventStore
}
}
private sealed class UserDomainObject : DomainObject
private sealed class MyUserDomainObject : DomainObject
{
public UserDomainObject(Guid id, int version)
public MyUserDomainObject(Guid id, int version)
: base(id, version)
{
}
@ -42,22 +42,22 @@ namespace Squidex.Infrastructure.CQRS.EventStore
public void Should_calculate_name()
{
var sut = new DefaultNameResolver("Squidex");
var user = new User(Guid.NewGuid(), 1);
var user = new MyUser(Guid.NewGuid(), 1);
var name = sut.GetStreamName(typeof(User), user.Id);
var name = sut.GetStreamName(typeof(MyUser), user.Id);
Assert.Equal($"squidex-user-{user.Id}", name);
Assert.Equal($"squidex-myUser-{user.Id}", name);
}
[Fact]
public void Should_calculate_name_and_remove_suffix()
{
var sut = new DefaultNameResolver("Squidex");
var user = new UserDomainObject(Guid.NewGuid(), 1);
var user = new MyUserDomainObject(Guid.NewGuid(), 1);
var name = sut.GetStreamName(typeof(UserDomainObject), user.Id);
var name = sut.GetStreamName(typeof(MyUserDomainObject), user.Id);
Assert.Equal($"squidex-user-{user.Id}", name);
Assert.Equal($"squidex-myUser-{user.Id}", name);
}
}
}

13
tests/Squidex.Infrastructure.Tests/CQRS/EventStore/EventStoreFormatterTests.cs

@ -18,12 +18,12 @@ namespace Squidex.Infrastructure.CQRS.EventStore
{
public class EventStoreFormatterTests
{
public sealed class Event : IEvent
public sealed class MyEvent : IEvent
{
public string MyProperty { get; set; }
}
public sealed class ReceivedEvent : IReceivedEvent
public sealed class MyReceivedEvent : IReceivedEvent
{
public int EventNumber { get; set; }
@ -45,17 +45,16 @@ namespace Squidex.Infrastructure.CQRS.EventStore
public EventStoreFormatterTests()
{
TypeNameRegistry.Map(typeof(Event), "Event");
TypeNameRegistry.Map(typeof(MyEvent), "Event");
}
[Fact]
public void Should_serialize_and_deserialize_envelope()
{
var commitId = Guid.NewGuid();
var inputEvent = new Envelope<Event>(new Event { MyProperty = "My-Property" });
var inputEvent = new Envelope<MyEvent>(new MyEvent { MyProperty = "My-Property" });
inputEvent.SetAggregateId(Guid.NewGuid());
inputEvent.SetAppId(Guid.NewGuid());
inputEvent.SetCommitId(commitId);
inputEvent.SetEventId(Guid.NewGuid());
inputEvent.SetEventNumber(1);
@ -65,7 +64,7 @@ namespace Squidex.Infrastructure.CQRS.EventStore
var eventData = sut.ToEventData(inputEvent.To<IEvent>(), commitId);
var receivedEvent = new ReceivedEvent
var receivedEvent = new MyReceivedEvent
{
Payload = eventData.Data,
Created = inputEvent.Headers.Timestamp().ToDateTimeUtc(),
@ -74,7 +73,7 @@ namespace Squidex.Infrastructure.CQRS.EventStore
Metadata = eventData.Metadata
};
var outputEvent = sut.Parse(receivedEvent).To<Event>();
var outputEvent = sut.Parse(receivedEvent).To<MyEvent>();
CompareHeaders(outputEvent.Headers, inputEvent.Headers);

56
tests/Squidex.Infrastructure.Tests/CQRS/Events/EnrichWithAggregateIdProcessorTests.cs

@ -0,0 +1,56 @@
// ==========================================================================
// EnrichWithAggregateIdProcessorTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Infrastructure.CQRS.Commands;
using Xunit;
namespace Squidex.Infrastructure.CQRS.Events
{
public class EnrichWithAggregateIdProcessorTests
{
public sealed class MyAggregateIdCommand : IAggregateCommand
{
public Guid AggregateId { get; set; }
}
public sealed class MyNormalCommand : ICommand
{
}
public sealed class MyEvent : IEvent
{
}
private readonly EnrichWithAggregateIdProcessor sut = new EnrichWithAggregateIdProcessor();
[Fact]
public async Task Should_not_do_anything_if_not_aggregate_command()
{
var envelope = new Envelope<IEvent>(new MyEvent());
await sut.ProcessEventAsync(envelope, null, new MyNormalCommand());
Assert.False(envelope.Headers.Contains("AggregateId"));
}
[Fact]
public async Task Should_attach_aggregate_to_event_envelope()
{
var aggregateId = Guid.NewGuid();
var aggregateCommand = new MyAggregateIdCommand { AggregateId = aggregateId };
var envelope = new Envelope<IEvent>(new MyEvent());
await sut.ProcessEventAsync(envelope, null, aggregateCommand);
Assert.Equal(aggregateId, envelope.Headers.AggregateId());
}
}
}

55
tests/Squidex.Infrastructure.Tests/CQRS/Events/EnrichWithUserProcessorTests.cs

@ -0,0 +1,55 @@
// ==========================================================================
// EnrichWithUserProcessorTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Infrastructure.CQRS.Commands;
using Xunit;
namespace Squidex.Infrastructure.CQRS.Events
{
public class EnrichWithUserProcessorTests
{
public sealed class MyUserCommand : IUserCommand
{
public UserToken User { get; set; }
}
public sealed class MyNormalCommand : ICommand
{
}
public sealed class MyEvent : IEvent
{
}
private readonly EnrichWithUserProcessor sut = new EnrichWithUserProcessor();
[Fact]
public async Task Should_not_do_anything_if_not_user_command()
{
var envelope = new Envelope<IEvent>(new MyEvent());
await sut.ProcessEventAsync(envelope, null, new MyNormalCommand());
Assert.False(envelope.Headers.Contains("User"));
}
[Fact]
public async Task Should_attach_user_to_event_envelope()
{
var user = new UserToken("subject", "123");
var userCommand = new MyUserCommand { User = user };
var envelope = new Envelope<IEvent>(new MyEvent());
await sut.ProcessEventAsync(envelope, null, userCommand);
Assert.Equal(user, envelope.Headers.User());
}
}
}

144
tests/Squidex.Infrastructure.Tests/DispatchingTests.cs

@ -19,155 +19,155 @@ namespace Squidex.Infrastructure
{
public sealed class DispatchingTests
{
private interface IEvent { }
private interface IMyEvent { }
private class EventA : IEvent { }
private class EventB : IEvent { }
private class Unknown : IEvent { }
private class MyEventA : IMyEvent { }
private class MyEventB : IMyEvent { }
private class MyUnknown : IMyEvent { }
private class AsyncFuncConsumer
private class MyAsyncFuncConsumer
{
public int EventATriggered { get; private set; }
public int EventBTriggered { get; private set; }
public Task<int> DispatchEventAsync(IEvent @event)
public Task<int> DispatchEventAsync(IMyEvent @event)
{
return this.DispatchFuncAsync(@event, 9);
}
public Task<int> DispatchEventAsync(IEvent @event, int context)
public Task<int> DispatchEventAsync(IMyEvent @event, int context)
{
return this.DispatchFuncAsync(@event, context, 13);
}
public Task<int> On(EventA @event)
public Task<int> On(MyEventA @event)
{
return Task.FromResult(++EventATriggered);
}
public Task<int> On(EventB @event)
public Task<int> On(MyEventB @event)
{
return Task.FromResult(++EventBTriggered);
}
public Task<int> On(EventA @event, int context)
public Task<int> On(MyEventA @event, int context)
{
return Task.FromResult(++EventATriggered + context);
}
public Task<int> On(EventB @event, int context)
public Task<int> On(MyEventB @event, int context)
{
return Task.FromResult(++EventBTriggered + context);
}
}
private class AsyncConsumer
private class MyAsyncConsumer
{
public int EventATriggered { get; private set; }
public int EventBTriggered { get; private set; }
public Task<bool> DispatchEventAsync(IEvent @event)
public Task<bool> DispatchEventAsync(IMyEvent @event)
{
return this.DispatchActionAsync(@event);
}
public Task<bool> DispatchEventAsync(IEvent @event, int context)
public Task<bool> DispatchEventAsync(IMyEvent @event, int context)
{
return this.DispatchActionAsync(@event, context);
}
public Task On(EventA @event)
public Task On(MyEventA @event)
{
EventATriggered++;
return TaskHelper.Done;
}
public Task On(EventB @event)
public Task On(MyEventB @event)
{
EventBTriggered++;
return TaskHelper.Done;
}
public Task On(EventA @event, int context)
public Task On(MyEventA @event, int context)
{
EventATriggered = EventATriggered + context;
return TaskHelper.Done;
}
public Task On(EventB @event, int context)
public Task On(MyEventB @event, int context)
{
EventBTriggered = EventATriggered + context;
return TaskHelper.Done;
}
}
private class SyncFuncConsumer
private class MySyncFuncConsumer
{
public int EventATriggered { get; private set; }
public int EventBTriggered { get; private set; }
public int DispatchEvent(IEvent @event)
public int DispatchEvent(IMyEvent @event)
{
return this.DispatchFunc(@event, 9);
}
public int DispatchEvent(IEvent @event, int context)
public int DispatchEvent(IMyEvent @event, int context)
{
return this.DispatchFunc(@event, context, 13);
}
public int On(EventA @event)
public int On(MyEventA @event)
{
return ++EventATriggered;
}
public int On(EventB @event)
public int On(MyEventB @event)
{
return ++EventBTriggered;
}
public int On(EventA @event, int context)
public int On(MyEventA @event, int context)
{
return ++EventATriggered + context;
}
public int On(EventB @event, int context)
public int On(MyEventB @event, int context)
{
return ++EventBTriggered + context;
}
}
private class SyncActionConsumer
private class MySyncActionConsumer
{
public int EventATriggered { get; private set; }
public int EventBTriggered { get; private set; }
public bool DispatchEvent(IEvent @event)
public bool DispatchEvent(IMyEvent @event)
{
return this.DispatchAction(@event);
}
public bool DispatchEvent(IEvent @event, int context)
public bool DispatchEvent(IMyEvent @event, int context)
{
return this.DispatchAction(@event, context);
}
public void On(EventA @event)
public void On(MyEventA @event)
{
EventATriggered++;
}
public void On(EventB @event)
public void On(MyEventB @event)
{
EventBTriggered++;
}
public void On(EventA @event, int context)
public void On(MyEventA @event, int context)
{
EventATriggered = EventATriggered + context;
}
public void On(EventB @event, int context)
public void On(MyEventB @event, int context)
{
EventBTriggered = EventATriggered + context;
}
@ -176,12 +176,12 @@ namespace Squidex.Infrastructure
[Fact]
public void Should_invoke_correct_event()
{
var consumer = new SyncActionConsumer();
var consumer = new MySyncActionConsumer();
consumer.DispatchEvent(new EventA());
consumer.DispatchEvent(new EventB());
consumer.DispatchEvent(new EventB());
consumer.DispatchEvent(new Unknown());
consumer.DispatchEvent(new MyEventA());
consumer.DispatchEvent(new MyEventB());
consumer.DispatchEvent(new MyEventB());
consumer.DispatchEvent(new MyUnknown());
Assert.Equal(1, consumer.EventATriggered);
Assert.Equal(2, consumer.EventBTriggered);
@ -190,12 +190,12 @@ namespace Squidex.Infrastructure
[Fact]
public void Should_invoke_correct_event_with_context()
{
var consumer = new SyncActionConsumer();
var consumer = new MySyncActionConsumer();
consumer.DispatchEvent(new EventA(), 2);
consumer.DispatchEvent(new EventB(), 2);
consumer.DispatchEvent(new EventB(), 2);
consumer.DispatchEvent(new Unknown(), 2);
consumer.DispatchEvent(new MyEventA(), 2);
consumer.DispatchEvent(new MyEventB(), 2);
consumer.DispatchEvent(new MyEventB(), 2);
consumer.DispatchEvent(new MyUnknown(), 2);
Assert.Equal(2, consumer.EventATriggered);
Assert.Equal(4, consumer.EventBTriggered);
@ -204,12 +204,12 @@ namespace Squidex.Infrastructure
[Fact]
public async Task Should_invoke_correct_event_asynchronously()
{
var consumer = new AsyncConsumer();
var consumer = new MyAsyncConsumer();
await consumer.DispatchEventAsync(new EventA());
await consumer.DispatchEventAsync(new EventB());
await consumer.DispatchEventAsync(new EventB());
await consumer.DispatchEventAsync(new Unknown());
await consumer.DispatchEventAsync(new MyEventA());
await consumer.DispatchEventAsync(new MyEventB());
await consumer.DispatchEventAsync(new MyEventB());
await consumer.DispatchEventAsync(new MyUnknown());
Assert.Equal(1, consumer.EventATriggered);
Assert.Equal(2, consumer.EventBTriggered);
@ -218,12 +218,12 @@ namespace Squidex.Infrastructure
[Fact]
public async Task Should_invoke_correct_event_with_context_asynchronously()
{
var consumer = new AsyncConsumer();
var consumer = new MyAsyncConsumer();
await consumer.DispatchEventAsync(new EventA(), 2);
await consumer.DispatchEventAsync(new EventB(), 2);
await consumer.DispatchEventAsync(new EventB(), 2);
await consumer.DispatchEventAsync(new Unknown(), 2);
await consumer.DispatchEventAsync(new MyEventA(), 2);
await consumer.DispatchEventAsync(new MyEventB(), 2);
await consumer.DispatchEventAsync(new MyEventB(), 2);
await consumer.DispatchEventAsync(new MyUnknown(), 2);
Assert.Equal(2, consumer.EventATriggered);
Assert.Equal(4, consumer.EventBTriggered);
@ -232,12 +232,12 @@ namespace Squidex.Infrastructure
[Fact]
public void Should_invoke_correct_event_and_return()
{
var consumer = new SyncFuncConsumer();
var consumer = new MySyncFuncConsumer();
Assert.Equal(1, consumer.DispatchEvent(new EventA()));
Assert.Equal(1, consumer.DispatchEvent(new EventB()));
Assert.Equal(2, consumer.DispatchEvent(new EventB()));
Assert.Equal(9, consumer.DispatchEvent(new Unknown()));
Assert.Equal(1, consumer.DispatchEvent(new MyEventA()));
Assert.Equal(1, consumer.DispatchEvent(new MyEventB()));
Assert.Equal(2, consumer.DispatchEvent(new MyEventB()));
Assert.Equal(9, consumer.DispatchEvent(new MyUnknown()));
Assert.Equal(1, consumer.EventATriggered);
Assert.Equal(2, consumer.EventBTriggered);
@ -246,12 +246,12 @@ namespace Squidex.Infrastructure
[Fact]
public void Should_invoke_correct_event_with_context_and_return()
{
var consumer = new SyncFuncConsumer();
var consumer = new MySyncFuncConsumer();
Assert.Equal(11, consumer.DispatchEvent(new EventA(), 10));
Assert.Equal(11, consumer.DispatchEvent(new EventB(), 10));
Assert.Equal(12, consumer.DispatchEvent(new EventB(), 10));
Assert.Equal(13, consumer.DispatchEvent(new Unknown(), 10));
Assert.Equal(11, consumer.DispatchEvent(new MyEventA(), 10));
Assert.Equal(11, consumer.DispatchEvent(new MyEventB(), 10));
Assert.Equal(12, consumer.DispatchEvent(new MyEventB(), 10));
Assert.Equal(13, consumer.DispatchEvent(new MyUnknown(), 10));
Assert.Equal(1, consumer.EventATriggered);
Assert.Equal(2, consumer.EventBTriggered);
@ -260,12 +260,12 @@ namespace Squidex.Infrastructure
[Fact]
public async Task Should_invoke_correct_event_and_return_synchronously()
{
var consumer = new AsyncFuncConsumer();
var consumer = new MyAsyncFuncConsumer();
Assert.Equal(1, await consumer.DispatchEventAsync(new EventA()));
Assert.Equal(1, await consumer.DispatchEventAsync(new EventB()));
Assert.Equal(2, await consumer.DispatchEventAsync(new EventB()));
Assert.Equal(9, await consumer.DispatchEventAsync(new Unknown()));
Assert.Equal(1, await consumer.DispatchEventAsync(new MyEventA()));
Assert.Equal(1, await consumer.DispatchEventAsync(new MyEventB()));
Assert.Equal(2, await consumer.DispatchEventAsync(new MyEventB()));
Assert.Equal(9, await consumer.DispatchEventAsync(new MyUnknown()));
Assert.Equal(1, consumer.EventATriggered);
Assert.Equal(2, consumer.EventBTriggered);
@ -274,12 +274,12 @@ namespace Squidex.Infrastructure
[Fact]
public async Task Should_invoke_correct_event_with_context_and_return_synchronously()
{
var consumer = new AsyncFuncConsumer();
var consumer = new MyAsyncFuncConsumer();
Assert.Equal(11, await consumer.DispatchEventAsync(new EventA(), 10));
Assert.Equal(11, await consumer.DispatchEventAsync(new EventB(), 10));
Assert.Equal(12, await consumer.DispatchEventAsync(new EventB(), 10));
Assert.Equal(13, await consumer.DispatchEventAsync(new Unknown(), 10));
Assert.Equal(11, await consumer.DispatchEventAsync(new MyEventA(), 10));
Assert.Equal(11, await consumer.DispatchEventAsync(new MyEventB(), 10));
Assert.Equal(12, await consumer.DispatchEventAsync(new MyEventB(), 10));
Assert.Equal(13, await consumer.DispatchEventAsync(new MyUnknown(), 10));
Assert.Equal(1, consumer.EventATriggered);
Assert.Equal(2, consumer.EventBTriggered);

8
tests/Squidex.Infrastructure.Tests/GuardTests.cs

@ -12,14 +12,14 @@ using Xunit;
namespace Squidex.Infrastructure
{
public sealed class ValidatableValid : IValidatable
public sealed class MyValidatableValid : IValidatable
{
public void Validate(IList<ValidationError> errors)
{
}
}
public sealed class ValidatableInvalid : IValidatable
public sealed class MyValidatableInvalid : IValidatable
{
public void Validate(IList<ValidationError> errors)
{
@ -327,13 +327,13 @@ namespace Squidex.Infrastructure
[Fact]
public void Valid_should_throw_if_invalid()
{
Assert.Throws<ValidationException>(() => Guard.Valid(new ValidatableInvalid(), "Parameter", () => "Message"));
Assert.Throws<ValidationException>(() => Guard.Valid(new MyValidatableInvalid(), "Parameter", () => "Message"));
}
[Fact]
public void Valid_should_do_nothing_if_valid()
{
Guard.Valid(new ValidatableValid(), "Parameter", () => "Message");
Guard.Valid(new MyValidatableValid(), "Parameter", () => "Message");
}
}
}

12
tests/Squidex.Infrastructure.Tests/Reflection/ReflectionExtensionTests.cs

@ -14,22 +14,22 @@ namespace Squidex.Infrastructure.Reflection
{
public class ReflectionExtensionTests
{
private interface IMain : ISub1
private interface IMyMain : IMySub1
{
string MainProp { get; set; }
}
private interface ISub1 : ISub2
private interface IMySub1 : IMySub2
{
string Sub1Prop { get; set; }
}
private interface ISub2
private interface IMySub2
{
string Sub2Prop { get; set; }
}
private class Main
private class MyMain
{
public string MainProp { get; set; }
}
@ -37,7 +37,7 @@ namespace Squidex.Infrastructure.Reflection
[Fact]
public void Should_find_all_public_properties_of_interfaces()
{
var properties = typeof(IMain).GetPublicProperties().Select(x => x.Name).OrderBy(x => x).ToArray();
var properties = typeof(IMyMain).GetPublicProperties().Select(x => x.Name).OrderBy(x => x).ToArray();
Assert.Equal(new [] { "MainProp", "Sub1Prop", "Sub2Prop" }, properties);
}
@ -45,7 +45,7 @@ namespace Squidex.Infrastructure.Reflection
[Fact]
public void Should_find_all_public_properties_of_classes()
{
var properties = typeof(Main).GetPublicProperties().Select(x => x.Name).OrderBy(x => x).ToArray();
var properties = typeof(MyMain).GetPublicProperties().Select(x => x.Name).OrderBy(x => x).ToArray();
Assert.Equal(new[] { "MainProp" }, properties);
}

22
tests/Squidex.Infrastructure.Tests/Reflection/SimpleMapperTests.cs

@ -14,7 +14,7 @@ namespace Squidex.Infrastructure.Reflection
{
public class SimpleMapperTests
{
public class Class1Base
public class MyClass1Base
{
public string MappedString { get; set; }
@ -29,12 +29,12 @@ namespace Squidex.Infrastructure.Reflection
public long WrongType2 { get; set; }
}
public class Class1 : Class1Base
public class MyClass1 : MyClass1Base
{
public string UnmappedString { get; set; }
}
public class Class2Base
public class MyClass2Base
{
public string MappedString { get; protected set; }
@ -45,7 +45,7 @@ namespace Squidex.Infrastructure.Reflection
public string MappedGuid { get; set; }
}
public class Class2 : Class2Base
public class MyClass2 : MyClass2Base
{
public string UnmappedString
{
@ -60,19 +60,19 @@ namespace Squidex.Infrastructure.Reflection
[Fact]
public void Should_throw_if_mapping_with_null_source()
{
Assert.Throws<ArgumentNullException>(() => SimpleMapper.Map((Class1)null, new Class2()));
Assert.Throws<ArgumentNullException>(() => SimpleMapper.Map((MyClass1)null, new MyClass2()));
}
[Fact]
public void Should_throw_if_mapping_with_null_target()
{
Assert.Throws<ArgumentNullException>(() => SimpleMapper.Map(new Class1(), (Class2)null));
Assert.Throws<ArgumentNullException>(() => SimpleMapper.Map(new MyClass1(), (MyClass2)null));
}
[Fact]
public void Should_to_type()
{
var class1 = new Class1
var class1 = new MyClass1
{
UnmappedString = Guid.NewGuid().ToString(),
MappedString = Guid.NewGuid().ToString(),
@ -80,7 +80,7 @@ namespace Squidex.Infrastructure.Reflection
MappedGuid = Guid.NewGuid()
};
var class2 = SimpleMapper.Map<Class1, Class2>(class1);
var class2 = SimpleMapper.Map<MyClass1, MyClass2>(class1);
AssertObjectEquality(class1, class2);
}
@ -88,21 +88,21 @@ namespace Squidex.Infrastructure.Reflection
[Fact]
public void Should_map_between_types()
{
var class1 = new Class1
var class1 = new MyClass1
{
UnmappedString = Guid.NewGuid().ToString(),
MappedString = Guid.NewGuid().ToString(),
MappedNumber = 123,
MappedGuid = Guid.NewGuid()
};
var class2 = new Class2();
var class2 = new MyClass2();
SimpleMapper.Map(class1, class2);
AssertObjectEquality(class1, class2);
}
private static void AssertObjectEquality(Class1 class1, Class2 class2)
private static void AssertObjectEquality(MyClass1 class1, MyClass2 class2)
{
Assert.Equal(class1.MappedString, class2.MappedString);
Assert.Equal(class1.MappedNumber, class2.MappedNumber);

7
tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs

@ -43,12 +43,7 @@ namespace Squidex.Write.Apps
{
app = new AppDomainObject(Id, 0);
sut = new AppCommandHandler(
DomainObjectFactory.Object,
DomainObjectRepository.Object,
userRepository.Object,
appRepository.Object,
keyGenerator.Object);
sut = new AppCommandHandler(Handler, appRepository.Object, userRepository.Object, keyGenerator.Object);
}
[Fact]

58
tests/Squidex.Write.Tests/EnrichWithAppIdProcessorTests.cs

@ -0,0 +1,58 @@
// ==========================================================================
// EnrichWithAppIdProcessorTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Events;
using Squidex.Infrastructure.CQRS;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.CQRS.Events;
using Xunit;
namespace Squidex.Write
{
public class EnrichWithAppIdProcessorTests
{
public sealed class MyAppCommand : AppAggregateCommand
{
}
public sealed class MyNormalCommand : ICommand
{
}
public sealed class MyEvent : IEvent
{
}
private readonly EnrichWithAppIdProcessor sut = new EnrichWithAppIdProcessor();
[Fact]
public async Task Should_not_do_anything_if_not_app_command()
{
var envelope = new Envelope<IEvent>(new MyEvent());
await sut.ProcessEventAsync(envelope, null, new MyNormalCommand());
Assert.False(envelope.Headers.Contains("AppId"));
}
[Fact]
public async Task Should_attach_app_id_to_event_envelope()
{
var appId = Guid.NewGuid();
var appCommand = new MyAppCommand { AggregateId = appId };
var envelope = new Envelope<IEvent>(new MyEvent());
await sut.ProcessEventAsync(envelope, null, appCommand);
Assert.Equal(appId, envelope.Headers.AppId());
}
}
}

5
tests/Squidex.Write.Tests/Schemas/SchemaCommandHandlerTests.cs

@ -41,10 +41,7 @@ namespace Squidex.Write.Schemas
{
schema = new SchemaDomainObject(Id, 0, registry);
sut = new SchemaCommandHandler(
DomainObjectFactory.Object,
DomainObjectRepository.Object,
schemaProvider.Object);
sut = new SchemaCommandHandler(Handler, schemaProvider.Object);
}
[Fact]

2
tests/Squidex.Write.Tests/Schemas/SchemaDomainObjectTests.cs

@ -62,7 +62,7 @@ namespace Squidex.Write.Schemas
.ShouldBeEquivalentTo(
new IEvent[]
{
new SchemaCreated { Name = appName, AppId = appId, Properties = properties }
new SchemaCreated { Name = appName, Properties = properties }
});
}

62
tests/Squidex.Write.Tests/Utils/HandlerTestBase.cs

@ -8,7 +8,6 @@
using System;
using System.Threading.Tasks;
using Moq;
using Squidex.Infrastructure.CQRS;
using Squidex.Infrastructure.CQRS.Commands;
@ -16,8 +15,37 @@ namespace Squidex.Write.Utils
{
public abstract class HandlerTestBase<T> where T : DomainObject
{
private readonly Mock<IDomainObjectFactory> domainObjectFactory = new Mock<IDomainObjectFactory>();
private readonly Mock<IDomainObjectRepository> domainObjectRepository = new Mock<IDomainObjectRepository>();
private sealed class MockupHandler : IAggregateHandler
{
private T domainObject;
public bool IsCreated { get; private set; }
public bool IsUpdated { get; private set; }
public void Init(T newDomainObject)
{
domainObject = newDomainObject;
IsCreated = false;
IsUpdated = false;
}
public Task CreateAsync<V>(IAggregateCommand command, Func<V, Task> creator) where V : class, IAggregate
{
IsCreated = true;
return creator(domainObject as V);
}
public Task UpdateAsync<V>(IAggregateCommand command, Func<V, Task> updater) where V : class, IAggregate
{
IsUpdated = true;
return updater(domainObject as V);
}
}
private readonly MockupHandler handler = new MockupHandler();
private readonly Guid id = Guid.NewGuid();
protected Guid Id
@ -25,40 +53,32 @@ namespace Squidex.Write.Utils
get { return id; }
}
protected Mock<IDomainObjectFactory> DomainObjectFactory
{
get { return domainObjectFactory; }
}
protected Mock<IDomainObjectRepository> DomainObjectRepository
protected IAggregateHandler Handler
{
get { return domainObjectRepository; }
get { return handler; }
}
public async Task TestCreate(T domainObject, Func<T, Task> action, bool succeeded = true)
public async Task TestCreate(T domainObject, Func<T, Task> action, bool shouldCreate = true)
{
domainObjectFactory.Setup(x => x.CreateNew(typeof(T), id)).Returns(domainObject).Verifiable();
domainObjectRepository.Setup(x => x.SaveAsync(domainObject, It.IsAny<Guid>())).Returns(Task.FromResult(true)).Verifiable();
handler.Init(domainObject);
await action(domainObject);
if (succeeded)
if (!handler.IsCreated && shouldCreate)
{
domainObjectFactory.VerifyAll();
domainObjectRepository.VerifyAll();
throw new InvalidOperationException("Create not called");
}
}
public async Task TestUpdate(T domainObject, Func<T, Task> action, bool succeeded = true)
public async Task TestUpdate(T domainObject, Func<T, Task> action, bool shouldUpdate = true)
{
domainObjectRepository.Setup(x => x.GetByIdAsync<T>(domainObject.Id, int.MaxValue)).Returns(Task.FromResult(domainObject)).Verifiable();
domainObjectRepository.Setup(x => x.SaveAsync(domainObject, It.IsAny<Guid>())).Returns(Task.FromResult(true)).Verifiable();
handler.Init(domainObject);
await action(domainObject);
if (succeeded)
if (!handler.IsUpdated && shouldUpdate)
{
domainObjectRepository.VerifyAll();
throw new InvalidOperationException("Create not called");
}
}
}

Loading…
Cancel
Save