Browse Source

More tests and multisnapshot domain object

pull/306/head
Sebastian 8 years ago
parent
commit
81ce00822a
  1. 17
      src/Squidex.Domain.Apps.Core.Model/Contents/StatusChange.cs
  2. 4
      src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs
  3. 4
      src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs
  4. 4
      src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs
  5. 4
      src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs
  6. 4
      src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs
  7. 193
      src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs
  8. 207
      src/Squidex.Infrastructure/Commands/DomainObjectGrainBase.cs
  9. 93
      src/Squidex.Infrastructure/Commands/MultiSnapshotDomainObjectGrain.cs
  10. 2
      src/Squidex.Infrastructure/Orleans/GrainOfGuid.cs
  11. 2
      src/Squidex.Infrastructure/Orleans/GrainOfString.cs
  12. 15
      src/Squidex.Infrastructure/States/IPersistence.cs
  13. 24
      src/Squidex.Infrastructure/States/IPersistence{TState}.cs
  14. 2
      src/Squidex.Infrastructure/States/IStore.cs
  15. 11
      src/Squidex.Infrastructure/States/Store.cs
  16. 66
      tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainFormatterTests.cs
  17. 7
      tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs
  18. 6
      tests/Squidex.Infrastructure.Tests/TestHelpers/MyGrain.cs

17
src/Squidex.Domain.Apps.Core.Model/Contents/StatusChange.cs

@ -0,0 +1,17 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Domain.Apps.Core.Contents
{
public enum StatusChange
{
Archived,
Published,
Restored,
Unpublished
}
}

4
src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs

@ -334,9 +334,9 @@ namespace Squidex.Domain.Apps.Entities.Apps
return new AppContributorAssigned { ContributorId = actor.Identifier, Permission = AppContributorPermission.Owner };
}
public override void ApplyEvent(Envelope<IEvent> @event)
protected override AppState OnEvent(Envelope<IEvent> @event)
{
ApplySnapshot(Snapshot.Apply(@event));
return Snapshot.Apply(@event);
}
public Task<J<IAppEntity>> GetStateAsync()

4
src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs

@ -135,9 +135,9 @@ namespace Squidex.Domain.Apps.Entities.Assets
}
}
public override void ApplyEvent(Envelope<IEvent> @event)
protected override AssetState OnEvent(Envelope<IEvent> @event)
{
ApplySnapshot(Snapshot.Apply(@event));
return Snapshot.Apply(@event);
}
public Task<J<IAssetEntity>> GetStateAsync()

4
src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs

@ -292,9 +292,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
}
public override void ApplyEvent(Envelope<IEvent> @event)
protected override ContentState OnEvent(Envelope<IEvent> @event)
{
ApplySnapshot(Snapshot.Apply(@event));
return Snapshot.Apply(@event);
}
private async Task<ContentOperationContext> CreateContext(Guid appId, Guid schemaId, Func<string> message)

4
src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs

@ -123,9 +123,9 @@ namespace Squidex.Domain.Apps.Entities.Rules
}
}
public override void ApplyEvent(Envelope<IEvent> @event)
protected override RuleState OnEvent(Envelope<IEvent> @event)
{
ApplySnapshot(Snapshot.Apply(@event));
return Snapshot.Apply(@event);
}
public Task<J<IRuleEntity>> GetStateAsync()

4
src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs

@ -360,9 +360,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas
}
}
public override void ApplyEvent(Envelope<IEvent> @event)
protected override SchemaState OnEvent(Envelope<IEvent> @event)
{
ApplySnapshot(Snapshot.Apply(@event, registry));
return Snapshot.Apply(@event, registry);
}
public Task<J<ISchemaEntity>> GetStateAsync()

193
src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs

@ -6,220 +6,59 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.States;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Infrastructure.Commands
{
public abstract class DomainObjectGrain<T> : GrainOfGuid, IDomainObjectGrain where T : IDomainState, new()
public abstract class DomainObjectGrain<T> : DomainObjectGrainBase<T> where T : IDomainState, new()
{
private readonly List<Envelope<IEvent>> uncomittedEvents = new List<Envelope<IEvent>>();
private readonly IStore<Guid> store;
private readonly ISemanticLog log;
private Guid id;
private T snapshot = new T { Version = EtagVersion.Empty };
private IPersistence<T> persistence;
public Guid Id
{
get { return id; }
}
public long Version
{
get { return snapshot.Version; }
}
public long NewVersion
{
get { return snapshot.Version + uncomittedEvents.Count; }
}
public T Snapshot
public override T Snapshot
{
get { return snapshot; }
}
protected DomainObjectGrain(IStore<Guid> store, ISemanticLog log)
: base(log)
{
Guard.NotNull(store, nameof(store));
Guard.NotNull(log, nameof(log));
this.store = store;
this.log = log;
}
public override async Task OnActivateAsync(Guid key)
protected sealed override void ApplyEvent(Envelope<IEvent> @event)
{
using (log.MeasureInformation(w => w
.WriteProperty("action", "ActivateDomainObject")
.WriteProperty("domainObjectType", GetType().Name)
.WriteProperty("domainObjectKey", key.ToString())))
{
id = key;
persistence = store.WithSnapshotsAndEventSourcing<T, Guid>(GetType(), id, ApplySnapshot, ApplyEvent);
await persistence.ReadAsync();
}
snapshot = OnEvent(@event);
snapshot.Version = NewVersion + 1;
}
public void RaiseEvent(IEvent @event)
protected sealed override void RestorePreviousSnapshot(T previousSnapshot, long previousVersion)
{
RaiseEvent(Envelope.Create(@event));
snapshot = previousSnapshot;
}
public virtual void RaiseEvent(Envelope<IEvent> @event)
protected sealed override Task ReadAsync(Type type, Guid id)
{
Guard.NotNull(@event, nameof(@event));
@event.SetAggregateId(id);
persistence = store.WithSnapshotsAndEventSourcing<T, Guid>(GetType(), id, x => snapshot = x, ApplyEvent);
ApplyEvent(@event);
uncomittedEvents.Add(@event);
return persistence.ReadAsync();
}
public IReadOnlyList<Envelope<IEvent>> GetUncomittedEvents()
protected sealed override async Task WriteAsync(Envelope<IEvent>[] events, long previousVersion)
{
return uncomittedEvents;
}
public void ClearUncommittedEvents()
{
uncomittedEvents.Clear();
}
public virtual void ApplySnapshot(T newSnapshot)
{
snapshot = newSnapshot;
}
public virtual void ApplyEvent(Envelope<IEvent> @event)
{
}
protected Task<object> CreateReturnAsync<TCommand>(TCommand command, Func<TCommand, Task<object>> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler, false);
}
protected Task<object> CreateReturnAsync<TCommand>(TCommand command, Func<TCommand, object> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler?.ToAsync(), false);
}
protected Task<object> CreateAsync<TCommand>(TCommand command, Func<TCommand, Task> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler.ToDefault<TCommand, object>(), false);
}
protected Task<object> CreateAsync<TCommand>(TCommand command, Action<TCommand> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler?.ToDefault<TCommand, object>()?.ToAsync(), false);
}
protected Task<object> UpdateReturnAsync<TCommand>(TCommand command, Func<TCommand, Task<object>> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler, true);
}
protected Task<object> UpdateReturnAsync<TCommand>(TCommand command, Func<TCommand, object> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler?.ToAsync(), true);
}
protected Task<object> UpdateAsync<TCommand>(TCommand command, Func<TCommand, Task> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler?.ToDefault<TCommand, object>(), true);
}
protected Task<object> UpdateAsync<TCommand>(TCommand command, Action<TCommand> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler?.ToDefault<TCommand, object>()?.ToAsync(), true);
}
private async Task<object> InvokeAsync<TCommand>(TCommand command, Func<TCommand, Task<object>> handler, bool isUpdate) where TCommand : class, IAggregateCommand
{
Guard.NotNull(command, nameof(command));
if (command.ExpectedVersion != EtagVersion.Any && command.ExpectedVersion != Version)
{
throw new DomainObjectVersionException(id.ToString(), GetType(), Version, command.ExpectedVersion);
}
if (isUpdate && Version < 0)
{
try
{
DeactivateOnIdle();
}
catch (InvalidOperationException)
{
}
throw new DomainObjectNotFoundException(id.ToString(), GetType());
}
if (!isUpdate && Version >= 0)
{
throw new DomainException("Object has already been created.");
}
var previousSnapshot = snapshot;
try
if (events.Length > 0)
{
var result = await handler(command);
var events = uncomittedEvents.ToArray();
if (events.Length > 0)
{
snapshot.Version = NewVersion;
await persistence.WriteEventsAsync(events);
await persistence.WriteSnapshotAsync(snapshot);
}
if (result == null)
{
if (isUpdate)
{
result = new EntitySavedResult(Version);
}
else
{
result = EntityCreatedResult.Create(id, Version);
}
}
return result;
await persistence.WriteEventsAsync(events);
await persistence.WriteSnapshotAsync(Snapshot);
}
catch
{
snapshot = previousSnapshot;
throw;
}
finally
{
uncomittedEvents.Clear();
}
}
public async Task<J<object>> ExecuteAsync(J<IAggregateCommand> command)
{
var result = await ExecuteAsync(command.Value);
return result.AsJ();
}
protected abstract Task<object> ExecuteAsync(IAggregateCommand command);
protected abstract T OnEvent(Envelope<IEvent> @event);
}
}

207
src/Squidex.Infrastructure/Commands/DomainObjectGrainBase.cs

@ -0,0 +1,207 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Infrastructure.Commands
{
public abstract class DomainObjectGrainBase<T> : GrainOfGuid, IDomainObjectGrain where T : IDomainState, new()
{
private readonly List<Envelope<IEvent>> uncomittedEvents = new List<Envelope<IEvent>>();
private readonly ISemanticLog log;
private Guid id;
public Guid Id
{
get { return id; }
}
public long Version
{
get { return Snapshot.Version; }
}
public long NewVersion
{
get { return Snapshot.Version + uncomittedEvents.Count; }
}
public abstract T Snapshot { get; }
protected DomainObjectGrainBase(ISemanticLog log)
{
Guard.NotNull(log, nameof(log));
this.log = log;
}
public sealed override async Task OnActivateAsync(Guid key)
{
using (log.MeasureInformation(w => w
.WriteProperty("action", "ActivateDomainObject")
.WriteProperty("domainObjectType", GetType().Name)
.WriteProperty("domainObjectKey", key.ToString())))
{
id = key;
await ReadAsync(GetType(), id);
}
}
public void RaiseEvent(IEvent @event)
{
RaiseEvent(Envelope.Create(@event));
}
public virtual void RaiseEvent(Envelope<IEvent> @event)
{
Guard.NotNull(@event, nameof(@event));
@event.SetAggregateId(id);
ApplyEvent(@event);
uncomittedEvents.Add(@event);
}
public IReadOnlyList<Envelope<IEvent>> GetUncomittedEvents()
{
return uncomittedEvents;
}
public void ClearUncommittedEvents()
{
uncomittedEvents.Clear();
}
protected Task<object> CreateReturnAsync<TCommand>(TCommand command, Func<TCommand, Task<object>> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler, false);
}
protected Task<object> CreateReturnAsync<TCommand>(TCommand command, Func<TCommand, object> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler?.ToAsync(), false);
}
protected Task<object> CreateAsync<TCommand>(TCommand command, Func<TCommand, Task> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler.ToDefault<TCommand, object>(), false);
}
protected Task<object> CreateAsync<TCommand>(TCommand command, Action<TCommand> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler?.ToDefault<TCommand, object>()?.ToAsync(), false);
}
protected Task<object> UpdateReturnAsync<TCommand>(TCommand command, Func<TCommand, Task<object>> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler, true);
}
protected Task<object> UpdateReturnAsync<TCommand>(TCommand command, Func<TCommand, object> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler?.ToAsync(), true);
}
protected Task<object> UpdateAsync<TCommand>(TCommand command, Func<TCommand, Task> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler?.ToDefault<TCommand, object>(), true);
}
protected Task<object> UpdateAsync<TCommand>(TCommand command, Action<TCommand> handler) where TCommand : class, IAggregateCommand
{
return InvokeAsync(command, handler?.ToDefault<TCommand, object>()?.ToAsync(), true);
}
private async Task<object> InvokeAsync<TCommand>(TCommand command, Func<TCommand, Task<object>> handler, bool isUpdate) where TCommand : class, IAggregateCommand
{
Guard.NotNull(command, nameof(command));
if (command.ExpectedVersion != EtagVersion.Any && command.ExpectedVersion != Version)
{
throw new DomainObjectVersionException(id.ToString(), GetType(), Version, command.ExpectedVersion);
}
if (isUpdate && Version < 0)
{
try
{
DeactivateOnIdle();
}
catch (InvalidOperationException)
{
}
throw new DomainObjectNotFoundException(id.ToString(), GetType());
}
if (!isUpdate && Version >= 0)
{
throw new DomainException("Object has already been created.");
}
var previousSnapshot = Snapshot;
var previousVersion = Version;
try
{
var result = await handler(command);
var events = uncomittedEvents.ToArray();
await WriteAsync(events, previousVersion);
if (result == null)
{
if (isUpdate)
{
result = new EntitySavedResult(Version);
}
else
{
result = EntityCreatedResult.Create(id, Version);
}
}
return result;
}
catch
{
RestorePreviousSnapshot(previousSnapshot, previousVersion);
throw;
}
finally
{
uncomittedEvents.Clear();
}
}
protected abstract void RestorePreviousSnapshot(T previousSnapshot, long previousVersion);
protected abstract void ApplyEvent(Envelope<IEvent> @event);
protected abstract Task ReadAsync(Type type, Guid id);
protected abstract Task WriteAsync(Envelope<IEvent>[] events, long previousVersion);
public async Task<J<object>> ExecuteAsync(J<IAggregateCommand> command)
{
var result = await ExecuteAsync(command.Value);
return result.AsJ();
}
protected abstract Task<object> ExecuteAsync(IAggregateCommand command);
}
}

93
src/Squidex.Infrastructure/Commands/MultiSnapshotDomainObjectGrain.cs

@ -0,0 +1,93 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.States;
namespace Squidex.Infrastructure.Commands
{
public abstract class MultiSnapshotDomainObjectGrain<T> : DomainObjectGrainBase<T> where T : IDomainState, new()
{
private readonly IStore<Guid> store;
private readonly List<T> snapshots = new List<T> { new T { Version = EtagVersion.Empty } };
private IPersistence persistence;
public override T Snapshot
{
get { return snapshots.Last(); }
}
protected MultiSnapshotDomainObjectGrain(IStore<Guid> store, ISemanticLog log)
: base(log)
{
Guard.NotNull(log, nameof(log));
this.store = store;
}
public T GetSnapshot(long version)
{
if (version == EtagVersion.Any)
{
return Snapshot;
}
if (version == EtagVersion.Empty)
{
return snapshots[0];
}
if (version >= 0 && version < snapshots.Count - 1)
{
return snapshots[(int)version + 1];
}
return default(T);
}
protected sealed override void ApplyEvent(Envelope<IEvent> @event)
{
var snapshot = OnEvent(@event);
snapshot.Version = NewVersion + 1;
snapshots.Add(OnEvent(@event));
}
protected sealed override Task ReadAsync(Type type, Guid id)
{
persistence = store.WithEventSourcing<Guid>(type, id, ApplyEvent);
return persistence.ReadAsync();
}
protected sealed override async Task WriteAsync(Envelope<IEvent>[] events, long previousVersion)
{
if (events.Length > 0)
{
var snaphosts = store.GetSnapshotStore<T>();
await persistence.WriteEventsAsync(events);
await snaphosts.WriteAsync(Id, Snapshot, previousVersion, previousVersion + events.Length);
}
}
protected sealed override void RestorePreviousSnapshot(T previousSnapshot, long previousVersion)
{
while (snapshots.Count > previousVersion)
{
snapshots.RemoveAt(snapshots.Count - 1);
}
}
protected abstract T OnEvent(Envelope<IEvent> @event);
}
}

2
src/Squidex.Infrastructure/Orleans/GrainOfGuid.cs

@ -14,7 +14,7 @@ namespace Squidex.Infrastructure.Orleans
{
public abstract class GrainOfGuid : Grain
{
public override Task OnActivateAsync()
public sealed override Task OnActivateAsync()
{
return OnActivateAsync(this.GetPrimaryKey());
}

2
src/Squidex.Infrastructure/Orleans/GrainOfString.cs

@ -13,7 +13,7 @@ namespace Squidex.Infrastructure.Orleans
{
public abstract class GrainOfString : Grain
{
public override Task OnActivateAsync()
public sealed override Task OnActivateAsync()
{
return OnActivateAsync(this.GetPrimaryKeyString());
}

15
src/Squidex.Infrastructure/States/IPersistence.cs

@ -5,24 +5,9 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Infrastructure.States
{
public interface IPersistence : IPersistence<object>
{
}
public interface IPersistence<TState>
{
long Version { get; }
Task WriteEventsAsync(IEnumerable<Envelope<IEvent>> @events);
Task WriteSnapshotAsync(TState state);
Task ReadAsync(long expectedVersion = EtagVersion.Any);
}
}

24
src/Squidex.Infrastructure/States/IPersistence{TState}.cs

@ -0,0 +1,24 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Infrastructure.States
{
public interface IPersistence<TState>
{
long Version { get; }
Task WriteEventsAsync(IEnumerable<Envelope<IEvent>> @events);
Task WriteSnapshotAsync(TState state);
Task ReadAsync(long expectedVersion = EtagVersion.Any);
}
}

2
src/Squidex.Infrastructure/States/IStore.cs

@ -19,6 +19,8 @@ namespace Squidex.Infrastructure.States
IPersistence<TState> WithSnapshotsAndEventSourcing<TState>(Type owner, TKey key, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent);
ISnapshotStore<TState, TKey> GetSnapshotStore<TState>();
Task ClearSnapshotsAsync<TState>();
}
}

11
src/Squidex.Infrastructure/States/Store.cs

@ -44,7 +44,7 @@ namespace Squidex.Infrastructure.States
{
Guard.NotNull(key, nameof(key));
var snapshotStore = (ISnapshotStore<object, TKey>)services.GetService(typeof(ISnapshotStore<object, TKey>));
var snapshotStore = GetSnapshotStore<object>();
return new Persistence<TKey>(key, owner, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, applyEvent);
}
@ -53,16 +53,19 @@ namespace Squidex.Infrastructure.States
{
Guard.NotNull(key, nameof(key));
var snapshotStore = (ISnapshotStore<TState, TKey>)services.GetService(typeof(ISnapshotStore<TState, TKey>));
var snapshotStore = GetSnapshotStore<TState>();
return new Persistence<TState, TKey>(key, owner, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, mode, applySnapshot, applyEvent);
}
public Task ClearSnapshotsAsync<TState>()
{
var snapshotStore = (ISnapshotStore<TState, TKey>)services.GetService(typeof(ISnapshotStore<TState, TKey>));
return GetSnapshotStore<TState>().ClearAsync();
}
return snapshotStore.ClearAsync();
public ISnapshotStore<TState, TKey> GetSnapshotStore<TState>()
{
return (ISnapshotStore<TState, TKey>)services.GetService(typeof(ISnapshotStore<TState, TKey>));
}
}
}

66
tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainFormatterTests.cs

@ -0,0 +1,66 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Reflection;
using FakeItEasy;
using Orleans;
using Squidex.Infrastructure.TestHelpers;
using Xunit;
namespace Squidex.Infrastructure.Commands
{
public class DomainObjectGrainFormatterTests
{
private readonly IGrainCallContext context = A.Fake<IGrainCallContext>();
[Fact]
public void Should_return_fallback_if_no_method_is_defined()
{
A.CallTo(() => context.InterfaceMethod)
.Returns(null);
var result = DomainObjectGrainFormatter.Format(context);
Assert.Equal("Unknown", result);
}
[Fact]
public void Should_return_method_name_if_not_domain_object_method()
{
var methodInfo = A.Fake<MethodInfo>();
A.CallTo(() => methodInfo.Name)
.Returns("Calculate");
A.CallTo(() => context.InterfaceMethod)
.Returns(methodInfo);
var result = DomainObjectGrainFormatter.Format(context);
Assert.Equal("Calculate", result);
}
[Fact]
public void Should_return_nice_method_name_if_domain_object_execute()
{
var methodInfo = A.Fake<MethodInfo>();
A.CallTo(() => methodInfo.Name)
.Returns(nameof(IDomainObjectGrain.ExecuteAsync));
A.CallTo(() => context.Arguments)
.Returns(new object[] { new MyCommand() });
A.CallTo(() => context.InterfaceMethod)
.Returns(methodInfo);
var result = DomainObjectGrainFormatter.Format(context);
Assert.Equal("ExecuteAsync(MyCommand)", result);
}
}
}

7
tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs

@ -101,12 +101,9 @@ namespace Squidex.Infrastructure.Commands
return Task.FromResult<object>(null);
}
public override void ApplyEvent(Envelope<IEvent> @event)
protected override MyDomainState OnEvent(Envelope<IEvent> @event)
{
if (@event.Payload is ValueChanged valueChanged)
{
ApplySnapshot(new MyDomainState { Value = valueChanged.Value });
}
return new MyDomainState { Value = ((ValueChanged)@event.Payload).Value };
}
}

6
tests/Squidex.Infrastructure.Tests/TestHelpers/MyGrain.cs

@ -9,6 +9,7 @@ using System;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.States;
@ -25,5 +26,10 @@ namespace Squidex.Infrastructure.TestHelpers
{
return Task.FromResult<object>(null);
}
protected override MyDomainState OnEvent(Envelope<IEvent> @event)
{
return Snapshot;
}
}
}

Loading…
Cancel
Save