mirror of https://github.com/Squidex/squidex.git
37 changed files with 307 additions and 228 deletions
@ -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 |
|||
} |
|||
} |
|||
} |
|||
@ -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<string> |
|||
{ |
|||
public Task ActivateAsync(string key, IStore<string> store) |
|||
{ |
|||
return TaskHelper.Done; |
|||
} |
|||
} |
|||
|
|||
private readonly string key = Guid.NewGuid().ToString(); |
|||
private readonly MyStatefulObject statefulObject = new MyStatefulObject(); |
|||
private readonly IEventDataFormatter eventDataFormatter = A.Fake<IEventDataFormatter>(); |
|||
private readonly IEventStore eventStore = A.Fake<IEventStore>(); |
|||
private readonly IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); |
|||
private readonly IPubSub pubSub = new InMemoryPubSub(true); |
|||
private readonly IServiceProvider services = A.Fake<IServiceProvider>(); |
|||
private readonly ISnapshotStore<int, string> snapshotStore = A.Fake<ISnapshotStore<int, string>>(); |
|||
private readonly IStreamNameResolver streamNameResolver = A.Fake<IStreamNameResolver>(); |
|||
private readonly StateFactory sut; |
|||
|
|||
public StateFactoryTests() |
|||
{ |
|||
A.CallTo(() => services.GetService(typeof(MyStatefulObject))) |
|||
.Returns(statefulObject); |
|||
A.CallTo(() => services.GetService(typeof(ISnapshotStore<int, string>))) |
|||
.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<MyStatefulObject, string>(key); |
|||
|
|||
Assert.Same(statefulObject, actualObject); |
|||
Assert.NotNull(cache.Get<object>(key)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_serve_next_request_from_cache() |
|||
{ |
|||
var actualObject1 = await sut.GetSingleAsync<MyStatefulObject, string>(key); |
|||
|
|||
Assert.Same(statefulObject, actualObject1); |
|||
Assert.NotNull(cache.Get<object>(key)); |
|||
|
|||
var actualObject2 = await sut.GetSingleAsync<MyStatefulObject, string>(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<MyStatefulObject, string>(key); |
|||
|
|||
Assert.Same(statefulObject, actualObject1); |
|||
Assert.Null(cache.Get<object>(key)); |
|||
|
|||
var actualObject2 = await sut.CreateAsync<MyStatefulObject, string>(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<MyStatefulObject, string>(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<MyStatefulObject>(key); |
|||
|
|||
sut.Remove<MyStatefulObject, string>(key); |
|||
|
|||
Assert.False(cache.TryGetValue(key, out var t)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_send_invalidation_message_on_refresh() |
|||
{ |
|||
InvalidateMessage message = null; |
|||
|
|||
pubSub.Subscribe<InvalidateMessage>(m => |
|||
{ |
|||
message = m; |
|||
}); |
|||
|
|||
sut.Synchronize<MyStatefulObject, string>(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); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue