mirror of https://github.com/Squidex/squidex.git
43 changed files with 3049 additions and 296 deletions
@ -0,0 +1,29 @@ |
|||
// ==========================================================================
|
|||
// AppPlan.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Apps |
|||
{ |
|||
public sealed class AppPlan |
|||
{ |
|||
public RefToken Owner { get; } |
|||
|
|||
public string PlanId { get; } |
|||
|
|||
public AppPlan(RefToken owner, string planId) |
|||
{ |
|||
Guard.NotNull(owner, nameof(owner)); |
|||
Guard.NotNullOrEmpty(planId, nameof(planId)); |
|||
|
|||
Owner = owner; |
|||
|
|||
PlanId = planId; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
// ==========================================================================
|
|||
// GuardApp.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Read.Apps.Services; |
|||
using Squidex.Domain.Apps.Write.Apps.Commands; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Apps.Guards |
|||
{ |
|||
public static class GuardApp |
|||
{ |
|||
public static Task CanCreate(CreateApp command, IAppProvider apps) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
|
|||
return Validate.It(() => "Cannot create app.", async error => |
|||
{ |
|||
if (await apps.FindAppByNameAsync(command.Name) != null) |
|||
{ |
|||
error(new ValidationError($"An app with name '{command.Name}' already exists", nameof(command.Name))); |
|||
} |
|||
|
|||
if (!command.Name.IsSlug()) |
|||
{ |
|||
error(new ValidationError("Name must be a valid slug.", nameof(command.Name))); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public static void CanChangePlan(ChangePlan command, AppPlan plan, IAppPlansProvider appPlans) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
|
|||
Validate.It(() => "Cannot change plan.", error => |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(command.PlanId)) |
|||
{ |
|||
error(new ValidationError("PlanId is not defined.", nameof(command.PlanId))); |
|||
} |
|||
else if (appPlans.GetPlan(command.PlanId) == null) |
|||
{ |
|||
error(new ValidationError("Plan id not available.", nameof(command.PlanId))); |
|||
} |
|||
|
|||
if (!string.IsNullOrWhiteSpace(command.PlanId) && plan != null && !plan.Owner.Equals(command.Actor)) |
|||
{ |
|||
error(new ValidationError("Plan can only be changed from current user.")); |
|||
} |
|||
|
|||
if (string.Equals(command.PlanId, plan?.PlanId, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
error(new ValidationError("App has already this plan.")); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
// ==========================================================================
|
|||
// GuardApp.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Read.Apps.Services; |
|||
using Squidex.Domain.Apps.Write.Apps.Commands; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Shared.Users; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Apps.Guards |
|||
{ |
|||
public static class GuardAppContributors |
|||
{ |
|||
public static Task CanAssign(AppContributors contributors, AssignContributor command, IUserResolver users, IAppLimitsPlan plan) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
|
|||
return Validate.It(() => "Cannot assign contributor.", async error => |
|||
{ |
|||
if (!command.Permission.IsEnumValue()) |
|||
{ |
|||
error(new ValidationError("Permission is not valid.", nameof(command.Permission))); |
|||
} |
|||
|
|||
if (string.IsNullOrWhiteSpace(command.ContributorId)) |
|||
{ |
|||
error(new ValidationError("Contributor id not assigned.", nameof(command.ContributorId))); |
|||
} |
|||
else |
|||
{ |
|||
if (await users.FindByIdAsync(command.ContributorId) == null) |
|||
{ |
|||
error(new ValidationError("Cannot find contributor id.", nameof(command.ContributorId))); |
|||
} |
|||
else if (contributors.Contributors.TryGetValue(command.ContributorId, out var existing)) |
|||
{ |
|||
if (existing == command.Permission) |
|||
{ |
|||
error(new ValidationError("Contributor has already this permission.", nameof(command.Permission))); |
|||
} |
|||
} |
|||
else if (plan.MaxContributors == contributors.Contributors.Count) |
|||
{ |
|||
error(new ValidationError("You have reached the maximum number of contributors for your plan.")); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public static void CanRemove(AppContributors contributors, RemoveContributor command) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
|
|||
Validate.It(() => "Cannot remove contributor.", error => |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(command.ContributorId)) |
|||
{ |
|||
error(new ValidationError("Contributor id not assigned.", nameof(command.ContributorId))); |
|||
} |
|||
|
|||
var ownerIds = contributors.Contributors.Where(x => x.Value == AppContributorPermission.Owner).Select(x => x.Key).ToList(); |
|||
|
|||
if (ownerIds.Count == 1 && ownerIds.Contains(command.ContributorId)) |
|||
{ |
|||
error(new ValidationError("Cannot remove the only owner.", nameof(command.ContributorId))); |
|||
} |
|||
}); |
|||
|
|||
if (!contributors.Contributors.ContainsKey(command.ContributorId)) |
|||
{ |
|||
throw new DomainObjectNotFoundException(command.ContributorId, "Contributors", typeof(AppDomainObject)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
// ==========================================================================
|
|||
// GuardApp.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Core; |
|||
using Squidex.Domain.Apps.Write.Apps.Commands; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Apps.Guards |
|||
{ |
|||
public static class GuardAppLanguages |
|||
{ |
|||
public static void CanAdd(LanguagesConfig languages, AddLanguage command) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
|
|||
Validate.It(() => "Cannot add language.", error => |
|||
{ |
|||
if (command.Language == null) |
|||
{ |
|||
error(new ValidationError("Language cannot be null.", nameof(command.Language))); |
|||
} |
|||
else if (languages.Contains(command.Language)) |
|||
{ |
|||
error(new ValidationError("Language already added.", nameof(command.Language))); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public static void CanRemove(LanguagesConfig languages, RemoveLanguage command) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
|
|||
var languageConfig = GetLanguageConfigOrThrow(languages, command.Language); |
|||
|
|||
Validate.It(() => "Cannot remove language.", error => |
|||
{ |
|||
if (languages.Master == languageConfig) |
|||
{ |
|||
error(new ValidationError("Language config is master.", nameof(command.Language))); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public static void CanUpdate(LanguagesConfig languages, UpdateLanguage command) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
|
|||
var languageConfig = GetLanguageConfigOrThrow(languages, command.Language); |
|||
|
|||
Validate.It(() => "Cannot update language.", error => |
|||
{ |
|||
if ((languages.Master == languageConfig || command.IsMaster) && command.IsOptional) |
|||
{ |
|||
error(new ValidationError("Cannot make master language optional.", nameof(command.IsMaster))); |
|||
} |
|||
|
|||
if (command.Fallback != null) |
|||
{ |
|||
foreach (var fallback in command.Fallback) |
|||
{ |
|||
if (!languages.Contains(fallback)) |
|||
{ |
|||
error(new ValidationError($"Config does not contain fallback language {fallback}.", nameof(command.Fallback))); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private static LanguageConfig GetLanguageConfigOrThrow(LanguagesConfig languages, Language language) |
|||
{ |
|||
if (language == null) |
|||
{ |
|||
throw new DomainObjectNotFoundException(language, "Languages", typeof(AppDomainObject)); |
|||
} |
|||
|
|||
if (!languages.TryGetConfig(language, out var languageConfig)) |
|||
{ |
|||
throw new DomainObjectNotFoundException(language, "Languages", typeof(AppDomainObject)); |
|||
} |
|||
|
|||
return languageConfig; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
// ==========================================================================
|
|||
// GuardAsset.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Write.Assets.Commands; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Assets.Guards |
|||
{ |
|||
public static class GuardAsset |
|||
{ |
|||
public static void CanRename(RenameAsset command, string oldName) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
|
|||
Validate.It(() => "Cannot rename asset.", error => |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(command.FileName)) |
|||
{ |
|||
error(new ValidationError("Name must be defined.", nameof(command.FileName))); |
|||
} |
|||
|
|||
if (string.Equals(command.FileName, oldName)) |
|||
{ |
|||
error(new ValidationError("Name is equal to old name.", nameof(command.FileName))); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public static void CanCreate(CreateAsset command) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
} |
|||
|
|||
public static void CanUpdate(UpdateAsset command) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
} |
|||
|
|||
public static void CanDelete(DeleteAsset command) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,243 @@ |
|||
// ==========================================================================
|
|||
// AppCommandMiddlewareTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Read.Apps; |
|||
using Squidex.Domain.Apps.Read.Apps.Services; |
|||
using Squidex.Domain.Apps.Read.Apps.Services.Implementations; |
|||
using Squidex.Domain.Apps.Write.Apps.Commands; |
|||
using Squidex.Domain.Apps.Write.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.CQRS.Commands; |
|||
using Squidex.Shared.Users; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Apps |
|||
{ |
|||
public class AppCommandMiddlewareTests : HandlerTestBase<AppDomainObject> |
|||
{ |
|||
private readonly IAppProvider appProvider = A.Fake<IAppProvider>(); |
|||
private readonly IAppPlansProvider appPlansProvider = A.Fake<IAppPlansProvider>(); |
|||
private readonly IAppPlanBillingManager appPlansBillingManager = A.Fake<IAppPlanBillingManager>(); |
|||
private readonly IUserResolver userResolver = A.Fake<IUserResolver>(); |
|||
private readonly AppCommandMiddleware sut; |
|||
private readonly AppDomainObject app; |
|||
private readonly Language language = Language.DE; |
|||
private readonly string contributorId = Guid.NewGuid().ToString(); |
|||
private readonly string clientName = "client"; |
|||
|
|||
public AppCommandMiddlewareTests() |
|||
{ |
|||
app = new AppDomainObject(AppId, -1); |
|||
|
|||
A.CallTo(() => appProvider.FindAppByNameAsync(AppName)) |
|||
.Returns((IAppEntity)null); |
|||
|
|||
A.CallTo(() => userResolver.FindByIdAsync(contributorId)) |
|||
.Returns(A.Fake<IUser>()); |
|||
|
|||
sut = new AppCommandMiddleware(Handler, appProvider, appPlansProvider, appPlansBillingManager, userResolver); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_domain_object() |
|||
{ |
|||
var context = CreateContextForCommand(new CreateApp { Name = AppName, AppId = AppId }); |
|||
|
|||
await TestCreate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.Equal(AppId, context.Result<EntityCreatedResult<Guid>>().IdOrValue); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AssignContributor_should_assign_if_user_found() |
|||
{ |
|||
A.CallTo(() => appPlansProvider.GetPlan(null)) |
|||
.Returns(new ConfigAppLimitsPlan { MaxContributors = -1 }); |
|||
|
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new AssignContributor { ContributorId = contributorId }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveContributor_should_update_domain_object() |
|||
{ |
|||
CreateApp() |
|||
.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId })); |
|||
|
|||
var context = CreateContextForCommand(new RemoveContributor { ContributorId = contributorId }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AttachClient_should_update_domain_object() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new AttachClient { Id = clientName }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RenameClient_should_update_domain_object() |
|||
{ |
|||
CreateApp() |
|||
.AttachClient(CreateCommand(new AttachClient { Id = clientName })); |
|||
|
|||
var context = CreateContextForCommand(new UpdateClient { Id = clientName, Name = "New Name" }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RevokeClient_should_update_domain_object() |
|||
{ |
|||
CreateApp() |
|||
.AttachClient(CreateCommand(new AttachClient { Id = clientName })); |
|||
|
|||
var context = CreateContextForCommand(new RevokeClient { Id = clientName }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangePlan_should_update_domain_object() |
|||
{ |
|||
A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan")) |
|||
.Returns(true); |
|||
|
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, app.Id, app.Name, "my-plan")).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangePlan_should_not_make_update_for_redirect_result() |
|||
{ |
|||
A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan")) |
|||
.Returns(true); |
|||
|
|||
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, app.Id, app.Name, "my-plan")) |
|||
.Returns(CreateRedirectResult()); |
|||
|
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.Null(app.Plan); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangePlan_should_not_call_billing_manager_for_callback() |
|||
{ |
|||
A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan")) |
|||
.Returns(true); |
|||
|
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan", FromCallback = true }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, app.Id, app.Name, "my-plan")).MustNotHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AddLanguage_should_update_domain_object() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new AddLanguage { Language = language }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveLanguage_should_update_domain_object() |
|||
{ |
|||
CreateApp() |
|||
.AddLanguage(CreateCommand(new AddLanguage { Language = language })); |
|||
|
|||
var context = CreateContextForCommand(new RemoveLanguage { Language = language }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdateLanguage_should_update_domain_object() |
|||
{ |
|||
CreateApp() |
|||
.AddLanguage(CreateCommand(new AddLanguage { Language = language })); |
|||
|
|||
var context = CreateContextForCommand(new UpdateLanguage { Language = language }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
private AppDomainObject CreateApp() |
|||
{ |
|||
app.Create(CreateCommand(new CreateApp { Name = AppName })); |
|||
|
|||
return app; |
|||
} |
|||
|
|||
private static Task<IChangePlanResult> CreateRedirectResult() |
|||
{ |
|||
return Task.FromResult<IChangePlanResult>(new RedirectToCheckoutResult(new Uri("http://squidex.io"))); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,288 @@ |
|||
// ==========================================================================
|
|||
// AppDomainObjectTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Domain.Apps.Write.Apps.Commands; |
|||
using Squidex.Domain.Apps.Write.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.CQRS; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Apps |
|||
{ |
|||
public class AppDomainObjectTests : HandlerTestBase<AppDomainObject> |
|||
{ |
|||
private readonly AppDomainObject sut; |
|||
private readonly string contributorId = Guid.NewGuid().ToString(); |
|||
private readonly string clientId = "client"; |
|||
private readonly string clientNewName = "My Client"; |
|||
private readonly string planId = "premium"; |
|||
|
|||
public AppDomainObjectTests() |
|||
{ |
|||
sut = new AppDomainObject(AppId, 0); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_throw_exception_if_created() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Create(CreateCommand(new CreateApp { Name = AppName })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_specify_name_and_owner() |
|||
{ |
|||
sut.Create(CreateCommand(new CreateApp { Name = AppName, Actor = User, AppId = AppId })); |
|||
|
|||
Assert.Equal(AppName, sut.Name); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppCreated { Name = AppName }), |
|||
CreateEvent(new AppContributorAssigned { ContributorId = User.Identifier, Permission = AppContributorPermission.Owner }), |
|||
CreateEvent(new AppLanguageAdded { Language = Language.EN }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ChangePlan_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ChangePlan_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppPlanChanged { PlanId = planId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AssignContributor_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AssignContributor_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppContributorAssigned { ContributorId = contributorId, Permission = AppContributorPermission.Editor }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveContributor_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = contributorId })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveContributor_should_create_events_and_remove_contributor() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor })); |
|||
sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = contributorId })); |
|||
|
|||
sut.GetUncomittedEvents().Skip(1) |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppContributorRemoved { ContributorId = contributorId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AttachClient_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AttachClient_should_create_events() |
|||
{ |
|||
var command = new AttachClient { Id = clientId }; |
|||
|
|||
CreateApp(); |
|||
|
|||
sut.AttachClient(CreateCommand(command)); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppClientAttached { Id = clientId, Secret = command.Secret }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RevokeClient_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.RevokeClient(CreateCommand(new RevokeClient { Id = "not-found" })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RevokeClient_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
CreateClient(); |
|||
|
|||
sut.RevokeClient(CreateCommand(new RevokeClient { Id = clientId })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppClientRevoked { Id = clientId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdateClient_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.UpdateClient(CreateCommand(new UpdateClient { Id = "not-found", Name = clientNewName })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdateClient_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
CreateClient(); |
|||
|
|||
sut.UpdateClient(CreateCommand(new UpdateClient { Id = clientId, Name = clientNewName, Permission = AppClientPermission.Developer })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppClientRenamed { Id = clientId, Name = clientNewName }), |
|||
CreateEvent(new AppClientUpdated { Id = clientId, Permission = AppClientPermission.Developer }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AddLanguage_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.AddLanguage(CreateCommand(new AddLanguage { Language = Language.DE })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AddLanguage_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
sut.AddLanguage(CreateCommand(new AddLanguage { Language = Language.DE })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppLanguageAdded { Language = Language.DE }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveLanguage_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.EN })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveLanguage_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
CreateLanguage(Language.DE); |
|||
|
|||
sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.DE })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppLanguageRemoved { Language = Language.DE }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdateLanguage_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.EN })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdateLanguage_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
CreateLanguage(Language.DE); |
|||
|
|||
sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.DE, Fallback = new List<Language> { Language.EN } })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppLanguageUpdated { Language = Language.DE, Fallback = new List<Language> { Language.EN } }) |
|||
); |
|||
} |
|||
|
|||
private void CreateApp() |
|||
{ |
|||
sut.Create(CreateCommand(new CreateApp { Name = AppName })); |
|||
|
|||
((IAggregate)sut).ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void CreateClient() |
|||
{ |
|||
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId })); |
|||
|
|||
((IAggregate)sut).ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void CreateLanguage(Language language) |
|||
{ |
|||
sut.AddLanguage(CreateCommand(new AddLanguage { Language = language })); |
|||
|
|||
((IAggregate)sut).ClearUncommittedEvents(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
// ==========================================================================
|
|||
// AppEventTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Events; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Domain.Apps.Events.Apps.Old; |
|||
using Squidex.Domain.Apps.Write.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
#pragma warning disable CS0612 // Type or member is obsolete
|
|||
|
|||
namespace Squidex.Domain.Apps.Write.Apps |
|||
{ |
|||
public class AppEventTests |
|||
{ |
|||
private readonly RefToken actor = new RefToken("User", Guid.NewGuid().ToString()); |
|||
private readonly NamedId<Guid> appId = new NamedId<Guid>(Guid.NewGuid(), "my-app"); |
|||
|
|||
[Fact] |
|||
public void Should_migrate_client_changed_as_reader_to_client_updated() |
|||
{ |
|||
var source = CreateEvent(new AppClientChanged { IsReader = true }); |
|||
|
|||
source.Migrate().ShouldBeSameEvent(CreateEvent(new AppClientUpdated { Permission = AppClientPermission.Reader })); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_migrate_client_changed_as_writer_to_client_updated() |
|||
{ |
|||
var source = CreateEvent(new AppClientChanged { IsReader = false }); |
|||
|
|||
source.Migrate().ShouldBeSameEvent(CreateEvent(new AppClientUpdated { Permission = AppClientPermission.Editor })); |
|||
} |
|||
|
|||
private T CreateEvent<T>(T contentEvent) where T : AppEvent |
|||
{ |
|||
contentEvent.Actor = actor; |
|||
contentEvent.AppId = appId; |
|||
|
|||
return contentEvent; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,179 @@ |
|||
// ==========================================================================
|
|||
// GuardAppContributorsTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Read.Apps.Services; |
|||
using Squidex.Domain.Apps.Write.Apps.Commands; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Shared.Users; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Apps.Guards |
|||
{ |
|||
public class GuardAppContributorsTests |
|||
{ |
|||
private readonly IUserResolver users = A.Fake<IUserResolver>(); |
|||
private readonly IAppLimitsPlan appPlan = A.Fake<IAppLimitsPlan>(); |
|||
|
|||
public GuardAppContributorsTests() |
|||
{ |
|||
A.CallTo(() => users.FindByIdAsync(A<string>.Ignored)) |
|||
.Returns(A.Fake<IUser>()); |
|||
|
|||
A.CallTo(() => appPlan.MaxContributors) |
|||
.Returns(10); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task CanAssign_should_throw_exception_if_contributor_id_is_null() |
|||
{ |
|||
var command = new AssignContributor(); |
|||
|
|||
var contributors = new AppContributors(); |
|||
|
|||
return Assert.ThrowsAsync<ValidationException>(() => GuardAppContributors.CanAssign(contributors, command, users, appPlan)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task CanAssign_should_throw_exception_if_permission_not_valid() |
|||
{ |
|||
var command = new AssignContributor { ContributorId = "1", Permission = (AppContributorPermission)10 }; |
|||
|
|||
var contributors = new AppContributors(); |
|||
|
|||
return Assert.ThrowsAsync<ValidationException>(() => GuardAppContributors.CanAssign(contributors, command, users, appPlan)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task CanAssign_should_throw_exception_if_user_already_exists_with_same_permission() |
|||
{ |
|||
var command = new AssignContributor { ContributorId = "1" }; |
|||
|
|||
var contributors = new AppContributors(); |
|||
|
|||
contributors.Assign("1", AppContributorPermission.Owner); |
|||
|
|||
return Assert.ThrowsAsync<ValidationException>(() => GuardAppContributors.CanAssign(contributors, command, users, appPlan)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task CanAssign_should_throw_exception_if_user_not_found() |
|||
{ |
|||
A.CallTo(() => users.FindByIdAsync(A<string>.Ignored)) |
|||
.Returns(Task.FromResult<IUser>(null)); |
|||
|
|||
var command = new AssignContributor { ContributorId = "1", Permission = (AppContributorPermission)10 }; |
|||
|
|||
var contributors = new AppContributors(); |
|||
|
|||
return Assert.ThrowsAsync<ValidationException>(() => GuardAppContributors.CanAssign(contributors, command, users, appPlan)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task CanAssign_should_throw_exception_if_contributor_max_reached() |
|||
{ |
|||
A.CallTo(() => appPlan.MaxContributors) |
|||
.Returns(2); |
|||
|
|||
var command = new AssignContributor { ContributorId = "3" }; |
|||
|
|||
var contributors = new AppContributors(); |
|||
|
|||
contributors.Assign("1", AppContributorPermission.Owner); |
|||
contributors.Assign("2", AppContributorPermission.Editor); |
|||
|
|||
return Assert.ThrowsAsync<ValidationException>(() => GuardAppContributors.CanAssign(contributors, command, users, appPlan)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task CanAssign_should_not_throw_exception_if_user_found() |
|||
{ |
|||
var command = new AssignContributor { ContributorId = "1" }; |
|||
|
|||
var contributors = new AppContributors(); |
|||
|
|||
return GuardAppContributors.CanAssign(contributors, command, users, appPlan); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task CanAssign_should_not_throw_exception_if_contributor_has_another_permission() |
|||
{ |
|||
var command = new AssignContributor { ContributorId = "1" }; |
|||
|
|||
var contributors = new AppContributors(); |
|||
|
|||
contributors.Assign("1", AppContributorPermission.Editor); |
|||
|
|||
return GuardAppContributors.CanAssign(contributors, command, users, appPlan); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task CanAssign_should_not_throw_exception_if_contributor_max_reached_but_permission_changed() |
|||
{ |
|||
A.CallTo(() => appPlan.MaxContributors) |
|||
.Returns(2); |
|||
|
|||
var command = new AssignContributor { ContributorId = "1" }; |
|||
|
|||
var contributors = new AppContributors(); |
|||
|
|||
contributors.Assign("1", AppContributorPermission.Editor); |
|||
contributors.Assign("2", AppContributorPermission.Editor); |
|||
|
|||
return GuardAppContributors.CanAssign(contributors, command, users, appPlan); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanRemove_should_throw_exception_if_contributor_id_is_null() |
|||
{ |
|||
var command = new RemoveContributor(); |
|||
|
|||
var contributors = new AppContributors(); |
|||
|
|||
Assert.Throws<ValidationException>(() => GuardAppContributors.CanRemove(contributors, command)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanRemove_should_throw_exception_if_contributor_not_found() |
|||
{ |
|||
var command = new RemoveContributor { ContributorId = "1" }; |
|||
|
|||
var contributors = new AppContributors(); |
|||
|
|||
Assert.Throws<DomainObjectNotFoundException>(() => GuardAppContributors.CanRemove(contributors, command)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanRemove_should_throw_exception_if_contributor_is_only_owner() |
|||
{ |
|||
var command = new RemoveContributor { ContributorId = "1" }; |
|||
|
|||
var contributors = new AppContributors(); |
|||
|
|||
contributors.Assign("1", AppContributorPermission.Owner); |
|||
contributors.Assign("2", AppContributorPermission.Editor); |
|||
|
|||
Assert.Throws<ValidationException>(() => GuardAppContributors.CanRemove(contributors, command)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanRemove_should_not_throw_exception_if_contributor_not_only_owner() |
|||
{ |
|||
var command = new RemoveContributor { ContributorId = "1" }; |
|||
|
|||
var contributors = new AppContributors(); |
|||
|
|||
contributors.Assign("1", AppContributorPermission.Owner); |
|||
contributors.Assign("2", AppContributorPermission.Owner); |
|||
|
|||
GuardAppContributors.CanRemove(contributors, command); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,129 @@ |
|||
// ==========================================================================
|
|||
// GuardAppLanguagesTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using Squidex.Domain.Apps.Core; |
|||
using Squidex.Domain.Apps.Write.Apps.Commands; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Apps.Guards |
|||
{ |
|||
public class GuardAppLanguagesTests |
|||
{ |
|||
[Fact] |
|||
public void CanAddLanguage_should_throw_exception_if_language_is_null() |
|||
{ |
|||
var command = new AddLanguage(); |
|||
|
|||
var languages = LanguagesConfig.Build(Language.DE); |
|||
|
|||
Assert.Throws<ValidationException>(() => GuardAppLanguages.CanAdd(languages, command)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanAddLanguage_should_throw_exception_if_language_already_added() |
|||
{ |
|||
var command = new AddLanguage { Language = Language.DE }; |
|||
|
|||
var languages = LanguagesConfig.Build(Language.DE); |
|||
|
|||
Assert.Throws<ValidationException>(() => GuardAppLanguages.CanAdd(languages, command)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanAddLanguage_should_not_throw_exception_if_language_valid() |
|||
{ |
|||
var command = new AddLanguage { Language = Language.EN }; |
|||
|
|||
var languages = LanguagesConfig.Build(Language.DE); |
|||
|
|||
GuardAppLanguages.CanAdd(languages, command); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanRemoveLanguage_should_throw_exception_if_language_is_null() |
|||
{ |
|||
var command = new RemoveLanguage(); |
|||
|
|||
var languages = LanguagesConfig.Build(Language.DE); |
|||
|
|||
Assert.Throws<DomainObjectNotFoundException>(() => GuardAppLanguages.CanRemove(languages, command)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanRemoveLanguage_should_throw_exception_if_language_not_found() |
|||
{ |
|||
var command = new RemoveLanguage { Language = Language.EN }; |
|||
|
|||
var languages = LanguagesConfig.Build(Language.DE); |
|||
|
|||
Assert.Throws<DomainObjectNotFoundException>(() => GuardAppLanguages.CanRemove(languages, command)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanRemoveLanguage_should_throw_exception_if_language_is_master() |
|||
{ |
|||
var command = new RemoveLanguage { Language = Language.DE }; |
|||
|
|||
var languages = LanguagesConfig.Build(Language.DE); |
|||
|
|||
Assert.Throws<ValidationException>(() => GuardAppLanguages.CanRemove(languages, command)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanRemoveLanguage_should_not_throw_exception_if_language_is_valid() |
|||
{ |
|||
var command = new RemoveLanguage { Language = Language.EN }; |
|||
|
|||
var languages = LanguagesConfig.Build(Language.DE, Language.EN); |
|||
|
|||
GuardAppLanguages.CanRemove(languages, command); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanUpdateLanguage_should_throw_exception_if_language_is_optional_and_master() |
|||
{ |
|||
var command = new UpdateLanguage { Language = Language.DE, IsOptional = true }; |
|||
|
|||
var languages = LanguagesConfig.Build(Language.DE, Language.EN); |
|||
|
|||
Assert.Throws<ValidationException>(() => GuardAppLanguages.CanUpdate(languages, command)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanUpdateLanguage_should_throw_exception_if_language_has_invalid_fallback() |
|||
{ |
|||
var command = new UpdateLanguage { Language = Language.DE, Fallback = new List<Language> { Language.IT } }; |
|||
|
|||
var languages = LanguagesConfig.Build(Language.DE, Language.EN); |
|||
|
|||
Assert.Throws<ValidationException>(() => GuardAppLanguages.CanUpdate(languages, command)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanUpdateLanguage_should_throw_exception_if_not_found() |
|||
{ |
|||
var command = new UpdateLanguage { Language = Language.IT }; |
|||
|
|||
var languages = LanguagesConfig.Build(Language.DE, Language.EN); |
|||
|
|||
Assert.Throws<DomainObjectNotFoundException>(() => GuardAppLanguages.CanUpdate(languages, command)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanUpdateLanguage_should_not_throw_exception_if_language_is_valid() |
|||
{ |
|||
var command = new UpdateLanguage { Language = Language.DE, Fallback = new List<Language> { Language.EN } }; |
|||
|
|||
var languages = LanguagesConfig.Build(Language.DE, Language.EN); |
|||
|
|||
GuardAppLanguages.CanUpdate(languages, command); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,119 @@ |
|||
// ==========================================================================
|
|||
// GuardAppTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Read.Apps; |
|||
using Squidex.Domain.Apps.Read.Apps.Services; |
|||
using Squidex.Domain.Apps.Write.Apps.Commands; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Shared.Users; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Apps.Guards |
|||
{ |
|||
public class GuardAppTests |
|||
{ |
|||
private readonly IAppProvider apps = A.Fake<IAppProvider>(); |
|||
private readonly IUserResolver users = A.Fake<IUserResolver>(); |
|||
private readonly IAppPlansProvider appPlans = A.Fake<IAppPlansProvider>(); |
|||
|
|||
public GuardAppTests() |
|||
{ |
|||
A.CallTo(() => apps.FindAppByNameAsync("new-app")) |
|||
.Returns(Task.FromResult<IAppEntity>(null)); |
|||
|
|||
A.CallTo(() => users.FindByIdAsync(A<string>.Ignored)) |
|||
.Returns(A.Fake<IUser>()); |
|||
|
|||
A.CallTo(() => appPlans.GetPlan("free")) |
|||
.Returns(A.Fake<IAppLimitsPlan>()); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task CanCreate_should_throw_exception_if_name_already_in_use() |
|||
{ |
|||
A.CallTo(() => apps.FindAppByNameAsync("new-app")) |
|||
.Returns(A.Fake<IAppEntity>()); |
|||
|
|||
var command = new CreateApp { Name = "new-app" }; |
|||
|
|||
return Assert.ThrowsAsync<ValidationException>(() => GuardApp.CanCreate(command, apps)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task CanCreate_should_throw_exception_if_name_not_valid() |
|||
{ |
|||
var command = new CreateApp { Name = "INVALID NAME" }; |
|||
|
|||
return Assert.ThrowsAsync<ValidationException>(() => GuardApp.CanCreate(command, apps)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task CanCreate_should_not_throw_exception_if_app_name_is_free() |
|||
{ |
|||
var command = new CreateApp { Name = "new-app" }; |
|||
|
|||
return GuardApp.CanCreate(command, apps); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanChangePlan_should_throw_exception_if_plan_id_null() |
|||
{ |
|||
var command = new ChangePlan { Actor = new RefToken("user", "me") }; |
|||
|
|||
AppPlan plan = null; |
|||
|
|||
Assert.Throws<ValidationException>(() => GuardApp.CanChangePlan(command, plan, appPlans)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanChangePlan_should_throw_exception_if_plan_not_found() |
|||
{ |
|||
A.CallTo(() => appPlans.GetPlan("free")) |
|||
.Returns(null); |
|||
|
|||
var command = new ChangePlan { PlanId = "free", Actor = new RefToken("user", "me") }; |
|||
|
|||
AppPlan plan = null; |
|||
|
|||
Assert.Throws<ValidationException>(() => GuardApp.CanChangePlan(command, plan, appPlans)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanChangePlan_should_throw_exception_if_plan_was_configured_from_another_user() |
|||
{ |
|||
var command = new ChangePlan { PlanId = "free", Actor = new RefToken("user", "me") }; |
|||
|
|||
var plan = new AppPlan(new RefToken("user", "other"), "premium"); |
|||
|
|||
Assert.Throws<ValidationException>(() => GuardApp.CanChangePlan(command, plan, appPlans)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanChangePlan_should_throw_exception_if_plan_is_the_same() |
|||
{ |
|||
var command = new ChangePlan { PlanId = "free", Actor = new RefToken("user", "me") }; |
|||
|
|||
var plan = new AppPlan(new RefToken("user", "me"), "free"); |
|||
|
|||
Assert.Throws<ValidationException>(() => GuardApp.CanChangePlan(command, plan, appPlans)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanChangePlan_should_not_throw_exception_if_same_user_but_other_plan() |
|||
{ |
|||
var command = new ChangePlan { PlanId = "free", Actor = new RefToken("user", "me") }; |
|||
|
|||
var plan = new AppPlan(new RefToken("user", "me"), "premium"); |
|||
|
|||
GuardApp.CanChangePlan(command, plan, appPlans); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,139 @@ |
|||
// ==========================================================================
|
|||
// 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.Write.Assets.Commands; |
|||
using Squidex.Domain.Apps.Write.TestHelpers; |
|||
using Squidex.Infrastructure.Assets; |
|||
using Squidex.Infrastructure.CQRS.Commands; |
|||
using Squidex.Infrastructure.Tasks; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Assets |
|||
{ |
|||
public class AssetCommandMiddlewareTests : HandlerTestBase<AssetDomainObject> |
|||
{ |
|||
private readonly IAssetThumbnailGenerator assetThumbnailGenerator = A.Fake<IAssetThumbnailGenerator>(); |
|||
private readonly IAssetStore assetStore = A.Fake<IAssetStore>(); |
|||
private readonly AssetCommandMiddleware sut; |
|||
private readonly AssetDomainObject asset; |
|||
private readonly Guid assetId = Guid.NewGuid(); |
|||
private readonly Stream stream = new MemoryStream(); |
|||
private readonly ImageInfo image = new ImageInfo(2048, 2048); |
|||
private readonly AssetFile file; |
|||
|
|||
public AssetCommandMiddlewareTests() |
|||
{ |
|||
file = new AssetFile("my-image.png", "image/png", 1024, () => stream); |
|||
|
|||
asset = new AssetDomainObject(assetId, -1); |
|||
|
|||
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); |
|||
|
|||
VerifyStore(0, context.ContextId); |
|||
VerifyImageInfo(); |
|||
} |
|||
|
|||
[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); |
|||
}); |
|||
|
|||
VerifyStore(1, context.ContextId); |
|||
VerifyImageInfo(); |
|||
} |
|||
|
|||
[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(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 VerifyImageInfo() |
|||
{ |
|||
A.CallTo(() => assetThumbnailGenerator.GetImageInfoAsync(stream)).MustHaveHappened(); |
|||
} |
|||
|
|||
private void VerifyStore(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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,213 @@ |
|||
// ==========================================================================
|
|||
// AssetDomainObjectTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using Squidex.Domain.Apps.Events.Assets; |
|||
using Squidex.Domain.Apps.Write.Assets.Commands; |
|||
using Squidex.Domain.Apps.Write.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Assets; |
|||
using Squidex.Infrastructure.CQRS; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Assets |
|||
{ |
|||
public class AssetDomainObjectTests : HandlerTestBase<AssetDomainObject> |
|||
{ |
|||
private readonly AssetDomainObject sut; |
|||
private readonly ImageInfo image = new ImageInfo(2048, 2048); |
|||
private readonly AssetFile file = new AssetFile("my-image.png", "image/png", 1024, () => new MemoryStream()); |
|||
|
|||
public Guid AssetId { get; } = Guid.NewGuid(); |
|||
|
|||
public AssetDomainObjectTests() |
|||
{ |
|||
sut = new AssetDomainObject(AssetId, 0); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_throw_exception_if_created() |
|||
{ |
|||
sut.Create(new CreateAsset { File = file }); |
|||
|
|||
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 })); |
|||
|
|||
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 })); |
|||
|
|||
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" })); |
|||
|
|||
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.IsDeleted); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateAssetEvent(new AssetDeleted { DeletedSize = 2048 }) |
|||
); |
|||
} |
|||
|
|||
private void CreateAsset() |
|||
{ |
|||
sut.Create(CreateAssetCommand(new CreateAsset { File = file })); |
|||
|
|||
((IAggregate)sut).ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void UpdateAsset() |
|||
{ |
|||
sut.Update(CreateAssetCommand(new UpdateAsset { File = file })); |
|||
|
|||
((IAggregate)sut).ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void DeleteAsset() |
|||
{ |
|||
sut.Delete(CreateAssetCommand(new DeleteAsset())); |
|||
|
|||
((IAggregate)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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
// ==========================================================================
|
|||
// GuardAssetTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Write.Assets.Commands; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,249 @@ |
|||
// ==========================================================================
|
|||
// ContentCommandMiddlewareTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Core.Scripting; |
|||
using Squidex.Domain.Apps.Read.Apps; |
|||
using Squidex.Domain.Apps.Read.Apps.Services; |
|||
using Squidex.Domain.Apps.Read.Assets.Repositories; |
|||
using Squidex.Domain.Apps.Read.Contents.Repositories; |
|||
using Squidex.Domain.Apps.Read.Schemas; |
|||
using Squidex.Domain.Apps.Read.Schemas.Services; |
|||
using Squidex.Domain.Apps.Write.Contents.Commands; |
|||
using Squidex.Domain.Apps.Write.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.CQRS.Commands; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Contents |
|||
{ |
|||
public class ContentCommandMiddlewareTests : HandlerTestBase<ContentDomainObject> |
|||
{ |
|||
private readonly ContentCommandMiddleware sut; |
|||
private readonly ContentDomainObject content; |
|||
private readonly ISchemaProvider schemas = A.Fake<ISchemaProvider>(); |
|||
private readonly ISchemaEntity schema = A.Fake<ISchemaEntity>(); |
|||
private readonly IScriptEngine scriptEngine = A.Fake<IScriptEngine>(); |
|||
private readonly IAppProvider appProvider = A.Fake<IAppProvider>(); |
|||
private readonly IAppEntity app = A.Fake<IAppEntity>(); |
|||
private readonly ClaimsPrincipal user = new ClaimsPrincipal(); |
|||
private readonly LanguagesConfig languagesConfig = LanguagesConfig.Build(Language.DE); |
|||
private readonly Guid contentId = Guid.NewGuid(); |
|||
|
|||
private readonly NamedContentData invalidData = |
|||
new NamedContentData() |
|||
.AddField("my-field1", new ContentFieldData() |
|||
.AddValue(null)) |
|||
.AddField("my-field2", new ContentFieldData() |
|||
.AddValue(1)); |
|||
private readonly NamedContentData data = |
|||
new NamedContentData() |
|||
.AddField("my-field1", new ContentFieldData() |
|||
.AddValue(1)) |
|||
.AddField("my-field2", new ContentFieldData() |
|||
.AddValue(1)); |
|||
private readonly NamedContentData patch = |
|||
new NamedContentData() |
|||
.AddField("my-field1", new ContentFieldData() |
|||
.AddValue(1)); |
|||
|
|||
public ContentCommandMiddlewareTests() |
|||
{ |
|||
var schemaDef = new Schema("my-schema"); |
|||
|
|||
schemaDef.AddField(new NumberField(1, "my-field1", Partitioning.Invariant, |
|||
new NumberFieldProperties { IsRequired = true })); |
|||
schemaDef.AddField(new NumberField(2, "my-field2", Partitioning.Invariant, |
|||
new NumberFieldProperties { IsRequired = false })); |
|||
|
|||
content = new ContentDomainObject(contentId, -1); |
|||
|
|||
sut = new ContentCommandMiddleware(Handler, appProvider, A.Dummy<IAssetRepository>(), schemas, scriptEngine, A.Dummy<IContentRepository>()); |
|||
|
|||
A.CallTo(() => app.LanguagesConfig).Returns(languagesConfig); |
|||
A.CallTo(() => app.PartitionResolver).Returns(languagesConfig.ToResolver()); |
|||
|
|||
A.CallTo(() => appProvider.FindAppByIdAsync(AppId)).Returns(app); |
|||
|
|||
A.CallTo(() => schema.SchemaDef).Returns(schemaDef); |
|||
A.CallTo(() => schema.ScriptCreate).Returns("<create-script>"); |
|||
A.CallTo(() => schema.ScriptChange).Returns("<change-script>"); |
|||
A.CallTo(() => schema.ScriptUpdate).Returns("<update-script>"); |
|||
A.CallTo(() => schema.ScriptDelete).Returns("<delete-script>"); |
|||
|
|||
A.CallTo(() => schemas.FindSchemaByIdAsync(SchemaId, false)).Returns(schema); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_throw_exception_if_data_is_not_valid() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(invalidData); |
|||
|
|||
var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = invalidData, User = user }); |
|||
|
|||
await TestCreate(content, async _ => |
|||
{ |
|||
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context)); |
|||
}, false); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_content() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(data); |
|||
|
|||
var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = data, User = user }); |
|||
|
|||
await TestCreate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.Equal(data, context.Result<EntityCreatedResult<NamedContentData>>().IdOrValue); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<create-script>")).MustHaveHappened(); |
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")).MustNotHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_also_invoke_publish_script_when_publishing() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(data); |
|||
|
|||
var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = data, User = user, Publish = true }); |
|||
|
|||
await TestCreate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.Equal(data, context.Result<EntityCreatedResult<NamedContentData>>().IdOrValue); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<create-script>")).MustHaveHappened(); |
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_throw_exception_if_data_is_not_valid() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(invalidData); |
|||
|
|||
CreateContent(); |
|||
|
|||
var context = CreateContextForCommand(new UpdateContent { ContentId = contentId, Data = invalidData, User = user }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context)); |
|||
}, false); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_update_domain_object() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(data); |
|||
|
|||
CreateContent(); |
|||
|
|||
var context = CreateContextForCommand(new UpdateContent { ContentId = contentId, Data = data, User = user }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.Equal(data, context.Result<ContentDataChangedResult>().Data); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Patch_should_throw_exception_if_data_is_not_valid() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(invalidData); |
|||
|
|||
CreateContent(); |
|||
|
|||
var context = CreateContextForCommand(new PatchContent { ContentId = contentId, Data = invalidData, User = user }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context)); |
|||
}, false); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Patch_should_update_domain_object() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(data); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)).Returns(patch); |
|||
|
|||
CreateContent(); |
|||
|
|||
var context = CreateContextForCommand(new PatchContent { ContentId = contentId, Data = patch, User = user }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.NotNull(context.Result<ContentDataChangedResult>().Data); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangeStatus_should_publish_domain_object() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
var context = CreateContextForCommand(new ChangeContentStatus { ContentId = contentId, User = user, Status = Status.Published }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Delete_should_update_domain_object() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
var command = CreateContextForCommand(new DeleteContent { ContentId = contentId, User = user }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(command); |
|||
}); |
|||
|
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<delete-script>")).MustHaveHappened(); |
|||
} |
|||
|
|||
private void CreateContent() |
|||
{ |
|||
content.Create(new CreateContent { Data = data }); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,280 @@ |
|||
// ==========================================================================
|
|||
// ContentDomainObjectTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using FluentAssertions; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Events.Contents; |
|||
using Squidex.Domain.Apps.Write.Contents.Commands; |
|||
using Squidex.Domain.Apps.Write.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.CQRS; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Contents |
|||
{ |
|||
public class ContentDomainObjectTests : HandlerTestBase<ContentDomainObject> |
|||
{ |
|||
private readonly ContentDomainObject sut; |
|||
private readonly NamedContentData data = |
|||
new NamedContentData() |
|||
.AddField("field1", |
|||
new ContentFieldData() |
|||
.AddValue("iv", 1)); |
|||
private readonly NamedContentData otherData = |
|||
new NamedContentData() |
|||
.AddField("field2", |
|||
new ContentFieldData() |
|||
.AddValue("iv", 2)); |
|||
|
|||
public Guid ContentId { get; } = Guid.NewGuid(); |
|||
|
|||
public ContentDomainObjectTests() |
|||
{ |
|||
sut = new ContentDomainObject(ContentId, 0); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_throw_exception_if_created() |
|||
{ |
|||
sut.Create(new CreateContent { Data = data }); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Create(CreateContentCommand(new CreateContent { Data = data })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_create_events() |
|||
{ |
|||
sut.Create(CreateContentCommand(new CreateContent { Data = data })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentCreated { Data = data }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_also_publish_if_set_to_true() |
|||
{ |
|||
sut.Create(CreateContentCommand(new CreateContent { Data = data, Publish = true })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentCreated { Data = data }), |
|||
CreateContentEvent(new ContentStatusChanged { Status = Status.Published }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateContentCommand(new UpdateContent { Data = data })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_throw_exception_if_content_is_deleted() |
|||
{ |
|||
CreateContent(); |
|||
DeleteContent(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateContentCommand(new UpdateContent())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_create_events() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
sut.Update(CreateContentCommand(new UpdateContent { Data = otherData })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentUpdated { Data = otherData }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_not_create_event_for_same_data() |
|||
{ |
|||
CreateContent(); |
|||
UpdateContent(); |
|||
|
|||
sut.Update(CreateContentCommand(new UpdateContent { Data = data })); |
|||
|
|||
sut.GetUncomittedEvents().Should().BeEmpty(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Patch_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Patch(CreateContentCommand(new PatchContent { Data = data })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Patch_should_throw_exception_if_content_is_deleted() |
|||
{ |
|||
CreateContent(); |
|||
DeleteContent(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Patch(CreateContentCommand(new PatchContent())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Patch_should_create_events() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
sut.Patch(CreateContentCommand(new PatchContent { Data = otherData })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentUpdated { Data = otherData }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Patch_should_not_create_event_for_same_data() |
|||
{ |
|||
CreateContent(); |
|||
UpdateContent(); |
|||
|
|||
sut.Patch(CreateContentCommand(new PatchContent { Data = data })); |
|||
|
|||
sut.GetUncomittedEvents().Should().BeEmpty(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ChangeStatus_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ChangeStatus_should_throw_exception_if_content_is_deleted() |
|||
{ |
|||
CreateContent(); |
|||
DeleteContent(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ChangeStatus_should_refresh_properties_and_create_events() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus { Status = Status.Published })); |
|||
|
|||
Assert.Equal(Status.Published, sut.Status); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentStatusChanged { Status = Status.Published }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Delete(CreateContentCommand(new DeleteContent())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_throw_exception_if_already_deleted() |
|||
{ |
|||
CreateContent(); |
|||
DeleteContent(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Delete(CreateContentCommand(new DeleteContent())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_update_properties_and_create_events() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
sut.Delete(CreateContentCommand(new DeleteContent())); |
|||
|
|||
Assert.True(sut.IsDeleted); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentDeleted()) |
|||
); |
|||
} |
|||
|
|||
private void CreateContent() |
|||
{ |
|||
sut.Create(CreateContentCommand(new CreateContent { Data = data })); |
|||
|
|||
((IAggregate)sut).ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void UpdateContent() |
|||
{ |
|||
sut.Update(CreateContentCommand(new UpdateContent { Data = data })); |
|||
|
|||
((IAggregate)sut).ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void ChangeStatus(Status status) |
|||
{ |
|||
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus { Status = status })); |
|||
|
|||
((IAggregate)sut).ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void DeleteContent() |
|||
{ |
|||
sut.Delete(CreateContentCommand(new DeleteContent())); |
|||
|
|||
((IAggregate)sut).ClearUncommittedEvents(); |
|||
} |
|||
|
|||
protected T CreateContentEvent<T>(T @event) where T : ContentEvent |
|||
{ |
|||
@event.ContentId = ContentId; |
|||
|
|||
return CreateEvent(@event); |
|||
} |
|||
|
|||
protected T CreateContentCommand<T>(T command) where T : ContentCommand |
|||
{ |
|||
command.ContentId = ContentId; |
|||
|
|||
return CreateCommand(command); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
// ==========================================================================
|
|||
// SchemaEventTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Events.Contents; |
|||
using Squidex.Domain.Apps.Events.Contents.Old; |
|||
using Squidex.Domain.Apps.Write.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
#pragma warning disable CS0612 // Type or member is obsolete
|
|||
|
|||
namespace Squidex.Domain.Apps.Write.Contents |
|||
{ |
|||
public class ContentEventTests |
|||
{ |
|||
private readonly RefToken actor = new RefToken("User", Guid.NewGuid().ToString()); |
|||
private readonly NamedId<Guid> appId = new NamedId<Guid>(Guid.NewGuid(), "my-app"); |
|||
private readonly NamedId<Guid> schemaId = new NamedId<Guid>(Guid.NewGuid(), "my-schema"); |
|||
private readonly Guid contentId = Guid.NewGuid(); |
|||
|
|||
[Fact] |
|||
public void Should_migrate_content_published_to_content_status_changed() |
|||
{ |
|||
var source = CreateEvent(new ContentPublished()); |
|||
|
|||
source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Published })); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_migrate_content_unpublished_to_content_status_changed() |
|||
{ |
|||
var source = CreateEvent(new ContentUnpublished()); |
|||
|
|||
source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Draft })); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_migrate_content_restored_to_content_status_changed() |
|||
{ |
|||
var source = CreateEvent(new ContentRestored()); |
|||
|
|||
source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Draft })); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_migrate_content_archived_to_content_status_changed() |
|||
{ |
|||
var source = CreateEvent(new ContentArchived()); |
|||
|
|||
source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Archived })); |
|||
} |
|||
|
|||
private T CreateEvent<T>(T contentEvent) where T : ContentEvent |
|||
{ |
|||
contentEvent.Actor = actor; |
|||
contentEvent.AppId = appId; |
|||
contentEvent.SchemaId = schemaId; |
|||
contentEvent.ContentId = contentId; |
|||
|
|||
return contentEvent; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,139 @@ |
|||
// ==========================================================================
|
|||
// ContentVersionLoaderTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Events.Contents; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.CQRS.Events; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Contents |
|||
{ |
|||
public class ContentVersionLoaderTests |
|||
{ |
|||
private readonly IEventStore eventStore = A.Fake<IEventStore>(); |
|||
private readonly IStreamNameResolver nameResolver = A.Fake<IStreamNameResolver>(); |
|||
private readonly EventDataFormatter formatter = A.Fake<EventDataFormatter>(); |
|||
private readonly Guid id = Guid.NewGuid(); |
|||
private readonly Guid appId = Guid.NewGuid(); |
|||
private readonly string streamName = Guid.NewGuid().ToString(); |
|||
private readonly ContentVersionLoader sut; |
|||
|
|||
public ContentVersionLoaderTests() |
|||
{ |
|||
A.CallTo(() => nameResolver.GetStreamName(typeof(ContentDomainObject), id)) |
|||
.Returns(streamName); |
|||
|
|||
sut = new ContentVersionLoader(eventStore, nameResolver, formatter); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_throw_exception_when_event_store_returns_no_events() |
|||
{ |
|||
A.CallTo(() => eventStore.GetEventsAsync(streamName)) |
|||
.Returns(new List<StoredEvent>()); |
|||
|
|||
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.LoadAsync(appId, id, -1)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_throw_exception_when_version_not_found() |
|||
{ |
|||
A.CallTo(() => eventStore.GetEventsAsync(streamName)) |
|||
.Returns(new List<StoredEvent>()); |
|||
|
|||
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.LoadAsync(appId, id, 3)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_throw_exception_when_content_is_from_another_event() |
|||
{ |
|||
var eventData1 = new EventData(); |
|||
|
|||
var event1 = new ContentCreated { Data = new NamedContentData(), AppId = new NamedId<Guid>(Guid.NewGuid(), "my-app") }; |
|||
|
|||
var events = new List<StoredEvent> |
|||
{ |
|||
new StoredEvent("0", 0, eventData1) |
|||
}; |
|||
|
|||
A.CallTo(() => eventStore.GetEventsAsync(streamName)) |
|||
.Returns(events); |
|||
|
|||
A.CallTo(() => formatter.Parse(eventData1, true)) |
|||
.Returns(new Envelope<IEvent>(event1)); |
|||
|
|||
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.LoadAsync(appId, id, 0)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_load_content_from_created_event() |
|||
{ |
|||
var eventData1 = new EventData(); |
|||
var eventData2 = new EventData(); |
|||
|
|||
var event1 = new ContentCreated { Data = new NamedContentData(), AppId = new NamedId<Guid>(appId, "my-app") }; |
|||
var event2 = new ContentStatusChanged(); |
|||
|
|||
var events = new List<StoredEvent> |
|||
{ |
|||
new StoredEvent("0", 0, eventData1), |
|||
new StoredEvent("1", 1, eventData2) |
|||
}; |
|||
|
|||
A.CallTo(() => eventStore.GetEventsAsync(streamName)) |
|||
.Returns(events); |
|||
|
|||
A.CallTo(() => formatter.Parse(eventData1, true)) |
|||
.Returns(new Envelope<IEvent>(event1)); |
|||
A.CallTo(() => formatter.Parse(eventData2, true)) |
|||
.Returns(new Envelope<IEvent>(event2)); |
|||
|
|||
var data = await sut.LoadAsync(appId, id, 3); |
|||
|
|||
Assert.Same(event1.Data, data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_load_content_from_correct_version() |
|||
{ |
|||
var eventData1 = new EventData(); |
|||
var eventData2 = new EventData(); |
|||
var eventData3 = new EventData(); |
|||
|
|||
var event1 = new ContentCreated { Data = new NamedContentData(), AppId = new NamedId<Guid>(appId, "my-app") }; |
|||
var event2 = new ContentUpdated { Data = new NamedContentData() }; |
|||
var event3 = new ContentUpdated { Data = new NamedContentData() }; |
|||
|
|||
var events = new List<StoredEvent> |
|||
{ |
|||
new StoredEvent("0", 0, eventData1), |
|||
new StoredEvent("1", 1, eventData2), |
|||
new StoredEvent("2", 2, eventData3) |
|||
}; |
|||
|
|||
A.CallTo(() => eventStore.GetEventsAsync(streamName)) |
|||
.Returns(events); |
|||
|
|||
A.CallTo(() => formatter.Parse(eventData1, true)) |
|||
.Returns(new Envelope<IEvent>(event1)); |
|||
A.CallTo(() => formatter.Parse(eventData2, true)) |
|||
.Returns(new Envelope<IEvent>(event2)); |
|||
A.CallTo(() => formatter.Parse(eventData3, true)) |
|||
.Returns(new Envelope<IEvent>(event3)); |
|||
|
|||
var data = await sut.LoadAsync(appId, id, 1); |
|||
|
|||
Assert.Equal(event2.Data, data); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
// ==========================================================================
|
|||
// JsonFieldPropertiesTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Linq; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Schemas.Guards.FieldProperties |
|||
{ |
|||
public class JsonFieldPropertiesTests |
|||
{ |
|||
[Fact] |
|||
public void Should_add_error_if_editor_is_not_valid() |
|||
{ |
|||
var sut = new JsonFieldProperties(); |
|||
|
|||
var errors = FieldPropertiesValidator.Validate(sut).ToList(); |
|||
|
|||
Assert.Empty(errors); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,138 @@ |
|||
// ==========================================================================
|
|||
// GuardWebhookTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Webhooks; |
|||
using Squidex.Domain.Apps.Read.Schemas; |
|||
using Squidex.Domain.Apps.Read.Schemas.Services; |
|||
using Squidex.Domain.Apps.Write.Webhooks.Commands; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Webhooks.Guards |
|||
{ |
|||
public class GuardWebhookTests |
|||
{ |
|||
private readonly ISchemaProvider schemas = A.Fake<ISchemaProvider>(); |
|||
|
|||
public GuardWebhookTests() |
|||
{ |
|||
A.CallTo(() => schemas.FindSchemaByIdAsync(A<Guid>.Ignored, false)) |
|||
.Returns(A.Fake<ISchemaEntity>()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CanCreate_should_throw_exception_if_url_defined() |
|||
{ |
|||
var command = new CreateWebhook(); |
|||
|
|||
await Assert.ThrowsAsync<ValidationException>(() => GuardWebhook.CanCreate(command, schemas)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CanCreate_should_throw_exception_if_url_not_valid() |
|||
{ |
|||
var command = new CreateWebhook { Url = new Uri("/invalid", UriKind.Relative) }; |
|||
|
|||
await Assert.ThrowsAsync<ValidationException>(() => GuardWebhook.CanCreate(command, schemas)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CanCreate_should_throw_exception_if_schema_id_not_found() |
|||
{ |
|||
A.CallTo(() => schemas.FindSchemaByIdAsync(A<Guid>.Ignored, false)) |
|||
.Returns(Task.FromResult<ISchemaEntity>(null)); |
|||
|
|||
var command = new CreateWebhook |
|||
{ |
|||
Schemas = new List<WebhookSchema> |
|||
{ |
|||
new WebhookSchema() |
|||
}, |
|||
Url = new Uri("/invalid", UriKind.Relative) |
|||
}; |
|||
|
|||
await Assert.ThrowsAsync<ValidationException>(() => GuardWebhook.CanCreate(command, schemas)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CanCreate_should_not_throw_exception_if_schema_id_found() |
|||
{ |
|||
var command = new CreateWebhook |
|||
{ |
|||
Schemas = new List<WebhookSchema> |
|||
{ |
|||
new WebhookSchema() |
|||
}, |
|||
Url = new Uri("/invalid", UriKind.Relative) |
|||
}; |
|||
|
|||
await Assert.ThrowsAsync<ValidationException>(() => GuardWebhook.CanCreate(command, schemas)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CanUpdate_should_throw_exception_if_url_not_defined() |
|||
{ |
|||
var command = new UpdateWebhook(); |
|||
|
|||
await Assert.ThrowsAsync<ValidationException>(() => GuardWebhook.CanUpdate(command, schemas)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CanUpdate_should_throw_exception_if_url_not_valid() |
|||
{ |
|||
var command = new UpdateWebhook { Url = new Uri("/invalid", UriKind.Relative) }; |
|||
|
|||
await Assert.ThrowsAsync<ValidationException>(() => GuardWebhook.CanUpdate(command, schemas)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CanUpdate_should_throw_exception_if_schema_id_not_found() |
|||
{ |
|||
A.CallTo(() => schemas.FindSchemaByIdAsync(A<Guid>.Ignored, false)) |
|||
.Returns(Task.FromResult<ISchemaEntity>(null)); |
|||
|
|||
var command = new UpdateWebhook |
|||
{ |
|||
Schemas = new List<WebhookSchema> |
|||
{ |
|||
new WebhookSchema() |
|||
}, |
|||
Url = new Uri("/invalid", UriKind.Relative) |
|||
}; |
|||
|
|||
await Assert.ThrowsAsync<ValidationException>(() => GuardWebhook.CanUpdate(command, schemas)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CanUpdate_should_not_throw_exception_if_schema_id_found() |
|||
{ |
|||
var command = new UpdateWebhook |
|||
{ |
|||
Schemas = new List<WebhookSchema> |
|||
{ |
|||
new WebhookSchema() |
|||
}, |
|||
Url = new Uri("/invalid", UriKind.Relative) |
|||
}; |
|||
|
|||
await Assert.ThrowsAsync<ValidationException>(() => GuardWebhook.CanUpdate(command, schemas)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanDelete_should_not_throw_exception() |
|||
{ |
|||
var command = new DeleteWebhook(); |
|||
|
|||
GuardWebhook.CanDelete(command); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
// ==========================================================================
|
|||
// WebhookCommandMiddlewareTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Webhooks; |
|||
using Squidex.Domain.Apps.Read.Schemas; |
|||
using Squidex.Domain.Apps.Read.Schemas.Services; |
|||
using Squidex.Domain.Apps.Write.TestHelpers; |
|||
using Squidex.Domain.Apps.Write.Webhooks.Commands; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.CQRS.Commands; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Webhooks |
|||
{ |
|||
public class WebhookCommandMiddlewareTests : HandlerTestBase<WebhookDomainObject> |
|||
{ |
|||
private readonly ISchemaProvider schemas = A.Fake<ISchemaProvider>(); |
|||
private readonly WebhookCommandMiddleware sut; |
|||
private readonly WebhookDomainObject webhook; |
|||
private readonly Uri url = new Uri("http://squidex.io"); |
|||
private readonly Guid schemaId = Guid.NewGuid(); |
|||
private readonly Guid webhookId = Guid.NewGuid(); |
|||
private readonly List<WebhookSchema> webhookSchemas; |
|||
|
|||
public WebhookCommandMiddlewareTests() |
|||
{ |
|||
A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)) |
|||
.Returns(A.Fake<ISchemaEntity>()); |
|||
|
|||
webhook = new WebhookDomainObject(webhookId, -1); |
|||
|
|||
webhookSchemas = new List<WebhookSchema> |
|||
{ |
|||
new WebhookSchema { SchemaId = schemaId } |
|||
}; |
|||
|
|||
sut = new WebhookCommandMiddleware(Handler, schemas); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_domain_object() |
|||
{ |
|||
var context = CreateContextForCommand(new CreateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId }); |
|||
|
|||
await TestCreate(webhook, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_update_domain_object() |
|||
{ |
|||
var context = CreateContextForCommand(new UpdateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId }); |
|||
|
|||
A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns(A.Fake<ISchemaEntity>()); |
|||
|
|||
CreateWebhook(); |
|||
|
|||
await TestUpdate(webhook, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_throw_exception_when_schema_is_not_found() |
|||
{ |
|||
var context = CreateContextForCommand(new UpdateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId }); |
|||
|
|||
A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns((ISchemaEntity)null); |
|||
|
|||
CreateWebhook(); |
|||
|
|||
await Assert.ThrowsAsync<ValidationException>(async () => |
|||
{ |
|||
await TestCreate(webhook, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Delete_should_update_domain_object() |
|||
{ |
|||
CreateWebhook(); |
|||
|
|||
var command = CreateContextForCommand(new DeleteWebhook { WebhookId = webhookId }); |
|||
|
|||
await TestUpdate(webhook, async _ => |
|||
{ |
|||
await sut.HandleAsync(command); |
|||
}); |
|||
} |
|||
|
|||
private void CreateWebhook() |
|||
{ |
|||
webhook.Create(new CreateWebhook { Url = url }); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,159 @@ |
|||
// ==========================================================================
|
|||
// WebhookDomainObjectTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Squidex.Domain.Apps.Events.Webhooks; |
|||
using Squidex.Domain.Apps.Write.TestHelpers; |
|||
using Squidex.Domain.Apps.Write.Webhooks.Commands; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.CQRS; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Write.Webhooks |
|||
{ |
|||
public class WebhookDomainObjectTests : HandlerTestBase<WebhookDomainObject> |
|||
{ |
|||
private readonly Uri url = new Uri("http://squidex.io"); |
|||
private readonly WebhookDomainObject sut; |
|||
|
|||
public Guid WebhookId { get; } = Guid.NewGuid(); |
|||
|
|||
public WebhookDomainObjectTests() |
|||
{ |
|||
sut = new WebhookDomainObject(WebhookId, 0); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_throw_exception_if_created() |
|||
{ |
|||
sut.Create(new CreateWebhook { Url = url }); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Create(CreateWebhookCommand(new CreateWebhook { Url = url })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_create_events() |
|||
{ |
|||
var command = new CreateWebhook { Url = url }; |
|||
|
|||
sut.Create(CreateWebhookCommand(command)); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateWebhookEvent(new WebhookCreated |
|||
{ |
|||
Url = url, |
|||
Schemas = command.Schemas, |
|||
SharedSecret = command.SharedSecret, |
|||
WebhookId = command.WebhookId |
|||
}) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateWebhookCommand(new UpdateWebhook { Url = url })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_throw_exception_if_webhook_is_deleted() |
|||
{ |
|||
CreateWebhook(); |
|||
DeleteWebhook(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateWebhookCommand(new UpdateWebhook { Url = url })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_create_events() |
|||
{ |
|||
CreateWebhook(); |
|||
|
|||
var command = new UpdateWebhook { Url = url }; |
|||
|
|||
sut.Update(CreateWebhookCommand(command)); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateWebhookEvent(new WebhookUpdated { Url = url, Schemas = command.Schemas }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Delete(CreateWebhookCommand(new DeleteWebhook())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_throw_exception_if_already_deleted() |
|||
{ |
|||
CreateWebhook(); |
|||
DeleteWebhook(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Delete(CreateWebhookCommand(new DeleteWebhook())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_update_properties_create_events() |
|||
{ |
|||
CreateWebhook(); |
|||
|
|||
sut.Delete(CreateWebhookCommand(new DeleteWebhook())); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateWebhookEvent(new WebhookDeleted()) |
|||
); |
|||
} |
|||
|
|||
private void CreateWebhook() |
|||
{ |
|||
sut.Create(CreateWebhookCommand(new CreateWebhook { Url = url })); |
|||
|
|||
((IAggregate)sut).ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void DeleteWebhook() |
|||
{ |
|||
sut.Delete(CreateWebhookCommand(new DeleteWebhook())); |
|||
|
|||
((IAggregate)sut).ClearUncommittedEvents(); |
|||
} |
|||
|
|||
protected T CreateWebhookEvent<T>(T @event) where T : WebhookEvent |
|||
{ |
|||
@event.WebhookId = WebhookId; |
|||
|
|||
return CreateEvent(@event); |
|||
} |
|||
|
|||
protected T CreateWebhookCommand<T>(T command) where T : WebhookAggregateCommand |
|||
{ |
|||
command.WebhookId = WebhookId; |
|||
|
|||
return CreateCommand(command); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue