Browse Source

Tests for assets.

pull/206/head
Sebastian Stehle 8 years ago
parent
commit
50610d5cdd
  1. 16
      src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs
  2. 1
      src/Squidex.Domain.Apps.Entities/DomainObjectState.cs
  3. 31
      src/Squidex.Domain.Apps.Entities/EntityMapper.cs
  4. 6
      src/Squidex.Domain.Apps.Entities/IEntity.cs
  5. 22
      src/Squidex.Domain.Apps.Entities/IUpdateableEntity.cs
  6. 2
      src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore.cs
  7. 5
      src/Squidex.Infrastructure.MongoDb/MongoDb/MongoRepositoryBase.cs
  8. 49
      src/Squidex.Infrastructure.MongoDb/States/MongoSnapshotStore.cs
  9. 6
      src/Squidex.Infrastructure/States/ISnapshotStore.cs
  10. 28
      src/Squidex.Infrastructure/States/Persistence.cs
  11. 12
      src/Squidex.Infrastructure/States/StateFactory.cs
  12. 23
      src/Squidex.Infrastructure/States/Store.cs
  13. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppCommandMiddlewareTests.cs
  14. 7
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppDomainObjectTests.cs
  15. 137
      tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetCommandMiddlewareTests.cs
  16. 209
      tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetDomainObjectTests.cs
  17. 65
      tests/Squidex.Domain.Apps.Entities.Tests/Assets/Guards/GuardAssetTests.cs
  18. 6
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleCommandMiddlewareTests.cs
  19. 14
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDomainObjectTests.cs

16
src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs

@ -26,14 +26,19 @@ namespace Squidex.Domain.Apps.Entities.Assets
{
FileName = command.File.FileName,
FileSize = command.File.FileSize,
FileVersion = State.FileVersion + 1,
FileVersion = 0,
MimeType = command.File.MimeType,
PixelWidth = command.ImageInfo?.PixelWidth,
PixelHeight = command.ImageInfo?.PixelHeight,
IsImage = command.ImageInfo != null
});
UpdateState(command, s => SimpleMapper.Map(@event, s));
UpdateState(command, s =>
{
s.TotalSize = @event.FileSize;
SimpleMapper.Map(@event, s);
});
RaiseEvent(@event);
@ -54,7 +59,12 @@ namespace Squidex.Domain.Apps.Entities.Assets
IsImage = command.ImageInfo != null
});
UpdateState(command, s => SimpleMapper.Map(@event, s));
UpdateState(command, s =>
{
s.TotalSize += @event.FileSize;
SimpleMapper.Map(@event, s);
});
RaiseEvent(@event);

1
src/Squidex.Domain.Apps.Entities/DomainObjectState.cs

@ -14,6 +14,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities
{
public abstract class DomainObjectState<T> : Cloneable<T>,
IUpdateableEntity,
IUpdateableEntityWithCreatedBy,
IUpdateableEntityWithLastModifiedBy,
IUpdateableEntityWithVersion

31
src/Squidex.Domain.Apps.Entities/EntityMapper.cs

@ -8,6 +8,7 @@
using System;
using NodaTime;
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Entities
{
@ -17,28 +18,24 @@ namespace Squidex.Domain.Apps.Entities
{
var timestamp = SystemClock.Instance.GetCurrentInstant();
SetId(entity, command);
SetAppId(entity, command);
SetVersion(entity);
SetCreated(entity, timestamp);
SetCreatedBy(entity, command);
SetLastModified(entity, timestamp);
SetLastModifiedBy(entity, command);
SetVersion(entity);
updater?.Invoke(entity);
return entity;
}
private static void SetLastModified(IEntity entity, Instant timestamp)
{
entity.LastModified = timestamp;
}
private static void SetCreated(IEntity entity, Instant timestamp)
private static void SetId(IEntity entity, SquidexCommand command)
{
if (entity.Created == default(Instant))
if (entity is IUpdateableEntity updateable && command is IAggregateCommand aggregateCommand)
{
entity.Created = timestamp;
updateable.Id = aggregateCommand.AggregateId;
}
}
@ -50,6 +47,14 @@ namespace Squidex.Domain.Apps.Entities
}
}
private static void SetCreated(IEntity entity, Instant timestamp)
{
if (entity is IUpdateableEntity updateable && updateable.Created == default(Instant))
{
updateable.Created = timestamp;
}
}
private static void SetCreatedBy(IEntity entity, SquidexCommand command)
{
if (entity is IUpdateableEntityWithCreatedBy withCreatedBy && withCreatedBy.CreatedBy == null)
@ -58,6 +63,14 @@ namespace Squidex.Domain.Apps.Entities
}
}
private static void SetLastModified(IEntity entity, Instant timestamp)
{
if (entity is IUpdateableEntity updateable)
{
updateable.LastModified = timestamp;
}
}
private static void SetLastModifiedBy(IEntity entity, SquidexCommand command)
{
if (entity is IUpdateableEntityWithLastModifiedBy withModifiedBy)

6
src/Squidex.Domain.Apps.Entities/IEntity.cs

@ -13,10 +13,10 @@ namespace Squidex.Domain.Apps.Entities
{
public interface IEntity
{
Guid Id { get; set; }
Guid Id { get; }
Instant Created { get; set; }
Instant Created { get; }
Instant LastModified { get; set; }
Instant LastModified { get; }
}
}

22
src/Squidex.Domain.Apps.Entities/IUpdateableEntity.cs

@ -0,0 +1,22 @@
// ==========================================================================
// IUpdateableEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using NodaTime;
namespace Squidex.Domain.Apps.Entities
{
public interface IUpdateableEntity
{
Guid Id { get; set; }
Instant Created { get; set; }
Instant LastModified { get; set; }
}
}

2
src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore.cs

@ -200,7 +200,7 @@ namespace Squidex.Infrastructure.EventSourcing
{
var document =
await Collection.Find(Filter.Eq(EventStreamField, streamName))
.Project<BsonDocument>(Project
.Project<BsonDocument>(Projection
.Include(EventStreamOffsetField)
.Include(EventsCountField))
.Sort(Sort.Descending(EventStreamOffsetField)).Limit(1)

5
src/Squidex.Infrastructure.MongoDb/MongoDb/MongoRepositoryBase.cs

@ -13,18 +13,21 @@ using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Infrastructure.Tasks;
#pragma warning disable RECS0108 // Warns about static fields in generic types
namespace Squidex.Infrastructure.MongoDb
{
public abstract class MongoRepositoryBase<TEntity> : IExternalSystem
{
private const string CollectionFormat = "{0}Set";
protected static readonly UpdateOptions Upsert = new UpdateOptions { IsUpsert = true };
protected static readonly SortDefinitionBuilder<TEntity> Sort = Builders<TEntity>.Sort;
protected static readonly UpdateDefinitionBuilder<TEntity> Update = Builders<TEntity>.Update;
protected static readonly FieldDefinitionBuilder<TEntity> Fields = FieldDefinitionBuilder<TEntity>.Instance;
protected static readonly FilterDefinitionBuilder<TEntity> Filter = Builders<TEntity>.Filter;
protected static readonly IndexKeysDefinitionBuilder<TEntity> Index = Builders<TEntity>.IndexKeys;
protected static readonly ProjectionDefinitionBuilder<TEntity> Project = Builders<TEntity>.Projection;
protected static readonly ProjectionDefinitionBuilder<TEntity> Projection = Builders<TEntity>.Projection;
private readonly IMongoDatabase mongoDatabase;
private Lazy<IMongoCollection<TEntity>> mongoCollection;

49
src/Squidex.Infrastructure.MongoDb/States/MongoSnapshotStore.cs

@ -6,46 +6,34 @@
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
using MongoDB.Driver;
using Newtonsoft.Json;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Infrastructure.States
{
public sealed class MongoSnapshotStore : ISnapshotStore, IExternalSystem
public class MongoSnapshotStore<T> : MongoRepositoryBase<MongoState<T>>, ISnapshotStore<T>, IExternalSystem
{
private static readonly UpdateOptions Upsert = new UpdateOptions { IsUpsert = true };
private readonly IMongoDatabase database;
private readonly JsonSerializer serializer;
public MongoSnapshotStore(IMongoDatabase database, JsonSerializer serializer)
: base(database)
{
Guard.NotNull(database, nameof(database));
Guard.NotNull(serializer, nameof(serializer));
this.database = database;
this.serializer = serializer;
}
public void Connect()
protected override string CollectionName()
{
try
{
database.ListCollections();
}
catch (Exception ex)
{
throw new ConfigurationException($"MongoDb connection failed to connect to database {database.DatabaseNamespace.DatabaseName}", ex);
}
return $"States_{typeof(T).Name}";
}
public async Task<(T Value, long Version)> ReadAsync<T>(string key)
public async Task<(T Value, long Version)> ReadAsync(string key)
{
var collection = GetCollection<T>();
var existing =
await collection.Find(x => x.Id == key)
await Collection.Find(x => x.Id == key)
.FirstOrDefaultAsync();
if (existing != null)
@ -56,18 +44,16 @@ namespace Squidex.Infrastructure.States
return (default(T), -1);
}
public async Task WriteAsync<T>(string key, T value, long oldVersion, long newVersion)
public async Task WriteAsync(string key, T value, long oldVersion, long newVersion)
{
var collection = GetCollection<T>();
try
{
await collection.UpdateOneAsync(
Builders<MongoState<T>>.Filter.And(
Builders<MongoState<T>>.Filter.Eq(x => x.Id, key),
Builders<MongoState<T>>.Filter.Eq(x => x.Version, oldVersion)
await Collection.UpdateOneAsync(
Filter.And(
Filter.Eq(x => x.Id, key),
Filter.Eq(x => x.Version, oldVersion)
),
Builders<MongoState<T>>.Update
Update
.Set(x => x.Doc, value)
.Set(x => x.Version, newVersion),
Upsert);
@ -77,8 +63,8 @@ namespace Squidex.Infrastructure.States
if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{
var existingVersion =
await collection.Find(x => x.Id == key)
.Project<MongoState<T>>(Builders<MongoState<T>>.Projection.Exclude(x => x.Id)).FirstOrDefaultAsync();
await Collection.Find(x => x.Id == key)
.Project<MongoState<T>>(Projection.Exclude(x => x.Id)).FirstOrDefaultAsync();
if (existingVersion != null)
{
@ -91,10 +77,5 @@ namespace Squidex.Infrastructure.States
}
}
}
private IMongoCollection<MongoState<T>> GetCollection<T>()
{
return database.GetCollection<MongoState<T>>($"States_{typeof(T).Name}");
}
}
}

6
src/Squidex.Infrastructure/States/ISnapshotStore.cs

@ -10,10 +10,10 @@ using System.Threading.Tasks;
namespace Squidex.Infrastructure.States
{
public interface ISnapshotStore
public interface ISnapshotStore<T>
{
Task WriteAsync<T>(string key, T value, long oldVersion, long newVersion);
Task WriteAsync(string key, T value, long oldVersion, long newVersion);
Task<(T Value, long Version)> ReadAsync<T>(string key);
Task<(T Value, long Version)> ReadAsync(string key);
}
}

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

@ -13,10 +13,10 @@ using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Infrastructure.States
{
public sealed class Persistence<TOwner, TState> : IPersistence<TState>
internal sealed class Persistence<TOwner, TState> : IPersistence<TState>
{
private readonly string ownerKey;
private readonly ISnapshotStore snapshotStore;
private readonly ISnapshotStore<TState> snapshotStore;
private readonly IStreamNameResolver streamNameResolver;
private readonly IEventStore eventStore;
private readonly IEventDataFormatter eventDataFormatter;
@ -30,13 +30,11 @@ namespace Squidex.Infrastructure.States
Action invalidate,
IEventStore eventStore,
IEventDataFormatter eventDataFormatter,
ISnapshotStore snapshotStore,
ISnapshotStore<TState> snapshotStore,
IStreamNameResolver streamNameResolver,
Func<TState, Task> applyState,
Func<Envelope<IEvent>, Task> applyEvent)
{
Guard.NotNull(ownerKey, nameof(ownerKey));
this.ownerKey = ownerKey;
this.applyState = applyState;
this.applyEvent = applyEvent;
@ -54,7 +52,7 @@ namespace Squidex.Infrastructure.States
if (snapshotStore != null)
{
var (state, position) = await snapshotStore.ReadAsync<TState>(ownerKey);
var (state, position) = await snapshotStore.ReadAsync(ownerKey);
positionSnapshot = position;
positionEvent = position;
@ -104,13 +102,10 @@ namespace Squidex.Infrastructure.States
public async Task WriteSnapshotAsync(TState state)
{
if (snapshotStore == null)
{
throw new InvalidOperationException("Snapshots are not supported.");
}
var newPosition =
eventStore != null ? positionEvent : positionSnapshot + 1;
eventStore != null ?
positionEvent :
positionSnapshot + 1;
if (newPosition != positionSnapshot)
{
@ -126,18 +121,13 @@ namespace Squidex.Infrastructure.States
positionSnapshot = newPosition;
}
invalidate();
invalidate?.Invoke();
}
public async Task WriteEventsAsync(params Envelope<IEvent>[] @events)
{
Guard.NotNull(events, nameof(@events));
if (eventStore == null)
{
throw new InvalidOperationException("Events are not supported.");
}
if (@events.Length > 0)
{
var commitId = Guid.NewGuid();
@ -157,7 +147,7 @@ namespace Squidex.Infrastructure.States
positionEvent += events.Length;
}
invalidate();
invalidate?.Invoke();
}
private EventData[] GetEventData(Envelope<IEvent>[] events, Guid commitId)

12
src/Squidex.Infrastructure/States/StateFactory.cs

@ -21,7 +21,6 @@ namespace Squidex.Infrastructure.States
private readonly IPubSub pubSub;
private readonly IMemoryCache statesCache;
private readonly IServiceProvider services;
private readonly ISnapshotStore snapshotStore;
private readonly IStreamNameResolver streamNameResolver;
private readonly IEventStore eventStore;
private readonly IEventDataFormatter eventDataFormatter;
@ -54,22 +53,19 @@ namespace Squidex.Infrastructure.States
IEventStore eventStore,
IEventDataFormatter eventDataFormatter,
IServiceProvider services,
ISnapshotStore snapshotStore,
IStreamNameResolver streamNameResolver)
{
Guard.NotNull(services, nameof(services));
Guard.NotNull(eventStore, nameof(eventStore));
Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter));
Guard.NotNull(pubSub, nameof(pubSub));
Guard.NotNull(snapshotStore, nameof(snapshotStore));
Guard.NotNull(statesCache, nameof(statesCache));
Guard.NotNull(streamNameResolver, nameof(streamNameResolver));
this.services = services;
this.eventStore = eventStore;
this.eventDataFormatter = eventDataFormatter;
this.pubSub = pubSub;
this.snapshotStore = snapshotStore;
this.services = services;
this.statesCache = statesCache;
this.streamNameResolver = streamNameResolver;
}
@ -89,7 +85,7 @@ namespace Squidex.Infrastructure.States
{
Guard.NotNull(key, nameof(key));
var stateStore = new Store(() => { }, eventStore, eventDataFormatter, snapshotStore, streamNameResolver);
var stateStore = new Store(eventStore, eventDataFormatter, services, streamNameResolver);
var state = (T)services.GetService(typeof(T));
await state.ActivateAsync(key, stateStore);
@ -110,10 +106,10 @@ namespace Squidex.Infrastructure.States
var state = (T)services.GetService(typeof(T));
var stateStore = new Store(() =>
var stateStore = new Store(eventStore, eventDataFormatter, services, streamNameResolver, () =>
{
pubSub.Publish(new InvalidateMessage { Key = key }, false);
}, eventStore, eventDataFormatter, snapshotStore, streamNameResolver);
});
stateObj = new ObjectHolder<T>(state, key, stateStore);

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

@ -15,37 +15,46 @@ namespace Squidex.Infrastructure.States
public sealed class Store : IStore
{
private readonly Action invalidate;
private readonly ISnapshotStore snapshotStore;
private readonly IServiceProvider services;
private readonly IStreamNameResolver streamNameResolver;
private readonly IEventStore eventStore;
private readonly IEventDataFormatter eventDataFormatter;
public Store(
Action invalidate,
IEventStore eventStore,
IEventDataFormatter eventDataFormatter,
ISnapshotStore snapshotStore,
IStreamNameResolver streamNameResolver)
IServiceProvider services,
IStreamNameResolver streamNameResolver,
Action invalidate = null)
{
this.eventStore = eventStore;
this.eventDataFormatter = eventDataFormatter;
this.invalidate = invalidate;
this.snapshotStore = snapshotStore;
this.services = services;
this.streamNameResolver = streamNameResolver;
}
public IPersistence<object> WithEventSourcing<TOwner>(string key, Func<Envelope<IEvent>, Task> applyEvent)
{
return new Persistence<TOwner, object>(key, invalidate, eventStore, eventDataFormatter, null, streamNameResolver, null, applyEvent);
return CreatePersistence<TOwner, object>(key, null, applyEvent);
}
public IPersistence<TState> WithSnapshots<TOwner, TState>(string key, Func<TState, Task> applySnapshot)
{
return new Persistence<TOwner, TState>(key, invalidate, null, null, snapshotStore, null, applySnapshot, null);
return CreatePersistence<TOwner, TState>(key, applySnapshot, null);
}
public IPersistence<TState> WithSnapshotsAndEventSourcing<TOwner, TState>(string key, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent)
{
return CreatePersistence<TOwner, TState>(key, applySnapshot, applyEvent);
}
private IPersistence<TState> CreatePersistence<TOwner, TState>(string key, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent)
{
Guard.NotNullOrEmpty(key, nameof(key));
var snapshotStore = (ISnapshotStore<TState>)services.GetService(typeof(ISnapshotStore<TState>));
return new Persistence<TOwner, TState>(key, invalidate, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, applySnapshot, applyEvent);
}
}

4
tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppCommandMiddlewareTests.cs

@ -26,7 +26,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
private readonly IAppPlansProvider appPlansProvider = A.Fake<IAppPlansProvider>();
private readonly IAppPlanBillingManager appPlansBillingManager = A.Fake<IAppPlanBillingManager>();
private readonly IUserResolver userResolver = A.Fake<IUserResolver>();
private readonly AppDomainObject app;
private readonly AppDomainObject app = new AppDomainObject();
private readonly Language language = Language.DE;
private readonly string contributorId = Guid.NewGuid().ToString();
private readonly string clientName = "client";
@ -34,8 +34,6 @@ namespace Squidex.Domain.Apps.Entities.Apps
public AppCommandMiddlewareTests()
{
app = new AppDomainObject();
A.CallTo(() => appProvider.GetAppAsync(AppName))
.Returns((IAppEntity)null);

7
tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppDomainObjectTests.cs

@ -24,12 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
private readonly string clientId = "client";
private readonly string clientNewName = "My Client";
private readonly string planId = "premium";
private readonly AppDomainObject sut;
public AppDomainObjectTests()
{
sut = new AppDomainObject();
}
private readonly AppDomainObject sut = new AppDomainObject();
[Fact]
public void Create_should_throw_exception_if_created()

137
tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetCommandMiddlewareTests.cs

@ -0,0 +1,137 @@
// ==========================================================================
// AssetCommandMiddlewareTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.IO;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Tasks;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Assets
{
public class AssetCommandMiddlewareTests : HandlerTestBase<AssetDomainObject>
{
private readonly IAssetThumbnailGenerator assetThumbnailGenerator = A.Fake<IAssetThumbnailGenerator>();
private readonly IAssetStore assetStore = A.Fake<IAssetStore>();
private readonly Guid assetId = Guid.NewGuid();
private readonly Stream stream = new MemoryStream();
private readonly ImageInfo image = new ImageInfo(2048, 2048);
private readonly AssetDomainObject asset = new AssetDomainObject();
private readonly AssetFile file;
private readonly AssetCommandMiddleware sut;
public AssetCommandMiddlewareTests()
{
file = new AssetFile("my-image.png", "image/png", 1024, () => stream);
sut = new AssetCommandMiddleware(Handler, assetStore, assetThumbnailGenerator);
}
[Fact]
public async Task Create_should_create_domain_object()
{
var context = CreateContextForCommand(new CreateAsset { AssetId = assetId, File = file });
SetupStore(0, context.ContextId);
SetupImageInfo();
await TestCreate(asset, async _ =>
{
await sut.HandleAsync(context);
});
Assert.Equal(assetId, context.Result<EntityCreatedResult<Guid>>().IdOrValue);
AssertAssetHasBeenUploaded(0, context.ContextId);
AssertAssetImageChecked();
}
[Fact]
public async Task Update_should_update_domain_object()
{
var context = CreateContextForCommand(new UpdateAsset { AssetId = assetId, File = file });
SetupStore(1, context.ContextId);
SetupImageInfo();
CreateAsset();
await TestUpdate(asset, async _ =>
{
await sut.HandleAsync(context);
});
AssertAssetHasBeenUploaded(1, context.ContextId);
AssertAssetImageChecked();
}
[Fact]
public async Task Rename_should_update_domain_object()
{
CreateAsset();
var context = CreateContextForCommand(new RenameAsset { AssetId = assetId, FileName = "my-new-image.png" });
await TestUpdate(asset, async _ =>
{
await sut.HandleAsync(context);
});
}
[Fact]
public async Task Delete_should_update_domain_object()
{
CreateAsset();
var command = CreateContextForCommand(new DeleteAsset { AssetId = assetId });
await TestUpdate(asset, async _ =>
{
await sut.HandleAsync(command);
});
}
private void CreateAsset()
{
asset.Create(CreateCommand(new CreateAsset { File = file }));
}
private void SetupImageInfo()
{
A.CallTo(() => assetThumbnailGenerator.GetImageInfoAsync(stream))
.Returns(image);
}
private void SetupStore(long version, Guid commitId)
{
A.CallTo(() => assetStore.UploadTemporaryAsync(commitId.ToString(), stream))
.Returns(TaskHelper.Done);
A.CallTo(() => assetStore.CopyTemporaryAsync(commitId.ToString(), assetId.ToString(), version, null))
.Returns(TaskHelper.Done);
A.CallTo(() => assetStore.DeleteTemporaryAsync(commitId.ToString()))
.Returns(TaskHelper.Done);
}
private void AssertAssetImageChecked()
{
A.CallTo(() => assetThumbnailGenerator.GetImageInfoAsync(stream)).MustHaveHappened();
}
private void AssertAssetHasBeenUploaded(long version, Guid commitId)
{
A.CallTo(() => assetStore.UploadTemporaryAsync(commitId.ToString(), stream)).MustHaveHappened();
A.CallTo(() => assetStore.CopyTemporaryAsync(commitId.ToString(), assetId.ToString(), version, null)).MustHaveHappened();
A.CallTo(() => assetStore.DeleteTemporaryAsync(commitId.ToString())).MustHaveHappened();
}
}
}

209
tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetDomainObjectTests.cs

@ -0,0 +1,209 @@
// ==========================================================================
// AssetDomainObjectTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.IO;
using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Assets;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Assets
{
public class AssetDomainObjectTests : HandlerTestBase<AssetDomainObject>
{
private readonly ImageInfo image = new ImageInfo(2048, 2048);
private readonly Guid assetId = Guid.NewGuid();
private readonly AssetFile file = new AssetFile("my-image.png", "image/png", 1024, () => new MemoryStream());
private readonly AssetDomainObject sut = new AssetDomainObject();
[Fact]
public void Create_should_throw_exception_if_created()
{
CreateAsset();
Assert.Throws<DomainException>(() =>
{
sut.Create(CreateAssetCommand(new CreateAsset { File = file }));
});
}
[Fact]
public void Create_should_create_events()
{
sut.Create(CreateAssetCommand(new CreateAsset { File = file, ImageInfo = image }));
Assert.Equal(0, sut.State.FileVersion);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateAssetEvent(new AssetCreated
{
IsImage = true,
FileName = file.FileName,
FileSize = file.FileSize,
FileVersion = 0,
MimeType = file.MimeType,
PixelWidth = image.PixelWidth,
PixelHeight = image.PixelHeight
})
);
}
[Fact]
public void Update_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.Update(CreateAssetCommand(new UpdateAsset { File = file }));
});
}
[Fact]
public void Update_should_throw_exception_if_asset_is_deleted()
{
CreateAsset();
DeleteAsset();
Assert.Throws<DomainException>(() =>
{
sut.Update(CreateAssetCommand(new UpdateAsset()));
});
}
[Fact]
public void Update_should_create_events()
{
CreateAsset();
sut.Update(CreateAssetCommand(new UpdateAsset { File = file, ImageInfo = image }));
Assert.Equal(1, sut.State.FileVersion);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateAssetEvent(new AssetUpdated
{
IsImage = true,
FileSize = file.FileSize,
FileVersion = 1,
MimeType = file.MimeType,
PixelWidth = image.PixelWidth,
PixelHeight = image.PixelHeight
})
);
}
[Fact]
public void Rename_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.Rename(CreateAssetCommand(new RenameAsset { FileName = "new-file.png" }));
});
}
[Fact]
public void Rename_should_throw_exception_if_asset_is_deleted()
{
CreateAsset();
DeleteAsset();
Assert.Throws<DomainException>(() =>
{
sut.Update(CreateAssetCommand(new UpdateAsset()));
});
}
[Fact]
public void Rename_should_create_events()
{
CreateAsset();
sut.Rename(CreateAssetCommand(new RenameAsset { FileName = "my-new-image.png" }));
Assert.Equal("my-new-image.png", sut.State.FileName);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateAssetEvent(new AssetRenamed { FileName = "my-new-image.png" })
);
}
[Fact]
public void Delete_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.Delete(CreateAssetCommand(new DeleteAsset()));
});
}
[Fact]
public void Delete_should_throw_exception_if_already_deleted()
{
CreateAsset();
DeleteAsset();
Assert.Throws<DomainException>(() =>
{
sut.Delete(CreateAssetCommand(new DeleteAsset()));
});
}
[Fact]
public void Delete_should_create_events_with_total_file_size()
{
CreateAsset();
UpdateAsset();
sut.Delete(CreateAssetCommand(new DeleteAsset()));
Assert.True(sut.State.IsDeleted);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateAssetEvent(new AssetDeleted { DeletedSize = 2048 })
);
}
private void CreateAsset()
{
sut.Create(CreateAssetCommand(new CreateAsset { File = file }));
sut.ClearUncommittedEvents();
}
private void UpdateAsset()
{
sut.Update(CreateAssetCommand(new UpdateAsset { File = file }));
sut.ClearUncommittedEvents();
}
private void DeleteAsset()
{
sut.Delete(CreateAssetCommand(new DeleteAsset()));
sut.ClearUncommittedEvents();
}
protected T CreateAssetEvent<T>(T @event) where T : AssetEvent
{
@event.AssetId = assetId;
return CreateEvent(@event);
}
protected T CreateAssetCommand<T>(T command) where T : AssetAggregateCommand
{
command.AssetId = assetId;
return CreateCommand(command);
}
}
}

65
tests/Squidex.Domain.Apps.Entities.Tests/Assets/Guards/GuardAssetTests.cs

@ -0,0 +1,65 @@
// ==========================================================================
// GuardAssetTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Assets.Guards
{
public class GuardAssetTests
{
[Fact]
public void CanRename_should_throw_exception_if_name_not_defined()
{
var command = new RenameAsset();
Assert.Throws<ValidationException>(() => GuardAsset.CanRename(command, "asset-name"));
}
[Fact]
public void CanRename_should_throw_exception_if_name_are_the_same()
{
var command = new RenameAsset { FileName = "asset-name" };
Assert.Throws<ValidationException>(() => GuardAsset.CanRename(command, "asset-name"));
}
[Fact]
public void CanRename_not_should_throw_exception_if_name_are_different()
{
var command = new RenameAsset { FileName = "new-name" };
GuardAsset.CanRename(command, "asset-name");
}
[Fact]
public void CanCreate_should_not_throw_exception()
{
var command = new CreateAsset();
GuardAsset.CanCreate(command);
}
[Fact]
public void CanUpdate_should_not_throw_exception()
{
var command = new UpdateAsset();
GuardAsset.CanUpdate(command);
}
[Fact]
public void CanDelete_should_not_throw_exception()
{
var command = new DeleteAsset();
GuardAsset.CanDelete(command);
}
}
}

6
tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleCommandMiddlewareTests.cs

@ -23,19 +23,17 @@ namespace Squidex.Domain.Apps.Entities.Rules
public class RuleCommandMiddlewareTests : HandlerTestBase<RuleDomainObject>
{
private readonly IAppProvider appProvider = A.Fake<IAppProvider>();
private readonly RuleCommandMiddleware sut;
private readonly RuleDomainObject rule;
private readonly RuleDomainObject rule = new RuleDomainObject();
private readonly RuleTrigger ruleTrigger = new ContentChangedTrigger();
private readonly RuleAction ruleAction = new WebhookAction { Url = new Uri("https://squidex.io") };
private readonly Guid ruleId = Guid.NewGuid();
private readonly RuleCommandMiddleware sut;
public RuleCommandMiddlewareTests()
{
A.CallTo(() => appProvider.GetSchemaAsync(A<string>.Ignored, A<Guid>.Ignored, false))
.Returns(A.Fake<ISchemaEntity>());
rule = new RuleDomainObject();
sut = new RuleCommandMiddleware(Handler, appProvider);
}

14
tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDomainObjectTests.cs

@ -21,16 +21,10 @@ namespace Squidex.Domain.Apps.Entities.Rules
{
public class RuleDomainObjectTests : HandlerTestBase<RuleDomainObject>
{
private readonly Guid ruleId = Guid.NewGuid();
private readonly RuleTrigger ruleTrigger = new ContentChangedTrigger();
private readonly RuleAction ruleAction = new WebhookAction { Url = new Uri("https://squidex.io") };
private readonly RuleDomainObject sut;
public Guid RuleId { get; } = Guid.NewGuid();
public RuleDomainObjectTests()
{
sut = new RuleDomainObject();
}
private readonly RuleDomainObject sut = new RuleDomainObject();
[Fact]
public void Create_should_throw_exception_if_created()
@ -234,14 +228,14 @@ namespace Squidex.Domain.Apps.Entities.Rules
protected T CreateRuleEvent<T>(T @event) where T : RuleEvent
{
@event.RuleId = RuleId;
@event.RuleId = ruleId;
return CreateEvent(@event);
}
protected T CreateRuleCommand<T>(T command) where T : RuleAggregateCommand
{
command.RuleId = RuleId;
command.RuleId = ruleId;
return CreateCommand(command);
}

Loading…
Cancel
Save