/// <summary> /// Creates instance of <see cref="IValidator{T}"/> that can validate objects of type <typeparamref name="T"/> against the specification provided in the <paramref name="specificationHolder"/>. /// </summary> /// <param name="specificationHolder">Object that provides specification used to validate models (its member <see cref="ISpecificationHolder{T}.Specification"/>) and, optionally, settings (<see cref="ISettingsHolder.Settings"/>, if it implements also <see cref="ISettingsHolder"/>.</param> /// <param name="settings">Settings builder that helps adjust the created <see cref="IValidator{T}"/>'s settings. If not present, the default values are provided. Overrides translations delivered by <paramref name="specificationHolder"/>, if it implements also <see cref="ISettingsHolder"/>.</param> /// <typeparam name="T">Type of the models that this instance of <see cref="IValidator{T}"/> can validate.</typeparam> /// <returns>Instance of <see cref="IValidator{T}"/>, fully initialized and ready to work.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="specificationHolder"/> is null.</exception> /// <exception cref="InvalidOperationException">Thrown if <paramref name="specificationHolder"/>'s <see cref="ISettingsHolder.Settings"/> is null.</exception> public IValidator <T> Create <T>(ISpecificationHolder <T> specificationHolder, Func <ValidatorSettings, ValidatorSettings> settings = null) { if (specificationHolder is null) { throw new ArgumentNullException(nameof(specificationHolder)); } var validatorSettings = ValidatorSettings.GetDefault(); if (specificationHolder is ISettingsHolder validatorSettingsHolder) { if (validatorSettingsHolder.Settings is null) { throw new ArgumentException($"{nameof(ISettingsHolder)} can't have null {nameof(ISettingsHolder.Settings)}", nameof(specificationHolder)); } validatorSettings = GetResolvedSettings(validatorSettings, validatorSettingsHolder.Settings); } validatorSettings = GetResolvedSettings(validatorSettings, settings); var modelScheme = ModelSchemeFactory.Create(specificationHolder.Specification); SetReferenceLoopProtection(validatorSettings, modelScheme.IsReferenceLoopPossible); return(Create(specificationHolder.Specification, validatorSettings)); }
public void Should_ThrowException_When_NullSpecification() { var capacityInfo = Substitute.For <ICapacityInfo>(); Action action = () => ModelSchemeFactory.Create <TestClass>(null, capacityInfo); action.Should().ThrowExactly <ArgumentNullException>(); }
public void Should_BeFalse_When_NoLoop_InDirectCase() { Specification <DirectLoopClassB> specificationB = c => c; Specification <DirectLoopClassA> specificationA = c => c.Member(m => m.B, m => m.AsModel(specificationB)); var modelScheme = ModelSchemeFactory.Create(specificationA); modelScheme.IsReferenceLoopPossible.Should().BeFalse(); }
public void Should_BeTrue_When_SelfLoop() { Specification <SelfLoop> specification = null; specification = c => c.Member(m => m.Self, m => m.AsModel(specification)); var modelScheme = ModelSchemeFactory.Create(specification); modelScheme.IsReferenceLoopPossible.Should().BeTrue(); }
/// <summary> /// Creates instance of <see cref="IValidator{T}"/> that can validate objects of type <typeparamref name="T"/> against the provided specification. /// </summary> /// <param name="specification">Specification used to validate models.</param> /// <param name="settings">Settings builder that helps adjust the created <see cref="IValidator{T}"/>'s settings. If not present, the default values are provided.</param> /// <typeparam name="T">Type of the models that this instance of <see cref="IValidator{T}"/> can validate.</typeparam> /// <returns>Instance of <see cref="IValidator{T}"/>, fully initialized and ready to work.</returns> public IValidator <T> Create <T>(Specification <T> specification, Func <ValidatorSettings, ValidatorSettings> settings = null) { var resolvedSettings = GetResolvedSettings(ValidatorSettings.GetDefault(), settings); var modelScheme = ModelSchemeFactory.Create(specification); SetReferenceLoopProtection(resolvedSettings, modelScheme.IsReferenceLoopPossible); return(Create(specification, resolvedSettings)); }
public void Should_BeFalse_When_NoLoop_InSelfCase() { Specification <SelfLoop> specificationB = c => c; Specification <SelfLoop> specificationA = c => c.Member(m => m.Self, m => m.AsModel(specificationB)); var modelScheme = ModelSchemeFactory.Create(specificationA); modelScheme.IsReferenceLoopPossible.Should().BeFalse(); }
public void Should_GetLoopProtectionReferencesStackCount_BeOne_BeforeAndAfterEnteringRootScope_When_RootModelReference_Exists(string id, Specification <TraversingTestCases.TestClassA> rootSpecification, TraversingTestCases.TestClassA model) { _ = id; var modelScheme = ModelSchemeFactory.Create(rootSpecification); var context = new IsValidValidationContext(modelScheme, new ReferenceLoopProtectionSettings(new object())); context.GetLoopProtectionReferencesStackCount().Should().Be(1); context.EnterScope(modelScheme.RootSpecificationScopeId, model); context.GetLoopProtectionReferencesStackCount().Should().Be(1); }
public void Should_BeTrue_When_NestedLoop() { Specification <NestedLoopClassC> specificationC = null; Specification <NestedLoopClassB> specificationB = c => c.Member(m => m.C, m => m.AsModel(specificationC)); Specification <NestedLoopClassA> specificationA = c => c.Member(m => m.B, m => m.AsModel(specificationB)); specificationC = c => c.Member(m => m.A, m => m.AsModel(specificationA)); var modelScheme = ModelSchemeFactory.Create(specificationA); modelScheme.IsReferenceLoopPossible.Should().BeTrue(); }
public void Should_GetLoopProtectionReferencesStackCount_BeNull_BeforeAndAfterEnteringRootScope_When_RootModelReference_IsNull(string id, Specification <TraversingTestCases.TestClassA> rootSpecification, TraversingTestCases.TestClassA model) { _ = id; var modelScheme = ModelSchemeFactory.Create(rootSpecification); var context = new IsValidValidationContext(modelScheme, default); context.GetLoopProtectionReferencesStackCount().Should().BeNull(); context.EnterScope(modelScheme.RootSpecificationScopeId, model); context.GetLoopProtectionReferencesStackCount().Should().BeNull(); }
public void Should_CreateModelScheme_With_Error() { Specification <TestClass> classSpecification = c => c.Rule(x => false).WithMessage("Invalid value custom message"); var modelScheme = ModelSchemeFactory.Create(classSpecification); var error = modelScheme.ErrorRegistry.Where(e => e.Value.Args.Count == 0 && e.Value.Codes.Count == 0 && e.Value.Messages.Count == 1 && e.Value.Messages.Single() == "Invalid value custom message"); error.Should().HaveCount(1); modelScheme.Template.Keys.Should().HaveCount(1); modelScheme.Template.Keys.Should().Contain(""); modelScheme.Template[""].Should().Contain(error.Single().Key); }
public void Should_CreateModelScheme_And_InjectCapacityInfoHelpers_When_CapacityInfo_Is_CapacityInfoHelpersConsumer(bool feedable) { Specification <TestClass> classSpecification = c => c .Optional() .RuleTemplate(x => false, "Invalid value template message {argName}", Arg.Number("argName", 666L)) .Rule(x => false).WithMessage("Invalid value custom message") .Rule(x => false).WithCode("CODE1"); var capacityInfo = feedable ? Substitute.For <IFeedableCapacityInfo, ICapacityInfoHelpersConsumer>() : Substitute.For <ICapacityInfo, ICapacityInfoHelpersConsumer>(); ModelSchemeFactory.Create(classSpecification, capacityInfo); (capacityInfo as ICapacityInfoHelpersConsumer).Received(1).InjectHelpers(NSubstitute.Arg.Is(ModelSchemeFactory.CapacityInfoHelpers)); }
public void Should_CreateModelScheme_With_Errors() { Specification <TestClass> classSpecification = c => c .RuleTemplate(x => false, "Invalid value template message {argName}", Arg.Number("argName", 666L)) .Rule(x => false).WithMessage("Invalid value custom message") .Rule(x => false).WithCode("CODE1"); var capacityInfo = Substitute.For <ICapacityInfo>(); var modelScheme = ModelSchemeFactory.Create(classSpecification, capacityInfo); var error1Candidates = modelScheme.ErrorRegistry.Where(e => e.Value.Messages.Count == 1 && e.Value.Messages.Single() == "Invalid value custom message"); error1Candidates.Should().HaveCount(1); var error1 = error1Candidates.Single(); error1.Value.Codes.Should().BeEmpty(); error1.Value.Args.Should().BeEmpty(); var error2Candidates = modelScheme.ErrorRegistry.Where(e => e.Value.Messages.Count == 1 && e.Value.Messages.Single() == "Invalid value template message {argName}"); error2Candidates.Should().HaveCount(1); var error2 = error2Candidates.Single(); error2.Value.Codes.Should().BeEmpty(); error2.Value.Args.Should().HaveCount(1); var error2Arg = error2.Value.Args.Single(); error2Arg.Should().BeOfType <NumberArg <long> >(); ((NumberArg <long>)error2Arg).Name.Should().Be("argName"); ((NumberArg <long>)error2Arg).Value.Should().Be(666); var error3Candidates = modelScheme.ErrorRegistry.Where(e => e.Value.Codes.Count == 1 && e.Value.Codes.Single() == "CODE1"); error3Candidates.Should().HaveCount(1); var error3 = error3Candidates.Single(); error3.Value.Messages.Should().BeEmpty(); error3.Value.Args.Should().BeEmpty(); modelScheme.Template.Keys.Should().HaveCount(1); modelScheme.Template.Keys.Should().Contain(""); modelScheme.Template[""].Should().Contain(error1.Key); modelScheme.Template[""].Should().Contain(error2.Key); modelScheme.Template[""].Should().Contain(error3.Key); }
/// <summary> /// Creates instance of <see cref="IValidator{T}"/> that can validate objects of type <typeparamref name="T"/> against the provided specification. /// </summary> /// <param name="specification">Specification used to validate models.</param> /// <param name="settings">Settings used to validate models.</param> /// <typeparam name="T">Type of the models that this instance of <see cref="IValidator{T}"/> can validate.</typeparam> /// <returns>Instance of <see cref="IValidator{T}"/>, fully initialized and ready to work.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="settings"/> is null.</exception> /// <exception cref="ArgumentException">Thrown if <paramref name="settings"/> is not an instance of <see cref="ValidatorSettings"/>.</exception> public IValidator <T> Create <T>(Specification <T> specification, IValidatorSettings settings) { var modelScheme = ModelSchemeFactory.Create(specification); if (settings is null) { throw new ArgumentNullException(nameof(settings)); } if (!(settings is ValidatorSettings validatorSettings)) { throw new ArgumentException($"Custom {nameof(IValidatorSettings)} implementations are not supported.", nameof(settings)); } validatorSettings.IsLocked = true; return(new Validator <T>(modelScheme, settings)); }
public void Should_ThrowException_InfiniteReferencesLoopException_WithDetectedLoopInfo_When_ReferencesLoopDetected(string testId, Specification <TraversingTestCases.LoopClassA> specification, TraversingTestCases.LoopClassA model, string path, string infiniteLoopNestedPath, Type type) { _ = testId; _ = path; _ = infiniteLoopNestedPath; var modelScheme = ModelSchemeFactory.Create(specification); var context = new IsValidValidationContext(modelScheme, new ReferenceLoopProtectionSettings()); Action action = () => context.EnterScope(modelScheme.RootSpecificationScopeId, model); var exception = action.Should().ThrowExactly <ReferenceLoopException>().And; exception.Path.Should().BeNull(); exception.NestedPath.Should().BeNull(); exception.Type.Should().Be(type); exception.Message.Should().Be($"Reference loop detected: object of type {type.GetFriendlyName()} has been detected twice in the reference graph, effectively creating the infinite references loop (where exactly, that information is not available - is that validation comes from IsValid method, please repeat it using the Validate method and examine the exception thrown)"); }
public void Should_CreateModelScheme_And_FeedCapacityInfo_When_ShouldFeed_IsTrue() { Specification <TestClass> classSpecification = c => c .Optional() .RuleTemplate(x => false, "Invalid value template message {argName}", Arg.Number("argName", 666L)) .Rule(x => false).WithMessage("Invalid value custom message") .Rule(x => false).WithCode("CODE1"); var feedableCapacityInfo = Substitute.For <IFeedableCapacityInfo>(); IErrorsHolder errorsHolders = null; feedableCapacityInfo.ShouldFeed.Returns(true); feedableCapacityInfo.When(x => x.Feed(NSubstitute.Arg.Any <IErrorsHolder>())).Do(callInfo => { errorsHolders = callInfo.Arg <IErrorsHolder>(); }); ModelSchemeFactory.Create(classSpecification, feedableCapacityInfo); feedableCapacityInfo.ReceivedWithAnyArgs(1).Feed(default);
public void Should_CreateModelScheme_With_PathResolved() { Specification <TestMember> memberSpecification = c => c.Rule(x => false).WithMessage("Member error"); Specification <TestClass> classSpecification = c => c .Member(m => m.Member, memberSpecification).WithPath("TestNested") .Rule(x => false).WithPath("TestNested").WithMessage("Base error"); var modelScheme = ModelSchemeFactory.Create(classSpecification); var memberError = modelScheme.ErrorRegistry.Single(e => e.Value.Messages.Count == 1 && e.Value.Messages.Single() == "Member error"); var baseError = modelScheme.ErrorRegistry.Single(e => e.Value.Messages.Count == 1 && e.Value.Messages.Single() == "Base error"); modelScheme.Template.Keys.Should().HaveCount(2); modelScheme.Template.Keys.Should().Contain(""); modelScheme.Template[""].Should().NotContain(memberError.Key); modelScheme.Template[""].Should().NotContain(baseError.Key); modelScheme.Template.Keys.Should().Contain("TestNested"); modelScheme.Template["TestNested"].Should().Contain(memberError.Key); modelScheme.Template["TestNested"].Should().Contain(baseError.Key); }
public void Should_ThrowException_When_NullCapacityInfo() { Action action = () => ModelSchemeFactory.Create <TestClass>(m => m, null); action.Should().ThrowExactly <ArgumentNullException>(); }
public void Should_ThrowException_When_NullSpecification() { Action action = () => ModelSchemeFactory.Create <TestClass>(null); action.Should().ThrowExactly <ArgumentNullException>(); }
public void Should_CreateModelScheme() { _ = ModelSchemeFactory.Create <TestClass>(m => m); }
public void Should_CreateModelScheme() { var capacityInfo = Substitute.For <ICapacityInfo>(); _ = ModelSchemeFactory.Create <TestClass>(m => m, capacityInfo); }
public void Should_CreateModelScheme_With_Errors_And_NestedSpecifications() { Specification <TestMember> memberSpecification = c => c.Optional().RuleTemplate(x => false, "Nested template message", Arg.Number("nestedArg", 100M)).WithExtraCode("CODE_N"); Specification <TestClass> classSpecification = c => c .Optional() .Member(m => m.Member, memberSpecification) .RuleTemplate(x => false, "Invalid value template message {argName}", Arg.Number("argName", 666L)) .Rule(x => false).WithMessage("Invalid value custom message") .Rule(x => false).WithCode("CODE1"); var modelScheme = ModelSchemeFactory.Create(classSpecification); var error1Candidates = modelScheme.ErrorRegistry.Where(e => e.Value.Messages.Count == 1 && e.Value.Messages.Single() == "Invalid value template message {argName}"); error1Candidates.Should().HaveCount(1); var error1 = error1Candidates.Single(); error1.Value.Codes.Should().BeEmpty(); error1.Value.Args.Should().HaveCount(1); var error1Arg = error1.Value.Args.Single(); error1Arg.Should().BeOfType <NumberArg <long> >(); ((NumberArg <long>)error1Arg).Name.Should().Be("argName"); ((NumberArg <long>)error1Arg).Value.Should().Be(666); var error2Candidates = modelScheme.ErrorRegistry.Where(e => e.Value.Messages.Count == 1 && e.Value.Messages.Single() == "Invalid value custom message"); error2Candidates.Should().HaveCount(1); var error2 = error2Candidates.Single(); error2.Value.Codes.Should().BeEmpty(); error2.Value.Args.Should().BeEmpty(); var error3Candidates = modelScheme.ErrorRegistry.Where(e => e.Value.Codes.Count == 1 && e.Value.Codes.Single() == "CODE1"); error3Candidates.Should().HaveCount(1); var error3 = error3Candidates.Single(); error3.Value.Messages.Should().BeEmpty(); error3.Value.Args.Should().BeEmpty(); var errorNestedCandidates = modelScheme.ErrorRegistry.Where(e => e.Value.Messages.Count == 1 && e.Value.Messages.Single() == "Nested template message"); errorNestedCandidates.Should().HaveCount(1); var errorNested = errorNestedCandidates.Single(); errorNested.Value.Codes.Should().HaveCount(1); errorNested.Value.Codes.Single().Should().Be("CODE_N"); errorNested.Value.Args.Should().HaveCount(1); var errorNestedArg = errorNested.Value.Args.Single(); errorNestedArg.Should().BeOfType <NumberArg <decimal> >(); (errorNestedArg as NumberArg <decimal>).Name.Should().Be("nestedArg"); (errorNestedArg as NumberArg <decimal>).Value.Should().Be(100); modelScheme.Template.Keys.Should().HaveCount(2); modelScheme.Template.Keys.Should().Contain(""); modelScheme.Template[""].Should().Contain(error2.Key); modelScheme.Template[""].Should().Contain(error2.Key); modelScheme.Template[""].Should().Contain(error3.Key); modelScheme.Template.Keys.Should().Contain("Member"); modelScheme.Template["Member"].Should().Contain(errorNested.Key); }