diff --git a/src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs b/src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs index 940605f44..859e546ef 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs @@ -36,9 +36,9 @@ namespace Squidex.Domain.Apps.Core.Contents new Dictionary { [Status.Archived] = new WorkflowTransition(), - [Status.Published] = new WorkflowTransition() + [Status.Draft] = new WorkflowTransition() }, - StatusColors.Archived) + StatusColors.Published) }, Status.Draft); public IReadOnlyDictionary Steps { get; } diff --git a/src/Squidex/Config/Domain/EntitiesServices.cs b/src/Squidex/Config/Domain/EntitiesServices.cs index ce886628c..47c2e44b9 100644 --- a/src/Squidex/Config/Domain/EntitiesServices.cs +++ b/src/Squidex/Config/Domain/EntitiesServices.cs @@ -120,7 +120,7 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .As(); - services.AddSingletonAs() + services.AddSingletonAs() .AsOptional(); services.AddSingletonAs() diff --git a/src/Squidex/app/shared/services/contributors.service.ts b/src/Squidex/app/shared/services/contributors.service.ts index a7cdeac72..def17609a 100644 --- a/src/Squidex/app/shared/services/contributors.service.ts +++ b/src/Squidex/app/shared/services/contributors.service.ts @@ -79,7 +79,7 @@ export class ContributorsService { const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors`); return HTTP.postVersioned(this.http, url, dto, version).pipe( - mapVersioned(({ body }) => { + mapVersioned(({ body }) => { return parseContributors(body); }), tap(() => { diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InviteUserCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InviteUserCommandMiddlewareTests.cs index 32a17a952..43070abf8 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InviteUserCommandMiddlewareTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InviteUserCommandMiddlewareTests.cs @@ -34,13 +34,13 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation A.CallTo(() => userResolver.CreateUserIfNotExists("me@email.com", true)) .Returns(true); - var result = A.Fake(); + var app = A.Fake(); - context.Complete(result); + context.Complete(app); await sut.HandleAsync(context); - Assert.Same(context.Result().App, result); + Assert.Same(context.Result().App, app); A.CallTo(() => userResolver.CreateUserIfNotExists("me@email.com", true)) .MustHaveHappened(); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs index 045966cce..197f88146 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs @@ -36,7 +36,7 @@ namespace Squidex.Domain.Apps.Entities.Contents private readonly IContentRepository contentRepository = A.Dummy(); private readonly IContentWorkflow contentWorkflow = A.Fake(x => x.Wrapping(new DefaultContentWorkflow())); private readonly IAppProvider appProvider = A.Fake(); - private readonly IAppEntity app = A.Fake(); + private readonly IAppEntity appEntity = A.Fake(); private readonly LanguagesConfig languagesConfig = LanguagesConfig.Build(Language.DE); private readonly NamedContentData invalidData = @@ -92,10 +92,10 @@ namespace Squidex.Domain.Apps.Entities.Contents new NumberFieldProperties { IsRequired = false }) .ConfigureScripts(scripts); - A.CallTo(() => app.LanguagesConfig).Returns(languagesConfig); + A.CallTo(() => appEntity.LanguagesConfig).Returns(languagesConfig); - A.CallTo(() => appProvider.GetAppAsync(AppName)).Returns(app); - A.CallTo(() => appProvider.GetAppWithSchemaAsync(AppId, SchemaId)).Returns((app, schema)); + A.CallTo(() => appProvider.GetAppAsync(AppName)).Returns(appEntity); + A.CallTo(() => appProvider.GetAppWithSchemaAsync(AppId, SchemaId)).Returns((appEntity, schema)); A.CallTo(() => schema.SchemaDef).Returns(schemaDef); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs new file mode 100644 index 000000000..037fcb694 --- /dev/null +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs @@ -0,0 +1,262 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using FakeItEasy; +using FluentAssertions; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.Scripting; +using Squidex.Domain.Apps.Entities.Apps; +using Squidex.Domain.Apps.Entities.Schemas; +using Squidex.Infrastructure; +using Xunit; + +namespace Squidex.Domain.Apps.Entities.Contents +{ + public class DynamicContentWorkflowTests + { + private readonly NamedId appId = NamedId.Of(Guid.NewGuid(), "my-app"); + private readonly IAppProvider appProvider = A.Fake(); + private readonly IAppEntity appEntity = A.Fake(); + private readonly DynamicContentWorkflow sut; + + private readonly Workflow workflow = new Workflow( + new Dictionary + { + [Status.Archived] = + new WorkflowStep( + new Dictionary + { + [Status.Draft] = new WorkflowTransition() + }, + StatusColors.Archived, true), + [Status.Draft] = + new WorkflowStep( + new Dictionary + { + [Status.Archived] = new WorkflowTransition(), + [Status.Published] = new WorkflowTransition("data.field.iv === 2", "Editor") + }, + StatusColors.Draft), + [Status.Published] = + new WorkflowStep( + new Dictionary + { + [Status.Archived] = new WorkflowTransition(), + [Status.Draft] = new WorkflowTransition() + }, + StatusColors.Published) + }, + Status.Draft); + + public DynamicContentWorkflowTests() + { + A.CallTo(() => appProvider.GetAppAsync(appId.Id)) + .Returns(appEntity); + + A.CallTo(() => appEntity.Workflows) + .Returns(Workflows.Empty.Set(workflow)); + + sut = new DynamicContentWorkflow(new JintScriptEngine(), appProvider); + } + + [Fact] + public async Task Should_draft_as_initial_status() + { + var expected = new StatusInfo(Status.Draft, StatusColors.Draft); + + var result = await sut.GetInitialStatusAsync(CreateSchema()); + + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public async Task Should_check_is_valid_next() + { + var content = CreateContent(Status.Draft, 2); + + var result = await sut.CanMoveToAsync(content, Status.Published, User("Editor")); + + Assert.True(result); + } + + [Fact] + public async Task Should_not_allow_transition_if_role_is_not_allowed() + { + var content = CreateContent(Status.Draft, 2); + + var result = await sut.CanMoveToAsync(content, Status.Published, User("Developer")); + + Assert.False(result); + } + + [Fact] + public async Task Should_not_allow_transition_if_expression_does_not_evauate_to_true() + { + var content = CreateContent(Status.Draft, 4); + + var result = await sut.CanMoveToAsync(content, Status.Published, User("Editor")); + + Assert.False(result); + } + + [Fact] + public async Task Should_be_able_to_update_published() + { + var content = CreateContent(Status.Published, 2); + + var result = await sut.CanUpdateAsync(content); + + Assert.True(result); + } + + [Fact] + public async Task Should_be_able_to_update_draft() + { + var content = CreateContent(Status.Published, 2); + + var result = await sut.CanUpdateAsync(content); + + Assert.True(result); + } + + [Fact] + public async Task Should_not_be_able_to_update_archived() + { + var content = CreateContent(Status.Archived, 2); + + var result = await sut.CanUpdateAsync(content); + + Assert.False(result); + } + + [Fact] + public async Task Should_get_next_statuses_for_draft() + { + var content = CreateContent(Status.Draft, 2); + + var expected = new[] + { + new StatusInfo(Status.Archived, StatusColors.Archived) + }; + + var result = await sut.GetNextsAsync(content, User("Developer")); + + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public async Task Should_limit_next_statuses_if_expression_does_not_evauate_to_true() + { + var content = CreateContent(Status.Draft, 4); + + var expected = new[] + { + new StatusInfo(Status.Archived, StatusColors.Archived) + }; + + var result = await sut.GetNextsAsync(content, User("Editor")); + + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public async Task Should_limit_next_statuses_if_role_is_not_allowed() + { + var content = CreateContent(Status.Draft, 2); + + var expected = new[] + { + new StatusInfo(Status.Archived, StatusColors.Archived), + new StatusInfo(Status.Published, StatusColors.Published) + }; + + var result = await sut.GetNextsAsync(content, User("Editor")); + + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public async Task Should_get_next_statuses_for_archived() + { + var content = CreateContent(Status.Archived, 2); + + var expected = new[] + { + new StatusInfo(Status.Draft, StatusColors.Draft) + }; + + var result = await sut.GetNextsAsync(content, null); + + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public async Task Should_get_next_statuses_for_published() + { + var content = CreateContent(Status.Published, 2); + + var expected = new[] + { + new StatusInfo(Status.Archived, StatusColors.Archived), + new StatusInfo(Status.Draft, StatusColors.Draft) + }; + + var result = await sut.GetNextsAsync(content, null); + + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public async Task Should_return_all_statuses() + { + var expected = new[] + { + new StatusInfo(Status.Archived, StatusColors.Archived), + new StatusInfo(Status.Draft, StatusColors.Draft), + new StatusInfo(Status.Published, StatusColors.Published) + }; + + var result = await sut.GetAllAsync(CreateSchema()); + + result.Should().BeEquivalentTo(expected); + } + + private ISchemaEntity CreateSchema() + { + var schema = A.Fake(); + + A.CallTo(() => schema.AppId).Returns(appId); + + return schema; + } + + private IContentEntity CreateContent(Status status, int value) + { + var data = + new NamedContentData() + .AddField("field", + new ContentFieldData() + .AddValue("iv", value)); + + return new ContentEntity { AppId = appId, Status = status, DataDraft = data }; + } + + private ClaimsPrincipal User(string role) + { + var userIdentity = new ClaimsIdentity(); + var userPrincipal = new ClaimsPrincipal(userIdentity); + + userIdentity.AddClaim(new Claim(ClaimTypes.Role, role)); + + return userPrincipal; + } + } +} diff --git a/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs b/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs index 5c4ff17fe..a249eacf5 100644 --- a/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs +++ b/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs @@ -32,12 +32,12 @@ namespace Squidex.Web.CommandMiddlewares A.CallTo(() => httpContextAccessor.HttpContext) .Returns(httpContext); - var appEntity = A.Fake(); + var app = A.Fake(); - A.CallTo(() => appEntity.Id).Returns(appId.Id); - A.CallTo(() => appEntity.Name).Returns(appId.Name); + A.CallTo(() => app.Id).Returns(appId.Id); + A.CallTo(() => app.Name).Returns(appId.Name); - httpContext.Features.Set(new AppResolver.AppFeature(appEntity)); + httpContext.Features.Set(new AppResolver.AppFeature(app)); sut = new EnrichWithAppIdCommandMiddleware(httpContextAccessor); } diff --git a/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs b/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs index 4513b51b6..bf5b185d1 100644 --- a/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs +++ b/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs @@ -44,22 +44,22 @@ namespace Squidex.Web.CommandMiddlewares A.CallTo(() => actionContextAccessor.ActionContext) .Returns(actionContext); - var appEntity = A.Fake(); + var app = A.Fake(); - A.CallTo(() => appEntity.Id).Returns(appId.Id); - A.CallTo(() => appEntity.Name).Returns(appId.Name); + A.CallTo(() => app.Id).Returns(appId.Id); + A.CallTo(() => app.Name).Returns(appId.Name); - httpContext.Features.Set(new AppResolver.AppFeature(appEntity)); + httpContext.Features.Set(new AppResolver.AppFeature(app)); - var schemaEntity = A.Fake(); + var schema = A.Fake(); - A.CallTo(() => schemaEntity.Id).Returns(schemaId.Id); - A.CallTo(() => schemaEntity.SchemaDef).Returns(new Schema(schemaId.Name)); + A.CallTo(() => schema.Id).Returns(schemaId.Id); + A.CallTo(() => schema.SchemaDef).Returns(new Schema(schemaId.Name)); A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Name)) - .Returns(schemaEntity); + .Returns(schema); A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false)) - .Returns(schemaEntity); + .Returns(schema); sut = new EnrichWithSchemaIdCommandMiddleware(appProvider, actionContextAccessor); } diff --git a/tests/Squidex.Web.Tests/Pipeline/AppResolverTests.cs b/tests/Squidex.Web.Tests/Pipeline/AppResolverTests.cs index 4aebe79cb..b7f7b00b5 100644 --- a/tests/Squidex.Web.Tests/Pipeline/AppResolverTests.cs +++ b/tests/Squidex.Web.Tests/Pipeline/AppResolverTests.cs @@ -161,34 +161,34 @@ namespace Squidex.Web.Pipeline private static IAppEntity CreateApp(string name, string appUser = null, string appClient = null) { - var app = A.Fake(); + var appEntity = A.Fake(); if (appUser != null) { - A.CallTo(() => app.Contributors) + A.CallTo(() => appEntity.Contributors) .Returns(AppContributors.Empty.Assign(appUser, Role.Owner)); } else { - A.CallTo(() => app.Contributors) + A.CallTo(() => appEntity.Contributors) .Returns(AppContributors.Empty); } if (appClient != null) { - A.CallTo(() => app.Clients) + A.CallTo(() => appEntity.Clients) .Returns(AppClients.Empty.Add(appClient, "secret")); } else { - A.CallTo(() => app.Clients) + A.CallTo(() => appEntity.Clients) .Returns(AppClients.Empty); } - A.CallTo(() => app.Roles) + A.CallTo(() => appEntity.Roles) .Returns(Roles.CreateDefaults(name)); - return app; + return appEntity; } } }