From 48c8dd6f68e20ca0a476606c96b61073f7680057 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Fri, 5 Jan 2018 16:51:02 +0100 Subject: [PATCH] 1) Synchronization fixes 2) Stylecop.json settings added --- Squidex.ruleset | 2 +- .../Squidex.Domain.Apps.Core.Model.csproj | 3 + ...Squidex.Domain.Apps.Core.Operations.csproj | 3 + .../Squidex.Domain.Apps.Core.csproj | 3 + ...quidex.Domain.Apps.Entities.MongoDb.csproj | 3 + .../Squidex.Domain.Apps.Entities.csproj | 3 + .../Squidex.Domain.Apps.Events.csproj | 3 + .../Squidex.Domain.Users.MongoDb.csproj | 3 + .../Squidex.Domain.Users.csproj | 3 + .../Squidex.Infrastructure.Azure.csproj | 3 + .../EventSourcing/GetEventStore.cs | 2 +- ...quidex.Infrastructure.GetEventStore.csproj | 3 + .../Squidex.Infrastructure.GoogleCloud.csproj | 3 + .../Squidex.Infrastructure.MongoDb.csproj | 3 + .../Squidex.Infrastructure.RabbitMq.csproj | 3 + .../Squidex.Infrastructure.Redis.csproj | 3 + .../Commands/AggregateHandler.cs | 13 +- .../Squidex.Infrastructure.csproj | 3 + .../States/IStateFactory.cs | 4 + .../States/Persistence.cs | 6 +- .../Persistence{TOwner,TSnapshot,TKey}.cs | 82 ++++------ .../States/StateFactory.cs | 20 +-- src/Squidex.Infrastructure/States/Store.cs | 12 +- src/Squidex.Shared/Squidex.Shared.csproj | 3 + src/Squidex/AppConfiguration.cs | 5 +- src/Squidex/Squidex.csproj | 4 + stylecop.json | 16 ++ .../Squidex.Domain.Apps.Core.Tests.csproj | 3 + .../Squidex.Domain.Apps.Entities.Tests.csproj | 3 + .../Squidex.Domain.Users.Tests.csproj | 3 + .../Squidex.Infrastructure.Tests.csproj | 3 + ...ts.cs => PersistenceEventSourcingTests.cs} | 75 +-------- ...otTests.cs => PersistenceSnapshotTests.cs} | 79 +--------- .../States/StateFactoryTests.cs | 146 ++++++++++++++++++ .../GenerateLanguages.csproj | 3 + tools/Migrate_00/Migrate_00.csproj | 3 + tools/Migrate_01/Migrate_01.csproj | 3 + 37 files changed, 307 insertions(+), 228 deletions(-) create mode 100644 stylecop.json rename tests/Squidex.Infrastructure.Tests/States/{StateEventSourcingTests.cs => PersistenceEventSourcingTests.cs} (83%) rename tests/Squidex.Infrastructure.Tests/States/{StateSnapshotTests.cs => PersistenceSnapshotTests.cs} (74%) create mode 100644 tests/Squidex.Infrastructure.Tests/States/StateFactoryTests.cs diff --git a/Squidex.ruleset b/Squidex.ruleset index ba0ffa583..6c96f8b6b 100644 --- a/Squidex.ruleset +++ b/Squidex.ruleset @@ -54,7 +54,7 @@ - + diff --git a/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj b/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj index 754774561..701e56aa9 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj +++ b/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj @@ -17,4 +17,7 @@ ..\..\Squidex.ruleset + + + diff --git a/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj b/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj index 10f9c17e4..d69665a81 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj +++ b/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj @@ -26,4 +26,7 @@ ..\..\Squidex.ruleset + + + diff --git a/src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj b/src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj index 721bd7392..b3c89d3b9 100644 --- a/src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj +++ b/src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj @@ -23,4 +23,7 @@ ..\..\Squidex.ruleset + + + diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj b/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj index ebaf89c62..bce4941ad 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj @@ -24,4 +24,7 @@ ..\..\Squidex.ruleset + + + diff --git a/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj b/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj index d273d2afb..740cccf99 100644 --- a/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj +++ b/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj @@ -22,4 +22,7 @@ ..\..\Squidex.ruleset + + + diff --git a/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj b/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj index de665699c..3fa2fa5db 100644 --- a/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj +++ b/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj @@ -22,4 +22,7 @@ ..\..\Squidex.ruleset + + + diff --git a/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj b/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj index 731890266..9d061d44c 100644 --- a/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj +++ b/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj @@ -24,4 +24,7 @@ ..\..\Squidex.ruleset + + + \ No newline at end of file diff --git a/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj b/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj index d0090e18f..96bbaab76 100644 --- a/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj +++ b/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj @@ -21,4 +21,7 @@ ..\..\Squidex.ruleset + + + \ No newline at end of file diff --git a/src/Squidex.Infrastructure.Azure/Squidex.Infrastructure.Azure.csproj b/src/Squidex.Infrastructure.Azure/Squidex.Infrastructure.Azure.csproj index 64cc95079..2a6b10e30 100644 --- a/src/Squidex.Infrastructure.Azure/Squidex.Infrastructure.Azure.csproj +++ b/src/Squidex.Infrastructure.Azure/Squidex.Infrastructure.Azure.csproj @@ -14,4 +14,7 @@ ..\..\Squidex.ruleset + + + \ No newline at end of file diff --git a/src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStore.cs b/src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStore.cs index 7f95c5e92..3ef632c78 100644 --- a/src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStore.cs +++ b/src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStore.cs @@ -72,7 +72,7 @@ namespace Squidex.Infrastructure.EventSourcing StreamEventsSlice currentSlice; do { - currentSlice = await connection.ReadStreamEventsForwardAsync(streamName, sliceStart, ReadPageSize, false); + currentSlice = await connection.ReadStreamEventsForwardAsync(streamName, sliceStart, ReadPageSize, true); if (currentSlice.Status == SliceReadStatus.Success) { diff --git a/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj b/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj index c10907d3a..9d576f112 100644 --- a/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj +++ b/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj @@ -18,4 +18,7 @@ ..\..\Squidex.ruleset + + + \ No newline at end of file diff --git a/src/Squidex.Infrastructure.GoogleCloud/Squidex.Infrastructure.GoogleCloud.csproj b/src/Squidex.Infrastructure.GoogleCloud/Squidex.Infrastructure.GoogleCloud.csproj index cce8551a3..23bbc623d 100644 --- a/src/Squidex.Infrastructure.GoogleCloud/Squidex.Infrastructure.GoogleCloud.csproj +++ b/src/Squidex.Infrastructure.GoogleCloud/Squidex.Infrastructure.GoogleCloud.csproj @@ -19,4 +19,7 @@ ..\..\Squidex.ruleset + + + \ No newline at end of file diff --git a/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj b/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj index 9578a77e0..5db5f7158 100644 --- a/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj +++ b/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj @@ -19,4 +19,7 @@ ..\..\Squidex.ruleset + + + diff --git a/src/Squidex.Infrastructure.RabbitMq/Squidex.Infrastructure.RabbitMq.csproj b/src/Squidex.Infrastructure.RabbitMq/Squidex.Infrastructure.RabbitMq.csproj index dd33ddd4f..8c7ffe98d 100644 --- a/src/Squidex.Infrastructure.RabbitMq/Squidex.Infrastructure.RabbitMq.csproj +++ b/src/Squidex.Infrastructure.RabbitMq/Squidex.Infrastructure.RabbitMq.csproj @@ -19,4 +19,7 @@ ..\..\Squidex.ruleset + + + \ No newline at end of file diff --git a/src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj b/src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj index 0a815a472..b811c6667 100644 --- a/src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj +++ b/src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj @@ -19,4 +19,7 @@ ..\..\Squidex.ruleset + + + diff --git a/src/Squidex.Infrastructure/Commands/AggregateHandler.cs b/src/Squidex.Infrastructure/Commands/AggregateHandler.cs index 78636fb55..fe3745420 100644 --- a/src/Squidex.Infrastructure/Commands/AggregateHandler.cs +++ b/src/Squidex.Infrastructure/Commands/AggregateHandler.cs @@ -106,7 +106,18 @@ namespace Squidex.Infrastructure.Commands await handler(domainObject); - await domainObject.WriteAsync(); + try + { + await domainObject.WriteAsync(); + + stateFactory.Synchronize(domainObjectId); + } + catch + { + stateFactory.Remove(domainObjectId); + + throw; + } if (!context.IsCompleted) { diff --git a/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index 92fbf6503..fdab3f5d8 100644 --- a/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -26,4 +26,7 @@ ..\..\Squidex.ruleset + + + diff --git a/src/Squidex.Infrastructure/States/IStateFactory.cs b/src/Squidex.Infrastructure/States/IStateFactory.cs index 68a99e1a2..16d72edfc 100644 --- a/src/Squidex.Infrastructure/States/IStateFactory.cs +++ b/src/Squidex.Infrastructure/States/IStateFactory.cs @@ -24,5 +24,9 @@ namespace Squidex.Infrastructure.States Task CreateAsync(Guid key) where T : IStatefulObject; Task CreateAsync(TKey key) where T : IStatefulObject; + + void Remove(TKey key) where T : IStatefulObject; + + void Synchronize(TKey key) where T : IStatefulObject; } } diff --git a/src/Squidex.Infrastructure/States/Persistence.cs b/src/Squidex.Infrastructure/States/Persistence.cs index a306e3327..1aa06d1f0 100644 --- a/src/Squidex.Infrastructure/States/Persistence.cs +++ b/src/Squidex.Infrastructure/States/Persistence.cs @@ -10,21 +10,17 @@ using System; using System.Threading.Tasks; using Squidex.Infrastructure.EventSourcing; -#pragma warning disable RECS0012 // 'if' statement can be re-written as 'switch' statement - namespace Squidex.Infrastructure.States { internal sealed class Persistence : Persistence, IPersistence { public Persistence(TKey ownerKey, - Action invalidate, - Action failed, IEventStore eventStore, IEventDataFormatter eventDataFormatter, ISnapshotStore snapshotStore, IStreamNameResolver streamNameResolver, Func, Task> applyEvent) - : base(ownerKey, invalidate, failed, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, PersistenceMode.EventSourcing, null, applyEvent) + : base(ownerKey, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, PersistenceMode.EventSourcing, null, applyEvent) { } } diff --git a/src/Squidex.Infrastructure/States/Persistence{TOwner,TSnapshot,TKey}.cs b/src/Squidex.Infrastructure/States/Persistence{TOwner,TSnapshot,TKey}.cs index 87d01b7b1..7c6d6fdb1 100644 --- a/src/Squidex.Infrastructure/States/Persistence{TOwner,TSnapshot,TKey}.cs +++ b/src/Squidex.Infrastructure/States/Persistence{TOwner,TSnapshot,TKey}.cs @@ -24,8 +24,6 @@ namespace Squidex.Infrastructure.States private readonly IEventStore eventStore; private readonly IEventDataFormatter eventDataFormatter; private readonly PersistenceMode persistenceMode; - private readonly Action invalidate; - private readonly Action failed; private readonly Func applyState; private readonly Func, Task> applyEvent; private long versionSnapshot = EtagVersion.Empty; @@ -38,8 +36,6 @@ namespace Squidex.Infrastructure.States } public Persistence(TKey ownerKey, - Action invalidate, - Action failed, IEventStore eventStore, IEventDataFormatter eventDataFormatter, ISnapshotStore snapshotStore, @@ -53,8 +49,6 @@ namespace Squidex.Infrastructure.States this.applyEvent = applyEvent; this.eventStore = eventStore; this.eventDataFormatter = eventDataFormatter; - this.invalidate = invalidate; - this.failed = failed; this.persistenceMode = persistenceMode; this.snapshotStore = snapshotStore; this.streamNameResolver = streamNameResolver; @@ -131,75 +125,53 @@ namespace Squidex.Infrastructure.States public async Task WriteSnapshotAsync(TSnapshot state) { - try - { - var newVersion = UseEventSourcing() ? versionEvents : versionSnapshot + 1; + var newVersion = UseEventSourcing() ? versionEvents : versionSnapshot + 1; - if (newVersion != versionSnapshot) + if (newVersion != versionSnapshot) + { + try { - try - { - await snapshotStore.WriteAsync(ownerKey, state, versionSnapshot, newVersion); - } - catch (InconsistentStateException ex) - { - throw new DomainObjectVersionException(ownerKey.ToString(), typeof(TOwner), ex.CurrentVersion, ex.ExpectedVersion); - } - - versionSnapshot = newVersion; + await snapshotStore.WriteAsync(ownerKey, state, versionSnapshot, newVersion); + } + catch (InconsistentStateException ex) + { + throw new DomainObjectVersionException(ownerKey.ToString(), typeof(TOwner), ex.CurrentVersion, ex.ExpectedVersion); } - UpdateVersion(); - - invalidate?.Invoke(); + versionSnapshot = newVersion; } - catch - { - failed?.Invoke(); - throw; - } + UpdateVersion(); } public async Task WriteEventsAsync(IEnumerable> events) { Guard.NotNull(events, nameof(@events)); - try - { - var eventArray = events.ToArray(); - - if (eventArray.Length > 0) - { - var expectedVersion = UseEventSourcing() ? version : EtagVersion.Any; + var eventArray = events.ToArray(); - var commitId = Guid.NewGuid(); + if (eventArray.Length > 0) + { + var expectedVersion = UseEventSourcing() ? version : EtagVersion.Any; - var eventStream = GetStreamName(); - var eventData = GetEventData(eventArray, commitId); + var commitId = Guid.NewGuid(); - try - { - await eventStore.AppendEventsAsync(commitId, GetStreamName(), expectedVersion, eventData); - } - catch (WrongEventVersionException ex) - { - throw new DomainObjectVersionException(ownerKey.ToString(), typeof(TOwner), ex.CurrentVersion, ex.ExpectedVersion); - } + var eventStream = GetStreamName(); + var eventData = GetEventData(eventArray, commitId); - versionEvents += eventArray.Length; + try + { + await eventStore.AppendEventsAsync(commitId, GetStreamName(), expectedVersion, eventData); + } + catch (WrongEventVersionException ex) + { + throw new DomainObjectVersionException(ownerKey.ToString(), typeof(TOwner), ex.CurrentVersion, ex.ExpectedVersion); } - UpdateVersion(); - - invalidate?.Invoke(); + versionEvents += eventArray.Length; } - catch - { - failed?.Invoke(); - throw; - } + UpdateVersion(); } private EventData[] GetEventData(Envelope[] events, Guid commitId) diff --git a/src/Squidex.Infrastructure/States/StateFactory.cs b/src/Squidex.Infrastructure/States/StateFactory.cs index dedc268f3..226b6f2a8 100644 --- a/src/Squidex.Infrastructure/States/StateFactory.cs +++ b/src/Squidex.Infrastructure/States/StateFactory.cs @@ -125,15 +125,7 @@ namespace Squidex.Infrastructure.States } var state = (T)services.GetService(typeof(T)); - - var stateStore = new Store(eventStore, eventDataFormatter, services, streamNameResolver, - () => - { - pubSub.Publish(new InvalidateMessage { Key = key.ToString() }, false); - }, () => - { - statesCache.Remove(key); - }); + var stateStore = new Store(eventStore, eventDataFormatter, services, streamNameResolver); stateObj = new ObjectHolder(state, key, stateStore); @@ -146,6 +138,16 @@ namespace Squidex.Infrastructure.States } } + public void Remove(TKey key) where T : IStatefulObject + { + statesCache.Remove(key); + } + + public void Synchronize(TKey key) where T : IStatefulObject + { + pubSub.Publish(new InvalidateMessage { Key = key.ToString() }, false); + } + protected override void DisposeObject(bool disposing) { if (disposing && pubSubSubscription != null) diff --git a/src/Squidex.Infrastructure/States/Store.cs b/src/Squidex.Infrastructure/States/Store.cs index 714fc3472..6191bc1f8 100644 --- a/src/Squidex.Infrastructure/States/Store.cs +++ b/src/Squidex.Infrastructure/States/Store.cs @@ -14,8 +14,6 @@ namespace Squidex.Infrastructure.States { internal sealed class Store : IStore { - private readonly Action invalidate; - private readonly Action failed; private readonly IServiceProvider services; private readonly IStreamNameResolver streamNameResolver; private readonly IEventStore eventStore; @@ -25,14 +23,10 @@ namespace Squidex.Infrastructure.States IEventStore eventStore, IEventDataFormatter eventDataFormatter, IServiceProvider services, - IStreamNameResolver streamNameResolver, - Action invalidate = null, - Action failed = null) + IStreamNameResolver streamNameResolver) { this.eventStore = eventStore; this.eventDataFormatter = eventDataFormatter; - this.failed = failed; - this.invalidate = invalidate; this.services = services; this.streamNameResolver = streamNameResolver; } @@ -53,7 +47,7 @@ namespace Squidex.Infrastructure.States var snapshotStore = (ISnapshotStore)services.GetService(typeof(ISnapshotStore)); - return new Persistence(key, invalidate, failed, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, applyEvent); + return new Persistence(key, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, applyEvent); } private IPersistence CreatePersistence(TKey key, PersistenceMode mode, Func applySnapshot, Func, Task> applyEvent) @@ -62,7 +56,7 @@ namespace Squidex.Infrastructure.States var snapshotStore = (ISnapshotStore)services.GetService(typeof(ISnapshotStore)); - return new Persistence(key, invalidate, failed, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, mode, applySnapshot, applyEvent); + return new Persistence(key, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, mode, applySnapshot, applyEvent); } } } diff --git a/src/Squidex.Shared/Squidex.Shared.csproj b/src/Squidex.Shared/Squidex.Shared.csproj index 7a978dff4..bd58e3fe2 100644 --- a/src/Squidex.Shared/Squidex.Shared.csproj +++ b/src/Squidex.Shared/Squidex.Shared.csproj @@ -14,4 +14,7 @@ ..\..\Squidex.ruleset + + + \ No newline at end of file diff --git a/src/Squidex/AppConfiguration.cs b/src/Squidex/AppConfiguration.cs index 16a9fa09e..6c707bafb 100644 --- a/src/Squidex/AppConfiguration.cs +++ b/src/Squidex/AppConfiguration.cs @@ -1,9 +1,8 @@ // ========================================================================== -// AppConfiguration.cs // Squidex Headless CMS // ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. // ========================================================================== using Microsoft.Extensions.Configuration; diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index 7e3d12ce0..0f1cfd7b0 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -91,4 +91,8 @@ ..\..\Squidex.ruleset + + + + \ No newline at end of file diff --git a/stylecop.json b/stylecop.json new file mode 100644 index 000000000..b24e7d110 --- /dev/null +++ b/stylecop.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "orderingRules": { + "usingDirectivesPlacement": "outsideNamespace" + }, + "documentationRules": { + "companyName": "Squidex UG (haftungsbeschraenkt)", + "copyrightText": "==========================================================================\n Squidex Headless CMS\n==========================================================================\n Copyright (c) {companyName}\n All rights reserved. Licensed under the {licenseName} license.\n==========================================================================", + "variables": { + "licenseName": "MIT" + }, + "xmlHeader": false + } + } +} diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj b/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj index e1188014a..209336f83 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj +++ b/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj @@ -25,4 +25,7 @@ ..\..\Squidex.ruleset + + + diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj b/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj index a7ac94f1a..cb0ce5db3 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj @@ -35,4 +35,7 @@ ..\..\Squidex.ruleset + + + \ No newline at end of file diff --git a/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj b/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj index 957bb6b03..f09faec8a 100644 --- a/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj +++ b/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj @@ -26,4 +26,7 @@ ..\..\Squidex.ruleset + + + \ No newline at end of file diff --git a/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj b/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj index 87a70e941..a2d884fcb 100644 --- a/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj +++ b/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj @@ -29,4 +29,7 @@ ..\..\Squidex.ruleset + + + \ No newline at end of file diff --git a/tests/Squidex.Infrastructure.Tests/States/StateEventSourcingTests.cs b/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs similarity index 83% rename from tests/Squidex.Infrastructure.Tests/States/StateEventSourcingTests.cs rename to tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs index 7af4608b9..bf372e571 100644 --- a/tests/Squidex.Infrastructure.Tests/States/StateEventSourcingTests.cs +++ b/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs @@ -1,5 +1,5 @@ // ========================================================================== -// StateEventSourcingTests.cs +// PersistenceEventSourcingTests.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -20,7 +20,7 @@ using Xunit; namespace Squidex.Infrastructure.States { - public class StateEventSourcingTests + public class PersistenceEventSourcingTests { private class MyStatefulObject : IStatefulObject { @@ -73,7 +73,7 @@ namespace Squidex.Infrastructure.States private readonly IStreamNameResolver streamNameResolver = A.Fake(); private readonly StateFactory sut; - public StateEventSourcingTests() + public PersistenceEventSourcingTests() { A.CallTo(() => services.GetService(typeof(MyStatefulObject))) .Returns(statefulObject); @@ -179,7 +179,7 @@ namespace Squidex.Infrastructure.States } [Fact] - public async Task Should_not_throw_exception_if_noting_expected() + public async Task Should_not_throw_exception_if_nothing_expected() { statefulObject.ExpectedVersion = EtagVersion.Any; @@ -188,45 +188,9 @@ namespace Squidex.Infrastructure.States await sut.GetSingleAsync(key); } - [Fact] - public async Task Should_provide_state_from_services_and_add_to_cache() - { - statefulObject.ExpectedVersion = EtagVersion.Any; - - SetupEventStore(0); - - var actualObject = await sut.GetSingleAsync(key); - - Assert.Same(statefulObject, actualObject); - Assert.NotNull(cache.Get(key)); - } - - [Fact] - public async Task Should_serve_next_request_from_cache() - { - SetupEventStore(0); - - var actualObject1 = await sut.GetSingleAsync(key); - - Assert.Same(statefulObject, actualObject1); - Assert.NotNull(cache.Get(key)); - - var actualObject2 = await sut.GetSingleAsync(key); - - A.CallTo(() => services.GetService(typeof(MyStatefulObject))) - .MustHaveHappened(Repeated.Exactly.Once); - } - [Fact] public async Task Should_write_to_store_with_previous_position() { - InvalidateMessage message = null; - - pubSub.Subscribe(m => - { - message = m; - }); - SetupEventStore(3); var actualObject = await sut.GetSingleAsync(key); @@ -240,9 +204,6 @@ namespace Squidex.Infrastructure.States .MustHaveHappened(); A.CallTo(() => eventStore.AppendEventsAsync(A.Ignored, key, 4, A>.That.Matches(x => x.Count == 2))) .MustHaveHappened(); - - Assert.NotNull(message); - Assert.Equal(key, message.Key); } [Fact] @@ -259,17 +220,7 @@ namespace Squidex.Infrastructure.States } [Fact] - public async Task Should_remove_from_cache_when_invalidation_message_received() - { - var actualObject = await sut.GetSingleAsync(key); - - await InvalidateCacheAsync(); - - Assert.False(cache.TryGetValue(key, out var t)); - } - - [Fact] - public async Task Should_remove_from_cache_when_write_failed() + public async Task Should_not_remove_from_cache_when_write_failed() { A.CallTo(() => eventStore.AppendEventsAsync(A.Ignored, A.Ignored, A.Ignored, A>.Ignored)) .Throws(new InvalidOperationException()); @@ -278,7 +229,7 @@ namespace Squidex.Infrastructure.States await Assert.ThrowsAsync(() => statefulObject.WriteEventsAsync(new MyEvent())); - Assert.False(cache.TryGetValue(key, out var t)); + Assert.True(cache.TryGetValue(key, out var t)); } [Fact] @@ -305,20 +256,6 @@ namespace Squidex.Infrastructure.States .MustHaveHappened(Repeated.Exactly.Once); } - private async Task RemoveFromCacheAsync() - { - cache.Remove(key); - - await Task.Delay(400); - } - - private async Task InvalidateCacheAsync() - { - pubSub.Publish(new InvalidateMessage { Key = key }, true); - - await Task.Delay(400); - } - private void SetupEventStore(int count, int eventOffset = 0, int readPosition = 0) { SetupEventStore(Enumerable.Repeat(0, count).Select(x => new MyEvent()).ToArray(), eventOffset, readPosition); diff --git a/tests/Squidex.Infrastructure.Tests/States/StateSnapshotTests.cs b/tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs similarity index 74% rename from tests/Squidex.Infrastructure.Tests/States/StateSnapshotTests.cs rename to tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs index 0243c0c55..5ba756746 100644 --- a/tests/Squidex.Infrastructure.Tests/States/StateSnapshotTests.cs +++ b/tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs @@ -19,7 +19,7 @@ using Xunit; namespace Squidex.Infrastructure.States { - public class StateSnapshotTests : IDisposable + public class PersistenceSnapshotTests : IDisposable { private class MyStatefulObject : IStatefulObject { @@ -67,7 +67,7 @@ namespace Squidex.Infrastructure.States private readonly IStreamNameResolver streamNameResolver = A.Fake(); private readonly StateFactory sut; - public StateSnapshotTests() + public PersistenceSnapshotTests() { A.CallTo(() => services.GetService(typeof(MyStatefulObject))) .Returns(statefulObject); @@ -142,53 +142,9 @@ namespace Squidex.Infrastructure.States await sut.GetSingleAsync(key); } - [Fact] - public async Task Should_provide_state_from_services_and_add_to_cache() - { - var actualObject = await sut.GetSingleAsync(key); - - Assert.Same(statefulObject, actualObject); - Assert.NotNull(cache.Get(key)); - } - - [Fact] - public async Task Should_serve_next_request_from_cache() - { - var actualObject1 = await sut.GetSingleAsync(key); - - Assert.Same(statefulObject, actualObject1); - Assert.NotNull(cache.Get(key)); - - var actualObject2 = await sut.GetSingleAsync(key); - - A.CallTo(() => services.GetService(typeof(MyStatefulObject))) - .MustHaveHappened(Repeated.Exactly.Once); - } - - [Fact] - public async Task Should_not_serve_next_request_from_cache_when_detached() - { - var actualObject1 = await sut.CreateAsync(key); - - Assert.Same(statefulObject, actualObject1); - Assert.Null(cache.Get(key)); - - var actualObject2 = await sut.CreateAsync(key); - - A.CallTo(() => services.GetService(typeof(MyStatefulObject))) - .MustHaveHappened(Repeated.Exactly.Twice); - } - [Fact] public async Task Should_write_to_store_with_previous_version() { - InvalidateMessage message = null; - - pubSub.Subscribe(m => - { - message = m; - }); - A.CallTo(() => snapshotStore.ReadAsync(key)) .Returns((123, 13)); @@ -203,9 +159,6 @@ namespace Squidex.Infrastructure.States A.CallTo(() => snapshotStore.WriteAsync(key, 456, 13, 14)) .MustHaveHappened(); - - Assert.NotNull(message); - Assert.Equal(key, message.Key); } [Fact] @@ -223,17 +176,7 @@ namespace Squidex.Infrastructure.States } [Fact] - public async Task Should_remove_from_cache_when_invalidation_message_received() - { - var actualObject = await sut.GetSingleAsync(key); - - await InvalidateCacheAsync(); - - Assert.False(cache.TryGetValue(key, out var t)); - } - - [Fact] - public async Task Should_remove_from_cache_when_write_failed() + public async Task Should_not_remove_from_cache_when_write_failed() { A.CallTo(() => snapshotStore.WriteAsync(A.Ignored, A.Ignored, A.Ignored, A.Ignored)) .Throws(new InvalidOperationException()); @@ -242,7 +185,7 @@ namespace Squidex.Infrastructure.States await Assert.ThrowsAsync(() => statefulObject.WriteStateAsync()); - Assert.False(cache.TryGetValue(key, out var t)); + Assert.True(cache.TryGetValue(key, out var t)); } [Fact] @@ -268,19 +211,5 @@ namespace Squidex.Infrastructure.States A.CallTo(() => snapshotStore.ReadAsync(key)) .MustHaveHappened(Repeated.Exactly.Once); } - - private async Task RemoveFromCacheAsync() - { - cache.Remove(key); - - await Task.Delay(400); - } - - private async Task InvalidateCacheAsync() - { - pubSub.Publish(new InvalidateMessage { Key = key }, true); - - await Task.Delay(400); - } } } \ No newline at end of file diff --git a/tests/Squidex.Infrastructure.Tests/States/StateFactoryTests.cs b/tests/Squidex.Infrastructure.Tests/States/StateFactoryTests.cs new file mode 100644 index 000000000..900726fad --- /dev/null +++ b/tests/Squidex.Infrastructure.Tests/States/StateFactoryTests.cs @@ -0,0 +1,146 @@ +// ========================================================================== +// StateSnapshotTests.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using FakeItEasy; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; +using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Tasks; +using Xunit; + +#pragma warning disable RECS0002 // Convert anonymous method to method group + +namespace Squidex.Infrastructure.States +{ + public class StateFactoryTests : IDisposable + { + private class MyStatefulObject : IStatefulObject + { + public Task ActivateAsync(string key, IStore store) + { + return TaskHelper.Done; + } + } + + private readonly string key = Guid.NewGuid().ToString(); + private readonly MyStatefulObject statefulObject = new MyStatefulObject(); + private readonly IEventDataFormatter eventDataFormatter = A.Fake(); + private readonly IEventStore eventStore = A.Fake(); + private readonly IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); + private readonly IPubSub pubSub = new InMemoryPubSub(true); + private readonly IServiceProvider services = A.Fake(); + private readonly ISnapshotStore snapshotStore = A.Fake>(); + private readonly IStreamNameResolver streamNameResolver = A.Fake(); + private readonly StateFactory sut; + + public StateFactoryTests() + { + A.CallTo(() => services.GetService(typeof(MyStatefulObject))) + .Returns(statefulObject); + A.CallTo(() => services.GetService(typeof(ISnapshotStore))) + .Returns(snapshotStore); + + sut = new StateFactory(pubSub, cache, eventStore, eventDataFormatter, services, streamNameResolver); + sut.Initialize(); + } + + public void Dispose() + { + sut.Dispose(); + } + + [Fact] + public async Task Should_provide_state_from_services_and_add_to_cache() + { + var actualObject = await sut.GetSingleAsync(key); + + Assert.Same(statefulObject, actualObject); + Assert.NotNull(cache.Get(key)); + } + + [Fact] + public async Task Should_serve_next_request_from_cache() + { + var actualObject1 = await sut.GetSingleAsync(key); + + Assert.Same(statefulObject, actualObject1); + Assert.NotNull(cache.Get(key)); + + var actualObject2 = await sut.GetSingleAsync(key); + + A.CallTo(() => services.GetService(typeof(MyStatefulObject))) + .MustHaveHappened(Repeated.Exactly.Once); + } + + [Fact] + public async Task Should_not_serve_next_request_from_cache_when_detached() + { + var actualObject1 = await sut.CreateAsync(key); + + Assert.Same(statefulObject, actualObject1); + Assert.Null(cache.Get(key)); + + var actualObject2 = await sut.CreateAsync(key); + + A.CallTo(() => services.GetService(typeof(MyStatefulObject))) + .MustHaveHappened(Repeated.Exactly.Twice); + } + + [Fact] + public async Task Should_remove_from_cache_when_invalidation_message_received() + { + var actualObject = await sut.GetSingleAsync(key); + + await InvalidateCacheAsync(); + + Assert.False(cache.TryGetValue(key, out var t)); + } + + [Fact] + public async Task Should_remove_from_cache_when_method_called() + { + var actualObject = await sut.GetSingleAsync(key); + + sut.Remove(key); + + Assert.False(cache.TryGetValue(key, out var t)); + } + + [Fact] + public void Should_send_invalidation_message_on_refresh() + { + InvalidateMessage message = null; + + pubSub.Subscribe(m => + { + message = m; + }); + + sut.Synchronize(key); + + Assert.NotNull(message); + Assert.Equal(key, message.Key); + } + + private async Task RemoveFromCacheAsync() + { + cache.Remove(key); + + await Task.Delay(400); + } + + private async Task InvalidateCacheAsync() + { + pubSub.Publish(new InvalidateMessage { Key = key }, true); + + await Task.Delay(400); + } + } +} \ No newline at end of file diff --git a/tools/GenerateLanguages/GenerateLanguages.csproj b/tools/GenerateLanguages/GenerateLanguages.csproj index 0e6127d69..7021a1d02 100644 --- a/tools/GenerateLanguages/GenerateLanguages.csproj +++ b/tools/GenerateLanguages/GenerateLanguages.csproj @@ -6,4 +6,7 @@ ..\..\Squidex.ruleset + + + diff --git a/tools/Migrate_00/Migrate_00.csproj b/tools/Migrate_00/Migrate_00.csproj index d9f665d29..c71b07de2 100644 --- a/tools/Migrate_00/Migrate_00.csproj +++ b/tools/Migrate_00/Migrate_00.csproj @@ -11,4 +11,7 @@ ..\..\Squidex.ruleset + + + diff --git a/tools/Migrate_01/Migrate_01.csproj b/tools/Migrate_01/Migrate_01.csproj index c782b2dec..c5b80e7f5 100644 --- a/tools/Migrate_01/Migrate_01.csproj +++ b/tools/Migrate_01/Migrate_01.csproj @@ -11,4 +11,7 @@ ..\..\Squidex.ruleset + + +