Browse Source

Usage trigger tests and improvements to persistence system.

pull/342/head
Sebastian Stehle 7 years ago
parent
commit
46f081f60f
  1. 5
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleTriggerHandler.cs
  2. 4
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs
  3. 15
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTriggerHandler.cs
  4. 4
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/AssetChangedTriggerHandler.cs
  5. 18
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/ContentChangedTriggerHandler.cs
  6. 23
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/UsageTriggerHandler.cs
  7. 2
      src/Squidex.Domain.Apps.Entities/Backup/BackupHandlerWithStore.cs
  8. 2
      src/Squidex.Domain.Apps.Entities/Comments/CommentsGrain.cs
  9. 48
      src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs
  10. 120
      src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTriggerHandler.cs
  11. 2
      src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs
  12. 2
      src/Squidex.Infrastructure/Commands/LogSnapshotDomainObjectGrain.cs
  13. 43
      src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs
  14. 20
      src/Squidex.Infrastructure/None.cs
  15. 2
      src/Squidex.Infrastructure/Orleans/GrainOfGuid{T}.cs
  16. 2
      src/Squidex.Infrastructure/Orleans/GrainOfString{T}.cs
  17. 2
      src/Squidex.Infrastructure/States/IPersistence.cs
  18. 11
      src/Squidex.Infrastructure/States/IStore.cs
  19. 7
      src/Squidex.Infrastructure/States/Persistence.cs
  20. 14
      src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs
  21. 15
      src/Squidex.Infrastructure/States/Store.cs
  22. 63
      src/Squidex.Infrastructure/States/StoreExtensions.cs
  23. 3
      src/Squidex/Config/Domain/RuleServices.cs
  24. 10
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs
  25. 41
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/AssetChangedTriggerTests.cs
  26. 77
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs
  27. 54
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/UsageTriggerHandlerTests.cs
  28. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs
  29. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByNameIndexGrainTests.cs
  30. 5
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByUserIndexGrainTests.cs
  31. 5
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesByAppIndexGrainTests.cs
  32. 5
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasByAppIndexGrainTests.cs
  33. 5
      tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs
  34. 5
      tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs
  35. 2
      tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs
  36. 2
      tests/Squidex.Infrastructure.Tests/Commands/LogSnapshotDomainObjectGrainTests.cs
  37. 6
      tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs
  38. 101
      tests/Squidex.Infrastructure.Tests/Orleans/GrainOfGuidTests.cs
  39. 101
      tests/Squidex.Infrastructure.Tests/Orleans/GrainOfStringTests.cs
  40. 45
      tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs
  41. 17
      tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs
  42. 2
      tools/Migrate_01/Rebuilder.cs

5
src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleTriggerHandler.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure.EventSourcing;
@ -17,8 +16,8 @@ namespace Squidex.Domain.Apps.Core.HandleRules
{
Type TriggerType { get; }
Task<bool> TriggersAsync(EnrichedEvent @event, RuleTrigger trigger);
bool Trigger(EnrichedEvent @event, RuleTrigger trigger);
Task<bool> TriggersAsync(IEvent @event, RuleTrigger trigger);
bool Trigger(IEvent @event, RuleTrigger trigger);
}
}

4
src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs

@ -83,7 +83,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
return null;
}
if (!await triggerHandler.TriggersAsync(@event.Payload, rule.Trigger))
if (!triggerHandler.Trigger(@event.Payload, rule.Trigger))
{
return null;
}
@ -106,7 +106,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
var enrichedEvent = await eventEnricher.EnrichAsync(appEventEnvelope);
if (!await triggerHandler.TriggersAsync(enrichedEvent, rule.Trigger))
if (!triggerHandler.Trigger(enrichedEvent, rule.Trigger))
{
return null;
}

15
src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTriggerHandler.cs

@ -10,7 +10,6 @@ using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Core.HandleRules
{
@ -24,21 +23,21 @@ namespace Squidex.Domain.Apps.Core.HandleRules
get { return typeof(TTrigger); }
}
Task<bool> IRuleTriggerHandler.TriggersAsync(EnrichedEvent @event, RuleTrigger trigger)
bool IRuleTriggerHandler.Trigger(EnrichedEvent @event, RuleTrigger trigger)
{
return @event is TEnrichedEvent e ? TriggersAsync(e, (TTrigger)trigger) : TaskHelper.False;
return @event is TEnrichedEvent e && Trigger(e, (TTrigger)trigger);
}
Task<bool> IRuleTriggerHandler.TriggersAsync(IEvent @event, RuleTrigger trigger)
bool IRuleTriggerHandler.Trigger(IEvent @event, RuleTrigger trigger)
{
return @event is TEvent e ? TriggersAsync(e, (TTrigger)trigger) : TaskHelper.False;
return @event is TEvent e && Trigger(e, (TTrigger)trigger);
}
protected abstract Task<bool> TriggersAsync(TEnrichedEvent @event, TTrigger trigger);
protected abstract bool Trigger(TEnrichedEvent @event, TTrigger trigger);
protected virtual Task<bool> TriggersAsync(TEvent @event, TTrigger trigger)
protected virtual bool Trigger(TEvent @event, TTrigger trigger)
{
return TaskHelper.True;
return true;
}
}
}

4
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/AssetChangedTriggerHandler.cs

@ -25,9 +25,9 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Triggers
this.scriptEngine = scriptEngine;
}
protected override Task<bool> TriggersAsync(EnrichedAssetEvent @event, AssetChangedTriggerV2 trigger)
protected override bool Trigger(EnrichedAssetEvent @event, AssetChangedTriggerV2 trigger)
{
return Task.FromResult(string.IsNullOrWhiteSpace(trigger.Condition) || scriptEngine.Evaluate("event", @event, trigger.Condition));
return string.IsNullOrWhiteSpace(trigger.Condition) || scriptEngine.Evaluate("event", @event, trigger.Condition);
}
}
}

18
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/ContentChangedTriggerHandler.cs

@ -6,13 +6,11 @@
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Core.HandleRules.Triggers
{
@ -27,11 +25,11 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Triggers
this.scriptEngine = scriptEngine;
}
protected override Task<bool> TriggersAsync(ContentEvent @event, ContentChangedTriggerV2 trigger)
protected override bool Trigger(ContentEvent @event, ContentChangedTriggerV2 trigger)
{
if (trigger.HandleAll)
{
return TaskHelper.True;
return true;
}
if (trigger.Schemas != null)
@ -40,19 +38,19 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Triggers
{
if (MatchsSchema(schema, @event.SchemaId))
{
return TaskHelper.True;
return true;
}
}
}
return TaskHelper.False;
return false;
}
protected override Task<bool> TriggersAsync(EnrichedContentEvent @event, ContentChangedTriggerV2 trigger)
protected override bool Trigger(EnrichedContentEvent @event, ContentChangedTriggerV2 trigger)
{
if (trigger.HandleAll)
{
return TaskHelper.True;
return true;
}
if (trigger.Schemas != null)
@ -61,12 +59,12 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Triggers
{
if (MatchsSchema(schema, @event.SchemaId) && MatchsCondition(schema, @event))
{
return TaskHelper.True;
return true;
}
}
}
return TaskHelper.False;
return false;
}
private static bool MatchsSchema(ContentChangedTriggerSchemaV2 schema, NamedId<Guid> eventId)

23
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/UsageTriggerHandler.cs

@ -0,0 +1,23 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Core.HandleRules.Triggers
{
public sealed class UsageTriggerHandler : RuleTriggerHandler<UsageTrigger, AppUsageExceeded, EnrichedUsageExceededEvent>
{
protected override bool Trigger(EnrichedUsageExceededEvent @event, UsageTrigger trigger)
{
return true;
}
}
}

2
src/Squidex.Domain.Apps.Entities/Backup/BackupHandlerWithStore.cs

@ -46,7 +46,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
Version = EtagVersion.Empty
};
var persistence = store.WithSnapshotsAndEventSourcing<TState, Guid>(typeof(TGrain), key, s => state = s, e =>
var persistence = store.WithSnapshotsAndEventSourcing(typeof(TGrain), key, (TState s) => state = s, e =>
{
state = func(e, state);

2
src/Squidex.Domain.Apps.Entities/Comments/CommentsGrain.cs

@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Entities.Comments
protected override Task ReadAsync(Type type, Guid id)
{
persistence = store.WithEventSourcing<Guid>(GetType(), id, ApplyEvent);
persistence = store.WithEventSourcing(GetType(), id, ApplyEvent);
return persistence.ReadAsync();
}

48
src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs

@ -12,19 +12,15 @@ using Orleans;
using Orleans.Runtime;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.States;
using Squidex.Infrastructure.UsageTracking;
namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
{
public sealed class UsageTrackerGrain : GrainOfString, IRemindable, IUsageTrackerGrain
public sealed class UsageTrackerGrain : GrainOfString<UsageTrackerGrain.GrainState>, IRemindable, IUsageTrackerGrain
{
private readonly IStore<string> store;
private readonly IUsageTracker usageTracker;
private IPersistence<State> persistence;
private State state;
public sealed class Target
{
@ -36,46 +32,33 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
}
[CollectionName("UsageTracker")]
public sealed class State
public sealed class GrainState
{
public Dictionary<NamedId<Guid>, Target> Targets { get; set; } = new Dictionary<NamedId<Guid>, Target>();
}
public UsageTrackerGrain(IStore<string> store, IUsageTracker usageTracker)
: base(store)
{
Guard.NotNull(store, nameof(store));
Guard.NotNull(usageTracker, nameof(usageTracker));
this.store = store;
this.usageTracker = usageTracker;
}
public override Task OnActivateAsync(string key)
protected override Task OnActivateAsync(string key)
{
DelayDeactivation(TimeSpan.FromDays(1));
RegisterOrUpdateReminder("Default", TimeSpan.Zero, TimeSpan.FromMinutes(10));
persistence = store.WithSnapshotsAndEventSourcing<State, string>(GetType(), key, ApplySnapshot, ApplyEvent);
return persistence.ReadAsync();
}
private void ApplySnapshot(State s)
{
state = s;
}
private void ApplyEvent(Envelope<IEvent> @event)
{
return Task.CompletedTask;
}
public async Task ReceiveReminder(string reminderName, TickStatus status)
{
var today = DateTime.Today;
foreach (var kvp in state.Targets)
foreach (var kvp in State.Targets)
{
var appId = kvp.Key;
@ -91,15 +74,12 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
var @event = new AppUsageExceeded { AppId = appId, Current = usage, Limit = limit };
await persistence.WriteEventsAsync(new[]
{
Envelope.Create(@event)
});
await Persistence.WriteEventAsync(@event);
}
}
}
await persistence.WriteSnapshotAsync(state);
await WriteStateAsync();
}
private static bool IsSameMonth(DateTime lhs, DateTime rhs)
@ -111,33 +91,33 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
{
UpdateTarget(appId, t => t.Enabled = true);
return persistence.WriteSnapshotAsync(state);
return WriteStateAsync();
}
public Task DeactivateTargetAsync(NamedId<Guid> appId)
{
UpdateTarget(appId, t => t.Enabled = false);
return persistence.WriteSnapshotAsync(state);
return WriteStateAsync();
}
public Task AddTargetAsync(NamedId<Guid> appId, int limits)
{
UpdateTarget(appId, t => t.Limit = limits);
return persistence.WriteSnapshotAsync(state);
return WriteStateAsync();
}
public Task RemoveTargetAsync(NamedId<Guid> appId)
{
state.Targets.Remove(appId);
State.Targets.Remove(appId);
return persistence.WriteSnapshotAsync(state);
return WriteStateAsync();
}
private void UpdateTarget(NamedId<Guid> appId, Action<Target> updater)
{
updater(state.Targets.GetOrAddNew(appId));;
updater(State.Targets.GetOrAddNew(appId));;
}
}
}

120
src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTriggerHandler.cs

@ -1,69 +1,69 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
//// ==========================================================================
//// Squidex Headless CMS
//// ==========================================================================
//// Copyright (c) Squidex UG (haftungsbeschraenkt)
//// All rights reserved. Licensed under the MIT license.
//// ==========================================================================
using System.Threading.Tasks;
using Orleans;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Rules;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.Tasks;
//using System.Threading.Tasks;
//using Orleans;
//using Squidex.Domain.Apps.Core.HandleRules;
//using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
//using Squidex.Domain.Apps.Core.Rules.Triggers;
//using Squidex.Domain.Apps.Events;
//using Squidex.Domain.Apps.Events.Rules;
//using Squidex.Infrastructure;
//using Squidex.Infrastructure.Orleans;
//using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
{
public sealed class UsageTriggerHandler : RuleTriggerHandler<UsageTrigger, AppEvent, EnrichedUsageExceededEvent>
{
private readonly IUsageTrackerGrain usageTrackerGrain;
//namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
//{
// public sealed class UsageTriggerHandler : RuleTriggerHandler<UsageTrigger, AppEvent, EnrichedUsageExceededEvent>
// {
// private readonly IUsageTrackerGrain usageTrackerGrain;
public UsageTriggerHandler(IGrainFactory grainFactory)
{
Guard.NotNull(grainFactory, nameof(grainFactory));
// public UsageTriggerHandler(IGrainFactory grainFactory)
// {
// Guard.NotNull(grainFactory, nameof(grainFactory));
usageTrackerGrain = grainFactory.GetGrain<IUsageTrackerGrain>(SingleGrain.Id);
}
// usageTrackerGrain = grainFactory.GetGrain<IUsageTrackerGrain>(SingleGrain.Id);
// }
protected override async Task<bool> TriggersAsync(AppEvent @event, UsageTrigger trigger)
{
switch (@event)
{
case RuleDeleted _:
await usageTrackerGrain.RemoveTargetAsync(@event.AppId);
break;
case RuleEnabled _:
await usageTrackerGrain.ActivateTargetAsync(@event.AppId);
break;
case RuleDisabled _:
await usageTrackerGrain.DeactivateTargetAsync(@event.AppId);
break;
case RuleCreated ruleCreated:
if (ruleCreated.Trigger is UsageTrigger createdTrigger)
{
await usageTrackerGrain.AddTargetAsync(ruleCreated.AppId, createdTrigger.Limit);
}
// protected override async Task<bool> Trigger(AppEvent @event, UsageTrigger trigger)
// {
// switch (@event)
// {
// case RuleDeleted _:
// await usageTrackerGrain.RemoveTargetAsync(@event.AppId);
// break;
// case RuleEnabled _:
// await usageTrackerGrain.ActivateTargetAsync(@event.AppId);
// break;
// case RuleDisabled _:
// await usageTrackerGrain.DeactivateTargetAsync(@event.AppId);
// break;
// case RuleCreated ruleCreated:
// if (ruleCreated.Trigger is UsageTrigger createdTrigger)
// {
// await usageTrackerGrain.AddTargetAsync(ruleCreated.AppId, createdTrigger.Limit);
// }
break;
case RuleUpdated ruleUpdated:
if (ruleUpdated.Trigger is UsageTrigger updatedTrigger)
{
await usageTrackerGrain.AddTargetAsync(ruleUpdated.AppId, updatedTrigger.Limit);
}
// break;
// case RuleUpdated ruleUpdated:
// if (ruleUpdated.Trigger is UsageTrigger updatedTrigger)
// {
// await usageTrackerGrain.AddTargetAsync(ruleUpdated.AppId, updatedTrigger.Limit);
// }
break;
}
// break;
// }
return @event is AppUsageExceeded;
}
// return @event is AppUsageExceeded;
// }
protected override Task<bool> TriggersAsync(EnrichedUsageExceededEvent @event, UsageTrigger trigger)
{
return TaskHelper.True;
}
}
}
// protected override Task<bool> Trigger(EnrichedUsageExceededEvent @event, UsageTrigger trigger)
// {
// return TaskHelper.True;
// }
// }
//}

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

@ -47,7 +47,7 @@ namespace Squidex.Infrastructure.Commands
protected sealed override Task ReadAsync(Type type, Guid id)
{
persistence = store.WithSnapshotsAndEventSourcing<T, Guid>(GetType(), id, ApplySnapshot, ApplyEvent);
persistence = store.WithSnapshotsAndEventSourcing(GetType(), id, new HandleSnapshot<T>(ApplySnapshot), ApplyEvent);
return persistence.ReadAsync();
}

2
src/Squidex.Infrastructure/Commands/LogSnapshotDomainObjectGrain.cs

@ -64,7 +64,7 @@ namespace Squidex.Infrastructure.Commands
protected sealed override Task ReadAsync(Type type, Guid id)
{
persistence = store.WithEventSourcing<Guid>(type, id, ApplyEvent);
persistence = store.WithEventSourcing(type, id, ApplyEvent);
return persistence.ReadAsync();
}

43
src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs

@ -17,18 +17,15 @@ using Squidex.Infrastructure.Tasks;
namespace Squidex.Infrastructure.EventSourcing.Grains
{
public class EventConsumerGrain : GrainOfString, IEventConsumerGrain
public class EventConsumerGrain : GrainOfString<EventConsumerState>, IEventConsumerGrain
{
private readonly EventConsumerFactory eventConsumerFactory;
private readonly IStore<string> store;
private readonly IEventDataFormatter eventDataFormatter;
private readonly IEventStore eventStore;
private readonly ISemanticLog log;
private TaskScheduler scheduler;
private IPersistence<EventConsumerState> persistence;
private IEventSubscription currentSubscription;
private IEventConsumer eventConsumer;
private EventConsumerState state = new EventConsumerState();
public EventConsumerGrain(
EventConsumerFactory eventConsumerFactory,
@ -36,18 +33,18 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
IEventStore eventStore,
IEventDataFormatter eventDataFormatter,
ISemanticLog log)
: base(store)
{
Guard.NotNull(log, nameof(log));
Guard.NotNull(store, nameof(store));
Guard.NotNull(eventStore, nameof(eventStore));
Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter));
Guard.NotNull(eventConsumerFactory, nameof(eventConsumerFactory));
Guard.NotNull(log, nameof(log));
this.log = log;
this.store = store;
this.eventStore = eventStore;
this.eventDataFormatter = eventDataFormatter;
this.eventConsumerFactory = eventConsumerFactory;
this.log = log;
}
protected override Task OnActivateAsync(string key)
@ -56,14 +53,12 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
eventConsumer = eventConsumerFactory(key);
persistence = store.WithSnapshots<EventConsumerState, string>(GetType(), eventConsumer.Name, s => state = s);
return persistence.ReadAsync();
return Task.CompletedTask;
}
public Task<Immutable<EventConsumerInfo>> GetStateAsync()
{
return Task.FromResult(state.ToInfo(eventConsumer.Name).AsImmutable());
return Task.FromResult(State.ToInfo(eventConsumer.Name).AsImmutable());
}
public Task OnEventAsync(Immutable<IEventSubscription> subscription, Immutable<StoredEvent> storedEvent)
@ -82,7 +77,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
await DispatchConsumerAsync(@event);
}
state = state.Handled(storedEvent.Value.EventPosition);
State = State.Handled(storedEvent.Value.EventPosition);
});
}
@ -97,15 +92,15 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
{
Unsubscribe();
state = state.Failed(exception.Value);
State = State.Failed(exception.Value);
});
}
public Task ActivateAsync()
{
if (!state.IsStopped)
if (!State.IsStopped)
{
Subscribe(state.Position);
Subscribe(State.Position);
}
return TaskHelper.Done;
@ -113,22 +108,22 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
public Task StartAsync()
{
if (!state.IsStopped)
if (!State.IsStopped)
{
return TaskHelper.Done;
}
return DoAndUpdateStateAsync(() =>
{
Subscribe(state.Position);
Subscribe(State.Position);
state = state.Started();
State = State.Started();
});
}
public Task StopAsync()
{
if (state.IsStopped)
if (State.IsStopped)
{
return TaskHelper.Done;
}
@ -137,7 +132,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
{
Unsubscribe();
state = state.Stopped();
State = State.Stopped();
});
}
@ -151,7 +146,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
Subscribe(null);
state = state.Reset();
State = State.Reset();
});
}
@ -182,10 +177,10 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
.WriteProperty("status", "Failed")
.WriteProperty("eventConsumer", eventConsumer.Name));
state = state.Failed(ex);
State = State.Failed(ex);
}
await persistence.WriteSnapshotAsync(state);
await WriteStateAsync();
}
private async Task ClearAsync()

20
src/Squidex.Infrastructure/None.cs

@ -0,0 +1,20 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
namespace Squidex.Infrastructure
{
public sealed class None
{
public static readonly Type Type = typeof(None);
private None()
{
}
}
}

2
src/Squidex.Infrastructure/Orleans/GrainOfGuid{T}.cs

@ -43,7 +43,7 @@ namespace Squidex.Infrastructure.Orleans
{
Key = key;
persistence = store.WithSnapshots<T, Guid>(GetType(), key, ApplyState);
persistence = store.WithSnapshots(GetType(), key, new HandleSnapshot<T>(ApplyState));
await persistence.ReadAsync();

2
src/Squidex.Infrastructure/Orleans/GrainOfString{T}.cs

@ -42,7 +42,7 @@ namespace Squidex.Infrastructure.Orleans
{
Key = key;
persistence = store.WithSnapshots<T, string>(GetType(), key, ApplyState);
persistence = store.WithSnapshots(GetType(), key, new HandleSnapshot<T>(ApplyState));
await persistence.ReadAsync();

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

@ -7,7 +7,7 @@
namespace Squidex.Infrastructure.States
{
public interface IPersistence : IPersistence<object>
public interface IPersistence : IPersistence<None>
{
}
}

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

@ -6,18 +6,21 @@
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Infrastructure.States
{
public delegate void HandleEvent(Envelope<IEvent> @event);
public delegate void HandleSnapshot<T>(T state);
public interface IStore<in TKey>
{
IPersistence WithEventSourcing(Type owner, TKey key, Func<Envelope<IEvent>, Task> applyEvent);
IPersistence WithEventSourcing(Type owner, TKey key, HandleEvent applyEvent);
IPersistence<TState> WithSnapshots<TState>(Type owner, TKey key, Func<TState, Task> applySnapshot);
IPersistence<TState> WithSnapshots<TState>(Type owner, TKey key, HandleSnapshot<TState> applySnapshot);
IPersistence<TState> WithSnapshotsAndEventSourcing<TState>(Type owner, TKey key, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent);
IPersistence<TState> WithSnapshotsAndEventSourcing<TState>(Type owner, TKey key, HandleSnapshot<TState> applySnapshot, HandleEvent applyEvent);
ISnapshotStore<TState, TKey> GetSnapshotStore<TState>();
}

7
src/Squidex.Infrastructure/States/Persistence.cs

@ -6,19 +6,18 @@
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Infrastructure.States
{
internal sealed class Persistence<TKey> : Persistence<object, TKey>, IPersistence
internal sealed class Persistence<TKey> : Persistence<None, TKey>, IPersistence
{
public Persistence(TKey ownerKey, Type ownerType,
IEventStore eventStore,
IEventDataFormatter eventDataFormatter,
ISnapshotStore<object, TKey> snapshotStore,
ISnapshotStore<None, TKey> snapshotStore,
IStreamNameResolver streamNameResolver,
Func<Envelope<IEvent>, Task> applyEvent)
HandleEvent applyEvent)
: base(ownerKey, ownerType, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, PersistenceMode.EventSourcing, null, applyEvent)
{
}

14
src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs

@ -24,8 +24,8 @@ namespace Squidex.Infrastructure.States
private readonly IEventStore eventStore;
private readonly IEventDataFormatter eventDataFormatter;
private readonly PersistenceMode persistenceMode;
private readonly Func<TSnapshot, Task> applyState;
private readonly Func<Envelope<IEvent>, Task> applyEvent;
private readonly HandleSnapshot<TSnapshot> applyState;
private readonly HandleEvent applyEvent;
private long versionSnapshot = EtagVersion.Empty;
private long versionEvents = EtagVersion.Empty;
private long version;
@ -41,8 +41,8 @@ namespace Squidex.Infrastructure.States
ISnapshotStore<TSnapshot, TKey> snapshotStore,
IStreamNameResolver streamNameResolver,
PersistenceMode persistenceMode,
Func<TSnapshot, Task> applyState,
Func<Envelope<IEvent>, Task> applyEvent)
HandleSnapshot<TSnapshot> applyState,
HandleEvent applyEvent)
{
this.ownerKey = ownerKey;
this.ownerType = ownerType;
@ -94,7 +94,7 @@ namespace Squidex.Infrastructure.States
if (applyState != null && position >= 0)
{
await applyState(state);
applyState(state);
}
}
}
@ -116,9 +116,9 @@ namespace Squidex.Infrastructure.States
var parsedEvent = ParseKnownEvent(@event);
if (parsedEvent != null && applyEvent != null)
if (applyEvent != null && parsedEvent != null)
{
await applyEvent(parsedEvent);
applyEvent(parsedEvent);
}
}
}

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

@ -30,26 +30,31 @@ namespace Squidex.Infrastructure.States
this.streamNameResolver = streamNameResolver;
}
public IPersistence<TState> WithSnapshots<TState>(Type owner, TKey key, Func<TState, Task> applySnapshot)
public IPersistence WithEventSourcing(Type owner, TKey key, HandleEvent applyEvent)
{
return CreatePersistence(owner, key, applyEvent);
}
public IPersistence<TState> WithSnapshots<TState>(Type owner, TKey key, HandleSnapshot<TState> applySnapshot)
{
return CreatePersistence(owner, key, PersistenceMode.Snapshots, applySnapshot, null);
}
public IPersistence<TState> WithSnapshotsAndEventSourcing<TState>(Type owner, TKey key, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent)
public IPersistence<TState> WithSnapshotsAndEventSourcing<TState>(Type owner, TKey key, HandleSnapshot<TState> applySnapshot, HandleEvent applyEvent)
{
return CreatePersistence(owner, key, PersistenceMode.SnapshotsAndEventSourcing, applySnapshot, applyEvent);
}
public IPersistence WithEventSourcing(Type owner, TKey key, Func<Envelope<IEvent>, Task> applyEvent)
private IPersistence CreatePersistence(Type owner, TKey key, HandleEvent applyEvent)
{
Guard.NotNull(key, nameof(key));
var snapshotStore = GetSnapshotStore<object>();
var snapshotStore = GetSnapshotStore<None>();
return new Persistence<TKey>(key, owner, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, applyEvent);
}
private IPersistence<TState> CreatePersistence<TState>(Type owner, TKey key, PersistenceMode mode, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent)
private IPersistence<TState> CreatePersistence<TState>(Type owner, TKey key, PersistenceMode mode, HandleSnapshot<TState> applySnapshot, HandleEvent applyEvent)
{
Guard.NotNull(key, nameof(key));

63
src/Squidex.Infrastructure/States/StoreExtensions.cs

@ -5,83 +5,36 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Infrastructure.States
{
public static class StoreExtensions
{
public static IPersistence WithEventSourcing<TOwner, TKey>(this IStore<TKey> store, TKey key, Func<Envelope<IEvent>, Task> applyEvent)
{
return store.WithEventSourcing(typeof(TOwner), key, applyEvent);
}
public static IPersistence<TState> WithSnapshots<TOwner, TState, TKey>(this IStore<TKey> store, TKey key, Func<TState, Task> applySnapshot)
{
return store.WithSnapshots(typeof(TOwner), key, applySnapshot);
}
public static IPersistence<TState> WithSnapshotsAndEventSourcing<TOwner, TState, TKey>(this IStore<TKey> store, TKey key, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent)
{
return store.WithSnapshotsAndEventSourcing(typeof(TOwner), key, applySnapshot, applyEvent);
}
public static IPersistence WithEventSourcing<TKey>(this IStore<TKey> store, Type owner, TKey key, Action<Envelope<IEvent>> applyEvent)
{
return store.WithEventSourcing(owner, key, applyEvent.ToAsync());
}
public static IPersistence<TState> WithSnapshots<TState, TKey>(this IStore<TKey> store, Type owner, TKey key, Action<TState> applySnapshot)
{
return store.WithSnapshots(owner, key, applySnapshot.ToAsync());
}
public static IPersistence<TState> WithSnapshotsAndEventSourcing<TState, TKey>(this IStore<TKey> store, Type owner, TKey key, Action<TState> applySnapshot, Action<Envelope<IEvent>> applyEvent)
{
return store.WithSnapshotsAndEventSourcing(owner, key, applySnapshot.ToAsync(), applyEvent.ToAsync());
}
public static IPersistence WithEventSourcing<TOwner, TKey>(this IStore<TKey> store, TKey key, Action<Envelope<IEvent>> applyEvent)
{
return store.WithEventSourcing(typeof(TOwner), key, applyEvent.ToAsync());
}
public static IPersistence<TState> WithSnapshots<TOwner, TState, TKey>(this IStore<TKey> store, TKey key, Action<TState> applySnapshot)
{
return store.WithSnapshots(typeof(TOwner), key, applySnapshot.ToAsync());
}
public static IPersistence<TState> WithSnapshotsAndEventSourcing<TOwner, TState, TKey>(this IStore<TKey> store, TKey key, Action<TState> applySnapshot, Action<Envelope<IEvent>> applyEvent)
{
return store.WithSnapshotsAndEventSourcing(typeof(TOwner), key, applySnapshot.ToAsync(), applyEvent.ToAsync());
}
public static Task WriteEventAsync<T>(IPersistence<T> persistence, Envelope<IEvent> @event)
public static Task WriteEventAsync<T>(this IPersistence<T> persistence, Envelope<IEvent> @event)
{
return persistence.WriteEventsAsync(new[] { @event });
}
public static Task WriteEventAsync<T>(IPersistence<T> persistence, IEvent @event)
public static Task WriteEventAsync<T>(this IPersistence<T> persistence, IEvent @event)
{
return persistence.WriteEventsAsync(new[] { Envelope.Create(@event) });
}
public static Task ClearSnapshotsAsync<TKey, TState>(this IStore<TKey> store)
public static Task ClearSnapshotsAsync<TKey, TSnapshot>(this IStore<TKey> store)
{
return store.GetSnapshotStore<TState>().ClearAsync();
return store.GetSnapshotStore<TSnapshot>().ClearAsync();
}
public static Task RemoveSnapshotAsync<TKey, TState>(this IStore<TKey> store, TKey key)
public static Task RemoveSnapshotAsync<TKey, TSnapshot>(this IStore<TKey> store, TKey key)
{
return store.GetSnapshotStore<TState>().RemoveAsync(key);
return store.GetSnapshotStore<TSnapshot>().RemoveAsync(key);
}
public static async Task<TState> GetSnapshotAsync<TKey, TState>(this IStore<TKey> store, TKey key)
public static async Task<TSnapshot> GetSnapshotAsync<TKey, TSnapshot>(this IStore<TKey> store, TKey key)
{
var result = await store.GetSnapshotStore<TState>().ReadAsync(key);
var result = await store.GetSnapshotStore<TSnapshot>().ReadAsync(key);
return result.Value;
}

3
src/Squidex/Config/Domain/RuleServices.cs

@ -27,6 +27,9 @@ namespace Squidex.Config.Domain
services.AddSingletonAs<ContentChangedTriggerHandler>()
.As<IRuleTriggerHandler>();
services.AddSingletonAs<UsageTriggerHandler>()
.As<IRuleTriggerHandler>();
services.AddSingletonAs<RuleEnqueuer>()
.As<IEventConsumer>();

10
tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs

@ -146,7 +146,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var ruleConfig = ValidRule();
var ruleEnvelope = Envelope.Create(new ContentCreated());
A.CallTo(() => ruleTriggerHandler.TriggersAsync(A<IEvent>.Ignored, ruleConfig.Trigger))
A.CallTo(() => ruleTriggerHandler.Trigger(A<IEvent>.Ignored, ruleConfig.Trigger))
.Returns(false);
var job = await sut.CreateJobAsync(ruleConfig, ruleEnvelope);
@ -163,10 +163,10 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var ruleConfig = ValidRule();
var ruleEnvelope = Envelope.Create(new ContentCreated());
A.CallTo(() => ruleTriggerHandler.TriggersAsync(A<IEvent>.Ignored, ruleConfig.Trigger))
A.CallTo(() => ruleTriggerHandler.Trigger(A<IEvent>.Ignored, ruleConfig.Trigger))
.Returns(true);
A.CallTo(() => ruleTriggerHandler.TriggersAsync(A<EnrichedEvent>.Ignored, ruleConfig.Trigger))
A.CallTo(() => ruleTriggerHandler.Trigger(A<EnrichedEvent>.Ignored, ruleConfig.Trigger))
.Returns(false);
var job = await sut.CreateJobAsync(ruleConfig, ruleEnvelope);
@ -215,10 +215,10 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
A.CallTo(() => clock.GetCurrentInstant())
.Returns(now);
A.CallTo(() => ruleTriggerHandler.TriggersAsync(A<EnrichedEvent>.Ignored, ruleConfig.Trigger))
A.CallTo(() => ruleTriggerHandler.Trigger(A<EnrichedEvent>.Ignored, ruleConfig.Trigger))
.Returns(true);
A.CallTo(() => ruleTriggerHandler.TriggersAsync(A<IEvent>.Ignored, ruleConfig.Trigger))
A.CallTo(() => ruleTriggerHandler.Trigger(A<IEvent>.Ignored, ruleConfig.Trigger))
.Returns(true);
A.CallTo(() => ruleActionHandler.CreateJobAsync(A<EnrichedEvent>.Ignored, ruleConfig.Action))

41
tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/AssetChangedTriggerTests.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
@ -36,76 +35,76 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules.Triggers
}
[Fact]
public Task Should_not_trigger_precheck_when_event_type_not_correct()
public void Should_not_trigger_precheck_when_event_type_not_correct()
{
return TestForConditionAsync(string.Empty, async trigger =>
TestForCondition(string.Empty, trigger =>
{
var result = await sut.TriggersAsync(new ContentCreated(), trigger);
var result = sut.Trigger(new ContentCreated(), trigger);
Assert.False(result);
});
}
[Fact]
public Task Should_trigger_precheck_when_event_type_correct()
public void Should_trigger_precheck_when_event_type_correct()
{
return TestForConditionAsync(string.Empty, async trigger =>
TestForCondition(string.Empty, trigger =>
{
var result = await sut.TriggersAsync(new AssetCreated(), trigger);
var result = sut.Trigger(new AssetCreated(), trigger);
Assert.True(result);
});
}
[Fact]
public Task Should_not_trigger_check_when_event_type_not_correct()
public void Should_not_trigger_check_when_event_type_not_correct()
{
return TestForConditionAsync(string.Empty, async trigger =>
TestForCondition(string.Empty, trigger =>
{
var result = await sut.TriggersAsync(new EnrichedContentEvent(), trigger);
var result = sut.Trigger(new EnrichedContentEvent(), trigger);
Assert.False(result);
});
}
[Fact]
public Task Should_trigger_check_when_condition_is_empty()
public void Should_trigger_check_when_condition_is_empty()
{
return TestForConditionAsync(string.Empty, async trigger =>
TestForCondition(string.Empty, trigger =>
{
var result = await sut.TriggersAsync(new EnrichedAssetEvent(), trigger);
var result = sut.Trigger(new EnrichedAssetEvent(), trigger);
Assert.True(result);
});
}
[Fact]
public Task Should_trigger_check_when_condition_matchs()
public void Should_trigger_check_when_condition_matchs()
{
return TestForConditionAsync("true", async trigger =>
TestForCondition("true", trigger =>
{
var result = await sut.TriggersAsync(new EnrichedAssetEvent(), trigger);
var result = sut.Trigger(new EnrichedAssetEvent(), trigger);
Assert.True(result);
});
}
[Fact]
public Task Should_not_trigger_check_when_condition_does_not_matchs()
public void Should_not_trigger_check_when_condition_does_not_matchs()
{
return TestForConditionAsync("false", async trigger =>
TestForCondition("false", trigger =>
{
var result = await sut.TriggersAsync(new EnrichedAssetEvent(), trigger);
var result = sut.Trigger(new EnrichedAssetEvent(), trigger);
Assert.False(result);
});
}
private async Task TestForConditionAsync(string condition, Func<AssetChangedTriggerV2, Task> action)
private void TestForCondition(string condition, Action<AssetChangedTriggerV2> action)
{
var trigger = new AssetChangedTriggerV2 { Condition = condition };
await action(trigger);
action(trigger);
if (string.IsNullOrWhiteSpace(condition))
{

77
tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs

@ -8,7 +8,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
@ -44,138 +43,138 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules.Triggers
}
[Fact]
public Task Should_not_trigger_precheck_when_event_type_not_correct()
public void Should_not_trigger_precheck_when_event_type_not_correct()
{
return TestForTriggerAsync(handleAll: true, schemaId: null, condition: null, action: async trigger =>
TestForTrigger(handleAll: true, schemaId: null, condition: null, action: trigger =>
{
var result = await sut.TriggersAsync(new AssetCreated(), trigger);
var result = sut.Trigger(new AssetCreated(), trigger);
Assert.False(result);
});
}
[Fact]
public Task Should_not_trigger_precheck_when_trigger_contains_no_schemas()
public void Should_not_trigger_precheck_when_trigger_contains_no_schemas()
{
return TestForTriggerAsync(handleAll: false, schemaId: null, condition: null, action: async trigger =>
TestForTrigger(handleAll: false, schemaId: null, condition: null, action: trigger =>
{
var result = await sut.TriggersAsync(new ContentCreated { SchemaId = SchemaMatch }, trigger);
var result = sut.Trigger(new ContentCreated { SchemaId = SchemaMatch }, trigger);
Assert.False(result);
});
}
[Fact]
public Task Should_trigger_precheck_when_handling_all_events()
public void Should_trigger_precheck_when_handling_all_events()
{
return TestForTriggerAsync(handleAll: true, schemaId: SchemaMatch, condition: null, action: async trigger =>
TestForTrigger(handleAll: true, schemaId: SchemaMatch, condition: null, action: trigger =>
{
var result = await sut.TriggersAsync(new ContentCreated { SchemaId = SchemaMatch }, trigger);
var result = sut.Trigger(new ContentCreated { SchemaId = SchemaMatch }, trigger);
Assert.True(result);
});
}
[Fact]
public Task Should_trigger_precheck_when_condition_is_empty()
public void Should_trigger_precheck_when_condition_is_empty()
{
return TestForTriggerAsync(handleAll: false, schemaId: SchemaMatch, condition: string.Empty, action: async trigger =>
TestForTrigger(handleAll: false, schemaId: SchemaMatch, condition: string.Empty, action: trigger =>
{
var result = await sut.TriggersAsync(new ContentCreated { SchemaId = SchemaMatch }, trigger);
var result = sut.Trigger(new ContentCreated { SchemaId = SchemaMatch }, trigger);
Assert.True(result);
});
}
[Fact]
public Task Should_not_trigger_precheck_when_schema_id_does_not_match()
public void Should_not_trigger_precheck_when_schema_id_does_not_match()
{
return TestForTriggerAsync(handleAll: false, schemaId: SchemaNonMatch, condition: null, action: async trigger =>
TestForTrigger(handleAll: false, schemaId: SchemaNonMatch, condition: null, action: trigger =>
{
var result = await sut.TriggersAsync(new ContentCreated { SchemaId = SchemaMatch }, trigger);
var result = sut.Trigger(new ContentCreated { SchemaId = SchemaMatch }, trigger);
Assert.False(result);
});
}
[Fact]
public Task Should_not_trigger_check_when_event_type_not_correct()
public void Should_not_trigger_check_when_event_type_not_correct()
{
return TestForTriggerAsync(handleAll: true, schemaId: null, condition: null, action: async trigger =>
TestForTrigger(handleAll: true, schemaId: null, condition: null, action: trigger =>
{
var result = await sut.TriggersAsync(new EnrichedAssetEvent(), trigger);
var result = sut.Trigger(new EnrichedAssetEvent(), trigger);
Assert.False(result);
});
}
[Fact]
public Task Should_not_trigger_check_when_trigger_contains_no_schemas()
public void Should_not_trigger_check_when_trigger_contains_no_schemas()
{
return TestForTriggerAsync(handleAll: false, schemaId: null, condition: null, action: async trigger =>
TestForTrigger(handleAll: false, schemaId: null, condition: null, action: trigger =>
{
var result = await sut.TriggersAsync(new EnrichedContentEvent { SchemaId = SchemaMatch }, trigger);
var result = sut.Trigger(new EnrichedContentEvent { SchemaId = SchemaMatch }, trigger);
Assert.False(result);
});
}
[Fact]
public Task Should_trigger_check_when_handling_all_events()
public void Should_trigger_check_when_handling_all_events()
{
return TestForTriggerAsync(handleAll: true, schemaId: SchemaMatch, condition: null, action: async trigger =>
TestForTrigger(handleAll: true, schemaId: SchemaMatch, condition: null, action: trigger =>
{
var result = await sut.TriggersAsync(new EnrichedContentEvent { SchemaId = SchemaMatch }, trigger);
var result = sut.Trigger(new EnrichedContentEvent { SchemaId = SchemaMatch }, trigger);
Assert.True(result);
});
}
[Fact]
public Task Should_trigger_check_when_condition_is_empty()
public void Should_trigger_check_when_condition_is_empty()
{
return TestForTriggerAsync(handleAll: false, schemaId: SchemaMatch, condition: string.Empty, action: async trigger =>
TestForTrigger(handleAll: false, schemaId: SchemaMatch, condition: string.Empty, action: trigger =>
{
var result = await sut.TriggersAsync(new EnrichedContentEvent { SchemaId = SchemaMatch }, trigger);
var result = sut.Trigger(new EnrichedContentEvent { SchemaId = SchemaMatch }, trigger);
Assert.True(result);
});
}
[Fact]
public Task Should_trigger_check_when_condition_matchs()
public void Should_trigger_check_when_condition_matchs()
{
return TestForTriggerAsync(handleAll: false, schemaId: SchemaMatch, condition: "true", action: async trigger =>
TestForTrigger(handleAll: false, schemaId: SchemaMatch, condition: "true", action: trigger =>
{
var result = await sut.TriggersAsync(new EnrichedContentEvent { SchemaId = SchemaMatch }, trigger);
var result = sut.Trigger(new EnrichedContentEvent { SchemaId = SchemaMatch }, trigger);
Assert.True(result);
});
}
[Fact]
public Task Should_not_trigger_check_when_schema_id_does_not_match()
public void Should_not_trigger_check_when_schema_id_does_not_match()
{
return TestForTriggerAsync(handleAll: false, schemaId: SchemaNonMatch, condition: null, action: async trigger =>
TestForTrigger(handleAll: false, schemaId: SchemaNonMatch, condition: null, action: trigger =>
{
var result = await sut.TriggersAsync(new EnrichedContentEvent { SchemaId = SchemaMatch }, trigger);
var result = sut.Trigger(new EnrichedContentEvent { SchemaId = SchemaMatch }, trigger);
Assert.False(result);
});
}
[Fact]
public Task Should_not_trigger_check_when_condition_does_not_matchs()
public void Should_not_trigger_check_when_condition_does_not_matchs()
{
return TestForTriggerAsync(handleAll: false, schemaId: SchemaMatch, condition: "false", action: async trigger =>
TestForTrigger(handleAll: false, schemaId: SchemaMatch, condition: "false", action: trigger =>
{
var result = await sut.TriggersAsync(new EnrichedContentEvent { SchemaId = SchemaMatch }, trigger);
var result = sut.Trigger(new EnrichedContentEvent { SchemaId = SchemaMatch }, trigger);
Assert.False(result);
});
}
private async Task TestForTriggerAsync(bool handleAll, NamedId<Guid> schemaId, string condition, Func<ContentChangedTriggerV2, Task> action)
private void TestForTrigger(bool handleAll, NamedId<Guid> schemaId, string condition, Action<ContentChangedTriggerV2> action)
{
var trigger = new ContentChangedTriggerV2 { HandleAll = handleAll };
@ -190,7 +189,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules.Triggers
});
}
await action(trigger);
action(trigger);
if (string.IsNullOrWhiteSpace(condition))
{

54
tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/UsageTriggerHandlerTests.cs

@ -0,0 +1,54 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.HandleRules.Triggers;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Contents;
using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.HandleRules.Triggers
{
public class UsageTriggerHandlerTests
{
private readonly IRuleTriggerHandler sut = new UsageTriggerHandler();
[Fact]
public void Should_not_trigger_precheck_when_event_type_not_correct()
{
var result = sut.Trigger(new ContentCreated(), new UsageTrigger());
Assert.False(result);
}
[Fact]
public void Should_trigger_precheck_when_event_type_correct()
{
var result = sut.Trigger(new AppUsageExceeded(), new UsageTrigger());
Assert.True(result);
}
[Fact]
public void Should_not_trigger_check_when_event_type_not_correct()
{
var result = sut.Trigger(new EnrichedContentEvent(), new UsageTrigger());
Assert.False(result);
}
[Fact]
public void Should_trigger_check_when_type_correct()
{
var result = sut.Trigger(new AppUsageExceeded(), new UsageTrigger());
Assert.True(result);
}
}
}

2
tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs

@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
public AppUISettingsGrainTests()
{
A.CallTo(() => store.WithSnapshots(A<Type>.Ignored, A<Guid>.Ignored, A<Func<AppUISettingsGrain.GrainState, Task>>.Ignored))
A.CallTo(() => store.WithSnapshots(typeof(AppUISettingsGrain), Guid.Empty, A<HandleSnapshot<AppUISettingsGrain.GrainState>>.Ignored))
.Returns(persistence);
sut = new AppUISettingsGrain(store);

2
tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByNameIndexGrainTests.cs

@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
public AppsByNameIndexGrainTests()
{
A.CallTo(() => store.WithSnapshots(A<Type>.Ignored, A<string>.Ignored, A<Func<AppsByNameIndexGrain.GrainState, Task>>.Ignored))
A.CallTo(() => store.WithSnapshots(typeof(AppsByNameIndexGrain), SingleGrain.Id, A<HandleSnapshot<AppsByNameIndexGrain.GrainState>>.Ignored))
.Returns(persistence);
sut = new AppsByNameIndexGrain(store);

5
tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByUserIndexGrainTests.cs

@ -21,15 +21,16 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
private readonly IPersistence<AppsByUserIndexGrain.GrainState> persistence = A.Fake<IPersistence<AppsByUserIndexGrain.GrainState>>();
private readonly Guid appId1 = Guid.NewGuid();
private readonly Guid appId2 = Guid.NewGuid();
private readonly string userId = "user";
private readonly AppsByUserIndexGrain sut;
public AppsByUserIndexGrainTests()
{
A.CallTo(() => store.WithSnapshots(A<Type>.Ignored, A<string>.Ignored, A<Func<AppsByUserIndexGrain.GrainState, Task>>.Ignored))
A.CallTo(() => store.WithSnapshots(typeof(AppsByUserIndexGrain), userId, A<HandleSnapshot<AppsByUserIndexGrain.GrainState>>.Ignored))
.Returns(persistence);
sut = new AppsByUserIndexGrain(store);
sut.ActivateAsync("user").Wait();
sut.ActivateAsync(userId).Wait();
}
[Fact]

5
tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesByAppIndexGrainTests.cs

@ -18,17 +18,18 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes
{
private readonly IStore<Guid> store = A.Fake<IStore<Guid>>();
private readonly IPersistence<RulesByAppIndexGrain.GrainState> persistence = A.Fake<IPersistence<RulesByAppIndexGrain.GrainState>>();
private readonly Guid appId = Guid.NewGuid();
private readonly Guid ruleId1 = Guid.NewGuid();
private readonly Guid ruleId2 = Guid.NewGuid();
private readonly RulesByAppIndexGrain sut;
public RulesByAppIndexGrainTests()
{
A.CallTo(() => store.WithSnapshots(A<Type>.Ignored, A<Guid>.Ignored, A<Func<RulesByAppIndexGrain.GrainState, Task>>.Ignored))
A.CallTo(() => store.WithSnapshots(typeof(RulesByAppIndexGrain), appId, A<HandleSnapshot<RulesByAppIndexGrain.GrainState>>.Ignored))
.Returns(persistence);
sut = new RulesByAppIndexGrain(store);
sut.ActivateAsync(Guid.NewGuid()).Wait();
sut.ActivateAsync(appId).Wait();
}
[Fact]

5
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasByAppIndexGrainTests.cs

@ -18,6 +18,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
{
private readonly IStore<Guid> store = A.Fake<IStore<Guid>>();
private readonly IPersistence<SchemasByAppIndexGrain.GrainState> persistence = A.Fake<IPersistence<SchemasByAppIndexGrain.GrainState>>();
private readonly Guid appId = Guid.NewGuid();
private readonly Guid schemaId1 = Guid.NewGuid();
private readonly Guid schemaId2 = Guid.NewGuid();
private readonly string schemaName1 = "my-schema1";
@ -26,11 +27,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
public SchemasByAppIndexGrainTests()
{
A.CallTo(() => store.WithSnapshots(A<Type>.Ignored, A<Guid>.Ignored, A<Func<SchemasByAppIndexGrain.GrainState, Task>>.Ignored))
A.CallTo(() => store.WithSnapshots(typeof(SchemasByAppIndexGrain), appId, A<HandleSnapshot<SchemasByAppIndexGrain.GrainState>>.Ignored))
.Returns(persistence);
sut = new SchemasByAppIndexGrain(store);
sut.ActivateAsync(Guid.NewGuid()).Wait();
sut.ActivateAsync(appId).Wait();
}
[Fact]

5
tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs

@ -20,15 +20,16 @@ namespace Squidex.Domain.Apps.Entities.Tags
{
private readonly IStore<string> store = A.Fake<IStore<string>>();
private readonly IPersistence<TagGrain.GrainState> persistence = A.Fake<IPersistence<TagGrain.GrainState>>();
private readonly string id = Guid.NewGuid().ToString();
private readonly TagGrain sut;
public TagGrainTests()
{
A.CallTo(() => store.WithSnapshots(A<Type>.Ignored, A<string>.Ignored, A<Func<TagGrain.GrainState, Task>>.Ignored))
A.CallTo(() => store.WithSnapshots(typeof(TagGrain), id, A<HandleSnapshot<TagGrain.GrainState>>.Ignored))
.Returns(persistence);
sut = new TagGrain(store);
sut.ActivateAsync(string.Empty).Wait();
sut.ActivateAsync(id).Wait();
}
[Fact]

5
tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs

@ -8,7 +8,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
@ -58,10 +57,10 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
protected HandlerTestBase()
{
A.CallTo(() => store.WithSnapshotsAndEventSourcing(A<Type>.Ignored, Id, A<Func<TState, Task>>.Ignored, A<Func<Envelope<IEvent>, Task>>.Ignored))
A.CallTo(() => store.WithSnapshotsAndEventSourcing(A<Type>.Ignored, Id, A<HandleSnapshot<TState>>.Ignored, A<HandleEvent>.Ignored))
.Returns(persistence1);
A.CallTo(() => store.WithEventSourcing(A<Type>.Ignored, Id, A<Func<Envelope<IEvent>, Task>>.Ignored))
A.CallTo(() => store.WithEventSourcing(A<Type>.Ignored, Id, A<HandleEvent>.Ignored))
.Returns(persistence2);
A.CallTo(() => persistence1.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored))

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

@ -109,7 +109,7 @@ namespace Squidex.Infrastructure.Commands
public DomainObjectGrainTests()
{
A.CallTo(() => store.WithSnapshotsAndEventSourcing(typeof(MyDomainObject), id, A<Func<MyDomainState, Task>>.Ignored, A<Func<Envelope<IEvent>, Task>>.Ignored))
A.CallTo(() => store.WithSnapshotsAndEventSourcing(typeof(MyDomainObject), id, A<HandleSnapshot<MyDomainState>>.Ignored, A<HandleEvent>.Ignored))
.Returns(persistence);
sut = new MyDomainObject(store);

2
tests/Squidex.Infrastructure.Tests/Commands/LogSnapshotDomainObjectGrainTests.cs

@ -104,7 +104,7 @@ namespace Squidex.Infrastructure.Commands
public LogSnapshotDomainObjectGrainTests()
{
A.CallTo(() => store.WithEventSourcing(typeof(MyDomainObject), id, A<Func<Envelope<IEvent>, Task>>.Ignored))
A.CallTo(() => store.WithEventSourcing(typeof(MyDomainObject), id, A<HandleEvent>.Ignored))
.Returns(persistence);
A.CallTo(() => store.GetSnapshotStore<MyDomainState>())

6
tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs

@ -54,7 +54,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
private readonly EventConsumerGrain sut;
private readonly string consumerName;
private readonly string initialPosition = Guid.NewGuid().ToString();
private Func<EventConsumerState, Task> apply;
private HandleSnapshot<EventConsumerState> apply;
private EventConsumerState state = new EventConsumerState();
public EventConsumerGrainTests()
@ -63,8 +63,8 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
consumerName = eventConsumer.GetType().Name;
A.CallTo(() => store.WithSnapshots(A<Type>.Ignored, consumerName, A<Func<EventConsumerState, Task>>.Ignored))
.Invokes(new Action<Type, string, Func<EventConsumerState, Task>>((t, key, a) =>
A.CallTo(() => store.WithSnapshots(A<Type>.Ignored, consumerName, A<HandleSnapshot<EventConsumerState>>.Ignored))
.Invokes(new Action<Type, string, HandleSnapshot<EventConsumerState>>((t, key, a) =>
{
apply = a;
}))

101
tests/Squidex.Infrastructure.Tests/Orleans/GrainOfGuidTests.cs

@ -0,0 +1,101 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Infrastructure.States;
using Xunit;
namespace Squidex.Infrastructure.Orleans
{
public class GrainOfGuidTests
{
private readonly IPersistence<MyGrain.GrainState> persistence = A.Fake<IPersistence<MyGrain.GrainState>>();
private readonly IStore<Guid> store = A.Fake<IStore<Guid>>();
private readonly Guid id = Guid.NewGuid();
private readonly MyGrain sut;
private HandleSnapshot<MyGrain.GrainState> read;
public sealed class MyGrain : GrainOfGuid<MyGrain.GrainState>
{
public sealed class GrainState
{
public Guid Id { get; set; }
}
public GrainState PublicState
{
get { return State; }
}
public MyGrain(IStore<Guid> store)
: base(store)
{
}
public Task PublicWriteAsync()
{
return WriteStateAsync();
}
public Task PublicClearAsync()
{
return ClearStateAsync();
}
}
public GrainOfGuidTests()
{
A.CallTo(() => persistence.ReadAsync(EtagVersion.Any))
.Invokes(_ =>
{
read(new MyGrain.GrainState { Id = id });
});
A.CallTo(() => store.WithSnapshots(typeof(MyGrain), id, A<HandleSnapshot<MyGrain.GrainState>>.Ignored))
.Invokes(new Action<Type, Guid, HandleSnapshot<MyGrain.GrainState>>((type, id, callback) =>
{
read = callback;
}))
.Returns(persistence);
sut = new MyGrain(store);
}
[Fact]
public async Task Should_read_on_activate()
{
await sut.ActivateAsync(id);
Assert.Equal(id, sut.PublicState.Id);
A.CallTo(() => persistence.ReadAsync(EtagVersion.Any))
.MustHaveHappened();
}
[Fact]
public async Task Should_invoke_persistence_on_write()
{
await sut.ActivateAsync(id);
await sut.PublicWriteAsync();
A.CallTo(() => persistence.WriteSnapshotAsync(sut.PublicState))
.MustHaveHappened();
}
[Fact]
public async Task Should_invoke_persistence_on_clear()
{
await sut.ActivateAsync(id);
await sut.PublicClearAsync();
A.CallTo(() => persistence.DeleteAsync())
.MustHaveHappened();
}
}
}

101
tests/Squidex.Infrastructure.Tests/Orleans/GrainOfStringTests.cs

@ -0,0 +1,101 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Infrastructure.States;
using Xunit;
namespace Squidex.Infrastructure.Orleans
{
public class GrainOfStringTests
{
private readonly IPersistence<MyGrain.GrainState> persistence = A.Fake<IPersistence<MyGrain.GrainState>>();
private readonly IStore<string> store = A.Fake<IStore<string>>();
private readonly string id = Guid.NewGuid().ToString();
private readonly MyGrain sut;
private HandleSnapshot<MyGrain.GrainState> read;
public sealed class MyGrain : GrainOfString<MyGrain.GrainState>
{
public sealed class GrainState
{
public string Id { get; set; }
}
public GrainState PublicState
{
get { return State; }
}
public MyGrain(IStore<string> store)
: base(store)
{
}
public Task PublicWriteAsync()
{
return WriteStateAsync();
}
public Task PublicClearAsync()
{
return ClearStateAsync();
}
}
public GrainOfStringTests()
{
A.CallTo(() => persistence.ReadAsync(EtagVersion.Any))
.Invokes(_ =>
{
read(new MyGrain.GrainState { Id = id });
});
A.CallTo(() => store.WithSnapshots(typeof(MyGrain), id, A<HandleSnapshot<MyGrain.GrainState>>.Ignored))
.Invokes(new Action<Type, string, HandleSnapshot<MyGrain.GrainState>>((type, id, callback) =>
{
read = callback;
}))
.Returns(persistence);
sut = new MyGrain(store);
}
[Fact]
public async Task Should_read_on_activate()
{
await sut.ActivateAsync(id);
Assert.Equal(id, sut.PublicState.Id);
A.CallTo(() => persistence.ReadAsync(EtagVersion.Any))
.MustHaveHappened();
}
[Fact]
public async Task Should_invoke_persistence_on_write()
{
await sut.ActivateAsync(id);
await sut.PublicWriteAsync();
A.CallTo(() => persistence.WriteSnapshotAsync(sut.PublicState))
.MustHaveHappened();
}
[Fact]
public async Task Should_invoke_persistence_on_clear()
{
await sut.ActivateAsync(id);
await sut.PublicClearAsync();
A.CallTo(() => persistence.DeleteAsync())
.MustHaveHappened();
}
}
}

45
tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs

@ -22,16 +22,19 @@ namespace Squidex.Infrastructure.States
private readonly IEventDataFormatter eventDataFormatter = A.Fake<IEventDataFormatter>();
private readonly IEventStore eventStore = A.Fake<IEventStore>();
private readonly IServiceProvider services = A.Fake<IServiceProvider>();
private readonly ISnapshotStore<object, string> snapshotStore = A.Fake<ISnapshotStore<object, string>>();
private readonly ISnapshotStore<int, string> snapshotStore = A.Fake<ISnapshotStore<int, string>>();
private readonly ISnapshotStore<None, string> snapshotStoreNone = A.Fake<ISnapshotStore<None, string>>();
private readonly IStreamNameResolver streamNameResolver = A.Fake<IStreamNameResolver>();
private readonly IStore<string> sut;
public PersistenceEventSourcingTests()
{
A.CallTo(() => services.GetService(typeof(ISnapshotStore<object, string>)))
A.CallTo(() => services.GetService(typeof(ISnapshotStore<int, string>)))
.Returns(snapshotStore);
A.CallTo(() => services.GetService(typeof(ISnapshotStore<None, string>)))
.Returns(snapshotStoreNone);
A.CallTo(() => streamNameResolver.GetStreamName(typeof(object), key))
A.CallTo(() => streamNameResolver.GetStreamName(None.Type, key))
.Returns(key);
sut = new Store<string>(eventStore, eventDataFormatter, services, streamNameResolver);
@ -46,7 +49,7 @@ namespace Squidex.Infrastructure.States
SetupEventStore(event1, event2);
var persistedEvents = new List<IEvent>();
var persistence = sut.WithEventSourcing<object, string>(key, x => persistedEvents.Add(x.Payload));
var persistence = sut.WithEventSourcing(None.Type, key, x => persistedEvents.Add(x.Payload));
await persistence.ReadAsync();
@ -65,7 +68,7 @@ namespace Squidex.Infrastructure.States
.Throws(new TypeNameNotFoundException());
var persistedEvents = new List<IEvent>();
var persistence = sut.WithEventSourcing<object, string>(key, x => persistedEvents.Add(x.Payload));
var persistence = sut.WithEventSourcing(None.Type, key, x => persistedEvents.Add(x.Payload));
await persistence.ReadAsync();
@ -81,9 +84,9 @@ namespace Squidex.Infrastructure.States
SetupEventStore(3, 2);
var persistedState = (object)null;
var persistedState = -1;
var persistedEvents = new List<IEvent>();
var persistence = sut.WithSnapshotsAndEventSourcing<object, object, string>(key, x => persistedState = x, x => persistedEvents.Add(x.Payload));
var persistence = sut.WithSnapshotsAndEventSourcing(None.Type, key, (int x) => persistedState = x, x => persistedEvents.Add(x.Payload));
await persistence.ReadAsync();
@ -99,9 +102,9 @@ namespace Squidex.Infrastructure.States
SetupEventStore(3, 0, 3);
var persistedState = (object)null;
var persistedState = -1;
var persistedEvents = new List<IEvent>();
var persistence = sut.WithSnapshotsAndEventSourcing<object, object, string>(key, x => persistedState = x, x => persistedEvents.Add(x.Payload));
var persistence = sut.WithSnapshotsAndEventSourcing(None.Type, key, (int x) => persistedState = x, x => persistedEvents.Add(x.Payload));
await Assert.ThrowsAsync<InvalidOperationException>(() => persistence.ReadAsync());
}
@ -114,9 +117,9 @@ namespace Squidex.Infrastructure.States
SetupEventStore(3, 4, 3);
var persistedState = (object)null;
var persistedState = -1;
var persistedEvents = new List<IEvent>();
var persistence = sut.WithSnapshotsAndEventSourcing<object, object, string>(key, x => persistedState = x, x => persistedEvents.Add(x.Payload));
var persistence = sut.WithSnapshotsAndEventSourcing(None.Type, key, (int x) => persistedState = x, x => persistedEvents.Add(x.Payload));
await Assert.ThrowsAsync<InvalidOperationException>(() => persistence.ReadAsync());
}
@ -127,7 +130,7 @@ namespace Squidex.Infrastructure.States
SetupEventStore(0);
var persistedEvents = new List<IEvent>();
var persistence = sut.WithEventSourcing<object, string>(key, x => persistedEvents.Add(x.Payload));
var persistence = sut.WithEventSourcing(None.Type, key, x => persistedEvents.Add(x.Payload));
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => persistence.ReadAsync(1));
}
@ -138,7 +141,7 @@ namespace Squidex.Infrastructure.States
SetupEventStore(3);
var persistedEvents = new List<IEvent>();
var persistence = sut.WithEventSourcing<object, string>(key, x => persistedEvents.Add(x.Payload));
var persistence = sut.WithEventSourcing(None.Type, key, x => persistedEvents.Add(x.Payload));
await Assert.ThrowsAsync<DomainObjectVersionException>(() => persistence.ReadAsync(1));
}
@ -151,9 +154,9 @@ namespace Squidex.Infrastructure.States
SetupEventStore(0);
var persistedState = (object)null;
var persistedState = -1;
var persistedEvents = new List<IEvent>();
var persistence = sut.WithSnapshotsAndEventSourcing<object, object, string>(key, x => persistedState = x, x => persistedEvents.Add(x.Payload));
var persistence = sut.WithSnapshotsAndEventSourcing(None.Type, key, (int x) => persistedState = x, x => persistedEvents.Add(x.Payload));
await Assert.ThrowsAsync<DomainObjectVersionException>(() => persistence.ReadAsync(1));
}
@ -163,9 +166,9 @@ namespace Squidex.Infrastructure.States
{
SetupEventStore(0);
var persistedState = (object)null;
var persistedState = -1;
var persistedEvents = new List<IEvent>();
var persistence = sut.WithSnapshotsAndEventSourcing<object, object, string>(key, x => persistedState = x, x => persistedEvents.Add(x.Payload));
var persistence = sut.WithSnapshotsAndEventSourcing(None.Type, key, (int x) => persistedState = x, x => persistedEvents.Add(x.Payload));
await persistence.ReadAsync();
}
@ -176,7 +179,7 @@ namespace Squidex.Infrastructure.States
SetupEventStore(3);
var persistedEvents = new List<IEvent>();
var persistence = sut.WithEventSourcing<object, string>(key, x => persistedEvents.Add(x.Payload));
var persistence = sut.WithEventSourcing(None.Type, key, x => persistedEvents.Add(x.Payload));
await persistence.ReadAsync();
@ -195,7 +198,7 @@ namespace Squidex.Infrastructure.States
SetupEventStore(3);
var persistedEvents = new List<IEvent>();
var persistence = sut.WithEventSourcing<object, string>(key, x => persistedEvents.Add(x.Payload));
var persistence = sut.WithEventSourcing(None.Type, key, x => persistedEvents.Add(x.Payload));
await persistence.ReadAsync();
@ -208,7 +211,7 @@ namespace Squidex.Infrastructure.States
[Fact]
public async Task Should_delete_events_but_not_snapshot_when_deleted_snapshot_only()
{
var persistence = sut.WithEventSourcing<object, string>(key, x => { });
var persistence = sut.WithEventSourcing(None.Type, key, null);
await persistence.DeleteAsync();
@ -222,7 +225,7 @@ namespace Squidex.Infrastructure.States
[Fact]
public async Task Should_delete_events_and_snapshot_when_deleted()
{
var persistence = sut.WithSnapshotsAndEventSourcing<object, object, string>(key, x => { }, x => { });
var persistence = sut.WithSnapshotsAndEventSourcing<int>(None.Type, key, null, null);
await persistence.DeleteAsync();

17
tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs

@ -40,7 +40,7 @@ namespace Squidex.Infrastructure.States
.Returns((20, 10));
var persistedState = 0;
var persistence = sut.WithSnapshots<object, int, string>(key, x => persistedState = x);
var persistence = sut.WithSnapshots(None.Type, key, (int x) => persistedState = x);
await persistence.ReadAsync();
@ -55,7 +55,7 @@ namespace Squidex.Infrastructure.States
.Returns((20, -10));
var persistedState = 0;
var persistence = sut.WithSnapshots<object, int, string>(key, x => persistedState = x);
var persistence = sut.WithSnapshots(None.Type, key, (int x) => persistedState = x);
await persistence.ReadAsync();
@ -69,7 +69,7 @@ namespace Squidex.Infrastructure.States
.Returns((20, EtagVersion.Empty));
var persistedState = 0;
var persistence = sut.WithSnapshots<object, int, string>(key, x => persistedState = x);
var persistence = sut.WithSnapshots(None.Type, key, (int x) => persistedState = x);
await persistence.ReadAsync();
@ -84,7 +84,7 @@ namespace Squidex.Infrastructure.States
.Returns((123, EtagVersion.Empty));
var persistedState = 0;
var persistence = sut.WithSnapshots<object, int, string>(key, x => persistedState = x);
var persistence = sut.WithSnapshots(None.Type, key, (int x) => persistedState = x);
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => persistence.ReadAsync(1));
}
@ -96,7 +96,7 @@ namespace Squidex.Infrastructure.States
.Returns((123, 2));
var persistedState = 0;
var persistence = sut.WithSnapshots<object, int, string>(key, x => persistedState = x);
var persistence = sut.WithSnapshots(None.Type, key, (int x) => persistedState = x);
await Assert.ThrowsAsync<DomainObjectVersionException>(() => persistence.ReadAsync(1));
}
@ -108,7 +108,7 @@ namespace Squidex.Infrastructure.States
.Returns((20, 10));
var persistedState = 0;
var persistence = sut.WithSnapshots<object, int, string>(key, x => persistedState = x);
var persistence = sut.WithSnapshots(None.Type, key, (int x) => persistedState = x);
await persistence.ReadAsync();
@ -131,7 +131,7 @@ namespace Squidex.Infrastructure.States
.Throws(new InconsistentStateException(1, 1, new InvalidOperationException()));
var persistedState = 0;
var persistence = sut.WithSnapshots<object, int, string>(key, x => persistedState = x);
var persistence = sut.WithSnapshots(None.Type, key, (int x) => persistedState = x);
await persistence.ReadAsync();
@ -141,7 +141,8 @@ namespace Squidex.Infrastructure.States
[Fact]
public async Task Should_delete_snapshot_but_not_events_when_deleted()
{
var persistence = sut.WithSnapshots<object, int, string>(key, x => { });
var persistedState = 0;
var persistence = sut.WithSnapshots(None.Type, key, (int x) => persistedState = x);
await persistence.DeleteAsync();

2
tools/Migrate_01/Rebuilder.cs

@ -126,7 +126,7 @@ namespace Migrate_01
Version = EtagVersion.Empty
};
var persistence = store.WithSnapshotsAndEventSourcing<TState, Guid>(typeof(TGrain), key, s => state = s, e =>
var persistence = store.WithSnapshotsAndEventSourcing(typeof(TGrain), key, (TState s) => state = s, e =>
{
state = func(e, state);

Loading…
Cancel
Save