mirror of https://github.com/Squidex/squidex.git
28 changed files with 1550 additions and 100 deletions
@ -0,0 +1,50 @@ |
|||||
|
// ==========================================================================
|
||||
|
// StringField.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using Squidex.Core.Schemas.Validators; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Core.Schemas |
||||
|
{ |
||||
|
public sealed class StringField : Field<StringFieldProperties> |
||||
|
{ |
||||
|
public StringField(long id, string name, StringFieldProperties properties) |
||||
|
: base(id, name, properties) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override IEnumerable<IValidator> CreateValidators() |
||||
|
{ |
||||
|
if (Properties.IsRequired) |
||||
|
{ |
||||
|
yield return new RequiredStringValidator(); |
||||
|
} |
||||
|
|
||||
|
if (Properties.MinLength.HasValue || Properties.MaxLength.HasValue) |
||||
|
{ |
||||
|
yield return new StringLengthValidator(Properties.MinLength, Properties.MaxLength); |
||||
|
} |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(Properties.Pattern)) |
||||
|
{ |
||||
|
yield return new PatternValidator(Properties.Pattern, Properties.PatternMessage); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override object ConvertValue(PropertyValue property) |
||||
|
{ |
||||
|
return property.ToString(); |
||||
|
} |
||||
|
|
||||
|
protected override Field Clone() |
||||
|
{ |
||||
|
return new StringField(Id, Name, Properties); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,97 @@ |
|||||
|
// ==========================================================================
|
||||
|
// StringFieldProperties.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text.RegularExpressions; |
||||
|
using Squidex.Infrastructure; |
||||
|
// ReSharper disable ObjectCreationAsStatement
|
||||
|
|
||||
|
namespace Squidex.Core.Schemas |
||||
|
{ |
||||
|
public sealed class StringFieldProperties : FieldProperties |
||||
|
{ |
||||
|
private int? minLength; |
||||
|
private int? maxLength; |
||||
|
private string pattern; |
||||
|
private string patternMessage; |
||||
|
|
||||
|
public int? MinLength |
||||
|
{ |
||||
|
get { return minLength; } |
||||
|
set |
||||
|
{ |
||||
|
ThrowIfFrozen(); |
||||
|
|
||||
|
minLength = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int? MaxLength |
||||
|
{ |
||||
|
get { return maxLength; } |
||||
|
set |
||||
|
{ |
||||
|
ThrowIfFrozen(); |
||||
|
|
||||
|
maxLength = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string Pattern |
||||
|
{ |
||||
|
get { return pattern; } |
||||
|
set |
||||
|
{ |
||||
|
ThrowIfFrozen(); |
||||
|
|
||||
|
pattern = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string PatternMessage |
||||
|
{ |
||||
|
get { return patternMessage; } |
||||
|
set |
||||
|
{ |
||||
|
ThrowIfFrozen(); |
||||
|
|
||||
|
patternMessage = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override IEnumerable<ValidationError> ValidateCore() |
||||
|
{ |
||||
|
if (MaxLength.HasValue && MinLength.HasValue && MinLength.Value >= MaxLength.Value) |
||||
|
{ |
||||
|
yield return new ValidationError("Max length must be greater than min length", nameof(MinLength), nameof(MaxLength)); |
||||
|
} |
||||
|
|
||||
|
if (Pattern == null) |
||||
|
{ |
||||
|
yield break; |
||||
|
} |
||||
|
|
||||
|
var isValidPattern = true; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
new Regex(Pattern); |
||||
|
} |
||||
|
catch (ArgumentException) |
||||
|
{ |
||||
|
isValidPattern = false; |
||||
|
} |
||||
|
|
||||
|
if (!isValidPattern) |
||||
|
{ |
||||
|
yield return new ValidationError("Pattern is not a valid expression", nameof(Pattern)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
// ==========================================================================
|
||||
|
// PatternValidator.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Text.RegularExpressions; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Infrastructure.Tasks; |
||||
|
|
||||
|
// ReSharper disable ConvertIfStatementToConditionalTernaryExpression
|
||||
|
// ReSharper disable InvertIf
|
||||
|
|
||||
|
namespace Squidex.Core.Schemas.Validators |
||||
|
{ |
||||
|
public class PatternValidator : IValidator |
||||
|
{ |
||||
|
private readonly Regex regex; |
||||
|
private readonly string errorMessage; |
||||
|
|
||||
|
public PatternValidator(string pattern, string errorMessage = null) |
||||
|
{ |
||||
|
this.errorMessage = errorMessage; |
||||
|
|
||||
|
regex = new Regex(pattern); |
||||
|
} |
||||
|
|
||||
|
public Task ValidateAsync(object value, ICollection<string> errors) |
||||
|
{ |
||||
|
var stringValue = value as string; |
||||
|
|
||||
|
if (stringValue == null) |
||||
|
{ |
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
if (!regex.IsMatch(stringValue)) |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(errorMessage)) |
||||
|
{ |
||||
|
errors.Add("<FIELD> is not valid"); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
errors.Add(errorMessage); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
// ==========================================================================
|
||||
|
// StringLengthValidator.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Infrastructure.Tasks; |
||||
|
|
||||
|
namespace Squidex.Core.Schemas.Validators |
||||
|
{ |
||||
|
public class StringLengthValidator : IValidator |
||||
|
{ |
||||
|
private readonly int? minLength; |
||||
|
private readonly int? maxLength; |
||||
|
|
||||
|
public StringLengthValidator(int? minLength, int? maxLength) |
||||
|
{ |
||||
|
if (minLength.HasValue && maxLength.HasValue && minLength.Value >= maxLength.Value) |
||||
|
{ |
||||
|
throw new ArgumentException("Min length must be greater than max length", nameof(minLength)); |
||||
|
} |
||||
|
|
||||
|
this.minLength = minLength; |
||||
|
this.maxLength = maxLength; |
||||
|
} |
||||
|
|
||||
|
public Task ValidateAsync(object value, ICollection<string> errors) |
||||
|
{ |
||||
|
var stringValue = value as string; |
||||
|
|
||||
|
if (stringValue == null) |
||||
|
{ |
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
if (minLength.HasValue && stringValue.Length < minLength.Value) |
||||
|
{ |
||||
|
errors.Add($"<FIELD> must have more than '{minLength}' characters"); |
||||
|
} |
||||
|
|
||||
|
if (maxLength.HasValue && stringValue.Length > maxLength.Value) |
||||
|
{ |
||||
|
errors.Add($"<FIELD> must have less than '{maxLength}' characters"); |
||||
|
} |
||||
|
|
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,209 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Guard.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Diagnostics; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
// ReSharper disable InvertIf
|
||||
|
|
||||
|
namespace Squidex.Infrastructure |
||||
|
{ |
||||
|
public static class UpdateProperties |
||||
|
{ |
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ValidNumber(float target, string parameterName) |
||||
|
{ |
||||
|
if (float.IsNaN(target) || float.IsPositiveInfinity(target) || float.IsNegativeInfinity(target)) |
||||
|
{ |
||||
|
throw new ArgumentException("Value must be a valid number.", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ValidNumber(double target, string parameterName) |
||||
|
{ |
||||
|
if (double.IsNaN(target) || double.IsPositiveInfinity(target) || double.IsNegativeInfinity(target)) |
||||
|
{ |
||||
|
throw new ArgumentException("Value must be a valid number.", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ValidSlug(string target, string parameterName) |
||||
|
{ |
||||
|
NotNullOrEmpty(target, parameterName); |
||||
|
|
||||
|
if (!target.IsSlug()) |
||||
|
{ |
||||
|
throw new ArgumentException("Target is not a valid slug.", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void HasType<T>(object target, string parameterName) |
||||
|
{ |
||||
|
if (target != null && target.GetType() != typeof(T)) |
||||
|
{ |
||||
|
throw new ArgumentException($"The parameter must be of type {typeof(T)}", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void HasType(object target, Type expectedType, string parameterName) |
||||
|
{ |
||||
|
if (target != null && expectedType != null && target.GetType() != expectedType) |
||||
|
{ |
||||
|
throw new ArgumentException($"The parameter must be of type {expectedType}", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Between<TValue>(TValue target, TValue lower, TValue upper, string parameterName) where TValue : IComparable |
||||
|
{ |
||||
|
if (!target.IsBetween(lower, upper)) |
||||
|
{ |
||||
|
throw new ArgumentException($"Value must be between {lower} and {upper}", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Enum<TEnum>(TEnum target, string parameterName) where TEnum : struct |
||||
|
{ |
||||
|
if (!target.IsEnumValue()) |
||||
|
{ |
||||
|
throw new ArgumentException($"Value must be a valid enum type {typeof(TEnum)}", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void GreaterThan<TValue>(TValue target, TValue lower, string parameterName) where TValue : IComparable |
||||
|
{ |
||||
|
if (target.CompareTo(lower) <= 0) |
||||
|
{ |
||||
|
throw new ArgumentException($"Value must be greater than {lower}", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void GreaterEquals<TValue>(TValue target, TValue lower, string parameterName) where TValue : IComparable |
||||
|
{ |
||||
|
if (target.CompareTo(lower) < 0) |
||||
|
{ |
||||
|
throw new ArgumentException($"Value must be greater or equals than {lower}", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void LessThan<TValue>(TValue target, TValue upper, string parameterName) where TValue : IComparable |
||||
|
{ |
||||
|
if (target.CompareTo(upper) >= 0) |
||||
|
{ |
||||
|
throw new ArgumentException($"Value must be less than {upper}", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void LessEquals<TValue>(TValue target, TValue upper, string parameterName) where TValue : IComparable |
||||
|
{ |
||||
|
if (target.CompareTo(upper) > 0) |
||||
|
{ |
||||
|
throw new ArgumentException($"Value must be less or equals than {upper}", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void NotEmpty<TType>(ICollection<TType> enumerable, string parameterName) |
||||
|
{ |
||||
|
NotNull(enumerable, parameterName); |
||||
|
|
||||
|
if (enumerable.Count == 0) |
||||
|
{ |
||||
|
throw new ArgumentException("Collection does not contain an item", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void NotEmpty(Guid target, string parameterName) |
||||
|
{ |
||||
|
if (target == Guid.Empty) |
||||
|
{ |
||||
|
throw new ArgumentException("Value cannot be empty.", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void NotNull(object target, string parameterName) |
||||
|
{ |
||||
|
if (target == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void NotDefault<T>(T target, string parameterName) |
||||
|
{ |
||||
|
if (Equals(target, default(T))) |
||||
|
{ |
||||
|
throw new ArgumentException("Value cannot be an the default value", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void NotNullOrEmpty(string target, string parameterName) |
||||
|
{ |
||||
|
NotNull(target, parameterName); |
||||
|
|
||||
|
if (string.IsNullOrWhiteSpace(target)) |
||||
|
{ |
||||
|
throw new ArgumentException("String parameter cannot be null or empty and cannot contain only blanks.", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ValidFileName(string target, string parameterName) |
||||
|
{ |
||||
|
NotNullOrEmpty(target, parameterName); |
||||
|
|
||||
|
if (target.Intersect(Path.GetInvalidFileNameChars()).Any()) |
||||
|
{ |
||||
|
throw new ArgumentException("Value contains an invalid character.", parameterName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[DebuggerStepThrough] |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Valid(IValidatable target, string parameterName, Func<string> message) |
||||
|
{ |
||||
|
NotNull(target, parameterName); |
||||
|
|
||||
|
target.Validate(message); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,79 @@ |
|||||
|
// ==========================================================================
|
||||
|
// FieldRegistryTests.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Squidex.Core.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Core.Tests.Schemas |
||||
|
{ |
||||
|
public class FieldRegistryTests |
||||
|
{ |
||||
|
private readonly FieldRegistry sut = new FieldRegistry(); |
||||
|
|
||||
|
public sealed class InvalidProperties : FieldProperties |
||||
|
{ |
||||
|
protected override IEnumerable<ValidationError> ValidateCore() |
||||
|
{ |
||||
|
yield break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static FieldRegistryTests() |
||||
|
{ |
||||
|
TypeNameRegistry.Map(typeof(NumberFieldProperties), "number"); |
||||
|
TypeNameRegistry.Map(typeof(InvalidProperties), "invalid"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_creating_field_and_field_is_not_registered() |
||||
|
{ |
||||
|
Assert.Throws<InvalidOperationException>(() => sut.CreateField(1, "name", new InvalidProperties())); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_create_field_by_properties() |
||||
|
{ |
||||
|
var properties = new NumberFieldProperties(); |
||||
|
|
||||
|
var field = sut.CreateField(1, "name", properties); |
||||
|
|
||||
|
Assert.Equal(properties, field.RawProperties); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_getting_by_type_and_field_is_not_registered() |
||||
|
{ |
||||
|
Assert.Throws<InvalidOperationException>(() => sut.FindByPropertiesType(typeof(InvalidProperties))); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_find_registration_by_properties_type() |
||||
|
{ |
||||
|
var registry = sut.FindByPropertiesType(typeof(NumberFieldProperties)); |
||||
|
|
||||
|
Assert.Equal(typeof(NumberFieldProperties), registry.PropertiesType); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_getting_by_name_and_field_is_not_registered() |
||||
|
{ |
||||
|
Assert.Throws<DomainException>(() => sut.FindByTypeName("invalid")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_find_registration_by_name() |
||||
|
{ |
||||
|
var registry = sut.FindByTypeName("number"); |
||||
|
|
||||
|
Assert.Equal(typeof(NumberFieldProperties), registry.PropertiesType); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
// ==========================================================================
|
||||
|
// NumberFieldTests.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Core.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Xunit; |
||||
|
using FluentAssertions; |
||||
|
|
||||
|
namespace Squidex.Core.Tests.Schemas |
||||
|
{ |
||||
|
public class NumberFieldTests |
||||
|
{ |
||||
|
private readonly List<string> errors = new List<string>(); |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_instantiate_field() |
||||
|
{ |
||||
|
var sut = new NumberField(1, "name", new NumberFieldProperties()); |
||||
|
|
||||
|
Assert.Equal("name", sut.Name); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_clone_object() |
||||
|
{ |
||||
|
var sut = new NumberField(1, "name", new NumberFieldProperties()); |
||||
|
|
||||
|
Assert.NotEqual(sut, sut.Enable()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_number_is_required() |
||||
|
{ |
||||
|
var sut = new NumberField(1, "name", new NumberFieldProperties { Label = "Name", IsRequired = true }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue(null), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new [] { "Name is required" }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_number_is_less_than_min() |
||||
|
{ |
||||
|
var sut = new NumberField(1, "name", new NumberFieldProperties { Label = "Name", MinValue = 10 }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue(5), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "Name must be greater than '10'" }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_number_is_greater_than_max() |
||||
|
{ |
||||
|
var sut = new NumberField(1, "name", new NumberFieldProperties { Label = "Name", MaxValue = 10 }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue(20), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "Name must be less than '10'" }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_number_is_not_allowed() |
||||
|
{ |
||||
|
var sut = new NumberField(1, "name", new NumberFieldProperties { Label = "Name", AllowedValues = ImmutableList.Create(10d) }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue(20), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "Name is not an allowed value" }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_value_is_not_valid() |
||||
|
{ |
||||
|
var sut = new NumberField(1, "name", new NumberFieldProperties { Label = "Name" }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue("Invalid"), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "Name is not a valid value" }); |
||||
|
} |
||||
|
|
||||
|
private static PropertyValue CreateValue(object v) |
||||
|
{ |
||||
|
var bag = new PropertiesBag().Set("value", v); |
||||
|
|
||||
|
return bag["value"]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,280 @@ |
|||||
|
// ==========================================================================
|
||||
|
// SchemaTests.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Core.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Xunit; |
||||
|
using FluentAssertions; |
||||
|
|
||||
|
namespace Squidex.Core.Tests.Schemas |
||||
|
{ |
||||
|
public class SchemaTests |
||||
|
{ |
||||
|
private Schema sut = Schema.Create("my-name", new SchemaProperties()); |
||||
|
|
||||
|
private sealed class InvalidProperties : FieldProperties |
||||
|
{ |
||||
|
protected override IEnumerable<ValidationError> ValidateCore() |
||||
|
{ |
||||
|
yield break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_instantiate_field() |
||||
|
{ |
||||
|
var properties = new SchemaProperties { Hints = "my-hint", Label = "my-label" }; |
||||
|
|
||||
|
var schema = Schema.Create("my-name", properties); |
||||
|
|
||||
|
Assert.Equal("my-name", schema.Name); |
||||
|
Assert.Equal(properties, schema.Properties); |
||||
|
|
||||
|
Assert.True(properties.IsFrozen); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_creating_schema_with_invalid_name() |
||||
|
{ |
||||
|
Assert.Throws<ValidationException>(() => Schema.Create("Invalid Name", new SchemaProperties())); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_update_schema() |
||||
|
{ |
||||
|
var properties = new SchemaProperties { Hints = "my-hint", Label = "my-label" }; |
||||
|
|
||||
|
sut = sut.Update(properties); |
||||
|
|
||||
|
Assert.Equal(properties, sut.Properties); |
||||
|
|
||||
|
Assert.True(properties.IsFrozen); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_add_field() |
||||
|
{ |
||||
|
var field = AddField(); |
||||
|
|
||||
|
Assert.Equal(field, sut.Fields[1]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_adding_field_with_name_that_already_exists() |
||||
|
{ |
||||
|
AddField(); |
||||
|
|
||||
|
Assert.Throws<ValidationException>(() => sut.AddOrUpdateField(new NumberField(2, "my-field", new NumberFieldProperties()))); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_hide_field() |
||||
|
{ |
||||
|
AddField(); |
||||
|
|
||||
|
sut = sut.HideField(1); |
||||
|
sut = sut.HideField(1); |
||||
|
|
||||
|
Assert.True(sut.Fields[1].IsHidden); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_field_to_hide_does_not_exist() |
||||
|
{ |
||||
|
Assert.Throws<DomainObjectNotFoundException>(() => sut.HideField(1)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_show_field() |
||||
|
{ |
||||
|
AddField(); |
||||
|
|
||||
|
sut = sut.HideField(1); |
||||
|
sut = sut.ShowField(1); |
||||
|
sut = sut.ShowField(1); |
||||
|
|
||||
|
Assert.False(sut.Fields[1].IsHidden); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_field_to_show_does_not_exist() |
||||
|
{ |
||||
|
Assert.Throws<DomainObjectNotFoundException>(() => sut.ShowField(2)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_disable_field() |
||||
|
{ |
||||
|
AddField(); |
||||
|
|
||||
|
sut = sut.DisableField(1); |
||||
|
sut = sut.DisableField(1); |
||||
|
|
||||
|
Assert.True(sut.Fields[1].IsDisabled); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_field_to_disable_does_not_exist() |
||||
|
{ |
||||
|
Assert.Throws<DomainObjectNotFoundException>(() => sut.DisableField(1)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_enable_field() |
||||
|
{ |
||||
|
AddField(); |
||||
|
|
||||
|
sut = sut.DisableField(1); |
||||
|
sut = sut.EnableField(1); |
||||
|
sut = sut.EnableField(1); |
||||
|
|
||||
|
Assert.False(sut.Fields[1].IsDisabled); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_field_to_enable_does_not_exist() |
||||
|
{ |
||||
|
Assert.Throws<DomainObjectNotFoundException>(() => sut.EnableField(1)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_rename_field() |
||||
|
{ |
||||
|
AddField(); |
||||
|
|
||||
|
sut = sut.RenameField(1, "new-name"); |
||||
|
|
||||
|
Assert.Equal("new-name", sut.Fields[1].Name); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_new_field_already_exists() |
||||
|
{ |
||||
|
AddField(); |
||||
|
|
||||
|
sut = sut.AddOrUpdateField(new NumberField(2, "other-field", new NumberFieldProperties())); |
||||
|
|
||||
|
Assert.Throws<ValidationException>(() => sut.RenameField(2, "my-field")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_new_field_name_is_not_valid() |
||||
|
{ |
||||
|
AddField(); |
||||
|
|
||||
|
Assert.Throws<ValidationException>(() => sut.RenameField(1, "new name")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_field_to_rename_does_not_exist() |
||||
|
{ |
||||
|
Assert.Throws<DomainObjectNotFoundException>(() => sut.RenameField(1, "new-name")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_delete_field() |
||||
|
{ |
||||
|
AddField(); |
||||
|
|
||||
|
sut = sut.DeleteField(1); |
||||
|
|
||||
|
Assert.Equal(0, sut.Fields.Count); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_not_throw_if_field_to_delete_does_not_exist() |
||||
|
{ |
||||
|
sut.DeleteField(1); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_update_field() |
||||
|
{ |
||||
|
AddField(); |
||||
|
|
||||
|
sut = sut.UpdateField(1, new NumberFieldProperties { Hints = "my-hints" }); |
||||
|
|
||||
|
Assert.Equal("my-hints", sut.Fields[1].RawProperties.Hints); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_updating_field_with_invalid_property_type() |
||||
|
{ |
||||
|
AddField(); |
||||
|
|
||||
|
Assert.Throws<ArgumentException>(() => sut.UpdateField(1, new InvalidProperties())); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_if_field_to_update_does_not_exist() |
||||
|
{ |
||||
|
Assert.Throws<DomainObjectNotFoundException>(() => sut.UpdateField(1, new NumberFieldProperties())); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_error_if_validating_bag_with_unknown_field() |
||||
|
{ |
||||
|
var errors = new List<ValidationError>(); |
||||
|
var bag = new PropertiesBag().Set("unknown", 123); |
||||
|
|
||||
|
await sut.ValidateAsync(bag, errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new List<ValidationError> |
||||
|
{ |
||||
|
new ValidationError("unknown is not a known field", "unknown") |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_error_if_validating_bag_with_invalid_field() |
||||
|
{ |
||||
|
sut = sut.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { MaxValue = 100 })); |
||||
|
|
||||
|
var errors = new List<ValidationError>(); |
||||
|
var bag = new PropertiesBag().Set("my-field", 123); |
||||
|
|
||||
|
await sut.ValidateAsync(bag, errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new List<ValidationError> |
||||
|
{ |
||||
|
new ValidationError("my-field must be less than '100'", "my-field") |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_error_if_required_field_is_not_in_bag() |
||||
|
{ |
||||
|
sut = sut.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { IsRequired = true })); |
||||
|
|
||||
|
var errors = new List<ValidationError>(); |
||||
|
var bag = new PropertiesBag(); |
||||
|
|
||||
|
await sut.ValidateAsync(bag, errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new List<ValidationError> |
||||
|
{ |
||||
|
new ValidationError("my-field is required", "my-field") |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private NumberField AddField() |
||||
|
{ |
||||
|
var field = new NumberField(1, "my-field", new NumberFieldProperties()); |
||||
|
|
||||
|
sut = sut.AddOrUpdateField(field); |
||||
|
|
||||
|
return field; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,95 @@ |
|||||
|
// ==========================================================================
|
||||
|
// StringFieldPropertiesTests.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using FluentAssertions; |
||||
|
using Squidex.Core.Schemas; |
||||
|
using Squidex.Core.Schemas.Validators; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Core.Tests.Schemas |
||||
|
{ |
||||
|
public class StringFieldPropertiesTests |
||||
|
{ |
||||
|
private readonly List<ValidationError> errors = new List<ValidationError>(); |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_add_error_if_min_greater_than_max() |
||||
|
{ |
||||
|
var sut = new StringFieldProperties { MinLength = 10, MaxLength = 5 }; |
||||
|
|
||||
|
sut.Validate(errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new List<ValidationError> |
||||
|
{ |
||||
|
new ValidationError("Max length must be greater than min length", "MinLength", "MaxLength") |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_add_error_if_pattern_is_not_valid_regex() |
||||
|
{ |
||||
|
var sut = new StringFieldProperties { Pattern = "[0-9{1}"}; |
||||
|
|
||||
|
sut.Validate(errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new List<ValidationError> |
||||
|
{ |
||||
|
new ValidationError("Pattern is not a valid expression", "Pattern") |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_set_or_freeze_sut() |
||||
|
{ |
||||
|
var sut = new StringFieldProperties(); |
||||
|
|
||||
|
foreach (var property in sut.GetType().GetRuntimeProperties().Where(x => x.Name != "IsFrozen")) |
||||
|
{ |
||||
|
var value = |
||||
|
property.PropertyType.GetTypeInfo().IsValueType ? |
||||
|
Activator.CreateInstance(property.PropertyType) : |
||||
|
null; |
||||
|
|
||||
|
property.SetValue(sut, value); |
||||
|
|
||||
|
var result = property.GetValue(sut); |
||||
|
|
||||
|
Assert.Equal(value, result); |
||||
|
} |
||||
|
|
||||
|
sut.Freeze(); |
||||
|
|
||||
|
foreach (var property in sut.GetType().GetRuntimeProperties().Where(x => x.Name != "IsFrozen")) |
||||
|
{ |
||||
|
var value = |
||||
|
property.PropertyType.GetTypeInfo().IsValueType ? |
||||
|
Activator.CreateInstance(property.PropertyType) : |
||||
|
null; |
||||
|
|
||||
|
Assert.Throws<InvalidOperationException>(() => |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
property.SetValue(sut, value); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
throw e.InnerException; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,100 @@ |
|||||
|
// ==========================================================================
|
||||
|
// StringFieldTests.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Core.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Xunit; |
||||
|
using FluentAssertions; |
||||
|
|
||||
|
namespace Squidex.Core.Tests.Schemas |
||||
|
{ |
||||
|
public class StringFieldTests |
||||
|
{ |
||||
|
private readonly List<string> errors = new List<string>(); |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_instantiate_field() |
||||
|
{ |
||||
|
var sut = new StringField(1, "name", new StringFieldProperties()); |
||||
|
|
||||
|
Assert.Equal("name", sut.Name); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_clone_object() |
||||
|
{ |
||||
|
var sut = new StringField(1, "name", new StringFieldProperties()); |
||||
|
|
||||
|
Assert.NotEqual(sut, sut.Enable()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_string_is_required() |
||||
|
{ |
||||
|
var sut = new StringField(1, "name", new StringFieldProperties { Label = "Name", IsRequired = true }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue(null), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "Name is required" }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_string_shorter_than_max() |
||||
|
{ |
||||
|
var sut = new StringField(1, "name", new StringFieldProperties { Label = "Name", MinLength = 10 }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue("123"), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "Name must have more than '10' characters" }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_number_is_greater_than_max() |
||||
|
{ |
||||
|
var sut = new StringField(1, "name", new StringFieldProperties { Label = "Name", MaxLength = 5 }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue("12345678"), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "Name must have less than '5' characters" }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_number_is_not_valid_pattern() |
||||
|
{ |
||||
|
var sut = new StringField(1, "name", new StringFieldProperties { Label = "Name", Pattern = "^[0-9]{3}$" }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue("abc"), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "Name is not valid" }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_number_is_not_valid_pattern_with_message() |
||||
|
{ |
||||
|
var sut = new StringField(1, "name", new StringFieldProperties { Label = "Name", Pattern = "^[0-9]{3}$", PatternMessage = "Custom Error Message" }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue("abc"), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "Custom Error Message" }); |
||||
|
} |
||||
|
|
||||
|
private static PropertyValue CreateValue(object v) |
||||
|
{ |
||||
|
var bag = new PropertiesBag().Set("value", v); |
||||
|
|
||||
|
return bag["value"]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
// ==========================================================================
|
||||
|
// PatternValidatorTests.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using FluentAssertions; |
||||
|
using Squidex.Core.Schemas.Validators; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Core.Tests.Schemas.Validators |
||||
|
{ |
||||
|
public class PatternValidatorTests |
||||
|
{ |
||||
|
private readonly List<string> errors = new List<string>(); |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_add_error_if_value_is_valid() |
||||
|
{ |
||||
|
var sut = new PatternValidator("^[a-z]{3}:[0-9]{2}$"); |
||||
|
|
||||
|
await sut.ValidateAsync("abc:12", errors); |
||||
|
|
||||
|
Assert.Equal(0, errors.Count); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_add_error_if_value_is_null() |
||||
|
{ |
||||
|
var sut = new PatternValidator("^[a-z]{3}:[0-9]{2}$"); |
||||
|
|
||||
|
await sut.ValidateAsync(null, errors); |
||||
|
|
||||
|
Assert.Equal(0, errors.Count); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_error_with_default_message_if_value_is_not_valid() |
||||
|
{ |
||||
|
var sut = new PatternValidator("^[a-z]{3}:[0-9]{2}$"); |
||||
|
|
||||
|
await sut.ValidateAsync("foo", errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "<FIELD> is not valid" }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_error_with_custom_message_if_value_is_not_valid() |
||||
|
{ |
||||
|
var sut = new PatternValidator("^[a-z]{3}:[0-9]{2}$", "Custom Error Message"); |
||||
|
|
||||
|
await sut.ValidateAsync("foo", errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "Custom Error Message" }); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,89 @@ |
|||||
|
// ==========================================================================
|
||||
|
// MinMaxValidatorTests.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using FluentAssertions; |
||||
|
using Squidex.Core.Schemas.Validators; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Core.Tests.Schemas.Validators |
||||
|
{ |
||||
|
public class StringLengthValidatorTests |
||||
|
{ |
||||
|
private readonly List<string> errors = new List<string>(); |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_error_if_value_is_null() |
||||
|
{ |
||||
|
var sut = new StringLengthValidator(100, 200); |
||||
|
|
||||
|
await sut.ValidateAsync(null, errors); |
||||
|
|
||||
|
Assert.Equal(0, errors.Count); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(null, null)] |
||||
|
[InlineData(1000, null)] |
||||
|
[InlineData(1000, 2000)] |
||||
|
[InlineData(null, 2000)] |
||||
|
public async Task Should_not_add_error_if_value_is_within_range(int? min, int? max) |
||||
|
{ |
||||
|
var sut = new StringLengthValidator(min, max); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateString(1500), errors); |
||||
|
|
||||
|
Assert.Equal(0, errors.Count); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(20, 10)] |
||||
|
[InlineData(10, 10)] |
||||
|
public void Should_throw_error_if_min_greater_than_max(int? min, int? max) |
||||
|
{ |
||||
|
Assert.Throws<ArgumentException>(() => new StringLengthValidator(min, max)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_error_if_value_is_smaller_than_min() |
||||
|
{ |
||||
|
var sut = new StringLengthValidator(2000, null); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateString(1500), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "<FIELD> must have more than '2000' characters" }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_error_if_value_is_greater_than_max() |
||||
|
{ |
||||
|
var sut = new StringLengthValidator(null, 1000); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateString(1500), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "<FIELD> must have less than '1000' characters" }); |
||||
|
} |
||||
|
|
||||
|
private static string CreateString(int size) |
||||
|
{ |
||||
|
var sb = new StringBuilder(); |
||||
|
|
||||
|
for (var i = 0; i < size; i++) |
||||
|
{ |
||||
|
sb.Append("x"); |
||||
|
} |
||||
|
|
||||
|
return sb.ToString(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue