From ca140c81253ee92e13834b7c4794f8444362cb43 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Thu, 2 Nov 2017 20:04:30 +0100 Subject: [PATCH] Missing tests added --- .../Apps/LanguagesConfig.cs | 2 +- .../Model/Apps/LanguagesConfigTests.cs | 8 + .../Model/PartitioningTests.cs | 40 +++ .../HandleRules/RuleServiceTests.cs | 229 ++++++++++++++++++ .../Triggers/ContentChangedTriggerTests.cs | 88 +++++++ .../Squidex.Domain.Apps.Core.Tests.csproj | 1 + .../Schemas/Guards/GuardSchemaFieldTests.cs | 14 +- 7 files changed, 379 insertions(+), 3 deletions(-) create mode 100644 tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs create mode 100644 tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs index 57f857877..d9c2c24e9 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs @@ -99,7 +99,7 @@ namespace Squidex.Domain.Apps.Core.Apps var newMaster = state.Master.Language != language ? state.Master : - state.Languages.Values.FirstOrDefault(); + newLanguages.Values.FirstOrDefault(); state = new State(newLanguages, newMaster); } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigTests.cs index a3fb1aafa..cfb46ba91 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigTests.cs @@ -172,6 +172,14 @@ namespace Squidex.Domain.Apps.Core.Model.Apps config.Remove(Language.EN); } + [Fact] + public void Should_hrow_exception_if_language_to_remove_is_master() + { + var config = LanguagesConfig.Build(Language.DE); + + Assert.Throws(() => config.Remove(Language.DE)); + } + [Fact] public void Should_update_language() { diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/PartitioningTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/PartitioningTests.cs index 10ed1df6f..49498115b 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/PartitioningTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/PartitioningTests.cs @@ -12,6 +12,46 @@ namespace Squidex.Domain.Apps.Core.Model { public sealed class PartitioningTests { + [Fact] + public void Should_consider_null_as_valid_partitioning() + { + string partitioning = null; + + Assert.True(partitioning.IsValidPartitioning()); + } + + [Fact] + public void Should_consider_invariant_as_valid_partitioning() + { + var partitioning = "invariant"; + + Assert.True(partitioning.IsValidPartitioning()); + } + + [Fact] + public void Should_consider_language_as_valid_partitioning() + { + var partitioning = "language"; + + Assert.True(partitioning.IsValidPartitioning()); + } + + [Fact] + public void Should_not_consider_empty_as_valid_partitioning() + { + var partitioning = string.Empty; + + Assert.False(partitioning.IsValidPartitioning()); + } + + [Fact] + public void Should_not_consider_other_string_as_valid_partitioning() + { + var partitioning = "invalid"; + + Assert.False(partitioning.IsValidPartitioning()); + } + [Fact] public void Should_provide_invariant_instance() { diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs new file mode 100644 index 000000000..207fdf0fc --- /dev/null +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs @@ -0,0 +1,229 @@ +// ========================================================================== +// RuleServiceTests.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using FakeItEasy; +using NodaTime; +using Squidex.Domain.Apps.Core.HandleRules; +using Squidex.Domain.Apps.Core.Rules; +using Squidex.Domain.Apps.Core.Rules.Actions; +using Squidex.Domain.Apps.Core.Rules.Triggers; +using Squidex.Domain.Apps.Events; +using Squidex.Domain.Apps.Events.Contents; +using Squidex.Infrastructure; +using Squidex.Infrastructure.CQRS.Events; +using Xunit; + +#pragma warning disable xUnit2009 // Do not use boolean check to check for string equality + +namespace Squidex.Domain.Apps.Core.Operations.HandleRules +{ + public class RuleServiceTests + { + private readonly IRuleTriggerHandler ruleTriggerHandler = A.Fake(); + private readonly IRuleActionHandler ruleActionHandler = A.Fake(); + private readonly IClock clock = A.Fake(); + private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry(); + private readonly RuleService sut; + + public sealed class InvalidEvent : IEvent + { + } + + public sealed class InvalidAction : RuleAction + { + public override T Accept(IRuleActionVisitor visitor) + { + return default(T); + } + } + + public sealed class InvalidTrigger : RuleTrigger + { + public override T Accept(IRuleTriggerVisitor visitor) + { + return default(T); + } + } + + public RuleServiceTests() + { + typeNameRegistry.Map(typeof(ContentCreated)); + typeNameRegistry.Map(typeof(WebhookAction)); + + A.CallTo(() => ruleActionHandler.ActionType) + .Returns(typeof(WebhookAction)); + + A.CallTo(() => ruleTriggerHandler.TriggerType) + .Returns(typeof(ContentChangedTrigger)); + + sut = new RuleService(new[] { ruleTriggerHandler }, new[] { ruleActionHandler }, clock, typeNameRegistry); + } + + [Fact] + public void Should_not_create_job_for_invalid_event() + { + var ruleConfig = new Rule(new ContentChangedTrigger(), new WebhookAction()); + var ruleEnvelope = Envelope.Create(new InvalidEvent()); + + var job = sut.CreateJob(ruleConfig, ruleEnvelope); + + Assert.Null(job); + } + + [Fact] + public void Should_not_create_trigger_if_no_trigger_handler_registered() + { + var ruleConfig = new Rule(new InvalidTrigger(), new WebhookAction()); + var ruleEnvelope = Envelope.Create(new ContentCreated()); + + var job = sut.CreateJob(ruleConfig, ruleEnvelope); + + Assert.Null(job); + } + + [Fact] + public void Should_not_create_trigger_if_no_action_handler_registered() + { + var ruleConfig = new Rule(new ContentChangedTrigger(), new InvalidAction()); + var ruleEnvelope = Envelope.Create(new ContentCreated()); + + var job = sut.CreateJob(ruleConfig, ruleEnvelope); + + Assert.Null(job); + } + + [Fact] + public void Should_not_create_if_not_triggered() + { + var ruleConfig = new Rule(new ContentChangedTrigger(), new InvalidAction()); + var ruleEnvelope = Envelope.Create(new ContentCreated()); + + A.CallTo(() => ruleTriggerHandler.Triggers(A>.Ignored, ruleConfig.Trigger)) + .Returns(false); + + var job = sut.CreateJob(ruleConfig, ruleEnvelope); + + Assert.Null(job); + } + + [Fact] + public void Should_create_job_if_triggeres() + { + var e = new ContentCreated { SchemaId = new NamedId(Guid.NewGuid(), "my-schema"), AppId = new NamedId(Guid.NewGuid(), "my-event") }; + + var now = SystemClock.Instance.GetCurrentInstant(); + + var ruleConfig = new Rule(new ContentChangedTrigger(), new WebhookAction()); + var ruleEnvelope = Envelope.Create(e); + + var actionName = "WebhookAction"; + var actionData = new RuleJobData(); + var actionDescription = "MyDescription"; + + var eventName = "MySchemaCreatedEvent"; + + A.CallTo(() => clock.GetCurrentInstant()) + .Returns(now); + + A.CallTo(() => ruleTriggerHandler.Triggers(A>.Ignored, ruleConfig.Trigger)) + .Returns(true); + + A.CallTo(() => ruleActionHandler.CreateJob(A>.Ignored, eventName, ruleConfig.Action)) + .Returns((actionDescription, actionData)); + + var job = sut.CreateJob(ruleConfig, ruleEnvelope); + + Assert.Equal(eventName, job.EventName); + + Assert.Equal(actionData, job.ActionData); + Assert.Equal(actionName, job.ActionName); + Assert.Equal(actionDescription, job.Description); + + Assert.Equal(now, job.Created); + Assert.Equal(now.Plus(Duration.FromDays(2)), job.Expires); + + Assert.Equal(e.AppId.Id, job.AppId); + + Assert.NotEqual(Guid.Empty, job.RuleId); + } + + [Fact] + public async Task Should_return_succeeded_job_with_full_dump_when_handler_returns_no_exception() + { + var ruleJob = new RuleJobData(); + var ruleEx = new InvalidOperationException(); + + var actionDump = "MyDump"; + + A.CallTo(() => ruleActionHandler.ExecuteJobAsync(ruleJob)) + .Returns((actionDump, null)); + + var result = await sut.InvokeAsync("WebhookAction", ruleJob); + + Assert.Equal(RuleResult.Success, result.Result); + + Assert.True(result.Elapsed >= TimeSpan.Zero); + Assert.StartsWith(actionDump, result.Dump, StringComparison.OrdinalIgnoreCase); + } + + [Fact] + public async Task Should_return_failed_job_with_full_dump_when_handler_returns_exception() + { + var ruleJob = new RuleJobData(); + var ruleEx = new InvalidOperationException(); + + var actionDump = "MyDump"; + + A.CallTo(() => ruleActionHandler.ExecuteJobAsync(ruleJob)) + .Returns((actionDump, new InvalidOperationException())); + + var result = await sut.InvokeAsync("WebhookAction", ruleJob); + + Assert.Equal(RuleResult.Failed, result.Result); + + Assert.True(result.Elapsed >= TimeSpan.Zero); + Assert.True(result.Dump.StartsWith(actionDump, StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public async Task Should_return_timedout_job_with_full_dump_when_exception_from_handler_indicates_timeout() + { + var ruleJob = new RuleJobData(); + var ruleEx = new InvalidOperationException(); + + var actionDump = "MyDump"; + + A.CallTo(() => ruleActionHandler.ExecuteJobAsync(ruleJob)) + .Returns((actionDump, new TimeoutException())); + + var result = await sut.InvokeAsync("WebhookAction", ruleJob); + + Assert.Equal(RuleResult.Timeout, result.Result); + + Assert.True(result.Elapsed >= TimeSpan.Zero); + Assert.True(result.Dump.StartsWith(actionDump, StringComparison.OrdinalIgnoreCase)); + Assert.True(result.Dump.IndexOf("Action timed out.", StringComparison.OrdinalIgnoreCase) >= 0); + } + + [Fact] + public async Task Should_create_exception_details_when_job_to_execute_failed() + { + var ruleJob = new RuleJobData(); + var ruleEx = new InvalidOperationException(); + + A.CallTo(() => ruleActionHandler.ExecuteJobAsync(ruleJob)) + .Throws(ruleEx); + + var result = await sut.InvokeAsync("WebhookAction", ruleJob); + + Assert.Equal((ruleEx.ToString(), RuleResult.Failed, TimeSpan.Zero), result); + } + } +} \ No newline at end of file diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs new file mode 100644 index 000000000..db51764cb --- /dev/null +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs @@ -0,0 +1,88 @@ +// ========================================================================== +// ContentChangedTriggerTests.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Collections.Generic; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.HandleRules; +using Squidex.Domain.Apps.Core.HandleRules.Triggers; +using Squidex.Domain.Apps.Core.Rules.Triggers; +using Squidex.Domain.Apps.Events; +using Squidex.Domain.Apps.Events.Contents; +using Squidex.Domain.Apps.Events.Rules; +using Squidex.Domain.Apps.Events.Schemas; +using Squidex.Infrastructure; +using Squidex.Infrastructure.CQRS.Events; +using Xunit; + +namespace Squidex.Domain.Apps.Core.Operations.HandleRules.Triggers +{ + public sealed class ContentChangedTriggerTests + { + private readonly IRuleTriggerHandler sut = new ContentChangedTriggerHandler(); + private static readonly NamedId SchemaMatch = new NamedId(Guid.NewGuid(), "my-schema1"); + private static readonly NamedId SchemaNonMatch = new NamedId(Guid.NewGuid(), "my-schema2"); + + public static IEnumerable TestData + { + get + { + return new[] + { + new object[] { 0, 1, 1, 1, 1, new RuleCreated() }, + new object[] { 0, 1, 1, 1, 1, new ContentCreated { SchemaId = SchemaNonMatch } }, + new object[] { 1, 1, 0, 0, 0, new ContentCreated { SchemaId = SchemaMatch } }, + new object[] { 0, 0, 0, 0, 0, new ContentCreated { SchemaId = SchemaMatch } }, + new object[] { 1, 0, 1, 0, 0, new ContentUpdated { SchemaId = SchemaMatch } }, + new object[] { 0, 0, 0, 0, 0, new ContentUpdated { SchemaId = SchemaMatch } }, + new object[] { 1, 0, 0, 1, 0, new ContentDeleted { SchemaId = SchemaMatch } }, + new object[] { 0, 0, 0, 0, 0, new ContentDeleted { SchemaId = SchemaMatch } }, + new object[] { 1, 0, 0, 0, 1, new ContentStatusChanged { SchemaId = SchemaMatch, Status = Status.Published } }, + new object[] { 0, 0, 0, 0, 0, new ContentStatusChanged { SchemaId = SchemaMatch, Status = Status.Published } }, + new object[] { 0, 1, 1, 1, 1, new ContentStatusChanged { SchemaId = SchemaMatch, Status = Status.Archived } }, + new object[] { 0, 1, 1, 1, 1, new ContentStatusChanged { SchemaId = SchemaMatch, Status = Status.Draft } }, + new object[] { 0, 1, 1, 1, 1, new SchemaCreated { SchemaId = SchemaNonMatch } } + }; + } + } + + [Fact] + public void Should_return_false_when_trigger_contains_no_schemas() + { + var trigger = new ContentChangedTrigger(); + + var result = sut.Triggers(new Envelope(new ContentCreated()), trigger); + + Assert.False(result); + } + + [Theory] + [MemberData(nameof(TestData))] + public void Should_return_result_depending_on_event(int expected, int sendCreate, int sendUpdate, int sendDelete, int sendPublish, AppEvent @event) + { + var trigger = new ContentChangedTrigger + { + Schemas = new List + { + new ContentChangedTriggerSchema + { + SendCreate = sendCreate == 1, + SendUpdate = sendUpdate == 1, + SendDelete = sendDelete == 1, + SendPublish = sendPublish == 1, + SchemaId = SchemaMatch.Id + } + } + }; + + var result = sut.Triggers(new Envelope(@event), trigger); + + Assert.Equal(expected == 1, result); + } + } +} diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj b/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj index db37388f0..d71558483 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj +++ b/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj @@ -10,6 +10,7 @@ + diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaFieldTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaFieldTests.cs index cf61d7f51..63bb7bdda 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaFieldTests.cs @@ -18,6 +18,8 @@ namespace Squidex.Domain.Apps.Write.Schemas public class GuardSchemaFieldTests { private readonly Schema schema = new Schema("my-schema"); + private readonly StringFieldProperties validProperties = new StringFieldProperties(); + private readonly StringFieldProperties invalidProperties = new StringFieldProperties { MinLength = 10, MaxLength = 5 }; public GuardSchemaFieldTests() { @@ -210,7 +212,7 @@ namespace Squidex.Domain.Apps.Write.Schemas [Fact] public void CanAdd_should_throw_exception_if_name_not_valid() { - var command = new AddField { Name = "INVALID_NAME", Properties = new StringFieldProperties() }; + var command = new AddField { Name = "INVALID_NAME", Properties = validProperties }; Assert.Throws(() => GuardSchemaField.CanAdd(schema, command)); } @@ -218,7 +220,15 @@ namespace Squidex.Domain.Apps.Write.Schemas [Fact] public void CanAdd_should_throw_exception_if_properties_not_valid() { - var command = new AddField { Name = "field3", Properties = new StringFieldProperties { MinLength = 10, MaxLength = 5 } }; + var command = new AddField { Name = "field3", Properties = invalidProperties }; + + Assert.Throws(() => GuardSchemaField.CanAdd(schema, command)); + } + + [Fact] + public void CanAdd_should_throw_exception_if_partitioning_not_valid() + { + var command = new AddField { Name = "field3", Partitioning = "INVALID_PARTITIONING", Properties = validProperties }; Assert.Throws(() => GuardSchemaField.CanAdd(schema, command)); }