Skip to content
This repository has been archived by the owner on Feb 20, 2022. It is now read-only.

Commit

Permalink
Merge pull request #2 from awesome-inc/master
Browse files Browse the repository at this point in the history
Update from Master
  • Loading branch information
wgnf authored Jul 8, 2019
2 parents 37c26ee + 0654d50 commit ad2e49e
Show file tree
Hide file tree
Showing 79 changed files with 1,645 additions and 745 deletions.
6 changes: 4 additions & 2 deletions NZazu.Contracts/CheckDefinition.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
namespace NZazu.Contracts
using System.Collections.Generic;

namespace NZazu.Contracts
{
public class CheckDefinition
{
public string Type { get; set; }
public string[] Values { get; set; }
public IDictionary<string, string> Settings { get; set; } = new Dictionary<string, string>();
}
}
9 changes: 5 additions & 4 deletions NZazu.Contracts/CheckDefinition_Should.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
using FluentAssertions;
using NEdifis.Attributes;
using NUnit.Framework;
using System.Collections.Generic;

namespace NZazu.Contracts
{
[TestFixtureFor(typeof (CheckDefinition))]
[TestFixtureFor(typeof(CheckDefinition))]
// ReSharper disable InconsistentNaming
internal class CheckDefinition_Should
{
[Test]
public void Be_Creatable()
{
var values = new[]{"value1", "value2"};
var sut = new CheckDefinition {Type = "type", Values = values};
var settings = new Dictionary<string, string>() { { "key1", "value1" }, { "key2", "value2" } };
var sut = new CheckDefinition { Type = "type", Settings = settings };
sut.Type.Should().Be("type");
sut.Values.Should().BeEquivalentTo(values);
sut.Settings.Should().BeEquivalentTo(settings);
}
}
}
97 changes: 47 additions & 50 deletions NZazu.Contracts/CheckFactory.cs
Original file line number Diff line number Diff line change
@@ -1,63 +1,68 @@
using System;
using NZazu.Contracts.Checks;
using NZazu.Contracts.FormChecks;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using NZazu.Contracts.Checks;
using NZazu.Contracts.FormChecks;
using System.Reflection;

namespace NZazu.Contracts
{
public class CheckFactory : ICheckFactory
{
public IValueCheck CreateCheck(CheckDefinition checkDefinition, Func<FormData> formData = null,
INZazuTableDataSerializer tableSerializer = null, int rowIdx = -1)
private IDictionary<string, Type> _registrations = new Dictionary<string, Type>();

public IEnumerable<string> AvailableTypes => _registrations.Keys;

public CheckFactory()
{
if (checkDefinition == null) throw new ArgumentNullException(nameof(checkDefinition));
if (string.IsNullOrWhiteSpace(checkDefinition.Type)) throw new ArgumentException("check type not specified");
switch (checkDefinition.Type)
{
case "required": return new RequiredCheck();
case "length": return CreateLengthCheck(checkDefinition.Values);
case "range": return CreateRangeCheck(checkDefinition.Values);
case "regex": return CreateRegexCheck(checkDefinition.Values);
case "dateTime": return CreateDateTimeComparisonCheck(checkDefinition.Values, formData, tableSerializer, rowIdx);
default: throw new NotSupportedException("The specified check is not supported");
}
RegisterCurrentAssemblyValueChecks();
}

private static IValueCheck CreateLengthCheck(IList<string> values)
private void RegisterCurrentAssemblyValueChecks()
{
if (values == null || values.Count < 1) throw new ArgumentException("At least a minimum string length must be specified");
if (values.Count > 2) throw new ArgumentException("At most minimum and maximum string length can be specified");
_registrations = Assembly.GetExecutingAssembly().GetTypes()
.Where(x => x.GetInterface(typeof(IValueCheck).Name) != null && x != typeof(AggregateCheck))
.ToDictionary(
GetClassDisplayName,
x => x);

if (values.Count == 1)
return new StringLengthCheck(int.Parse(values[0]));
return new StringLengthCheck(int.Parse(values[0]), int.Parse(values[1]));
var types = string.Join(",", AvailableTypes);
Trace.WriteLine($"[CheckFactory] Available check types: {types}");
}

private static IValueCheck CreateRangeCheck(IList<string> values)
private static string GetClassDisplayName(Type cls)
{
if (values == null || values.Count != 2) throw new ArgumentException("Must sepcify minimum and maximum");
return new RangeCheck(double.Parse(values[0], CultureInfo.InvariantCulture), double.Parse(values[1], CultureInfo.InvariantCulture));
}
var nameAttribute = cls.GetCustomAttribute<DisplayNameAttribute>();

private static IValueCheck CreateRegexCheck(IList<string> values)
{
if (values == null || values.Count < 2) throw new ArgumentException("At least a hint and one regex pattern must be specified");
var rx = values.Skip(1).Select(pattern => new Regex(pattern)).ToArray();
return new StringRegExCheck(values[0], rx);
return nameAttribute != null
? nameAttribute.DisplayName.ToLower()
: cls.Name.ToLower();
}

private static IValueCheck CreateDateTimeComparisonCheck(
IList<string> values, Func<FormData> formData, INZazuTableDataSerializer tableSerializer, int rowIdx)
public IValueCheck CreateCheck(
CheckDefinition checkDefinition,
FieldDefinition fieldDefinition,
Func<FormData> formData = null,
INZazuTableDataSerializer tableSerializer = null,
int rowIdx = -1)
{
if (values == null || values.Count < 3) throw new ArgumentException("Hint, comparison operator and field id to compare values needs to be specified");
if (values[1] != "<=" && values[1] != ">=" && values[1] != "=" && values[1] != "<" && values[1] != ">") throw new ArgumentException("Only <=, >=, =, < and > are supported operators!");
var optionalDateFormat = values.Count >= 4 ? values[3].Split('|') : null;
var tableId = values.Count >= 5 && values[4] != string.Empty? values[4] : null;

return new DateTimeComparisonCheck(values[0], values[1], values[2], formData, tableSerializer, tableId, optionalDateFormat, rowIdx);
if (checkDefinition == null) throw new ArgumentNullException(nameof(checkDefinition));
if (string.IsNullOrWhiteSpace(checkDefinition.Type)) throw new ArgumentException("check type not specified");

// lets check if the given validation type is available
if (!_registrations.ContainsKey(checkDefinition.Type.ToLower()))
throw new NotSupportedException($"The specified check '{checkDefinition.Type}' is not supported");

var concrete = _registrations[checkDefinition.Type.ToLower()];
Trace.WriteLine($"Found check for '{checkDefinition.Type.ToLower()}' as ({concrete.FullName})");

// lets use reflection to create an instance
var result = (IValueCheck)
Activator.CreateInstance(concrete, checkDefinition.Settings, formData, tableSerializer, rowIdx, fieldDefinition);

return result;
}

public IFormCheck CreateFormCheck(CheckDefinition checkDefinition)
Expand All @@ -66,17 +71,9 @@ public IFormCheck CreateFormCheck(CheckDefinition checkDefinition)
if (string.IsNullOrWhiteSpace(checkDefinition.Type)) throw new ArgumentException("form check type not specified");
switch (checkDefinition.Type)
{
case "gt": return CreateGreaterThanFormCheck(checkDefinition.Values);
case "gt": return new GreaterThanFormCheck(checkDefinition.Settings);
default: throw new NotSupportedException("The specified check is not supported");
}
}

private static IFormCheck CreateGreaterThanFormCheck(IList<string> values)
{
if (values == null || values.Count < 3) throw new ArgumentException("Hint source and target field needs to be specified");
if (values.Count > 3) throw new ArgumentException("Only hint source and target field needs to be specified.");
return new GreaterThanFormCheck(values[0],values[1],values[2]);
}

}
}
174 changes: 59 additions & 115 deletions NZazu.Contracts/CheckFactory_Should.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
using System;
using FluentAssertions;
using FluentAssertions;
using NEdifis;
using NEdifis.Attributes;
using NSubstitute;
using NUnit.Framework;
using NZazu.Contracts.Checks;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

namespace NZazu.Contracts
{
Expand All @@ -11,145 +16,84 @@ namespace NZazu.Contracts
internal class CheckFactory_Should
{
[Test]
[TestCase("required", null, typeof(RequiredCheck))]
[TestCase("length", new[] { "6", "8" }, typeof(StringLengthCheck))]
[TestCase("range", new[] { "-42", "42" }, typeof(RangeCheck))]
[TestCase("regex", new[] { "Must be true or false", "true", "false" }, typeof(StringRegExCheck))]
public void Support(string type, string[] values, Type checkType)
public void Throw_on_unsupported_types()
{
var sut = new CheckFactory();

var checkDefinition = new CheckDefinition { Type = type, Values = values };
var check = sut.CreateCheck(checkDefinition);

check.Should().NotBeNull();
check.Should().BeOfType(checkType);
sut.Invoking(x => x.CreateCheck(null, null)).Should().Throw<ArgumentNullException>();
sut.Invoking(x => x.CreateCheck(new CheckDefinition(), null)).Should().Throw<ArgumentException>().WithMessage("check type not specified");
sut.Invoking(x => x.CreateCheck(new CheckDefinition { Type = "foobar" }, null)).Should().Throw<NotSupportedException>().WithMessage("The specified check 'foobar' is not supported");
}

[Test]
public void Throw_on_unsupported_types()
public void Be_Creatable()
{
var sut = new CheckFactory();
sut.Invoking(x => x.CreateCheck(null)).Should().Throw<ArgumentNullException>();
sut.Invoking(x => x.CreateCheck(new CheckDefinition())).Should().Throw<ArgumentException>().WithMessage("check type not specified");
sut.Invoking(x => x.CreateCheck(new CheckDefinition { Type = "foobar" })).Should().Throw<NotSupportedException>().WithMessage("The specified check is not supported");
var ctx = new ContextFor<CheckFactory>();
var sut = ctx.BuildSut();

sut.Should().NotBeNull();
sut.Should().BeAssignableTo<ICheckFactory>();
}

[Test]
public void Support_Length_Check()
public void Print_Supported_Types()
{
var sut = new CheckFactory();

var checkDefinition = new CheckDefinition { Type = "length", Values = new[] { "6", "8" } };

var check = sut.CreateCheck(checkDefinition);
var actual = (StringLengthCheck)check;
actual.MinimumLength.Should().Be(6);
actual.MaximumLength.Should().Be(8);

checkDefinition.Values = new[] { "6" };
check = sut.CreateCheck(checkDefinition);
actual = (StringLengthCheck)check;
actual.MinimumLength.Should().Be(6);
actual.MaximumLength.Should().Be(int.MaxValue);

checkDefinition.Values = null;
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<ArgumentException>()
.WithMessage("At least a minimum string length must be specified");

checkDefinition.Values = new[] { "6", "4" };
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<ArgumentException>();

checkDefinition.Values = new[] { "1", "2", "3" };
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<ArgumentException>()
.WithMessage("At most minimum and maximum string length can be specified");
var ctx = new ContextFor<CheckFactory>();
var sut = ctx.BuildSut();

checkDefinition.Values = new[] { "a", "4" };
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<FormatException>();
foreach (var type in sut.AvailableTypes)
{
Console.WriteLine($"factory '{typeof(CheckFactory).Name}' supports type:\t{type}");
}

checkDefinition.Values = new[] { "4", "b" };
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<FormatException>();
Assert.Pass();
}

[Test]
public void Support_Regex_Check()
public void Not_Contain_AggregateCheck()
{
var sut = new CheckFactory();

const string hint = "Not a \"pattern\"";
const string pattern = "pattern";
var checkDefinition = new CheckDefinition { Type = "regex", Values = new[] { hint, pattern } };

var check = sut.CreateCheck(checkDefinition);
var actual = (StringRegExCheck)check;
actual.Should().NotBeNull();
actual.Validate(pattern, pattern);
actual.ShouldFailWith<ArgumentException>("foobar", "foobar", ex => ex.Message == hint);

checkDefinition.Values = null;
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<ArgumentException>()
.WithMessage("At least a hint and one regex pattern must be specified");
var ctx = new ContextFor<CheckFactory>();
var sut = ctx.BuildSut();

checkDefinition.Values = new string[] { };
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<ArgumentException>()
.WithMessage("At least a hint and one regex pattern must be specified");

checkDefinition.Values = new[] { hint };
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<ArgumentException>()
.WithMessage("At least a hint and one regex pattern must be specified");
sut.AvailableTypes.Should().NotContain(typeof(AggregateCheck).Name);
}

[Test]
public void Support_Range_Check()
[TestCaseSource(typeof(Create_All_Checks_Data), nameof(Create_All_Checks_Data.TestCases))]
public void Create_All_Checks(string type)
{
var sut = new CheckFactory();

var checkDefinition = new CheckDefinition { Type = "range", Values = new[] { "0", "4.5" } };
var ctx = new ContextFor<CheckFactory>();
var sut = ctx.BuildSut();

var check = sut.CreateCheck(checkDefinition);
var actual = (RangeCheck)check;
actual.Minimum.Should().Be(0);
actual.Maximum.Should().Be(4.5);
var settings = new Dictionary<string, string>()
{
{ "Hint", "This is a hint for the error message" },
{ "RegEx", "true|false" },
};

checkDefinition.Values = new[] { "5.1", "5.0" };
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<ArgumentOutOfRangeException>();
var checkDefinition = new CheckDefinition() { Type = type, Settings = settings };
var definition = new FieldDefinition() { Key = $"test_string", Type = "string", Checks = new[] { checkDefinition } };
var field = sut.CreateCheck(definition.Checks.Single(), definition, () => new FormData(), Substitute.For<INZazuTableDataSerializer>());

checkDefinition.Values = new[] { "a", "5" };
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<FormatException>();

checkDefinition.Values = new[] { "5", "a" };
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<FormatException>();

checkDefinition.Values = new[] { "5" };
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<ArgumentException>()
.WithMessage("Must sepcify minimum and maximum");

checkDefinition.Values = new string[] { };
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<ArgumentException>()
.WithMessage("Must sepcify minimum and maximum");
field.Should().NotBeNull();
}

checkDefinition.Values = null;
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<ArgumentException>()
.WithMessage("Must sepcify minimum and maximum");
#region testdata source

checkDefinition.Values = new[] { "1", "2", "3" };
sut.Invoking(x => x.CreateCheck(checkDefinition))
.Should().Throw<ArgumentException>()
.WithMessage("Must sepcify minimum and maximum");
[ExcludeFromCodeCoverage]
[Because("this is a data helper")]
private class Create_All_Checks_Data
{
public static IEnumerable<string> TestCases
{
get
{
var ctx = new ContextFor<CheckFactory>();
var sut = ctx.BuildSut();
return sut.AvailableTypes;
}
}
}

#endregion
}
}
Loading

0 comments on commit ad2e49e

Please sign in to comment.