From 46f081f60f1d4491294f4ded7593c4a650134735 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Fri, 11 Jan 2019 20:29:06 +0100 Subject: [PATCH] Usage trigger tests and improvements to persistence system. --- .../HandleRules/IRuleTriggerHandler.cs | 5 +- .../HandleRules/RuleService.cs | 4 +- .../HandleRules/RuleTriggerHandler.cs | 15 +-- .../Triggers/AssetChangedTriggerHandler.cs | 4 +- .../Triggers/ContentChangedTriggerHandler.cs | 18 ++- .../Triggers/UsageTriggerHandler.cs | 23 ++++ .../Backup/BackupHandlerWithStore.cs | 2 +- .../Comments/CommentsGrain.cs | 2 +- .../Rules/UsageTracking/UsageTrackerGrain.cs | 48 ++----- .../UsageTracking/UsageTriggerHandler.cs | 120 +++++++++--------- .../Commands/DomainObjectGrain.cs | 2 +- .../Commands/LogSnapshotDomainObjectGrain.cs | 2 +- .../Grains/EventConsumerGrain.cs | 43 +++---- src/Squidex.Infrastructure/None.cs | 20 +++ .../Orleans/GrainOfGuid{T}.cs | 2 +- .../Orleans/GrainOfString{T}.cs | 2 +- .../States/IPersistence.cs | 2 +- src/Squidex.Infrastructure/States/IStore.cs | 11 +- .../States/Persistence.cs | 7 +- .../States/Persistence{TSnapshot,TKey}.cs | 14 +- src/Squidex.Infrastructure/States/Store.cs | 15 ++- .../States/StoreExtensions.cs | 63 ++------- src/Squidex/Config/Domain/RuleServices.cs | 3 + .../HandleRules/RuleServiceTests.cs | 10 +- .../Triggers/AssetChangedTriggerTests.cs | 41 +++--- .../Triggers/ContentChangedTriggerTests.cs | 77 ++++++----- .../Triggers/UsageTriggerHandlerTests.cs | 54 ++++++++ .../Apps/AppUISettingsGrainTests.cs | 2 +- .../Apps/Indexes/AppsByNameIndexGrainTests.cs | 2 +- .../Apps/Indexes/AppsByUserIndexGrainTests.cs | 5 +- .../Indexes/RulesByAppIndexGrainTests.cs | 5 +- .../Indexes/SchemasByAppIndexGrainTests.cs | 5 +- .../Tags/TagGrainTests.cs | 5 +- .../TestHelpers/HandlerTestBase.cs | 5 +- .../Commands/DomainObjectGrainTests.cs | 2 +- .../LogSnapshotDomainObjectGrainTests.cs | 2 +- .../Grains/EventConsumerGrainTests.cs | 6 +- .../Orleans/GrainOfGuidTests.cs | 101 +++++++++++++++ .../Orleans/GrainOfStringTests.cs | 101 +++++++++++++++ .../States/PersistenceEventSourcingTests.cs | 45 ++++--- .../States/PersistenceSnapshotTests.cs | 17 +-- tools/Migrate_01/Rebuilder.cs | 2 +- 42 files changed, 576 insertions(+), 338 deletions(-) create mode 100644 src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/UsageTriggerHandler.cs create mode 100644 src/Squidex.Infrastructure/None.cs create mode 100644 tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/UsageTriggerHandlerTests.cs create mode 100644 tests/Squidex.Infrastructure.Tests/Orleans/GrainOfGuidTests.cs create mode 100644 tests/Squidex.Infrastructure.Tests/Orleans/GrainOfStringTests.cs diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleTriggerHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleTriggerHandler.cs index 31d786afb..023d0908d 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleTriggerHandler.cs +++ b/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 TriggersAsync(EnrichedEvent @event, RuleTrigger trigger); + bool Trigger(EnrichedEvent @event, RuleTrigger trigger); - Task TriggersAsync(IEvent @event, RuleTrigger trigger); + bool Trigger(IEvent @event, RuleTrigger trigger); } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs index 71f613ff7..14db51bf2 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs +++ b/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; } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTriggerHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTriggerHandler.cs index 48bcaa262..334e759d1 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTriggerHandler.cs +++ b/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 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 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 TriggersAsync(TEnrichedEvent @event, TTrigger trigger); + protected abstract bool Trigger(TEnrichedEvent @event, TTrigger trigger); - protected virtual Task TriggersAsync(TEvent @event, TTrigger trigger) + protected virtual bool Trigger(TEvent @event, TTrigger trigger) { - return TaskHelper.True; + return true; } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/AssetChangedTriggerHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/AssetChangedTriggerHandler.cs index 7ec57b881..8daa7db20 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/AssetChangedTriggerHandler.cs +++ b/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 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); } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/ContentChangedTriggerHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/ContentChangedTriggerHandler.cs index e8210cbe1..21bc4124c 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/ContentChangedTriggerHandler.cs +++ b/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 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 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 eventId) diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/UsageTriggerHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/UsageTriggerHandler.cs new file mode 100644 index 000000000..f3bb0965f --- /dev/null +++ b/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 + { + protected override bool Trigger(EnrichedUsageExceededEvent @event, UsageTrigger trigger) + { + return true; + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Backup/BackupHandlerWithStore.cs b/src/Squidex.Domain.Apps.Entities/Backup/BackupHandlerWithStore.cs index d6a2eba0d..3c2cb4354 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/BackupHandlerWithStore.cs +++ b/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(typeof(TGrain), key, s => state = s, e => + var persistence = store.WithSnapshotsAndEventSourcing(typeof(TGrain), key, (TState s) => state = s, e => { state = func(e, state); diff --git a/src/Squidex.Domain.Apps.Entities/Comments/CommentsGrain.cs b/src/Squidex.Domain.Apps.Entities/Comments/CommentsGrain.cs index 7e20027f8..e95b25248 100644 --- a/src/Squidex.Domain.Apps.Entities/Comments/CommentsGrain.cs +++ b/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(GetType(), id, ApplyEvent); + persistence = store.WithEventSourcing(GetType(), id, ApplyEvent); return persistence.ReadAsync(); } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs b/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs index 886aea2e6..d1607d097 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs +++ b/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, IRemindable, IUsageTrackerGrain { - private readonly IStore store; private readonly IUsageTracker usageTracker; - private IPersistence 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, Target> Targets { get; set; } = new Dictionary, Target>(); } public UsageTrackerGrain(IStore 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(GetType(), key, ApplySnapshot, ApplyEvent); - - return persistence.ReadAsync(); - } - - private void ApplySnapshot(State s) - { - state = s; - } - - private void ApplyEvent(Envelope @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 appId) { UpdateTarget(appId, t => t.Enabled = false); - return persistence.WriteSnapshotAsync(state); + return WriteStateAsync(); } public Task AddTargetAsync(NamedId appId, int limits) { UpdateTarget(appId, t => t.Limit = limits); - return persistence.WriteSnapshotAsync(state); + return WriteStateAsync(); } public Task RemoveTargetAsync(NamedId appId) { - state.Targets.Remove(appId); + State.Targets.Remove(appId); - return persistence.WriteSnapshotAsync(state); + return WriteStateAsync(); } private void UpdateTarget(NamedId appId, Action updater) { - updater(state.Targets.GetOrAddNew(appId));; + updater(State.Targets.GetOrAddNew(appId));; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTriggerHandler.cs b/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTriggerHandler.cs index a690b06ce..c33e903a8 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTriggerHandler.cs +++ b/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 - { - private readonly IUsageTrackerGrain usageTrackerGrain; +//namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking +//{ +// public sealed class UsageTriggerHandler : RuleTriggerHandler +// { +// 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(SingleGrain.Id); - } +// usageTrackerGrain = grainFactory.GetGrain(SingleGrain.Id); +// } - protected override async Task 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 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 TriggersAsync(EnrichedUsageExceededEvent @event, UsageTrigger trigger) - { - return TaskHelper.True; - } - } -} +// protected override Task Trigger(EnrichedUsageExceededEvent @event, UsageTrigger trigger) +// { +// return TaskHelper.True; +// } +// } +//} diff --git a/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs b/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs index a8a81df4b..16da5a476 100644 --- a/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs +++ b/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(GetType(), id, ApplySnapshot, ApplyEvent); + persistence = store.WithSnapshotsAndEventSourcing(GetType(), id, new HandleSnapshot(ApplySnapshot), ApplyEvent); return persistence.ReadAsync(); } diff --git a/src/Squidex.Infrastructure/Commands/LogSnapshotDomainObjectGrain.cs b/src/Squidex.Infrastructure/Commands/LogSnapshotDomainObjectGrain.cs index 417b4f1e3..cece08391 100644 --- a/src/Squidex.Infrastructure/Commands/LogSnapshotDomainObjectGrain.cs +++ b/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(type, id, ApplyEvent); + persistence = store.WithEventSourcing(type, id, ApplyEvent); return persistence.ReadAsync(); } diff --git a/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs b/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs index 4c916683a..51e584d12 100644 --- a/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs +++ b/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, IEventConsumerGrain { private readonly EventConsumerFactory eventConsumerFactory; - private readonly IStore store; private readonly IEventDataFormatter eventDataFormatter; private readonly IEventStore eventStore; private readonly ISemanticLog log; private TaskScheduler scheduler; - private IPersistence 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(GetType(), eventConsumer.Name, s => state = s); - - return persistence.ReadAsync(); + return Task.CompletedTask; } public Task> GetStateAsync() { - return Task.FromResult(state.ToInfo(eventConsumer.Name).AsImmutable()); + return Task.FromResult(State.ToInfo(eventConsumer.Name).AsImmutable()); } public Task OnEventAsync(Immutable subscription, Immutable 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() diff --git a/src/Squidex.Infrastructure/None.cs b/src/Squidex.Infrastructure/None.cs new file mode 100644 index 000000000..76fe93d4a --- /dev/null +++ b/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() + { + } + } +} diff --git a/src/Squidex.Infrastructure/Orleans/GrainOfGuid{T}.cs b/src/Squidex.Infrastructure/Orleans/GrainOfGuid{T}.cs index 315130710..87dd5724e 100644 --- a/src/Squidex.Infrastructure/Orleans/GrainOfGuid{T}.cs +++ b/src/Squidex.Infrastructure/Orleans/GrainOfGuid{T}.cs @@ -43,7 +43,7 @@ namespace Squidex.Infrastructure.Orleans { Key = key; - persistence = store.WithSnapshots(GetType(), key, ApplyState); + persistence = store.WithSnapshots(GetType(), key, new HandleSnapshot(ApplyState)); await persistence.ReadAsync(); diff --git a/src/Squidex.Infrastructure/Orleans/GrainOfString{T}.cs b/src/Squidex.Infrastructure/Orleans/GrainOfString{T}.cs index bbbd61b4e..241493c64 100644 --- a/src/Squidex.Infrastructure/Orleans/GrainOfString{T}.cs +++ b/src/Squidex.Infrastructure/Orleans/GrainOfString{T}.cs @@ -42,7 +42,7 @@ namespace Squidex.Infrastructure.Orleans { Key = key; - persistence = store.WithSnapshots(GetType(), key, ApplyState); + persistence = store.WithSnapshots(GetType(), key, new HandleSnapshot(ApplyState)); await persistence.ReadAsync(); diff --git a/src/Squidex.Infrastructure/States/IPersistence.cs b/src/Squidex.Infrastructure/States/IPersistence.cs index 523a9dd0b..e07b75b38 100644 --- a/src/Squidex.Infrastructure/States/IPersistence.cs +++ b/src/Squidex.Infrastructure/States/IPersistence.cs @@ -7,7 +7,7 @@ namespace Squidex.Infrastructure.States { - public interface IPersistence : IPersistence + public interface IPersistence : IPersistence { } } diff --git a/src/Squidex.Infrastructure/States/IStore.cs b/src/Squidex.Infrastructure/States/IStore.cs index 28c44fd78..e94d94558 100644 --- a/src/Squidex.Infrastructure/States/IStore.cs +++ b/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 @event); + + public delegate void HandleSnapshot(T state); + public interface IStore { - IPersistence WithEventSourcing(Type owner, TKey key, Func, Task> applyEvent); + IPersistence WithEventSourcing(Type owner, TKey key, HandleEvent applyEvent); - IPersistence WithSnapshots(Type owner, TKey key, Func applySnapshot); + IPersistence WithSnapshots(Type owner, TKey key, HandleSnapshot applySnapshot); - IPersistence WithSnapshotsAndEventSourcing(Type owner, TKey key, Func applySnapshot, Func, Task> applyEvent); + IPersistence WithSnapshotsAndEventSourcing(Type owner, TKey key, HandleSnapshot applySnapshot, HandleEvent applyEvent); ISnapshotStore GetSnapshotStore(); } diff --git a/src/Squidex.Infrastructure/States/Persistence.cs b/src/Squidex.Infrastructure/States/Persistence.cs index 5c0d79037..edf253026 100644 --- a/src/Squidex.Infrastructure/States/Persistence.cs +++ b/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 : Persistence, IPersistence + internal sealed class Persistence : Persistence, IPersistence { public Persistence(TKey ownerKey, Type ownerType, IEventStore eventStore, IEventDataFormatter eventDataFormatter, - ISnapshotStore snapshotStore, + ISnapshotStore snapshotStore, IStreamNameResolver streamNameResolver, - Func, Task> applyEvent) + HandleEvent applyEvent) : base(ownerKey, ownerType, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, PersistenceMode.EventSourcing, null, applyEvent) { } diff --git a/src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs b/src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs index 5b7f103e7..48a30f22c 100644 --- a/src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs +++ b/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 applyState; - private readonly Func, Task> applyEvent; + private readonly HandleSnapshot 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 snapshotStore, IStreamNameResolver streamNameResolver, PersistenceMode persistenceMode, - Func applyState, - Func, Task> applyEvent) + HandleSnapshot 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); } } } diff --git a/src/Squidex.Infrastructure/States/Store.cs b/src/Squidex.Infrastructure/States/Store.cs index 3bbacc36d..6d5e77772 100644 --- a/src/Squidex.Infrastructure/States/Store.cs +++ b/src/Squidex.Infrastructure/States/Store.cs @@ -30,26 +30,31 @@ namespace Squidex.Infrastructure.States this.streamNameResolver = streamNameResolver; } - public IPersistence WithSnapshots(Type owner, TKey key, Func applySnapshot) + public IPersistence WithEventSourcing(Type owner, TKey key, HandleEvent applyEvent) + { + return CreatePersistence(owner, key, applyEvent); + } + + public IPersistence WithSnapshots(Type owner, TKey key, HandleSnapshot applySnapshot) { return CreatePersistence(owner, key, PersistenceMode.Snapshots, applySnapshot, null); } - public IPersistence WithSnapshotsAndEventSourcing(Type owner, TKey key, Func applySnapshot, Func, Task> applyEvent) + public IPersistence WithSnapshotsAndEventSourcing(Type owner, TKey key, HandleSnapshot applySnapshot, HandleEvent applyEvent) { return CreatePersistence(owner, key, PersistenceMode.SnapshotsAndEventSourcing, applySnapshot, applyEvent); } - public IPersistence WithEventSourcing(Type owner, TKey key, Func, Task> applyEvent) + private IPersistence CreatePersistence(Type owner, TKey key, HandleEvent applyEvent) { Guard.NotNull(key, nameof(key)); - var snapshotStore = GetSnapshotStore(); + var snapshotStore = GetSnapshotStore(); return new Persistence(key, owner, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, applyEvent); } - private IPersistence CreatePersistence(Type owner, TKey key, PersistenceMode mode, Func applySnapshot, Func, Task> applyEvent) + private IPersistence CreatePersistence(Type owner, TKey key, PersistenceMode mode, HandleSnapshot applySnapshot, HandleEvent applyEvent) { Guard.NotNull(key, nameof(key)); diff --git a/src/Squidex.Infrastructure/States/StoreExtensions.cs b/src/Squidex.Infrastructure/States/StoreExtensions.cs index 1c82d57dd..789c94ce1 100644 --- a/src/Squidex.Infrastructure/States/StoreExtensions.cs +++ b/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(this IStore store, TKey key, Func, Task> applyEvent) - { - return store.WithEventSourcing(typeof(TOwner), key, applyEvent); - } - - public static IPersistence WithSnapshots(this IStore store, TKey key, Func applySnapshot) - { - return store.WithSnapshots(typeof(TOwner), key, applySnapshot); - } - - public static IPersistence WithSnapshotsAndEventSourcing(this IStore store, TKey key, Func applySnapshot, Func, Task> applyEvent) - { - return store.WithSnapshotsAndEventSourcing(typeof(TOwner), key, applySnapshot, applyEvent); - } - - public static IPersistence WithEventSourcing(this IStore store, Type owner, TKey key, Action> applyEvent) - { - return store.WithEventSourcing(owner, key, applyEvent.ToAsync()); - } - - public static IPersistence WithSnapshots(this IStore store, Type owner, TKey key, Action applySnapshot) - { - return store.WithSnapshots(owner, key, applySnapshot.ToAsync()); - } - - public static IPersistence WithSnapshotsAndEventSourcing(this IStore store, Type owner, TKey key, Action applySnapshot, Action> applyEvent) - { - return store.WithSnapshotsAndEventSourcing(owner, key, applySnapshot.ToAsync(), applyEvent.ToAsync()); - } - - public static IPersistence WithEventSourcing(this IStore store, TKey key, Action> applyEvent) - { - return store.WithEventSourcing(typeof(TOwner), key, applyEvent.ToAsync()); - } - - public static IPersistence WithSnapshots(this IStore store, TKey key, Action applySnapshot) - { - return store.WithSnapshots(typeof(TOwner), key, applySnapshot.ToAsync()); - } - - public static IPersistence WithSnapshotsAndEventSourcing(this IStore store, TKey key, Action applySnapshot, Action> applyEvent) - { - return store.WithSnapshotsAndEventSourcing(typeof(TOwner), key, applySnapshot.ToAsync(), applyEvent.ToAsync()); - } - - public static Task WriteEventAsync(IPersistence persistence, Envelope @event) + public static Task WriteEventAsync(this IPersistence persistence, Envelope @event) { return persistence.WriteEventsAsync(new[] { @event }); } - public static Task WriteEventAsync(IPersistence persistence, IEvent @event) + public static Task WriteEventAsync(this IPersistence persistence, IEvent @event) { return persistence.WriteEventsAsync(new[] { Envelope.Create(@event) }); } - public static Task ClearSnapshotsAsync(this IStore store) + public static Task ClearSnapshotsAsync(this IStore store) { - return store.GetSnapshotStore().ClearAsync(); + return store.GetSnapshotStore().ClearAsync(); } - public static Task RemoveSnapshotAsync(this IStore store, TKey key) + public static Task RemoveSnapshotAsync(this IStore store, TKey key) { - return store.GetSnapshotStore().RemoveAsync(key); + return store.GetSnapshotStore().RemoveAsync(key); } - public static async Task GetSnapshotAsync(this IStore store, TKey key) + public static async Task GetSnapshotAsync(this IStore store, TKey key) { - var result = await store.GetSnapshotStore().ReadAsync(key); + var result = await store.GetSnapshotStore().ReadAsync(key); return result.Value; } diff --git a/src/Squidex/Config/Domain/RuleServices.cs b/src/Squidex/Config/Domain/RuleServices.cs index 517cccd33..0b30fda37 100644 --- a/src/Squidex/Config/Domain/RuleServices.cs +++ b/src/Squidex/Config/Domain/RuleServices.cs @@ -27,6 +27,9 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .As(); + services.AddSingletonAs() + .As(); + services.AddSingletonAs() .As(); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs index 1b9229222..d69dddfcc 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs +++ b/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.Ignored, ruleConfig.Trigger)) + A.CallTo(() => ruleTriggerHandler.Trigger(A.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.Ignored, ruleConfig.Trigger)) + A.CallTo(() => ruleTriggerHandler.Trigger(A.Ignored, ruleConfig.Trigger)) .Returns(true); - A.CallTo(() => ruleTriggerHandler.TriggersAsync(A.Ignored, ruleConfig.Trigger)) + A.CallTo(() => ruleTriggerHandler.Trigger(A.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.Ignored, ruleConfig.Trigger)) + A.CallTo(() => ruleTriggerHandler.Trigger(A.Ignored, ruleConfig.Trigger)) .Returns(true); - A.CallTo(() => ruleTriggerHandler.TriggersAsync(A.Ignored, ruleConfig.Trigger)) + A.CallTo(() => ruleTriggerHandler.Trigger(A.Ignored, ruleConfig.Trigger)) .Returns(true); A.CallTo(() => ruleActionHandler.CreateJobAsync(A.Ignored, ruleConfig.Action)) diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/AssetChangedTriggerTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/AssetChangedTriggerTests.cs index 4a82ec138..4bc0ff28f 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/AssetChangedTriggerTests.cs +++ b/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 action) + private void TestForCondition(string condition, Action action) { var trigger = new AssetChangedTriggerV2 { Condition = condition }; - await action(trigger); + action(trigger); if (string.IsNullOrWhiteSpace(condition)) { diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs index e363a418b..a9e5bc937 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs +++ b/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 schemaId, string condition, Func action) + private void TestForTrigger(bool handleAll, NamedId schemaId, string condition, Action 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)) { diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/UsageTriggerHandlerTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/UsageTriggerHandlerTests.cs new file mode 100644 index 000000000..b3eb86013 --- /dev/null +++ b/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); + } + } +} diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs index 9935f7ebb..7859d6107 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs +++ b/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.Ignored, A.Ignored, A>.Ignored)) + A.CallTo(() => store.WithSnapshots(typeof(AppUISettingsGrain), Guid.Empty, A>.Ignored)) .Returns(persistence); sut = new AppUISettingsGrain(store); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByNameIndexGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByNameIndexGrainTests.cs index e3092cf06..f5cf63deb 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByNameIndexGrainTests.cs +++ b/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.Ignored, A.Ignored, A>.Ignored)) + A.CallTo(() => store.WithSnapshots(typeof(AppsByNameIndexGrain), SingleGrain.Id, A>.Ignored)) .Returns(persistence); sut = new AppsByNameIndexGrain(store); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByUserIndexGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByUserIndexGrainTests.cs index 78ca858e3..7bb9f1bad 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByUserIndexGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsByUserIndexGrainTests.cs @@ -21,15 +21,16 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes private readonly IPersistence persistence = A.Fake>(); 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.Ignored, A.Ignored, A>.Ignored)) + A.CallTo(() => store.WithSnapshots(typeof(AppsByUserIndexGrain), userId, A>.Ignored)) .Returns(persistence); sut = new AppsByUserIndexGrain(store); - sut.ActivateAsync("user").Wait(); + sut.ActivateAsync(userId).Wait(); } [Fact] diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesByAppIndexGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesByAppIndexGrainTests.cs index db4558e2f..9d252b869 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesByAppIndexGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesByAppIndexGrainTests.cs @@ -18,17 +18,18 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes { private readonly IStore store = A.Fake>(); private readonly IPersistence persistence = A.Fake>(); + 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.Ignored, A.Ignored, A>.Ignored)) + A.CallTo(() => store.WithSnapshots(typeof(RulesByAppIndexGrain), appId, A>.Ignored)) .Returns(persistence); sut = new RulesByAppIndexGrain(store); - sut.ActivateAsync(Guid.NewGuid()).Wait(); + sut.ActivateAsync(appId).Wait(); } [Fact] diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasByAppIndexGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasByAppIndexGrainTests.cs index fcb735274..5ee8bb093 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasByAppIndexGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasByAppIndexGrainTests.cs @@ -18,6 +18,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes { private readonly IStore store = A.Fake>(); private readonly IPersistence persistence = A.Fake>(); + 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.Ignored, A.Ignored, A>.Ignored)) + A.CallTo(() => store.WithSnapshots(typeof(SchemasByAppIndexGrain), appId, A>.Ignored)) .Returns(persistence); sut = new SchemasByAppIndexGrain(store); - sut.ActivateAsync(Guid.NewGuid()).Wait(); + sut.ActivateAsync(appId).Wait(); } [Fact] diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs index dfcd5ad9d..9642dd244 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs @@ -20,15 +20,16 @@ namespace Squidex.Domain.Apps.Entities.Tags { private readonly IStore store = A.Fake>(); private readonly IPersistence persistence = A.Fake>(); + private readonly string id = Guid.NewGuid().ToString(); private readonly TagGrain sut; public TagGrainTests() { - A.CallTo(() => store.WithSnapshots(A.Ignored, A.Ignored, A>.Ignored)) + A.CallTo(() => store.WithSnapshots(typeof(TagGrain), id, A>.Ignored)) .Returns(persistence); sut = new TagGrain(store); - sut.ActivateAsync(string.Empty).Wait(); + sut.ActivateAsync(id).Wait(); } [Fact] diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs b/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs index af1f88032..b48fb4f24 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs +++ b/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.Ignored, Id, A>.Ignored, A, Task>>.Ignored)) + A.CallTo(() => store.WithSnapshotsAndEventSourcing(A.Ignored, Id, A>.Ignored, A.Ignored)) .Returns(persistence1); - A.CallTo(() => store.WithEventSourcing(A.Ignored, Id, A, Task>>.Ignored)) + A.CallTo(() => store.WithEventSourcing(A.Ignored, Id, A.Ignored)) .Returns(persistence2); A.CallTo(() => persistence1.WriteEventsAsync(A>>.Ignored)) diff --git a/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs b/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs index 91996181d..4810221d3 100644 --- a/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs +++ b/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>.Ignored, A, Task>>.Ignored)) + A.CallTo(() => store.WithSnapshotsAndEventSourcing(typeof(MyDomainObject), id, A>.Ignored, A.Ignored)) .Returns(persistence); sut = new MyDomainObject(store); diff --git a/tests/Squidex.Infrastructure.Tests/Commands/LogSnapshotDomainObjectGrainTests.cs b/tests/Squidex.Infrastructure.Tests/Commands/LogSnapshotDomainObjectGrainTests.cs index 71d509a1e..68ed4cf06 100644 --- a/tests/Squidex.Infrastructure.Tests/Commands/LogSnapshotDomainObjectGrainTests.cs +++ b/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, Task>>.Ignored)) + A.CallTo(() => store.WithEventSourcing(typeof(MyDomainObject), id, A.Ignored)) .Returns(persistence); A.CallTo(() => store.GetSnapshotStore()) diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs index 9d4c9cc0e..d1423f98c 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs +++ b/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 apply; + private HandleSnapshot 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.Ignored, consumerName, A>.Ignored)) - .Invokes(new Action>((t, key, a) => + A.CallTo(() => store.WithSnapshots(A.Ignored, consumerName, A>.Ignored)) + .Invokes(new Action>((t, key, a) => { apply = a; })) diff --git a/tests/Squidex.Infrastructure.Tests/Orleans/GrainOfGuidTests.cs b/tests/Squidex.Infrastructure.Tests/Orleans/GrainOfGuidTests.cs new file mode 100644 index 000000000..4a12b4c94 --- /dev/null +++ b/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 persistence = A.Fake>(); + private readonly IStore store = A.Fake>(); + private readonly Guid id = Guid.NewGuid(); + private readonly MyGrain sut; + private HandleSnapshot read; + + public sealed class MyGrain : GrainOfGuid + { + public sealed class GrainState + { + public Guid Id { get; set; } + } + + public GrainState PublicState + { + get { return State; } + } + + public MyGrain(IStore 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>.Ignored)) + .Invokes(new Action>((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(); + } + } +} diff --git a/tests/Squidex.Infrastructure.Tests/Orleans/GrainOfStringTests.cs b/tests/Squidex.Infrastructure.Tests/Orleans/GrainOfStringTests.cs new file mode 100644 index 000000000..40e3b6a4c --- /dev/null +++ b/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 persistence = A.Fake>(); + private readonly IStore store = A.Fake>(); + private readonly string id = Guid.NewGuid().ToString(); + private readonly MyGrain sut; + private HandleSnapshot read; + + public sealed class MyGrain : GrainOfString + { + public sealed class GrainState + { + public string Id { get; set; } + } + + public GrainState PublicState + { + get { return State; } + } + + public MyGrain(IStore 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>.Ignored)) + .Invokes(new Action>((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(); + } + } +} diff --git a/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs b/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs index 72e37048b..686a57c95 100644 --- a/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs +++ b/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs @@ -22,16 +22,19 @@ namespace Squidex.Infrastructure.States private readonly IEventDataFormatter eventDataFormatter = A.Fake(); private readonly IEventStore eventStore = A.Fake(); private readonly IServiceProvider services = A.Fake(); - private readonly ISnapshotStore snapshotStore = A.Fake>(); + private readonly ISnapshotStore snapshotStore = A.Fake>(); + private readonly ISnapshotStore snapshotStoreNone = A.Fake>(); private readonly IStreamNameResolver streamNameResolver = A.Fake(); private readonly IStore sut; public PersistenceEventSourcingTests() { - A.CallTo(() => services.GetService(typeof(ISnapshotStore))) + A.CallTo(() => services.GetService(typeof(ISnapshotStore))) .Returns(snapshotStore); + A.CallTo(() => services.GetService(typeof(ISnapshotStore))) + .Returns(snapshotStoreNone); - A.CallTo(() => streamNameResolver.GetStreamName(typeof(object), key)) + A.CallTo(() => streamNameResolver.GetStreamName(None.Type, key)) .Returns(key); sut = new Store(eventStore, eventDataFormatter, services, streamNameResolver); @@ -46,7 +49,7 @@ namespace Squidex.Infrastructure.States SetupEventStore(event1, event2); var persistedEvents = new List(); - var persistence = sut.WithEventSourcing(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(); - var persistence = sut.WithEventSourcing(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(); - var persistence = sut.WithSnapshotsAndEventSourcing(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(); - var persistence = sut.WithSnapshotsAndEventSourcing(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(() => 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(); - var persistence = sut.WithSnapshotsAndEventSourcing(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(() => persistence.ReadAsync()); } @@ -127,7 +130,7 @@ namespace Squidex.Infrastructure.States SetupEventStore(0); var persistedEvents = new List(); - var persistence = sut.WithEventSourcing(key, x => persistedEvents.Add(x.Payload)); + var persistence = sut.WithEventSourcing(None.Type, key, x => persistedEvents.Add(x.Payload)); await Assert.ThrowsAsync(() => persistence.ReadAsync(1)); } @@ -138,7 +141,7 @@ namespace Squidex.Infrastructure.States SetupEventStore(3); var persistedEvents = new List(); - var persistence = sut.WithEventSourcing(key, x => persistedEvents.Add(x.Payload)); + var persistence = sut.WithEventSourcing(None.Type, key, x => persistedEvents.Add(x.Payload)); await Assert.ThrowsAsync(() => persistence.ReadAsync(1)); } @@ -151,9 +154,9 @@ namespace Squidex.Infrastructure.States SetupEventStore(0); - var persistedState = (object)null; + var persistedState = -1; var persistedEvents = new List(); - var persistence = sut.WithSnapshotsAndEventSourcing(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(() => persistence.ReadAsync(1)); } @@ -163,9 +166,9 @@ namespace Squidex.Infrastructure.States { SetupEventStore(0); - var persistedState = (object)null; + var persistedState = -1; var persistedEvents = new List(); - var persistence = sut.WithSnapshotsAndEventSourcing(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(); - var persistence = sut.WithEventSourcing(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(); - var persistence = sut.WithEventSourcing(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(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(key, x => { }, x => { }); + var persistence = sut.WithSnapshotsAndEventSourcing(None.Type, key, null, null); await persistence.DeleteAsync(); diff --git a/tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs b/tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs index 06701c721..f23ce4d40 100644 --- a/tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs +++ b/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(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(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(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(key, x => persistedState = x); + var persistence = sut.WithSnapshots(None.Type, key, (int x) => persistedState = x); await Assert.ThrowsAsync(() => persistence.ReadAsync(1)); } @@ -96,7 +96,7 @@ namespace Squidex.Infrastructure.States .Returns((123, 2)); var persistedState = 0; - var persistence = sut.WithSnapshots(key, x => persistedState = x); + var persistence = sut.WithSnapshots(None.Type, key, (int x) => persistedState = x); await Assert.ThrowsAsync(() => persistence.ReadAsync(1)); } @@ -108,7 +108,7 @@ namespace Squidex.Infrastructure.States .Returns((20, 10)); var persistedState = 0; - var persistence = sut.WithSnapshots(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(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(key, x => { }); + var persistedState = 0; + var persistence = sut.WithSnapshots(None.Type, key, (int x) => persistedState = x); await persistence.DeleteAsync(); diff --git a/tools/Migrate_01/Rebuilder.cs b/tools/Migrate_01/Rebuilder.cs index 030257d8e..39fac9933 100644 --- a/tools/Migrate_01/Rebuilder.cs +++ b/tools/Migrate_01/Rebuilder.cs @@ -126,7 +126,7 @@ namespace Migrate_01 Version = EtagVersion.Empty }; - var persistence = store.WithSnapshotsAndEventSourcing(typeof(TGrain), key, s => state = s, e => + var persistence = store.WithSnapshotsAndEventSourcing(typeof(TGrain), key, (TState s) => state = s, e => { state = func(e, state);