diff --git a/backend/extensions/Squidex.Extensions/Validation/CompositeUniqueValidatorFactory.cs b/backend/extensions/Squidex.Extensions/Validation/CompositeUniqueValidatorFactory.cs index 944ad88fc..0ca427f3b 100644 --- a/backend/extensions/Squidex.Extensions/Validation/CompositeUniqueValidatorFactory.cs +++ b/backend/extensions/Squidex.Extensions/Validation/CompositeUniqueValidatorFactory.cs @@ -25,7 +25,7 @@ namespace Squidex.Extensions.Validation this.contentRepository = contentRepository; } - public IEnumerable CreateContentValidators(ValidatorContext context, FieldValidatorFactory createFieldValidator) + public IEnumerable CreateContentValidators(ValidatorContext context, ValidatorFactory createFieldValidator) { foreach (var validatorTag in ValidatorTags(context.Schema.Properties.Tags)) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs index c6decb238..a0f72dc42 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs @@ -118,7 +118,12 @@ namespace Squidex.Domain.Apps.Core.ValidateContent private IValidator CreateFieldValidator(IField field) { - return new FieldValidator(CreateValueValidators(field), field); + return new FieldValidator(CreateValueValidator(field), field); + } + + private IValidator CreateValueValidator(IField field) + { + return new AggregateValidator(CreateValueValidators(field), log); } private IEnumerable CreateContentValidators() diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/DefaultFieldValueValidatorsFactory.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/DefaultFieldValueValidatorsFactory.cs index e261e1945..d3add88ee 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/DefaultFieldValueValidatorsFactory.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/DefaultFieldValueValidatorsFactory.cs @@ -18,15 +18,15 @@ namespace Squidex.Domain.Apps.Core.ValidateContent internal sealed class DefaultFieldValueValidatorsFactory : IFieldVisitor> { private readonly ValidatorContext context; - private readonly FieldValidatorFactory createFieldValidator; + private readonly ValidatorFactory createFieldValidator; - private DefaultFieldValueValidatorsFactory(ValidatorContext context, FieldValidatorFactory createFieldValidator) + private DefaultFieldValueValidatorsFactory(ValidatorContext context, ValidatorFactory createFieldValidator) { this.context = context; this.createFieldValidator = createFieldValidator; } - public static IEnumerable CreateValidators(ValidatorContext context, IField field, FieldValidatorFactory createFieldValidator) + public static IEnumerable CreateValidators(ValidatorContext context, IField field, ValidatorFactory createFieldValidator) { Guard.NotNull(context, nameof(context)); Guard.NotNull(field, nameof(field)); diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/DefaultValidatorsFactory.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/DefaultValidatorsFactory.cs index df8bd5d56..b98abc98b 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/DefaultValidatorsFactory.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/DefaultValidatorsFactory.cs @@ -13,7 +13,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent { public sealed class DefaultValidatorsFactory : IValidatorsFactory { - public IEnumerable CreateFieldValidators(ValidatorContext context, IField field, FieldValidatorFactory createFieldValidator) + public IEnumerable CreateFieldValidators(ValidatorContext context, IField field, ValidatorFactory createFieldValidator) { if (field is IField) { @@ -21,7 +21,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent } } - public IEnumerable CreateValueValidators(ValidatorContext context, IField field, FieldValidatorFactory createFieldValidator) + public IEnumerable CreateValueValidators(ValidatorContext context, IField field, ValidatorFactory createFieldValidator) { return DefaultFieldValueValidatorsFactory.CreateValidators(context, field, createFieldValidator); } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/IValidatorsFactory.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/IValidatorsFactory.cs index e0b982325..7458902d4 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/IValidatorsFactory.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/IValidatorsFactory.cs @@ -10,21 +10,21 @@ using Squidex.Domain.Apps.Core.Schemas; namespace Squidex.Domain.Apps.Core.ValidateContent { - public delegate IValidator FieldValidatorFactory(IField field); + public delegate IValidator ValidatorFactory(IField field); public interface IValidatorsFactory { - IEnumerable CreateFieldValidators(ValidatorContext context, IField field, FieldValidatorFactory createFieldValidator) + IEnumerable CreateFieldValidators(ValidatorContext context, IField field, ValidatorFactory createFieldValidator) { yield break; } - IEnumerable CreateValueValidators(ValidatorContext context, IField field, FieldValidatorFactory createFieldValidator) + IEnumerable CreateValueValidators(ValidatorContext context, IField field, ValidatorFactory createFieldValidator) { yield break; } - IEnumerable CreateContentValidators(ValidatorContext context, FieldValidatorFactory createFieldValidator) + IEnumerable CreateContentValidators(ValidatorContext context, ValidatorFactory createFieldValidator) { yield break; } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs index d7afe8647..c831e445a 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using NodaTime.Text; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure; using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Validation; @@ -36,12 +37,12 @@ namespace Squidex.Domain.Apps.Core.ValidateContent public (object? Result, JsonError? Error) Visit(IField field) { - return ConvertToStringList(); + return ConvertToIdList(); } public (object? Result, JsonError? Error) Visit(IField field) { - return ConvertToStringList(); + return ConvertToIdList(); } public (object? Result, JsonError? Error) Visit(IField field) @@ -153,6 +154,30 @@ namespace Squidex.Domain.Apps.Core.ValidateContent return (value, null); } + private (object? Result, JsonError? Error) ConvertToIdList() + { + if (value is JsonArray array) + { + var result = new List(array.Count); + + foreach (var item in array) + { + if (item is JsonString s && !string.IsNullOrWhiteSpace(s.Value)) + { + result.Add(DomainId.Create(s.Value)); + } + else + { + return (null, new JsonError("Invalid json type, expected array of strings.")); + } + } + + return (result, null); + } + + return (null, new JsonError("Invalid json type, expected array of strings.")); + } + private (object? Result, JsonError? Error) ConvertToStringList() { if (value is JsonArray array) diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/AggregateValidator.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/AggregateValidator.cs index 258348e11..d4d31a22e 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/AggregateValidator.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/AggregateValidator.cs @@ -14,7 +14,7 @@ using Squidex.Infrastructure.Translations; namespace Squidex.Domain.Apps.Core.ValidateContent.Validators { - internal sealed class AggregateValidator : IValidator + public sealed class AggregateValidator : IValidator { private readonly IValidator[]? validators; private readonly ISemanticLog log; diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/CollectionItemValidator.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/CollectionItemValidator.cs index 42d4485da..33536b378 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/CollectionItemValidator.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/CollectionItemValidator.cs @@ -14,13 +14,13 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators { public sealed class CollectionItemValidator : IValidator { - private readonly IValidator[] itemValidators; + private readonly IValidator itemValidator; - public CollectionItemValidator(params IValidator[] itemValidators) + public CollectionItemValidator(IValidator itemValidator) { - Guard.NotEmpty(itemValidators, nameof(itemValidators)); + Guard.NotNull(itemValidator, nameof(itemValidator)); - this.itemValidators = itemValidators; + this.itemValidator = itemValidator; } public async Task ValidateAsync(object? value, ValidationContext context, AddError addError) @@ -34,10 +34,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators { var innerContext = context.Nested($"[{index}]"); - foreach (var itemValidator in itemValidators) - { - innerTasks.Add(itemValidator.ValidateAsync(item, innerContext, addError)); - } + await itemValidator.ValidateAsync(item, innerContext, addError); index++; } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs index 79911b17d..091fef4eb 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs @@ -5,9 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; @@ -18,16 +15,16 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators { public sealed class FieldValidator : IValidator { - private readonly IValidator[] validators; + private readonly IValidator fieldValueValidator; private readonly IField field; - public FieldValidator(IEnumerable? validators, IField field) + public FieldValidator(IValidator fieldValueValidator, IField field) { Guard.NotNull(field, nameof(field)); - - this.validators = validators?.ToArray() ?? Array.Empty(); + Guard.NotNull(fieldValueValidator, nameof(fieldValueValidator)); this.field = field; + this.fieldValueValidator = fieldValueValidator; } public async Task ValidateAsync(object? value, ValidationContext context, AddError addError) @@ -63,17 +60,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators return; } - if (validators.Length > 0) - { - var tasks = new List(); - - foreach (var validator in validators) - { - tasks.Add(validator.ValidateAsync(typedValue, context, addError)); - } - - await Task.WhenAll(tasks); - } + await fieldValueValidator.ValidateAsync(typedValue, context, addError); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Validation/DependencyValidatorsFactory.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Validation/DependencyValidatorsFactory.cs index 2a70b4d71..30cab7545 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Validation/DependencyValidatorsFactory.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Validation/DependencyValidatorsFactory.cs @@ -29,7 +29,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Validation this.contentRepository = contentRepository; } - public IEnumerable CreateValueValidators(ValidatorContext context, IField field, FieldValidatorFactory createFieldValidator) + public IEnumerable CreateValueValidators(ValidatorContext context, IField field, ValidatorFactory createFieldValidator) { if (context.Mode == ValidationMode.Optimized) { diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs index 4b1609c2b..cfb4aec98 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs @@ -5,10 +5,16 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using FluentAssertions; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Domain.Apps.Core.ValidateContent; +using Squidex.Domain.Apps.Core.ValidateContent.Validators; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -16,6 +22,28 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent public class AssetsFieldTests : IClassFixture { private readonly List errors = new List(); + private readonly IValidatorsFactory factory; + + private class CustomFactory : IValidatorsFactory + { + public IEnumerable CreateValueValidators(ValidatorContext context, IField field, ValidatorFactory createFieldValidator) + { + if (field is IField assets) + { + yield return new AssetsValidator(assets.Properties.IsRequired, assets.Properties, ids => + { + var result = ids.Select(TestAssets.Document).ToList(); + + return Task.FromResult>(result); + }); + } + } + } + + public AssetsFieldTests() + { + factory = new CustomFactory(); + } [Fact] public void Should_instantiate_field() @@ -30,11 +58,93 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new AssetsFieldProperties()); - await sut.ValidateAsync(null, errors); + await sut.ValidateAsync(CreateValue(null), errors, factory: factory); + + Assert.Empty(errors); + } + + [Fact] + public async Task Should_not_add_error_if_number_of_assets_is_equal_to_min_and_max_items() + { + var sut = Field(new AssetsFieldProperties { MinItems = 2, MaxItems = 2 }); + + await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors, factory: factory); Assert.Empty(errors); } + [Fact] + public async Task Should_not_add_error_if_duplicate_values_are_ignored() + { + var sut = Field(new AssetsFieldProperties { AllowDuplicates = true }); + + await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors, factory: factory); + + Assert.Empty(errors); + } + + [Fact] + public async Task Should_add_error_if_assets_are_required_and_null() + { + var sut = Field(new AssetsFieldProperties { IsRequired = true }); + + await sut.ValidateAsync(CreateValue(null), errors, factory: factory); + + errors.Should().BeEquivalentTo( + new[] { "Field is required." }); + } + + [Fact] + public async Task Should_add_error_if_assets_are_required_and_empty() + { + var sut = Field(new AssetsFieldProperties { IsRequired = true }); + + await sut.ValidateAsync(CreateValue(), errors, factory: factory); + + errors.Should().BeEquivalentTo( + new[] { "Field is required." }); + } + + [Fact] + public async Task Should_add_error_if_value_has_not_enough_items() + { + var sut = Field(new AssetsFieldProperties { MinItems = 3 }); + + await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors, factory: factory); + + errors.Should().BeEquivalentTo( + new[] { "Must have at least 3 item(s)." }); + } + + [Fact] + public async Task Should_add_error_if_value_has_too_much_items() + { + var sut = Field(new AssetsFieldProperties { MaxItems = 1 }); + + await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors, factory: factory); + + errors.Should().BeEquivalentTo( + new[] { "Must not have more than 1 item(s)." }); + } + + [Fact] + public async Task Should_add_error_if_values_contains_duplicate() + { + var sut = Field(new AssetsFieldProperties()); + + var id = Guid.NewGuid(); + + await sut.ValidateAsync(CreateValue(id, id), errors, factory: factory); + + errors.Should().BeEquivalentTo( + new[] { "Must not contain duplicate values." }); + } + + private static IJsonValue CreateValue(params Guid[]? ids) + { + return ids == null ? JsonValue.Null : JsonValue.Array(ids.Select(x => (object)x.ToString()).ToArray()); + } + private static RootField Field(AssetsFieldProperties properties) { return Fields.Assets(1, "my-assets", Partitioning.Invariant, properties); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs index 526ebe139..5b4319b3a 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs @@ -39,7 +39,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent var validatorFactory = A.Fake(); - A.CallTo(() => validatorFactory.CreateValueValidators(A._, A._, A._)) + A.CallTo(() => validatorFactory.CreateValueValidators(A._, A._, A._)) .Returns(Enumerable.Repeat(validator, 1)); schema = schema.AddNumber(1, "my-field", Partitioning.Invariant, @@ -70,7 +70,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent var validatorFactory = A.Fake(); - A.CallTo(() => validatorFactory.CreateFieldValidators(A._, A._, A._)) + A.CallTo(() => validatorFactory.CreateFieldValidators(A._, A._, A._)) .Returns(Enumerable.Repeat(validator, 1)); schema = schema.AddNumber(1, "my-field", Partitioning.Invariant, diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs index 9a1d05542..27c83f9b9 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs @@ -6,9 +6,16 @@ // ========================================================================== using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using FluentAssertions; +using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Domain.Apps.Core.ValidateContent; +using Squidex.Domain.Apps.Core.ValidateContent.Validators; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -16,6 +23,38 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent public class ReferencesFieldTests : IClassFixture { private readonly List errors = new List(); + private readonly DomainId schemaId = DomainId.NewGuid(); + private readonly DomainId ref1 = DomainId.NewGuid(); + private readonly DomainId ref2 = DomainId.NewGuid(); + private readonly IValidatorsFactory factory; + + private class CustomFactory : IValidatorsFactory + { + private readonly DomainId schemaId; + + public CustomFactory(DomainId schemaId) + { + this.schemaId = schemaId; + } + + public IEnumerable CreateValueValidators(ValidatorContext context, IField field, ValidatorFactory createFieldValidator) + { + if (field is IField references) + { + yield return new ReferencesValidator(references.Properties.IsRequired, references.Properties, ids => + { + var result = ids.Select(x => (schemaId, x, Status.Published)).ToList(); + + return Task.FromResult>(result); + }); + } + } + } + + public ReferencesFieldTests() + { + factory = new CustomFactory(schemaId); + } [Fact] public void Should_instantiate_field() @@ -25,16 +64,106 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent Assert.Equal("my-refs", sut.Name); } + [Fact] + public async Task Should_not_add_error_if_references_are_valid() + { + var sut = Field(new ReferencesFieldProperties()); + + await sut.ValidateAsync(CreateValue(ref1), errors, factory: factory); + + Assert.Empty(errors); + } + [Fact] public async Task Should_not_add_error_if_references_are_null_and_valid() { var sut = Field(new ReferencesFieldProperties()); - await sut.ValidateAsync(null, errors); + await sut.ValidateAsync(CreateValue(null), errors, factory: factory); + + Assert.Empty(errors); + } + + [Fact] + public async Task Should_not_add_error_if_number_of_references_is_equal_to_min_and_max_items() + { + var sut = Field(new ReferencesFieldProperties { MinItems = 2, MaxItems = 2 }); + + await sut.ValidateAsync(CreateValue(ref1, ref2), errors, factory: factory); Assert.Empty(errors); } + [Fact] + public async Task Should_not_add_error_if_duplicate_values_are_allowed() + { + var sut = Field(new ReferencesFieldProperties { MinItems = 2, MaxItems = 2, AllowDuplicates = true }); + + await sut.ValidateAsync(CreateValue(ref1, ref1), errors, factory: factory); + + Assert.Empty(errors); + } + + [Fact] + public async Task Should_add_error_if_references_are_required_and_null() + { + var sut = Field(new ReferencesFieldProperties { SchemaId = schemaId, IsRequired = true }); + + await sut.ValidateAsync(CreateValue(null), errors, factory: factory); + + errors.Should().BeEquivalentTo( + new[] { "Field is required." }); + } + + [Fact] + public async Task Should_add_error_if_references_are_required_and_empty() + { + var sut = Field(new ReferencesFieldProperties { SchemaId = schemaId, IsRequired = true }); + + await sut.ValidateAsync(CreateValue(), errors, factory: factory); + + errors.Should().BeEquivalentTo( + new[] { "Field is required." }); + } + + [Fact] + public async Task Should_add_error_if_value_has_not_enough_items() + { + var sut = Field(new ReferencesFieldProperties { SchemaId = schemaId, MinItems = 3 }); + + await sut.ValidateAsync(CreateValue(ref1, ref2), errors, factory: factory); + + errors.Should().BeEquivalentTo( + new[] { "Must have at least 3 item(s)." }); + } + + [Fact] + public async Task Should_add_error_if_value_has_too_much_items() + { + var sut = Field(new ReferencesFieldProperties { SchemaId = schemaId, MaxItems = 1 }); + + await sut.ValidateAsync(CreateValue(ref1, ref2), errors, factory: factory); + + errors.Should().BeEquivalentTo( + new[] { "Must not have more than 1 item(s)." }); + } + + [Fact] + public async Task Should_add_error_if_reference_contains_duplicate_values() + { + var sut = Field(new ReferencesFieldProperties { SchemaId = schemaId }); + + await sut.ValidateAsync(CreateValue(ref1, ref1), errors, factory: factory); + + errors.Should().BeEquivalentTo( + new[] { "Must not contain duplicate values." }); + } + + private static IJsonValue CreateValue(params DomainId[]? ids) + { + return ids == null ? JsonValue.Null : JsonValue.Array(ids.Select(x => (object)x.ToString()).ToArray()); + } + private static RootField Field(ReferencesFieldProperties properties) { return Fields.References(1, "my-refs", Partitioning.Invariant, properties); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs index ac56d96f2..b4e7e53cc 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs @@ -49,8 +49,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent var context = CreateContext(schema, mode, updater, action); var validators = Factories(factory).SelectMany(x => x.CreateValueValidators(context, field, null!)).ToArray(); + var validator = new AggregateValidator(validators, Log); - return new FieldValidator(validators, field) + return new FieldValidator(validator, field) .ValidateAsync(value, context, CreateFormatter(errors)); } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/AssetsValidatorTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/AssetsValidatorTests.cs index e5eee4b04..efa627ac0 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/AssetsValidatorTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/AssetsValidatorTests.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; -using Squidex.Domain.Apps.Core.Assets; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Core.ValidateContent; @@ -23,61 +22,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators public class AssetsValidatorTests : IClassFixture { private readonly List errors = new List(); - - public sealed class AssetInfo : IAssetInfo - { - public DomainId AssetId { get; set; } - - public string FileName { get; set; } - - public string FileHash { get; set; } - - public string Slug { get; set; } - - public long FileSize { get; set; } - - public bool IsImage { get; set; } - - public int? PixelWidth { get; set; } - - public int? PixelHeight { get; set; } - - public AssetMetadata Metadata { get; set; } - - public AssetType Type { get; set; } - } - - private readonly AssetInfo document = new AssetInfo - { - AssetId = DomainId.NewGuid(), - FileName = "MyDocument.pdf", - FileSize = 1024 * 4, - Type = AssetType.Unknown - }; - - private readonly AssetInfo image1 = new AssetInfo - { - AssetId = DomainId.NewGuid(), - FileName = "MyImage.png", - FileSize = 1024 * 8, - Type = AssetType.Image, - Metadata = - new AssetMetadata() - .SetPixelWidth(800) - .SetPixelHeight(600) - }; - - private readonly AssetInfo image2 = new AssetInfo - { - AssetId = DomainId.NewGuid(), - FileName = "MyImage.png", - FileSize = 1024 * 8, - Type = AssetType.Image, - Metadata = - new AssetMetadata() - .SetPixelWidth(800) - .SetPixelHeight(600) - }; + private readonly IAssetInfo document = TestAssets.Document(DomainId.NewGuid()); + private readonly IAssetInfo image1 = TestAssets.Image(DomainId.NewGuid()); + private readonly IAssetInfo image2 = TestAssets.Image(DomainId.NewGuid()); [Fact] public async Task Should_not_add_error_if_assets_are_valid() diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestAssets.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestAssets.cs new file mode 100644 index 000000000..645106b28 --- /dev/null +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestAssets.cs @@ -0,0 +1,65 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Domain.Apps.Core.ValidateContent; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Core.TestHelpers +{ + public static class TestAssets + { + public sealed class AssetInfo : IAssetInfo + { + public DomainId AssetId { get; set; } + + public string FileName { get; set; } + + public string FileHash { get; set; } + + public string Slug { get; set; } + + public long FileSize { get; set; } + + public bool IsImage { get; set; } + + public int? PixelWidth { get; set; } + + public int? PixelHeight { get; set; } + + public AssetMetadata Metadata { get; set; } + + public AssetType Type { get; set; } + } + + public static AssetInfo Document(DomainId id) + { + return new AssetInfo + { + AssetId = id, + FileName = "MyDocument.pdf", + FileSize = 1024 * 4, + Type = AssetType.Unknown + }; + } + + public static AssetInfo Image(DomainId id) + { + return new AssetInfo + { + AssetId = id, + FileName = "MyImage.png", + FileSize = 1024 * 8, + Type = AssetType.Image, + Metadata = + new AssetMetadata() + .SetPixelWidth(800) + .SetPixelHeight(600) + }; + } + } +}