mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
35 changed files with 975 additions and 728 deletions
@ -0,0 +1,107 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Core; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Core.ValidateContent; |
|||
using Squidex.Domain.Apps.Entities.Contents.Repositories; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
using Squidex.Infrastructure.Queries; |
|||
|
|||
namespace Squidex.Extensions.Validation |
|||
{ |
|||
internal sealed class CompositeUniqueValidator : IValidator |
|||
{ |
|||
private readonly string tag; |
|||
private readonly IContentRepository contentRepository; |
|||
|
|||
public CompositeUniqueValidator(string tag, IContentRepository contentRepository) |
|||
{ |
|||
this.tag = tag; |
|||
|
|||
this.contentRepository = contentRepository; |
|||
} |
|||
|
|||
public async Task ValidateAsync(object value, ValidationContext context, AddError addError) |
|||
{ |
|||
if (value is NamedContentData data) |
|||
{ |
|||
var validateableFields = context.Schema.Fields.Where(IsValidateableField); |
|||
|
|||
var filters = new List<FilterNode<ClrValue>>(); |
|||
|
|||
foreach (var field in validateableFields) |
|||
{ |
|||
var fieldValue = TryGetValue(field, data); |
|||
|
|||
if (fieldValue != null) |
|||
{ |
|||
filters.Add(ClrFilter.Eq($"data.{field.Name}.iv", fieldValue)); |
|||
} |
|||
} |
|||
|
|||
if (filters.Count > 0) |
|||
{ |
|||
var filter = ClrFilter.And(filters); |
|||
|
|||
var found = await contentRepository.QueryIdsAsync(context.AppId.Id, context.SchemaId.Id, filter); |
|||
|
|||
if (found.Any(x => x.Id != context.ContentId)) |
|||
{ |
|||
addError(Enumerable.Empty<string>(), "A content with the same values already exist."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static ClrValue? TryGetValue(IRootField field, NamedContentData data) |
|||
{ |
|||
var value = JsonValue.Null; |
|||
|
|||
if (data.TryGetValue(field.Name, out var fieldValue)) |
|||
{ |
|||
if (fieldValue.TryGetValue(InvariantPartitioning.Key, out var temp) && temp != null) |
|||
{ |
|||
value = temp; |
|||
} |
|||
} |
|||
|
|||
switch (field.RawProperties) |
|||
{ |
|||
case BooleanFieldProperties _ when value is JsonBoolean boolean: |
|||
return boolean.Value; |
|||
case BooleanFieldProperties _ when value is JsonNull _: |
|||
return ClrValue.Null; |
|||
case NumberFieldProperties _ when value is JsonNumber number: |
|||
return number.Value; |
|||
case NumberFieldProperties _ when value is JsonNull _: |
|||
return ClrValue.Null; |
|||
case StringFieldProperties _ when value is JsonString @string: |
|||
return @string.Value; |
|||
case StringFieldProperties _ when value is JsonNull _: |
|||
return ClrValue.Null; |
|||
case ReferencesFieldProperties _ when value is JsonArray array && array.FirstOrDefault() is JsonString @string: |
|||
return @string.Value; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
private bool IsValidateableField(IRootField field) |
|||
{ |
|||
return field.Partitioning == Partitioning.Invariant && field.RawProperties.Tags.Contains(tag) && |
|||
(field.RawProperties is BooleanFieldProperties || |
|||
field.RawProperties is NumberFieldProperties || |
|||
field.RawProperties is ReferencesFieldProperties || |
|||
field.RawProperties is StringFieldProperties); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Squidex.Domain.Apps.Core.ValidateContent; |
|||
using Squidex.Domain.Apps.Entities.Contents.Repositories; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Extensions.Validation |
|||
{ |
|||
public sealed class CompositeUniqueValidatorFactory : IValidatorsFactory |
|||
{ |
|||
private const string Prefix = "unique:"; |
|||
private readonly IContentRepository contentRepository; |
|||
|
|||
public CompositeUniqueValidatorFactory(IContentRepository contentRepository) |
|||
{ |
|||
Guard.NotNull(contentRepository); |
|||
|
|||
this.contentRepository = contentRepository; |
|||
} |
|||
|
|||
public IEnumerable<IValidator> CreateContentValidators(ValidationContext context, FieldValidatorFactory createFieldValidator) |
|||
{ |
|||
foreach (var validatorTag in ValidatorTags(context.Schema.Properties.Tags)) |
|||
{ |
|||
yield return new CompositeUniqueValidator(validatorTag, contentRepository); |
|||
} |
|||
} |
|||
|
|||
private static IEnumerable<string> ValidatorTags(IEnumerable<string> tags) |
|||
{ |
|||
foreach (var tag in tags) |
|||
{ |
|||
if (tag.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase) && tag.Length > Prefix.Length) |
|||
{ |
|||
yield return tag; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Squidex.Domain.Apps.Core.ValidateContent; |
|||
using Squidex.Infrastructure.Plugins; |
|||
|
|||
namespace Squidex.Extensions.Validation |
|||
{ |
|||
public sealed class CompositeUniqueValidatorPlugin : IPlugin |
|||
{ |
|||
public void ConfigureServices(IServiceCollection services, IConfiguration config) |
|||
{ |
|||
services.AddSingletonAs<CompositeUniqueValidatorFactory>() |
|||
.As<IValidatorsFactory>(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,68 +0,0 @@ |
|||
// ==========================================================================
|
|||
// 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.Linq; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Infrastructure.Validation; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ValidateContent |
|||
{ |
|||
public static class ContentValidationExtensions |
|||
{ |
|||
public static async Task ValidateAsync(this NamedContentData data, ValidationContext context, Schema schema, PartitionResolver partitionResolver, IList<ValidationError> errors) |
|||
{ |
|||
var validator = new ContentValidator(schema, partitionResolver, context); |
|||
|
|||
await validator.ValidateAsync(data); |
|||
|
|||
foreach (var error in validator.Errors) |
|||
{ |
|||
errors.Add(error); |
|||
} |
|||
} |
|||
|
|||
public static async Task ValidateAsync(this NamedContentData data, ValidationContext context, Schema schema, PartitionResolver partitionResolver, Func<string> message) |
|||
{ |
|||
var validator = new ContentValidator(schema, partitionResolver, context); |
|||
|
|||
await validator.ValidateAsync(data); |
|||
|
|||
if (validator.Errors.Count > 0) |
|||
{ |
|||
throw new ValidationException(message(), validator.Errors.ToList()); |
|||
} |
|||
} |
|||
|
|||
public static async Task ValidatePartialAsync(this NamedContentData data, ValidationContext context, Schema schema, PartitionResolver partitionResolver, IList<ValidationError> errors) |
|||
{ |
|||
var validator = new ContentValidator(schema, partitionResolver, context); |
|||
|
|||
await validator.ValidatePartialAsync(data); |
|||
|
|||
foreach (var error in validator.Errors) |
|||
{ |
|||
errors.Add(error); |
|||
} |
|||
} |
|||
|
|||
public static async Task ValidatePartialAsync(this NamedContentData data, ValidationContext context, Schema schema, PartitionResolver partitionResolver, Func<string> message) |
|||
{ |
|||
var validator = new ContentValidator(schema, partitionResolver, context); |
|||
|
|||
await validator.ValidatePartialAsync(data); |
|||
|
|||
if (validator.Errors.Count > 0) |
|||
{ |
|||
throw new ValidationException(message(), validator.Errors.ToList()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ValidateContent |
|||
{ |
|||
public delegate IValidator FieldValidatorFactory(IField field); |
|||
|
|||
public interface IValidatorsFactory |
|||
{ |
|||
IEnumerable<IValidator> CreateFieldValidators(ValidationContext context, IField field, FieldValidatorFactory createFieldValidator) |
|||
{ |
|||
yield break; |
|||
} |
|||
|
|||
IEnumerable<IValidator> CreateValueValidators(ValidationContext context, IField field, FieldValidatorFactory createFieldValidator) |
|||
{ |
|||
yield break; |
|||
} |
|||
|
|||
IEnumerable<IValidator> CreateContentValidators(ValidationContext context, FieldValidatorFactory createFieldValidator) |
|||
{ |
|||
yield break; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Core.ValidateContent; |
|||
using Squidex.Domain.Apps.Core.ValidateContent.Validators; |
|||
using Squidex.Domain.Apps.Entities.Assets.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Contents.Repositories; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Validation |
|||
{ |
|||
public sealed class DependencyValidatorsFactory : IValidatorsFactory |
|||
{ |
|||
private readonly IAssetRepository assetRepository; |
|||
private readonly IContentRepository contentRepository; |
|||
|
|||
public DependencyValidatorsFactory(IAssetRepository assetRepository, IContentRepository contentRepository) |
|||
{ |
|||
Guard.NotNull(assetRepository); |
|||
Guard.NotNull(contentRepository); |
|||
|
|||
this.assetRepository = assetRepository; |
|||
this.contentRepository = contentRepository; |
|||
} |
|||
|
|||
public IEnumerable<IValidator> CreateValueValidators(ValidationContext context, IField field, FieldValidatorFactory createFieldValidator) |
|||
{ |
|||
if (field is IField<AssetsFieldProperties> assetsField) |
|||
{ |
|||
var checkAssets = new CheckAssets(async ids => |
|||
{ |
|||
return await assetRepository.QueryAsync(context.AppId.Id, new HashSet<Guid>(ids)); |
|||
}); |
|||
|
|||
yield return new AssetsValidator(assetsField.Properties, checkAssets); |
|||
} |
|||
|
|||
if (field is IField<ReferencesFieldProperties> referencesField) |
|||
{ |
|||
var checkReferences = new CheckContentsByIds(async ids => |
|||
{ |
|||
return await contentRepository.QueryIdsAsync(context.AppId.Id, ids, SearchScope.All); |
|||
}); |
|||
|
|||
yield return new ReferencesValidator(referencesField.Properties.SchemaIds, checkReferences); |
|||
} |
|||
|
|||
if (field is IField<NumberFieldProperties> numberField && numberField.Properties.IsUnique) |
|||
{ |
|||
var checkUniqueness = new CheckUniqueness(async filter => |
|||
{ |
|||
return await contentRepository.QueryIdsAsync(context.AppId.Id, context.SchemaId.Id, filter); |
|||
}); |
|||
|
|||
yield return new UniqueValidator(checkUniqueness); |
|||
} |
|||
|
|||
if (field is IField<StringFieldProperties> stringField && stringField.Properties.IsUnique) |
|||
{ |
|||
var checkUniqueness = new CheckUniqueness(async filter => |
|||
{ |
|||
return await contentRepository.QueryIdsAsync(context.AppId.Id, context.SchemaId.Id, filter); |
|||
}); |
|||
|
|||
yield return new UniqueValidator(checkUniqueness); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,233 @@ |
|||
// ==========================================================================
|
|||
// 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.Linq; |
|||
using System.Threading.Tasks; |
|||
using FluentAssertions; |
|||
using Squidex.Domain.Apps.Core.Assets; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Core.ValidateContent; |
|||
using Squidex.Domain.Apps.Core.ValidateContent.Validators; |
|||
using Squidex.Infrastructure.Collections; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators |
|||
{ |
|||
public class AssetsValidatorTests |
|||
{ |
|||
private readonly List<string> errors = new List<string>(); |
|||
|
|||
public sealed class AssetInfo : IAssetInfo |
|||
{ |
|||
public Guid 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 = Guid.NewGuid(), |
|||
FileName = "MyDocument.pdf", |
|||
FileSize = 1024 * 4, |
|||
Type = AssetType.Unknown |
|||
}; |
|||
|
|||
private readonly AssetInfo image1 = new AssetInfo |
|||
{ |
|||
AssetId = Guid.NewGuid(), |
|||
FileName = "MyImage.png", |
|||
FileSize = 1024 * 8, |
|||
Type = AssetType.Image, |
|||
Metadata = |
|||
new AssetMetadata() |
|||
.SetPixelWidth(800) |
|||
.SetPixelHeight(600) |
|||
}; |
|||
|
|||
private readonly AssetInfo image2 = new AssetInfo |
|||
{ |
|||
AssetId = Guid.NewGuid(), |
|||
FileName = "MyImage.png", |
|||
FileSize = 1024 * 8, |
|||
Type = AssetType.Image, |
|||
Metadata = |
|||
new AssetMetadata() |
|||
.SetPixelWidth(800) |
|||
.SetPixelHeight(600) |
|||
}; |
|||
|
|||
[Fact] |
|||
public async Task Should_not_add_error_if_assets_are_valid() |
|||
{ |
|||
var sut = Validator(new AssetsFieldProperties()); |
|||
|
|||
await sut.ValidateAsync(CreateValue(document.AssetId), errors); |
|||
|
|||
Assert.Empty(errors); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_asset_are_not_valid() |
|||
{ |
|||
var assetId = Guid.NewGuid(); |
|||
|
|||
var sut = Validator(new AssetsFieldProperties()); |
|||
|
|||
await sut.ValidateAsync(CreateValue(assetId), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { $"[1]: Id '{assetId}' not found." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_add_error_if_asset_are_not_valid_but_in_optimized_mode() |
|||
{ |
|||
var assetId = Guid.NewGuid(); |
|||
|
|||
var sut = Validator(new AssetsFieldProperties()); |
|||
|
|||
await sut.ValidateAsync(CreateValue(assetId), errors, updater: c => c.Optimized()); |
|||
|
|||
Assert.Empty(errors); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_document_is_too_small() |
|||
{ |
|||
var sut = Validator(new AssetsFieldProperties { MinSize = 5 * 1024 }); |
|||
|
|||
await sut.ValidateAsync(CreateValue(document.AssetId, image1.AssetId), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "[1]: \'4 kB\' less than minimum of \'5 kB\'." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_document_is_too_big() |
|||
{ |
|||
var sut = Validator(new AssetsFieldProperties { MaxSize = 5 * 1024 }); |
|||
|
|||
await sut.ValidateAsync(CreateValue(document.AssetId, image1.AssetId), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "[2]: \'8 kB\' greater than maximum of \'5 kB\'." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_document_is_not_an_image() |
|||
{ |
|||
var sut = Validator(new AssetsFieldProperties { MustBeImage = true }); |
|||
|
|||
await sut.ValidateAsync(CreateValue(document.AssetId, image1.AssetId), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "[1]: Not an image." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_image_width_is_too_small() |
|||
{ |
|||
var sut = Validator(new AssetsFieldProperties { MinWidth = 1000 }); |
|||
|
|||
await sut.ValidateAsync(CreateValue(document.AssetId, image1.AssetId), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "[2]: Width \'800px\' less than minimum of \'1000px\'." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_image_width_is_too_big() |
|||
{ |
|||
var sut = Validator(new AssetsFieldProperties { MaxWidth = 700 }); |
|||
|
|||
await sut.ValidateAsync(CreateValue(document.AssetId, image1.AssetId), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "[2]: Width \'800px\' greater than maximum of \'700px\'." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_image_height_is_too_small() |
|||
{ |
|||
var sut = Validator(new AssetsFieldProperties { MinHeight = 800 }); |
|||
|
|||
await sut.ValidateAsync(CreateValue(document.AssetId, image1.AssetId), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "[2]: Height \'600px\' less than minimum of \'800px\'." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_image_height_is_too_big() |
|||
{ |
|||
var sut = Validator(new AssetsFieldProperties { MaxHeight = 500 }); |
|||
|
|||
await sut.ValidateAsync(CreateValue(document.AssetId, image1.AssetId), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "[2]: Height \'600px\' greater than maximum of \'500px\'." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_image_has_invalid_aspect_ratio() |
|||
{ |
|||
var sut = Validator(new AssetsFieldProperties { AspectWidth = 1, AspectHeight = 1 }); |
|||
|
|||
await sut.ValidateAsync(CreateValue(document.AssetId, image1.AssetId), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "[2]: Aspect ratio not '1:1'." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_image_has_invalid_extension() |
|||
{ |
|||
var sut = Validator(new AssetsFieldProperties { AllowedExtensions = ReadOnlyCollection.Create("mp4") }); |
|||
|
|||
await sut.ValidateAsync(CreateValue(document.AssetId, image1.AssetId), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] |
|||
{ |
|||
"[1]: Invalid file extension.", |
|||
"[2]: Invalid file extension." |
|||
}); |
|||
} |
|||
|
|||
private static object CreateValue(params Guid[] ids) |
|||
{ |
|||
return ids.ToList(); |
|||
} |
|||
|
|||
private IValidator Validator(AssetsFieldProperties properties) |
|||
{ |
|||
return new AssetsValidator(properties, new CheckAssets(ids => |
|||
{ |
|||
return Task.FromResult<IReadOnlyList<IAssetInfo>>(new List<IAssetInfo> { document, image1, image2 }); |
|||
})); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
// ==========================================================================
|
|||
// 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.Linq; |
|||
using System.Threading.Tasks; |
|||
using FluentAssertions; |
|||
using Squidex.Domain.Apps.Core.ValidateContent.Validators; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent |
|||
{ |
|||
public class ReferencesValidatorTests |
|||
{ |
|||
private readonly List<string> errors = new List<string>(); |
|||
private readonly Guid schemaId = Guid.NewGuid(); |
|||
private readonly Guid ref1 = Guid.NewGuid(); |
|||
private readonly Guid ref2 = Guid.NewGuid(); |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_references_are_not_valid() |
|||
{ |
|||
var sut = new ReferencesValidator(Enumerable.Repeat(schemaId, 1), FoundReferences()); |
|||
|
|||
await sut.ValidateAsync(CreateValue(ref1), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { $"Contains invalid reference '{ref1}'." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_add_error_if_reference_are_not_valid_but_in_optimized_mode() |
|||
{ |
|||
var sut = new ReferencesValidator(Enumerable.Repeat(schemaId, 1), FoundReferences()); |
|||
|
|||
await sut.ValidateAsync(CreateValue(ref1), errors, updater: c => c.Optimized()); |
|||
|
|||
Assert.Empty(errors); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_add_error_if_schemas_not_defined() |
|||
{ |
|||
var sut = new ReferencesValidator(null, FoundReferences((Guid.NewGuid(), ref2))); |
|||
|
|||
await sut.ValidateAsync(CreateValue(ref2), errors); |
|||
|
|||
Assert.Empty(errors); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_reference_schema_is_not_valid() |
|||
{ |
|||
var sut = new ReferencesValidator(Enumerable.Repeat(schemaId, 1), FoundReferences((Guid.NewGuid(), ref2))); |
|||
|
|||
await sut.ValidateAsync(CreateValue(ref2), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { $"Contains reference '{ref2}' to invalid schema." }); |
|||
} |
|||
|
|||
private static List<Guid> CreateValue(params Guid[] ids) |
|||
{ |
|||
return ids.ToList(); |
|||
} |
|||
|
|||
private static CheckContentsByIds FoundReferences(params (Guid SchemaId, Guid Id)[] references) |
|||
{ |
|||
return new CheckContentsByIds(x => Task.FromResult<IReadOnlyList<(Guid SchemaId, Guid Id)>>(references.ToList())); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue