├── .config └── dotnet-tools.json ├── .gitignore ├── .nuget └── NuGet.Config ├── GitReleaseManager.yaml ├── LICENSE ├── README.md ├── appveyor.yml ├── build-tool.ps1 ├── build-tool.sh ├── build.cake ├── build.ps1 ├── build.sh ├── cake.config ├── config.wyam ├── config.wyam.packages.xml ├── docs └── input │ ├── Shared │ ├── Section │ │ └── _Exceptions.cshtml │ ├── _ChildPages.cshtml │ ├── _Support.cshtml │ └── _Validation.cshtml │ ├── _ApiAfterContent.cshtml │ ├── _ApiLayout.cshtml │ ├── _Bottom.cshtml │ ├── _Footer.cshtml │ ├── _Head.cshtml │ ├── _Layout.cshtml │ ├── _Master.cshtml │ ├── _Navbar.cshtml │ ├── assets │ ├── css │ │ ├── bootstrap │ │ │ └── variables.less │ │ └── override.less │ └── img │ │ ├── favicon.ico │ │ ├── logo-mini.png │ │ ├── logo-small.png │ │ ├── logo.png │ │ └── logo.svg │ ├── blog │ ├── new-release-1.0.0.md │ ├── new-release-1.1.0.md │ ├── new-release-1.2.0.md │ ├── new-release-1.2.1.md │ ├── new-release-1.2.2.md │ └── new-release-1.3.0.md │ ├── built-in │ ├── common │ │ ├── all.md │ │ ├── any.md │ │ ├── contains.md │ │ ├── credit-card.md │ │ ├── email.md │ │ ├── empty.md │ │ ├── equal.md │ │ ├── exclusive-between.md │ │ ├── expression.md │ │ ├── false.md │ │ ├── greater-than-or-equal.md │ │ ├── greater-than.md │ │ ├── inclusive-between.md │ │ ├── index.cshtml │ │ ├── is-type.md │ │ ├── length-between.md │ │ ├── length.md │ │ ├── less-than-or-equal.md │ │ ├── less-than.md │ │ ├── match.md │ │ ├── max-length.md │ │ ├── min-length.md │ │ ├── null.md │ │ └── true.md │ ├── core │ │ ├── and.md │ │ ├── cast.md │ │ ├── for-property.md │ │ ├── index.cshtml │ │ ├── not.md │ │ └── or.md │ └── index.cshtml │ ├── docs │ ├── concept │ │ ├── abstraction.md │ │ ├── common-specifications.md │ │ ├── core-specifications.md │ │ ├── entity-framework-support.md │ │ ├── index.cshtml │ │ ├── specification-design-pattern.md │ │ └── validation-result.md │ ├── custom-specifications │ │ ├── all-in-one.md │ │ ├── extensions.md │ │ ├── index.cshtml │ │ ├── linq-specification.md │ │ ├── negatable-specification.md │ │ ├── specification.md │ │ └── validation-specification.md │ ├── index.cshtml │ └── usage │ │ ├── building-specifications.md │ │ ├── candidate-verification.md │ │ ├── custom-validation-messages.md │ │ ├── index.cshtml │ │ └── installation.md │ └── index.cshtml ├── signing.cake ├── src ├── FluentSpecification.Abstractions │ ├── Annotations.cs │ ├── FluentSpecification.Abstractions.csproj │ ├── Generic │ │ ├── IComplexSpecification.cs │ │ ├── ICompositeSpecification.cs │ │ ├── ILinqSpecification.cs │ │ ├── INegatableLinqSpecification.cs │ │ ├── INegatableSpecification.cs │ │ ├── INegatableValidationSpecification.cs │ │ ├── ISpecification.cs │ │ └── IValidationSpecification.cs │ ├── ILinqSpecification.cs │ ├── ISpecification.cs │ └── Validation │ │ ├── FailedSpecification.cs │ │ └── SpecificationResult.cs ├── FluentSpecification.Core.Tests │ ├── Api │ │ ├── FluentProxyTests.cs │ │ ├── SpecificationTests.And.cs │ │ ├── SpecificationTests.AndNot.cs │ │ ├── SpecificationTests.Or.cs │ │ ├── SpecificationTests.OrNot.cs │ │ ├── SpecificationTests.WithMessage.cs │ │ └── SpecificationTests.cs │ ├── ComplexSpecificationTests.GetExpression.cs │ ├── ComplexSpecificationTests.GetNegationExpression.cs │ ├── ComplexSpecificationTests.IsNotSatisfiedBy.cs │ ├── ComplexSpecificationTests.IsSatisfiedBy.cs │ ├── ComplexSpecificationTests.cs │ ├── Composite │ │ ├── AndSpecificationTests.GetExpression.cs │ │ ├── AndSpecificationTests.IsSatisfiedBy.cs │ │ ├── AndSpecificationTests.cs │ │ ├── CastSpecificationTests.GetExpression.cs │ │ ├── CastSpecificationTests.IsSatisfiedBy.cs │ │ ├── CastSpecificationTests.cs │ │ ├── NotSpecificationTests.GetExpression.cs │ │ ├── NotSpecificationTests.IsSatisfiedBy.cs │ │ ├── NotSpecificationTests.cs │ │ ├── OrSpecificationTests.GetExpression.cs │ │ ├── OrSpecificationTests.IsSatisfiedBy.cs │ │ ├── OrSpecificationTests.cs │ │ ├── PropertySpecificationTests.GetExpression.cs │ │ ├── PropertySpecificationTests.IsSatisfiedBy.cs │ │ └── PropertySpecificationTests.cs │ ├── Data │ │ ├── AdapterData.cs │ │ ├── AndData.cs │ │ ├── ComplexData.cs │ │ ├── NotData.cs │ │ ├── OrData.cs │ │ ├── PropertyData.cs │ │ ├── PropertyExpressionData.cs │ │ ├── PropertySelectorData.cs │ │ ├── TranslatableCompositeData.cs │ │ ├── TranslatableData.cs │ │ ├── TranslatableWithFactoryAndParametersData.cs │ │ ├── TranslatableWithFactoryData.cs │ │ └── TypeData.cs │ ├── FluentSpecification.Core.Tests.csproj │ ├── Mocks │ │ ├── ChildComparableFakeType.cs │ │ ├── ChildFakeType.cs │ │ ├── ComparableFakeType.cs │ │ ├── FakeType.cs │ │ ├── FalseMockComplexSpecification.cs │ │ ├── FalseMockNegatableComplexSpecification.cs │ │ ├── FalseMockNegatableSpecification.cs │ │ ├── FalseMockNegatableValidationSpecification.cs │ │ ├── FalseMockSpecification.cs │ │ ├── FalseMockValidationSpecification.cs │ │ ├── InterFakeType.cs │ │ ├── MockCommonSpecification.cs │ │ ├── MockComplexSpecification.cs │ │ ├── MockCompositeSpecification.cs │ │ ├── MockLinqSpecification.cs │ │ ├── MockNegatableComplexSpecification.cs │ │ ├── MockNegatableSpecification.cs │ │ ├── MockNegatableValidationSpecification.cs │ │ ├── MockSpecification.cs │ │ ├── MockValidationSpecification.cs │ │ ├── TrueMockComplexSpecification.cs │ │ ├── TrueMockNegatableComplexSpecification.cs │ │ ├── TrueMockNegatableSpecification.cs │ │ ├── TrueMockNegatableValidationSpecification.cs │ │ ├── TrueMockSpecification.cs │ │ └── TrueMockValidationSpecification.cs │ ├── SpecificationAdapterTests.GetExpression.cs │ ├── SpecificationAdapterTests.GetNegationExpression.cs │ ├── SpecificationAdapterTests.IsNotSatisfiedBy.cs │ ├── SpecificationAdapterTests.IsSatisfiedBy.cs │ ├── SpecificationAdapterTests.cs │ ├── TranslatableSpecificationTests.IsSatisfiedBy.cs │ ├── TranslatableSpecificationTests.cs │ └── Utils │ │ ├── TypeExtensionsTests.EnumerableType.cs │ │ ├── TypeExtensionsTests.GetCompareTo.cs │ │ ├── TypeExtensionsTests.GetEquals.cs │ │ ├── TypeExtensionsTests.GetOperator.cs │ │ ├── TypeExtensionsTests.HasOperator.cs │ │ └── TypeExtensionsTests.IsNumericType.cs ├── FluentSpecification.Core │ ├── Annotations.cs │ ├── ComplexSpecification.cs │ ├── Composite │ │ ├── AndSpecification.cs │ │ ├── CastSpecification.Validation.cs │ │ ├── CastSpecification.cs │ │ ├── CompositeSpecification.Validation.cs │ │ ├── CompositeSpecification.cs │ │ ├── NotSpecification.cs │ │ ├── OrSpecification.cs │ │ ├── PropertySpecification.Validation.cs │ │ └── PropertySpecification.cs │ ├── FluentSpecification.Core.csproj │ ├── NegatableValidationSpecification.cs │ ├── Properties │ │ ├── AssemblyInfo.PublicKey.cs │ │ └── AssemblyInfo.cs │ ├── SpecificationAdapter.cs │ ├── SpecificationCore.cs │ ├── TranslatableSpecification.cs │ ├── Utils │ │ ├── AndFluentProxy.cs │ │ ├── AndNotFluentProxy.cs │ │ ├── ExpressionComposer.cs │ │ ├── ExpressionParameterRebinder.cs │ │ ├── OrFluentProxy.cs │ │ ├── OrNotFluentProxy.cs │ │ ├── SpecificationResultExtensions.cs │ │ └── TypeExtensions.cs │ ├── Validation │ │ ├── SpecificationResultGenerator.cs │ │ └── TraceMessageModifier.cs │ └── ValidationSpecification.cs ├── FluentSpecification.Integration.Tests │ ├── Annotations.cs │ ├── BaseEfCoreTests.cs │ ├── BaseEfTests.cs │ ├── CollectionIntegrationTests.Validation.cs │ ├── CollectionIntegrationTests.cs │ ├── Composite │ │ ├── CollectionIntegrationTests.Validation.cs │ │ ├── CollectionIntegrationTests.cs │ │ ├── EfCoreIntegrationTests.cs │ │ └── EfIntegrationTests.cs │ ├── Data │ │ ├── CreditCard.cs │ │ ├── Customer.cs │ │ ├── DataFixture.cs │ │ ├── DatabaseFixture.cs │ │ ├── DatabaseFixtureCollection.cs │ │ ├── EfCoreDbContext.cs │ │ ├── EfCoreIntegrationData.cs │ │ ├── EfDbContext.cs │ │ ├── Event.cs │ │ ├── IntegrationData.cs │ │ ├── Item.cs │ │ └── SqlToEntitiesIntegrationData.cs │ ├── EfCoreIntegrationTests.cs │ ├── EfIntegrationTests.cs │ ├── FluentSpecification.Integration.Tests.csproj │ ├── Logic │ │ ├── ActiveItemsSpecification.cs │ │ ├── CodeStartsWithSpecification.cs │ │ ├── CustomerIsNotChildSpecification.cs │ │ ├── FavouriteItemSpecification.cs │ │ ├── ItemBigIdSpecification.cs │ │ └── NameContainsSpecification.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── TranslationsIntegrationTests.cs │ ├── app.config │ └── packages.config ├── FluentSpecification.Tests.Sdk │ ├── Annotations.cs │ ├── CorrectDataAttribute.cs │ ├── CorrectValidationDataAttribute.cs │ ├── Data │ │ ├── FailedSpecificationDataRow.cs │ │ ├── ISpecificationResultCreator.cs │ │ ├── SpecificationData.cs │ │ └── SpecificationDataRow.cs │ ├── FailedSpecificationComparer.cs │ ├── FluentSpecification.Tests.Sdk.csproj │ ├── IncorrectDataAttribute.cs │ ├── IncorrectValidationDataAttribute.cs │ ├── SpecificationDataAttribute.cs │ └── SpecificationResultComparer.cs ├── FluentSpecification.Tests │ ├── Annotations.cs │ ├── Api │ │ ├── SpecificationTests.All.cs │ │ ├── SpecificationTests.And.cs │ │ ├── SpecificationTests.AndNot.cs │ │ ├── SpecificationTests.Any.cs │ │ ├── SpecificationTests.Cast.cs │ │ ├── SpecificationTests.Contains.cs │ │ ├── SpecificationTests.CreditCard.cs │ │ ├── SpecificationTests.Email.cs │ │ ├── SpecificationTests.Empty.cs │ │ ├── SpecificationTests.Equal.cs │ │ ├── SpecificationTests.ExclusiveBetween.cs │ │ ├── SpecificationTests.Expression.cs │ │ ├── SpecificationTests.False.cs │ │ ├── SpecificationTests.ForProperty.cs │ │ ├── SpecificationTests.GreaterThan.cs │ │ ├── SpecificationTests.GreaterThanOrEqual.cs │ │ ├── SpecificationTests.InclusiveBetween.cs │ │ ├── SpecificationTests.IsNotType.cs │ │ ├── SpecificationTests.IsType.cs │ │ ├── SpecificationTests.Length.cs │ │ ├── SpecificationTests.LengthBetween.cs │ │ ├── SpecificationTests.LessThan.cs │ │ ├── SpecificationTests.LessThanOrEqual.cs │ │ ├── SpecificationTests.Match.cs │ │ ├── SpecificationTests.MaxLength.cs │ │ ├── SpecificationTests.MinLength.cs │ │ ├── SpecificationTests.NotContains.cs │ │ ├── SpecificationTests.NotCreditCard.cs │ │ ├── SpecificationTests.NotEmail.cs │ │ ├── SpecificationTests.NotEmpty.cs │ │ ├── SpecificationTests.NotEqual.cs │ │ ├── SpecificationTests.NotExclusiveBetween.cs │ │ ├── SpecificationTests.NotGreaterThan.cs │ │ ├── SpecificationTests.NotGreaterThanOrEqual.cs │ │ ├── SpecificationTests.NotInclusiveBetween.cs │ │ ├── SpecificationTests.NotLength.cs │ │ ├── SpecificationTests.NotLengthBetween.cs │ │ ├── SpecificationTests.NotLessThan.cs │ │ ├── SpecificationTests.NotLessThanOrEqual.cs │ │ ├── SpecificationTests.NotMatch.cs │ │ ├── SpecificationTests.NotMaxLength.cs │ │ ├── SpecificationTests.NotMinLength.cs │ │ ├── SpecificationTests.NotNull.cs │ │ ├── SpecificationTests.Null.cs │ │ ├── SpecificationTests.Or.cs │ │ ├── SpecificationTests.OrNot.cs │ │ ├── SpecificationTests.StringContains.cs │ │ ├── SpecificationTests.StringNotContains.cs │ │ ├── SpecificationTests.True.cs │ │ └── SpecificationTests.cs │ ├── Common │ │ ├── AllSpecificationTests.GetExpression.cs │ │ ├── AllSpecificationTests.IsSatisfiedBy.cs │ │ ├── AllSpecificationTests.cs │ │ ├── AnySpecificationTests.GetExpression.cs │ │ ├── AnySpecificationTests.IsSatisfiedBy.cs │ │ ├── AnySpecificationTests.cs │ │ ├── ContainsSpecificationTests.GetNegationExpression.cs │ │ ├── ContainsSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── ContainsSpecificationTests.IsSatisfiedBy.cs │ │ ├── ContainsSpecificationTests.cs │ │ ├── CreditCardSpecificationTests.GetNegationExpression.cs │ │ ├── CreditCardSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── CreditCardSpecificationTests.IsSatisfiedBy.cs │ │ ├── CreditCardSpecificationTests.cs │ │ ├── EmailSpecificationTests.GetNegationExpression.cs │ │ ├── EmailSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── EmailSpecificationTests.IsSatisfiedBy.cs │ │ ├── EmailSpecificationTests.cs │ │ ├── EmptySpecificationTests.GetNegationExpression.cs │ │ ├── EmptySpecificationTests.IsNotSatisfiedBy.cs │ │ ├── EmptySpecificationTests.IsSatisfiedBy.cs │ │ ├── EmptySpecificationTests.cs │ │ ├── EqualSpecificationTests.GetNegationExpression.cs │ │ ├── EqualSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── EqualSpecificationTests.IsSatisfiedBy.cs │ │ ├── EqualSpecificationTests.cs │ │ ├── ExclusiveBetweenSpecificationTests.GetExpression.cs │ │ ├── ExclusiveBetweenSpecificationTests.GetNegationExpression.cs │ │ ├── ExclusiveBetweenSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── ExclusiveBetweenSpecificationTests.IsSatisfiedBy.cs │ │ ├── ExclusiveBetweenSpecificationTests.cs │ │ ├── ExpressionSpecificationTests.GetExpression.cs │ │ ├── ExpressionSpecificationTests.IsSatisfiedBy.cs │ │ ├── ExpressionSpecificationTests.cs │ │ ├── FalseSpecificationTests.IsSatsfiedBy.cs │ │ ├── FalseSpecificationTests.cs │ │ ├── GreaterThanOrEqualSpecificationTests.GetExpression.cs │ │ ├── GreaterThanOrEqualSpecificationTests.GetNegationExpression.cs │ │ ├── GreaterThanOrEqualSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── GreaterThanOrEqualSpecificationTests.IsSatisfiedBy.cs │ │ ├── GreaterThanOrEqualSpecificationTests.cs │ │ ├── GreaterThanSpecificationTests.GetExpression.cs │ │ ├── GreaterThanSpecificationTests.GetNegationExpression.cs │ │ ├── GreaterThanSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── GreaterThanSpecificationTests.IsSatisfiedBy.cs │ │ ├── GreaterThanSpecificationTests.cs │ │ ├── InclusiveBetweenSpecificationTests.GetExpression.cs │ │ ├── InclusiveBetweenSpecificationTests.GetNegationExpression.cs │ │ ├── InclusiveBetweenSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── InclusiveBetweenSpecificationTests.IsSatisfiedBy.cs │ │ ├── InclusiveBetweenSpecificationTests.cs │ │ ├── IsTypeSpecificationTests.GetExpression.cs │ │ ├── IsTypeSpecificationTests.GetNegationExpression.cs │ │ ├── IsTypeSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── IsTypeSpecificationTests.IsSatisfiedBy.cs │ │ ├── IsTypeSpecificationTests.cs │ │ ├── LengthBetweenSpecificationTests.GetExpression.cs │ │ ├── LengthBetweenSpecificationTests.GetNegationExpression.cs │ │ ├── LengthBetweenSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── LengthBetweenSpecificationTests.IsSatisfiedBy.cs │ │ ├── LengthBetweenSpecificationTests.cs │ │ ├── LengthSpecificationTests.GetNegationExpression.cs │ │ ├── LengthSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── LengthSpecificationTests.IsSatisfiedBy.cs │ │ ├── LengthSpecificationTests.cs │ │ ├── LessThanOrEqualSpecificationTests.GetExpression.cs │ │ ├── LessThanOrEqualSpecificationTests.GetNegationExpression.cs │ │ ├── LessThanOrEqualSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── LessThanOrEqualSpecificationTests.IsSatisfiedBy.cs │ │ ├── LessThanOrEqualSpecificationTests.cs │ │ ├── LessThanSpecificationTests.GetExpression.cs │ │ ├── LessThanSpecificationTests.GetNegationExpression.cs │ │ ├── LessThanSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── LessThanSpecificationTests.IsSatisfiedBy.cs │ │ ├── LessThanSpecificationTests.cs │ │ ├── MatchSpecificationTests.GetExpression.cs │ │ ├── MatchSpecificationTests.GetNegationExpression.cs │ │ ├── MatchSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── MatchSpecificationTests.IsSatisfiedBy.cs │ │ ├── MatchSpecificationTests.cs │ │ ├── MaxLengthSpecificationTests.GetNegationExpression.cs │ │ ├── MaxLengthSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── MaxLengthSpecificationTests.IsSatisfiedBy.cs │ │ ├── MaxLengthSpecificationTests.cs │ │ ├── MinLengthSpecificationTests.GetNegationExpression.cs │ │ ├── MinLengthSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── MinLengthSpecificationTests.IsSatisfiedBy.cs │ │ ├── MinLengthSpecificationTests.cs │ │ ├── NullSpecificationTests.GetNegationExpression.cs │ │ ├── NullSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── NullSpecificationTests.IsSatisfiedBy.cs │ │ ├── NullSpecificationTests.cs │ │ ├── StringContainsSpecificationTests.GetExpression.cs │ │ ├── StringContainsSpecificationTests.GetNegationExpression.cs │ │ ├── StringContainsSpecificationTests.IsNotSatisfiedBy.cs │ │ ├── StringContainsSpecificationTests.IsSatisfiedBy.cs │ │ ├── StringContainsSpecificationTests.cs │ │ ├── TrueSpecificationTests.IsSatsfiedBy.cs │ │ └── TrueSpecificationTests.cs │ ├── Data │ │ ├── AllData.cs │ │ ├── AnyData.cs │ │ ├── ContainsData.cs │ │ ├── CreditCardData.cs │ │ ├── EmailData.cs │ │ ├── EmptyData.cs │ │ ├── EqualData.cs │ │ ├── EqualNullableData.cs │ │ ├── ExclusiveBetweenConstructorData.cs │ │ ├── ExclusiveBetweenData.cs │ │ ├── ExclusiveBetweenNullableData.cs │ │ ├── GreaterThanData.cs │ │ ├── GreaterThanNullableData.cs │ │ ├── GreaterThanOrEqualData.cs │ │ ├── GreaterThanOrEqualNullableData.cs │ │ ├── InclusiveBetweenConstructorData.cs │ │ ├── InclusiveBetweenData.cs │ │ ├── InclusiveBetweenNullableData.cs │ │ ├── IsTypeData.cs │ │ ├── LengthBetweenData.cs │ │ ├── LengthData.cs │ │ ├── LessThanData.cs │ │ ├── LessThanNullableData.cs │ │ ├── LessThanOrEqualData.cs │ │ ├── LessThanOrEqualNullableData.cs │ │ ├── MaxLengthData.cs │ │ ├── MinLengthData.cs │ │ └── StringContainsData.cs │ ├── FluentSpecification.Tests.csproj │ └── Mocks │ │ ├── ComparableFakeType.cs │ │ ├── ComparableInterFakeType.cs │ │ ├── EquatableFakeType.cs │ │ ├── FakeIntComparer.cs │ │ ├── FakeType.cs │ │ ├── FakeTypeComparer.cs │ │ ├── FalseMockComplexSpecification.cs │ │ ├── InterFakeType.cs │ │ ├── MockComplexSpecification.cs │ │ ├── MockCompositeSpecification.cs │ │ ├── SpecificationComparer.cs │ │ └── TrueMockComplexSpecification.cs ├── FluentSpecification.sln ├── FluentSpecification.sln.DotSettings ├── FluentSpecification.snk.enc ├── FluentSpecification │ ├── Annotations.cs │ ├── Common │ │ ├── Abstractions │ │ │ ├── BaseBetweenSpecification.cs │ │ │ ├── BaseCollectionSpecification.cs │ │ │ ├── BaseCompareSpecification.cs │ │ │ ├── BaseGreaterCompareSpecification.cs │ │ │ ├── BaseLengthSpecification.cs │ │ │ └── BaseLessCompareSpecification.cs │ │ ├── AllSpecification.cs │ │ ├── AnySpecification.cs │ │ ├── ContainsSpecification.cs │ │ ├── CreditCardSpecification.cs │ │ ├── EmailSpecification.cs │ │ ├── EmptySpecification.cs │ │ ├── EqualSpecification.cs │ │ ├── ExclusiveBetweenSpecification.cs │ │ ├── ExpressionSpecification.cs │ │ ├── FalseSpecification.cs │ │ ├── GreaterThanOrEqualSpecification.cs │ │ ├── GreaterThanSpecification.cs │ │ ├── InclusiveBetweenSpecification.cs │ │ ├── IsTypeSpecification.cs │ │ ├── LengthBetweenSpecification.cs │ │ ├── LengthSpecification.cs │ │ ├── LessThanOrEqualSpecification.cs │ │ ├── LessThanSpecification.cs │ │ ├── MatchSpecification.cs │ │ ├── MaxLengthSpecification.cs │ │ ├── MinLengthSpecification.cs │ │ ├── NullSpecification.cs │ │ ├── StringContainsSpecification.cs │ │ └── TrueSpecification.cs │ ├── FluentSpecification.csproj │ ├── Specification.Composite.cs │ ├── Specification.PropertySelectors.Composite.cs │ ├── Specification.PropertySelectors.cs │ └── Specification.cs ├── Shared.msbuild ├── SolutionInfo.cs ├── global.json └── images │ ├── icon.png │ └── icon.svg └── tools └── packages.config /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "cake.tool": { 6 | "version": "1.3.0", 7 | "commands": [ 8 | "dotnet-cake" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /GitReleaseManager.yaml: -------------------------------------------------------------------------------- 1 | issue-labels-include: 2 | - Breaking change 3 | - Feature 4 | - Bug 5 | - Improvement 6 | - Documentation 7 | issue-labels-exclude: 8 | - Build 9 | issue-labels-alias: 10 | - name: Documentation 11 | header: Documentation 12 | plural: Documentation -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | #---------------------------------# 2 | # Build Image # 3 | #---------------------------------# 4 | image: Visual Studio 2022 5 | 6 | #---------------------------------# 7 | # Build Script # 8 | #---------------------------------# 9 | build_script: 10 | - ps: .\build.ps1 -Target CI -Verbosity Diagnostic 11 | 12 | # Tests 13 | test: off 14 | 15 | #---------------------------------# 16 | # Branches to build # 17 | #---------------------------------# 18 | branches: 19 | # Whitelist 20 | only: 21 | - develop 22 | - master 23 | - /release/.*/ 24 | - /hotfix/.*/ 25 | 26 | #---------------------------------# 27 | # Build Cache # 28 | #---------------------------------# 29 | cache: 30 | - Tools -> build.ps1 -------------------------------------------------------------------------------- /build-tool.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop' 2 | 3 | Set-Location -LiteralPath $PSScriptRoot 4 | 5 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1' 6 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = '1' 7 | $env:DOTNET_NOLOGO = '1' 8 | 9 | dotnet tool restore 10 | if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } 11 | 12 | dotnet cake @args 13 | if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } 14 | -------------------------------------------------------------------------------- /build-tool.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euox pipefail 3 | 4 | cd "$(dirname "${BASH_SOURCE[0]}")" 5 | 6 | export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 7 | export DOTNET_CLI_TELEMETRY_OPTOUT=1 8 | export DOTNET_NOLOGO=1 9 | 10 | dotnet tool restore 11 | 12 | dotnet cake "$@" 13 | -------------------------------------------------------------------------------- /cake.config: -------------------------------------------------------------------------------- 1 | [Settings] 2 | SkipVerification=true 3 | 4 | [NuGet] 5 | ConfigFile=./.nuget/NuGet.Config 6 | -------------------------------------------------------------------------------- /config.wyam: -------------------------------------------------------------------------------- 1 | #n Wyam.Highlight 2 | 3 | System.Globalization.CultureInfo.DefaultThreadCurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture("en-GB"); 4 | 5 | Settings[DocsKeys.Logo] = "assets/img/logo-small.png"; 6 | Settings["LogoMini"] = "assets/img/logo-mini.png"; -------------------------------------------------------------------------------- /docs/input/Shared/Section/_Exceptions.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Html; 2 | @{ 3 | IReadOnlyList exceptions = Model.List(CodeAnalysisKeys.Exceptions); 4 | } 5 | @if(exceptions != null && exceptions.Count > 0) 6 | { 7 |

Exceptions

8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | @foreach(ReferenceComment exception in exceptions) 18 | { 19 | 20 | 21 | 22 | 23 | } 24 |
TypeDescription
@Html.Raw(@exception.Link)@Html.Raw(exception.Html)
25 |
26 |
27 | } -------------------------------------------------------------------------------- /docs/input/Shared/_ChildPages.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | if(Model.ContainsKey(DocsKeys.Description)) 3 | { 4 | foreach (var description in Model.String(DocsKeys.Description).Split(new char[] { ';' })) 5 | { 6 |

@Html.Raw(description)

7 | } 8 | } 9 | 10 | IReadOnlyList children = Model.DocumentList(Keys.Children); 11 | if(children != null && children.Count > 0) 12 | { 13 | IList> categoryGroups = 14 | children 15 | .GroupBy(x => x.String(DocsKeys.Category)) 16 | .OrderBy(x => x.Key) 17 | .ToList(); 18 | 19 | foreach(IGrouping categoryGroup in categoryGroups) 20 | { 21 |
22 |
23 | 24 | @if(!string.IsNullOrWhiteSpace(categoryGroup.Key)) 25 | { 26 | 27 | 28 | 29 | } 30 | 31 | 32 | @foreach(IDocument child in categoryGroup 33 | .OrderBy(x => x.Get(DocsKeys.Order, 1000)) 34 | .ThenBy(x => x.WithoutSettings.String(Keys.Title))) 35 | { 36 | object[] childTreePath = child.Get(Keys.TreePath); 37 | 38 | 39 | 47 | 48 | } 49 | 50 |
@categoryGroup.Key
@(child.WithoutSettings.String(Keys.Title, childTreePath.Last().ToString())) 40 | @{ 41 | if(child.ContainsKey(DocsKeys.Description)) 42 | { 43 | @Html.Raw(child.String(DocsKeys.Description)) 44 | } 45 | } 46 |
51 |
52 |
53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /docs/input/Shared/_Support.cshtml: -------------------------------------------------------------------------------- 1 | @functions { 2 | public string GetSupportIcon(string supportType) 3 | { 4 | string supportClass; 5 | switch (supportType) 6 | { 7 | case "Full": 8 | supportClass = "full-support"; 9 | break; 10 | case "Partial": 11 | supportClass = "partial-support"; 12 | break; 13 | default: 14 | supportClass = "not-supported"; 15 | break; 16 | } 17 | 18 | return $""; 19 | } 20 | } 21 | 22 | @if(Model.ContainsKey("Support")) 23 | { 24 | var support = Model.String("Support").Split(new char[] { ';' }); 25 |
26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | @if (support.Length == 5) 38 | { 39 | 40 | } 41 | 42 |
FeatureIs supported
Null safe@Html.Raw(GetSupportIcon(support[0]))
Validation@Html.Raw(GetSupportIcon(support[1]))
EF Core@Html.Raw(GetSupportIcon(support[2]))
EF 6@Html.Raw(GetSupportIcon(support[3]))
External Comparer@Html.Raw(GetSupportIcon(support[4]))
43 |
44 |
45 | } -------------------------------------------------------------------------------- /docs/input/Shared/_Validation.cshtml: -------------------------------------------------------------------------------- 1 | @if(Model.ContainsKey("ValidationParameters") || Model.ContainsKey("ValidationError")) 2 | { 3 |

Validation result

4 | @if(Model.ContainsKey("ValidationError")) 5 | { 6 | var errors = Model.String("ValidationError").Split(new char[] { ';' }); 7 |

Error

8 |
9 | @foreach(var err in errors) 10 | { 11 |

@Html.Raw(err)

12 | } 13 |
14 | } 15 | @if(Model.ContainsKey("ValidationParameters")) 16 | { 17 | var parameters = Model.String("ValidationParameters").Split(new char[] { ';' }); 18 |

Parameters

19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | @foreach(var param in parameters) 28 | { 29 | var keyValue = param.Split(new char[] { ':' }); 30 | 31 | } 32 | 33 |
ParameterDescription
@Html.Raw(keyValue[0])@Html.Raw(keyValue[1])
34 |
35 |
36 | } 37 | } -------------------------------------------------------------------------------- /docs/input/_ApiAfterContent.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | IReadOnlyList exceptions = Model.List(CodeAnalysisKeys.Exceptions); 3 | if (exceptions != null) 4 | { 5 | @Html.Partial("Section\\_Exceptions") 6 | } 7 | } -------------------------------------------------------------------------------- /docs/input/_ApiLayout.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "/_Master.cshtml"; 3 | 4 | IDocument containingType = Model.Get("ContainingType"); 5 | string containingTypeString = string.Empty; 6 | while(containingType != null) 7 | { 8 | containingTypeString = containingTypeString + containingType.String("DisplayName") + "."; 9 | containingType = containingType.Get("ContainingType"); 10 | } 11 | ViewData[Keys.Title] = "API - " + containingTypeString + Model["DisplayName"] + " " + Model["SpecificKind"]; 12 | } 13 | 14 | @section Infobar { 15 |
16 | } 17 | 18 | @section Search { 19 | @Html.Partial("_ApiSearch") 20 |
21 | } 22 | 23 | @section Sidebar { 24 | @Html.Partial("Sidebar/_" + Model.String("Kind")) 25 | } 26 | 27 |
28 | @{ 29 | containingType = Model.Get("ContainingType"); 30 | while(containingType != null) 31 | { 32 |

@Context.GetTypeLink(containingType).

33 | containingType = containingType.Get("ContainingType"); 34 | } 35 | } 36 |

@Model.Name() @Model["SpecificKind"]

37 |
38 |
39 | @Html.Partial("_ApiBeforeContent") 40 | @RenderBody() 41 | @Html.Partial("Kind/_" + Model.String("Kind")) 42 | @Html.Partial("_ApiAfterContent") 43 |
-------------------------------------------------------------------------------- /docs/input/_Bottom.cshtml: -------------------------------------------------------------------------------- 1 | 2 |
GitHub
3 |
4 | 5 | -------------------------------------------------------------------------------- /docs/input/_Footer.cshtml: -------------------------------------------------------------------------------- 1 |

2 | GitHub 3 |
4 | Copyright © Michał Kowal. 5 |
6 | Generated by Wyam 7 |

-------------------------------------------------------------------------------- /docs/input/_Head.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | -------------------------------------------------------------------------------- /docs/input/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "/_Master.cshtml"; 3 | ViewData[Keys.Title] = @Model.WithoutSettings.String(Keys.Title); 4 | } 5 | 6 | @section Infobar { 7 | @if (IsSectionDefined("Infobar")) { 8 | RenderSection("Infobar"); 9 | } 10 | else { 11 | @Html.Partial("_Infobar") 12 | } 13 | } 14 | 15 | @section Search { 16 | @if (IsSectionDefined("Search")) { 17 | RenderSection("Search"); 18 | } 19 | } 20 | 21 | @section Sidebar { 22 | @if (IsSectionDefined("Sidebar")) { 23 | RenderSection("Sidebar"); 24 | } 25 | else { 26 | @Html.Partial("Sidebar/_ChildPages") 27 | } 28 | } 29 | 30 | @if(Model.Bool(DocsKeys.NoContainer, false)) 31 | { 32 | @RenderBody() 33 | } 34 | else 35 | { 36 | @if(!Model.Bool(DocsKeys.NoTitle, false)) 37 | { 38 |
39 |

@ViewData[Keys.Title]

40 |
41 | } 42 |
43 | @Html.Partial("_Support") 44 | @Html.Partial("_Validation") 45 | @if(Model.Bool("DisplayDescription", false) && Model.ContainsKey(DocsKeys.Description)) 46 | { 47 |

Info

48 |

@Html.Raw(Model.String(DocsKeys.Description))

49 | } 50 | @RenderBody() 51 |
52 | } -------------------------------------------------------------------------------- /docs/input/_Navbar.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | List> pages = Context 3 | .Documents[Docs.Pages] 4 | .Where(x => x.Bool(DocsKeys.ShowInNavbar, true)) 5 | .Where(x => x.FilePath(Keys.RelativeFilePath)?.FullPath?.StartsWith("index") == (bool?)false) 6 | .Select(x => Tuple.Create(x.WithoutSettings.String(Keys.Title), Context.GetLink(x), x.Get(DocsKeys.Order, 1000))) 7 | .Where(x => !string.IsNullOrEmpty(x.Item1)) 8 | .OrderBy(x => x.Item3) 9 | .ToList(); 10 | if(Documents[Docs.BlogPosts].Any()) 11 | { 12 | pages.Add(Tuple.Create(Context.String(DocsKeys.BlogTitle), Context.GetLink(Context.String(DocsKeys.BlogPath)), 0)); 13 | } 14 | if(Documents[Docs.Api].Any()) 15 | { 16 | pages.Add(Tuple.Create("API", Context.GetLink(Context.DirectoryPath(DocsKeys.ApiPath)), 0)); 17 | } 18 | foreach(Tuple page in pages) 19 | { 20 | string active = Context.GetLink(Document).StartsWith(page.Item2) ? "active" : null; 21 |
  • @(page.Item1)
  • 22 | } 23 | } -------------------------------------------------------------------------------- /docs/input/assets/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michalkowal/FluentSpecification/96be7fde4e0eb445a512f153816f290e3143ccec/docs/input/assets/img/favicon.ico -------------------------------------------------------------------------------- /docs/input/assets/img/logo-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michalkowal/FluentSpecification/96be7fde4e0eb445a512f153816f290e3143ccec/docs/input/assets/img/logo-mini.png -------------------------------------------------------------------------------- /docs/input/assets/img/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michalkowal/FluentSpecification/96be7fde4e0eb445a512f153816f290e3143ccec/docs/input/assets/img/logo-small.png -------------------------------------------------------------------------------- /docs/input/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michalkowal/FluentSpecification/96be7fde4e0eb445a512f153816f290e3143ccec/docs/input/assets/img/logo.png -------------------------------------------------------------------------------- /docs/input/blog/new-release-1.0.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | Title: New Release - 1.0.0 3 | Published: 5/5/2019 4 | Category: Release 5 | Author: Michał Kowal 6 | --- 7 | 8 | # 1.0.0 Release 9 | 10 | *FluentSpecification* is stable and ready to use! 11 | 12 | In this release: 13 | - `FluentSpecification.Abstractions` package - abstraction layer for *Specification Design Pattern*. 14 | - `FluentSpecification.Core` package - base implementations and utils. 15 | - `FluentSpecification` package with many common *Specifications*. 16 | - **48** built-in *Specifications* with *validation* and *Linq* support. 17 | - About 4000 unit tests. 18 | - Integration tests with `Entity Framework` (core and normal). 19 | - Support for *.NET Standard 1.0-2.0* (and *.NET Core 1.0-2.2*). 20 | - Support for *.NET Framework 4.5* and greater. 21 | - *Fluent API* - simple and understandable way to use *Specifications* 22 | - Full documentation with description about concept, usage, customizing and built-in *Specifications*. 23 | - API description -------------------------------------------------------------------------------- /docs/input/blog/new-release-1.1.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | Title: New Release - 1.1.0 3 | Published: 25/8/2019 4 | Category: Release 5 | Author: Michał Kowal 6 | --- 7 | 8 | # 1.1.0 Release 9 | 10 | As part of this release we had [6 commits](https://github.com/michalkowal/FluentSpecification/compare/1.0.0...1.1.0) which resulted in [1 issue](https://github.com/michalkowal/FluentSpecification/milestone/3?closed=1) being closed. 11 | 12 | 13 | __Improvement__ 14 | 15 | - [__#18__](https://github.com/michalkowal/FluentSpecification/issues/18) Sign assemblies 16 | -------------------------------------------------------------------------------- /docs/input/blog/new-release-1.2.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | Title: New Release - 1.2.0 3 | Published: 11/8/2022 4 | Category: Release 5 | Author: Michał Kowal 6 | --- 7 | 8 | # 1.2.0 Release 9 | 10 | As part of this release we had [7 commits](https://github.com/michalkowal/FluentSpecification/compare/1.1.0...1.2.0) which resulted in [2 issues](https://github.com/michalkowal/FluentSpecification/milestone/4?closed=1) being closed. 11 | 12 | 13 | __Feature__ 14 | 15 | - [__#14__](https://github.com/michalkowal/FluentSpecification/issues/14) Localization support for validation 16 | -------------------------------------------------------------------------------- /docs/input/blog/new-release-1.2.1.md: -------------------------------------------------------------------------------- 1 | --- 2 | Title: New Release - 1.2.1 3 | Published: 11/8/2022 4 | Category: Release 5 | Author: Michał Kowal 6 | --- 7 | 8 | # 1.2.1 Release 9 | 10 | As part of this release we had [5 commits](https://github.com/michalkowal/FluentSpecification/compare/1.2.0...1.2.1) which resulted in [1 issue](https://github.com/michalkowal/FluentSpecification/milestone/5?closed=1) being closed. 11 | 12 | 13 | __Bug__ 14 | 15 | - [__#24__](https://github.com/michalkowal/FluentSpecification/issues/24) 'Specification' is an ambiguous reference 16 | -------------------------------------------------------------------------------- /docs/input/blog/new-release-1.2.2.md: -------------------------------------------------------------------------------- 1 | --- 2 | Title: New Release - 1.2.2 3 | Published: 13/8/2022 4 | Category: Release 5 | Author: Michał Kowal 6 | --- 7 | 8 | # 1.2.2 Release 9 | 10 | As part of this release we had [11 commits](https://github.com/michalkowal/FluentSpecification/compare/1.2.1...1.2.2) which resulted in [2 issues](https://github.com/michalkowal/FluentSpecification/milestone/6?closed=1) being closed. 11 | 12 | 13 | __Bugs__ 14 | 15 | - [__#26__](https://github.com/michalkowal/FluentSpecification/issues/26) Incorrect SpecificationResult generated by TranslatableSpecification 16 | - [__#25__](https://github.com/michalkowal/FluentSpecification/pull/25) Bump System.Security.Cryptography.Xml from 4.5.0 to 4.7.1 in /src/FluentSpecification.Integration.Tests 17 | -------------------------------------------------------------------------------- /docs/input/blog/new-release-1.3.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | Title: New Release - 1.3.0 3 | Published: 15/8/2022 4 | Category: Release 5 | Author: Michał Kowal 6 | --- 7 | 8 | # 1.3.0 Release 9 | 10 | As part of this release we had [7 commits](https://github.com/michalkowal/FluentSpecification/compare/1.2.2...1.3.0) which resulted in [1 issue](https://github.com/michalkowal/FluentSpecification/milestone/7?closed=1) being closed. 11 | 12 | 13 | __Improvement__ 14 | 15 | - [__#28__](https://github.com/michalkowal/FluentSpecification/issues/28) Specification validation message customization with Func<> -------------------------------------------------------------------------------- /docs/input/built-in/common/email.md: -------------------------------------------------------------------------------- 1 | Description: Checks if string is valid email address. 2 | Order: 1800 3 | DisplayDescription: true 4 | Support: Full;Full;Full;None 5 | ValidationError: "String is invalid email".;"String is valid email" - for negation. 6 | --- 7 | 8 | # Usage 9 | 10 | ```csharp 11 | var spec = Specification.Email(); 12 | 13 | spec.IsSatisfiedBy("john.doe@gmail.com"); // true 14 | spec.IsSatisfiedBy("email.example.com"); // false 15 | spec.IsSatisfiedBy(null); // false 16 | ``` 17 | 18 | ## As property 19 | 20 | ```csharp 21 | var customerSpec = Specification.Email(c => c.Email); 22 | 23 | customerSpec.IsSatisfiedBy(new Customer { Email = "john.doe@gmail.com" }); // true 24 | customerSpec.IsSatisfiedBy(new Customer { Email = "email.example.com" }); // false 25 | customerSpec.IsSatisfiedBy(new Customer { Email = null }); // false 26 | ``` 27 | 28 | # Not Email 29 | 30 | ```csharp 31 | var spec = Specification.NotEmail(); 32 | 33 | spec.IsSatisfiedBy("john.doe@gmail.com"); // false 34 | spec.IsSatisfiedBy("email.example.com"); // true 35 | spec.IsSatisfiedBy(null); // true 36 | ``` 37 | 38 | ## As property 39 | 40 | ```csharp 41 | var customerSpec = Specification.NotEmail(c => c.Email); 42 | 43 | customerSpec.IsSatisfiedBy(new Customer { Email = "john.doe@gmail.com" }); // false 44 | customerSpec.IsSatisfiedBy(new Customer { Email = "email.example.com" }); // true 45 | customerSpec.IsSatisfiedBy(new Customer { Email = null }); // true 46 | ``` 47 | 48 | # EF 6 support 49 | 50 | Right now `EmailSpecification` uses `Regex` for verification - it is not supported in *LinqToEntities* (*LinqToSql*). -------------------------------------------------------------------------------- /docs/input/built-in/common/expression.md: -------------------------------------------------------------------------------- 1 | Description: Checks if external Linq Expression is satisfied by candidate. 2 | Order: 100 3 | DisplayDescription: true 4 | Support: Partial;Full;Partial;Partial 5 | ValidationError: "Specification doesn't meet expression:[{Expression}]". 6 | ValidationParameters: Expression:Linq expression object passed in constructor. 7 | --- 8 | 9 | # Usage 10 | 11 | ```csharp 12 | var spec = Specification.Expression( 13 | s => s.Split(new char[] {';'}).Length == 3); 14 | 15 | spec.IsSatisfiedBy("1;2;3"); // true 16 | spec.IsSatisfiedBy(""); // false 17 | spec.IsSatisfiedBy(null); // NullReferenceException! 18 | ``` 19 | 20 | ## As property 21 | 22 | ```csharp 23 | var customerSpec = Specification.Expression(c => c.Comments, 24 | s => s.Split(new char[] { ';' }).Length == 3); 25 | 26 | customerSpec.IsSatisfiedBy(new Customer { Comments = "1;2;3" }); // true 27 | customerSpec.IsSatisfiedBy(new Customer { Comments = "" }); // false 28 | customerSpec.IsSatisfiedBy(new Customer { Comments = null }); // NullReferenceException 29 | ``` 30 | 31 | # Null support 32 | 33 | `ExpressionSpecification` doesn't check if candidate is null. 34 | If provided *Linq expression* is not prepared for *null* candidate value - `NullReferenceException` may occured. 35 | 36 | # EF support 37 | 38 | *Entity Framework* support (both - Core and "normall") depends on provided *Linq expression* construction. 39 | `ExpressionSpecification` doesn't modify expression in any way. -------------------------------------------------------------------------------- /docs/input/built-in/common/false.md: -------------------------------------------------------------------------------- 1 | Description: Checks if candidate is False. 2 | Order: 300 3 | DisplayDescription: true 4 | Support: Full;Full;Full;Full 5 | ValidationError: "Value is True".;"Value is False" - for negation. 6 | --- 7 | 8 | # Usage 9 | 10 | ```csharp 11 | var spec = Specification.False(); 12 | 13 | spec.IsSatisfiedBy(true); // false 14 | spec.IsSatisfiedBy(false); // true 15 | ``` 16 | 17 | ## As property 18 | 19 | ```csharp 20 | var customerSpec = Specification.False(c => c.IsActive); 21 | 22 | customerSpec.IsSatisfiedBy(new Customer { IsActive = true }); // false 23 | customerSpec.IsSatisfiedBy(new Customer { IsActive = false }); // true 24 | ``` -------------------------------------------------------------------------------- /docs/input/built-in/common/index.cshtml: -------------------------------------------------------------------------------- 1 | Description: Description of all complex, built-in Specifications in FluentSpecification package. 2 | Order: 200 3 | --- 4 | 5 | @Html.Partial("_ChildPages") -------------------------------------------------------------------------------- /docs/input/built-in/common/is-type.md: -------------------------------------------------------------------------------- 1 | Description: Checks if candidate is compatible with a given type. 2 | Order: 2300 3 | DisplayDescription: true 4 | Support: Full;Full;Full;None 5 | ValidationError: "Object is not type of [{Expected}]".;"Object is type of [{Expected}]" - for negation. 6 | ValidationParameters: Expected:Type passed in constructor. 7 | --- 8 | 9 | # Usage 10 | 11 | ```csharp 12 | var spec = Specification.IsType(typeof(List)); 13 | 14 | spec.IsSatisfiedBy(new List()); // true 15 | spec.IsSatisfiedBy("lorem ipsum"); // false 16 | spec.IsSatisfiedBy(null); // false 17 | ``` 18 | 19 | ## As property 20 | 21 | ```csharp 22 | var customerSpec = Specification.IsType>( 23 | c => c.Items, typeof(List)); 24 | 25 | customerSpec.IsSatisfiedBy(new Customer { Items = new List() }); // true 26 | customerSpec.IsSatisfiedBy(new Customer { Items = new LinkedList() }); // false 27 | customerSpec.IsSatisfiedBy(new Customer { Items = null }); // false 28 | ``` 29 | 30 | # Is Not Type 31 | 32 | ```csharp 33 | var spec = Specification.IsNotType(typeof(List)); 34 | 35 | spec.IsSatisfiedBy(new List()); // false 36 | spec.IsSatisfiedBy("lorem ipsum"); // true 37 | spec.IsSatisfiedBy(null); // true 38 | ``` 39 | 40 | ## As property 41 | 42 | ```csharp 43 | var customerSpec = Specification.IsNotType>( 44 | c => c.Items, typeof(List)); 45 | 46 | customerSpec.IsSatisfiedBy(new Customer { Items = new List() }); // false 47 | customerSpec.IsSatisfiedBy(new Customer { Items = new LinkedList() }); // true 48 | customerSpec.IsSatisfiedBy(new Customer { Items = null }); // true 49 | ``` 50 | 51 | # EF 6 support 52 | 53 | Right now `IsTypeSpecification` uses `is operator` for verification - it is not supported in *LinqToEntities* (*LinqToSql*). -------------------------------------------------------------------------------- /docs/input/built-in/common/match.md: -------------------------------------------------------------------------------- 1 | Description: Checks if string candidate match a given Regex pattern. 2 | Order: 2000 3 | DisplayDescription: true 4 | Support: Full;Full;Full;None 5 | ValidationError: "String not match pattern [{Pattern}]".;"String match pattern [{Pattern}]" - for negation. 6 | ValidationParameters: Pattern:Regex pattern passed in constructor. 7 | --- 8 | 9 | # Usage 10 | 11 | ```csharp 12 | var spec = Specification.Match("^[1-9][0-9]{3}-[0-9]{2}-[0-9]{2}$"); 13 | 14 | spec.IsSatisfiedBy("2019-02-26"); // true 15 | spec.IsSatisfiedBy("2019/02/26"); // false 16 | spec.IsSatisfiedBy(null); // false 17 | ``` 18 | 19 | ## As property 20 | 21 | ```csharp 22 | var customerSpec = Specification.Match(c => c.Email, "^.*@email.com$"); 23 | 24 | customerSpec.IsSatisfiedBy(new Customer { Email = "john.doe@email.com" }); // true 25 | customerSpec.IsSatisfiedBy(new Customer { Email = "jd@gmail.com" }); // false 26 | customerSpec.IsSatisfiedBy(new Customer { Email = null }); // false 27 | ``` 28 | 29 | # Not Match 30 | 31 | ```csharp 32 | var spec = Specification.NotMatch("^[1-9][0-9]{3}-[0-9]{2}-[0-9]{2}$"); 33 | 34 | spec.IsSatisfiedBy("2019-02-26"); // false 35 | spec.IsSatisfiedBy("2019/02/26"); // true 36 | spec.IsSatisfiedBy(null); // true 37 | ``` 38 | 39 | ## As property 40 | 41 | ```csharp 42 | var customerSpec = Specification.NotMatch(c => c.Email, "^.*@email.com$"); 43 | 44 | customerSpec.IsSatisfiedBy(new Customer { Email = "john.doe@email.com" }); // false 45 | customerSpec.IsSatisfiedBy(new Customer { Email = "jd@gmail.com" }); // true 46 | customerSpec.IsSatisfiedBy(new Customer { Email = null }); // true 47 | ``` 48 | 49 | # EF 6 support 50 | 51 | Right now `MatchSpecification` uses `Regex` for verification - it is not supported in *LinqToEntities* (*LinqToSql*). -------------------------------------------------------------------------------- /docs/input/built-in/common/null.md: -------------------------------------------------------------------------------- 1 | Description: Checks if candidate is null. 2 | Order: 400 3 | DisplayDescription: true 4 | Support: Full;Full;Full;Partial 5 | ValidationError: "Object is not null".;"Object is null" - for negation. 6 | --- 7 | 8 | # Usage 9 | 10 | ```csharp 11 | var spec = Specification.Null(); 12 | 13 | spec.IsSatisfiedBy(null); // true 14 | spec.IsSatisfiedBy(""); // false 15 | ``` 16 | 17 | ## As property 18 | 19 | ```csharp 20 | var customerSpec = Specification.Null(c => c.Comments); 21 | 22 | customerSpec.IsSatisfiedBy(new Customer { Comments = null }); // true 23 | customerSpec.IsSatisfiedBy(new Customer { Comments = "VIP" }); // false 24 | ``` 25 | 26 | # Not Null 27 | 28 | ```csharp 29 | var spec = Specification.NotNull(); 30 | 31 | spec.IsSatisfiedBy(null); // false 32 | spec.IsSatisfiedBy(""); // true 33 | ``` 34 | 35 | ## As property 36 | 37 | ```csharp 38 | var customerSpec = Specification.NotNull(c => c.Comments); 39 | 40 | customerSpec.IsSatisfiedBy(new Customer { Comments = null }); // false 41 | customerSpec.IsSatisfiedBy(new Customer { Comments = "VIP" }); // true 42 | ``` 43 | 44 | # EF 6 support 45 | 46 | Normally `NullSpecification` is fully supported in EF 6. But be aware, that different types of candidates can raise exceptions. 47 | Only entity types and primitive types are comparable. -------------------------------------------------------------------------------- /docs/input/built-in/common/true.md: -------------------------------------------------------------------------------- 1 | Description: Checks if candidate is True. 2 | Order: 200 3 | DisplayDescription: true 4 | Support: Full;Full;Full;Full 5 | ValidationError: "Value is False".;"Value is True" - for negation. 6 | --- 7 | 8 | # Usage 9 | 10 | ```csharp 11 | var spec = Specification.True(); 12 | 13 | spec.IsSatisfiedBy(true); // true 14 | spec.IsSatisfiedBy(false); // false 15 | ``` 16 | 17 | ## As property 18 | 19 | ```csharp 20 | var customerSpec = Specification.True(c => c.IsActive); 21 | 22 | customerSpec.IsSatisfiedBy(new Customer { IsActive = true }); // true 23 | customerSpec.IsSatisfiedBy(new Customer { IsActive = false }); // false 24 | ``` -------------------------------------------------------------------------------- /docs/input/built-in/core/cast.md: -------------------------------------------------------------------------------- 1 | Description: Converts Specification (candidate to verification) from one type to another. 2 | Order: 500 3 | DisplayDescription: true 4 | Support: Full;Full;Full;Partial 5 | ValidationError: "Cannot cast type [{typeof(T)}] to [{typeof(TCast)}]" - when InvalidCastException. 6 | ValidationParameters: Specification:Specification object passed in constructor. 7 | --- 8 | 9 | Useful, when you have two *Specifications* with different types and you want to join them, for one type of candidate. 10 | 11 | # Usage 12 | 13 | ```csharp 14 | var doubleSpec = Specification.Cast( 15 | Specification.GreaterThan(0)); 16 | 17 | doubleSpec.IsSatisfiedBy(1.45); 18 | 19 | var interfaceSpec = Specification.Cast( 20 | Specification.NotNull()); 21 | 22 | interfaceSpec.IsSatisfiedBy(""); 23 | ``` 24 | 25 | ## As property 26 | 27 | ```csharp 28 | var itemSpec = Specification.Cast(i => i.Price, 29 | Specification.GreaterThan(0)); 30 | 31 | itemSpec.IsSatisfiedBy(new Item { Price = 1.45 }); 32 | 33 | var customerSpec = Specification.Cast( 34 | c => c.Comments, 35 | Specification.NotNull()); 36 | 37 | customerSpec.IsSatisfiedBy(new Customer { Comments = "" }); 38 | ``` 39 | 40 | # EF 6 support 41 | 42 | In *LinqToEntities* only primitive types, enumeration types and entity types are supported. 43 | Casting of any other types, generate `NotSupportedException`. -------------------------------------------------------------------------------- /docs/input/built-in/core/index.cshtml: -------------------------------------------------------------------------------- 1 | Description: Description of all core, built-in Specifications in FluentSpecification.Core package.;These specifications are used most often along with others. 2 | Order: 100 3 | --- 4 | 5 | @Html.Partial("_ChildPages") -------------------------------------------------------------------------------- /docs/input/built-in/index.cshtml: -------------------------------------------------------------------------------- 1 | Title: Built-in Specifications 2 | Order: 200 3 | --- 4 | 5 | @foreach(IDocument child in Model.DocumentList(Keys.Children).OrderBy(x => x.Get(DocsKeys.Order, 1000))) 6 | { 7 |

    @(child.String(Keys.Title))

    8 | 9 | @Html.Partial("_ChildPages", child) 10 | } -------------------------------------------------------------------------------- /docs/input/docs/concept/common-specifications.md: -------------------------------------------------------------------------------- 1 | Description: Description of FluentSpecification library content. Common Specifications and Fluent API construction. 2 | Order: 500 3 | --- 4 | 5 | [`FluentSpecification`](/FluentSpecification/api/FluentSpecification/) package contains common implementation of small reusable Specifications. 6 | All *Specifications* are based on *Specification design pattern*. 7 | Every built-in *Specification* implements `IComplexSpecification` interface - support validation scenarios and also can be used like *Linq* expressions, because they are designed and implemented especially for *Entity Framework Core* support and partially for *Entity Framework 6* and tested with these frameworks. 8 | Library also contains *Fluent API* as a set of methods and extensions for each built-in *Specification*. 9 | 10 | [List of available built-in *Specifications*](/FluentSpecification/built-in/common/). -------------------------------------------------------------------------------- /docs/input/docs/concept/entity-framework-support.md: -------------------------------------------------------------------------------- 1 | Description: Entity Framework support description. 2 | Order: 600 3 | --- 4 | 5 | One of the main goal of *FluentSpecification* is *Linq expressions* support. *FluentSpecification* concentrates on well known ORM - `Entity Framework`. 6 | 7 | # EF Core 8 | 9 | `EF Core` is fully redesigned relative to `EF 6`. Framework, first of all try to convert whatever he can to SQL, 10 | then executes query and at the end in memory executes non-convertible parts of expression. 11 | Thanks to that, many of ideas, not available so far, can be realized on `EF Core` solution. 12 | All built-in *Specifications* works without problems with `EF Core`. Because, there is no possibility to test all cases, 13 | some of *Specifications* may not work as expected in some exceptional conditions. 14 | Good to remember - `EF Core` is still under development and do not cover all `EF 6` functionalities right now. 15 | 16 | # EF 6 17 | 18 | `EF 6` is mature ORM solution, with many tutorials and big community. Many of *FluentSpecification* ideas is not working correctly with `EF 6` (or works partially), 19 | because of used components or by accepted architecture. It's because whole expression is converted to proper SQL queries, so only primitives and entities types can be used and also built-in SQL functions. 20 | In [Built-in Specifications](/FluentSpecification/built-in/) section, every *Specification* contains proper addnotation about `Entity Framework' support 21 | and restrictions or tips. 22 | 23 | ## LinqToEntities flag 24 | 25 | Few *Specifications* to meet special *LinqToEntities* requirements, provides special behavior for that. This state can be force in constructors or globally - 26 | by `Specification.LinqToEntities` flag. 27 | 28 | For more information - visit [Usage section](/FluentSpecification/docs/usage/candidate-verification#linqtoentities). -------------------------------------------------------------------------------- /docs/input/docs/concept/index.cshtml: -------------------------------------------------------------------------------- 1 | Order: 100 2 | --- 3 | 4 | @Html.Partial("_ChildPages") -------------------------------------------------------------------------------- /docs/input/docs/custom-specifications/index.cshtml: -------------------------------------------------------------------------------- 1 | Description: This section describes how to create custom specifications, from scratch based on FluentSpecification.Abstractions or using base Specification classes in FluentSpecification.Core. 2 | Order: 300 3 | --- 4 | 5 | @Html.Partial("_ChildPages") -------------------------------------------------------------------------------- /docs/input/docs/custom-specifications/linq-specification.md: -------------------------------------------------------------------------------- 1 | Description: Custom Specification implementation for Linq. 2 | Order: 300 3 | --- 4 | 5 | For *Linq* scenarios, use `ILinqSpecification` interface from [`FluentSpecification.Abstractions`](/FluentSpecification/api/FluentSpecification.Abstractions/) package. 6 | `ILinqSpecification` inherit from `ISpecification`. 7 | 8 | ```csharp 9 | public class ExpiredCreditCardSpecification : 10 | ILinqSpecification 11 | { 12 | public Expression> GetExpression() 13 | { 14 | DateTime now = DateTime.Now; 15 | return creditCard => creditCard.ValidityDate < now; 16 | } 17 | 18 | Expression ILinqSpecification.GetExpression() 19 | { 20 | return GetExpression(); 21 | } 22 | 23 | public bool IsSatisfiedBy(CreditCard candidate) 24 | { 25 | return GetExpression().Compile().Invoke(candidate); 26 | } 27 | } 28 | ``` 29 | 30 | You don't need to use *Expression* in [`IsSatisfiedBy`](/FluentSpecification/api/FluentSpecification.Abstractions.Generic/ISpecification_1/D6A7440D) method, but it's highly recommended to provide the same result here and here. -------------------------------------------------------------------------------- /docs/input/docs/custom-specifications/specification.md: -------------------------------------------------------------------------------- 1 | Description: Custom, normall Specification implementation. 2 | Order: 100 3 | --- 4 | 5 | To create custom *Specification* type, just need to implement `ISpecification` interface from [`FluentSpecification.Abstractions`](/FluentSpecification/api/FluentSpecification.Abstractions/) package. 6 | 7 | ```csharp 8 | public class VipCustomerSpecification : 9 | ISpecification 10 | { 11 | public bool IsSatisfiedBy(Customer candidate) 12 | { 13 | return candidate 14 | .Comments 15 | .Split(new char[] {';'}) 16 | .Contains("Vip"); 17 | } 18 | } 19 | ``` 20 | 21 | Type, prepared in this way can be used with other, built-in *Specifications*. 22 | 23 | ```csharp 24 | var customerSpec = Specification 25 | .NotNull() 26 | // Create 'VipCustomerSpecification' by parameterless constructor 27 | .And(); 28 | ``` -------------------------------------------------------------------------------- /docs/input/docs/index.cshtml: -------------------------------------------------------------------------------- 1 | Title: Documentation 2 | Order: 100 3 | --- 4 | 5 | @foreach(IDocument child in Model.DocumentList(Keys.Children).OrderBy(x => x.Get(DocsKeys.Order, 1000))) 6 | { 7 |

    @(child.String(Keys.Title))

    8 | 9 | @Html.Partial("_ChildPages", child) 10 | } -------------------------------------------------------------------------------- /docs/input/docs/usage/index.cshtml: -------------------------------------------------------------------------------- 1 | Description: How to start with FluentSpecification.;Description of installation process, how to create Specifications and how to use them. 2 | Order: 200 3 | --- 4 | 5 | @Html.Partial("_ChildPages") -------------------------------------------------------------------------------- /docs/input/docs/usage/installation.md: -------------------------------------------------------------------------------- 1 | Description: Where FluentSpecification can be found and how to install it. 2 | Order: 100 3 | --- 4 | 5 | *FluentSpecification* is available via *NuGet*. 6 | 7 |
    8 | graph TD 9 | subgraph NuGet 10 | FluentSpecification 11 | subgraph 12 | FluentSpecification.Core 13 | subgraph 14 | FluentSpecification.Abstractions 15 | end 16 | end 17 | end 18 |
    19 | 20 | Main package is `FluentSpecification` and contains `FluentSpecification.Core` and `FluentSpecification.Abstractions`. 21 | If needed, *Core* and *Abstractions* can be install separately, also by *NuGet*. 22 | More information about packages content can be found in [Concept section](/FluentSpecification/docs/concept). 23 | 24 | # .NET Core 25 | 26 | ``` 27 | dotnet add package FluentSpecification 28 | ``` 29 | 30 | # NuGet 31 | 32 | ``` 33 | nuget install FluentSpecification 34 | ``` 35 | 36 | ## Package Manager (Visual Studio) 37 | 38 | ``` 39 | Install-Package FluentSpecification 40 | ``` 41 | 42 | # Local reference 43 | 44 | Latest NuGet packages, can be downloaded also from [GitHub releases](https://github.com/michalkowal/FluentSpecification/releases/latest) page. 45 | NuGet package is normal *ZIP* package and contains all binaries for `.NET Core` and `.NET Framework`. 46 | These binaries can be referenced directly in your project. -------------------------------------------------------------------------------- /src/FluentSpecification.Abstractions/FluentSpecification.Abstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FluentSpecification.Abstractions 5 | 6 | 7 | 8 | Abstraction layer for Specification design pattern. Contains also interfaces and structures for validation and Linq scenarios. 9 | Commonly used types: 10 | - FluentSpecification.Abstractions.Generic.ISpecification 11 | - FluentSpecification.Abstractions.Generic.IValidationSpecification 12 | - FluentSpecification.Abstractions.Generic.ILinqSpecification 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/FluentSpecification.Abstractions/Generic/IComplexSpecification.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace FluentSpecification.Abstractions.Generic 4 | { 5 | /// 6 | /// Base generic Specification interface for 7 | /// complex solutions with validation and Linq support. 8 | /// 9 | /// 10 | /// Default "handler" for all kinds of complex Specifications. 11 | /// 12 | /// Type of candidate to verify. 13 | [PublicAPI] 14 | public interface IComplexSpecification : 15 | IValidationSpecification, 16 | ILinqSpecification 17 | { 18 | } 19 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Abstractions/Generic/ICompositeSpecification.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace FluentSpecification.Abstractions.Generic 4 | { 5 | /// 6 | /// Used for composing Specifications e.g. in fluent API. 7 | /// 8 | /// Type of candidate to verify. 9 | [PublicAPI] 10 | public interface ICompositeSpecification 11 | { 12 | /// 13 | /// "Left" Specification, ready for compose. 14 | /// 15 | [PublicAPI] 16 | [NotNull] 17 | ISpecification BaseSpecification { get; } 18 | 19 | /// 20 | /// Compose two separate Specifications ( and ) into 21 | /// one. 22 | /// 23 | /// 24 | /// Result of Composing is always . 25 | /// 26 | /// Compose with. 27 | /// 28 | /// Complex Specification () composed by 29 | /// and . 30 | /// 31 | [NotNull] 32 | IComplexSpecification Compose([NotNull] ISpecification other); 33 | } 34 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Abstractions/Generic/ILinqSpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using JetBrains.Annotations; 4 | 5 | namespace FluentSpecification.Abstractions.Generic 6 | { 7 | /// 8 | /// Base generic Linq Specification interface. 9 | /// 10 | /// 11 | /// Default "handler" for all kinds of generic Linq Specifications. 12 | /// Can be used for searching valid objects in collections or DB sets. 13 | /// 14 | /// Type of candidate to verify. 15 | [PublicAPI] 16 | public interface ILinqSpecification : 17 | ILinqSpecification, 18 | ISpecification 19 | { 20 | /// 21 | /// Gets typed lambda Linq Expression with candidate object verification. 22 | /// 23 | /// Strongly typed lambda Linq Expression. 24 | [PublicAPI] 25 | [NotNull] 26 | new Expression> GetExpression(); 27 | } 28 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Abstractions/Generic/INegatableLinqSpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using JetBrains.Annotations; 4 | 5 | namespace FluentSpecification.Abstractions.Generic 6 | { 7 | /// 8 | /// Base negation of generic Linq Specification interface. 9 | /// 10 | /// 11 | /// Default "handler" for all kinds of negation generic Linq Specifications. 12 | /// Can be used for searching valid objects in collections or DB sets. 13 | /// 14 | /// Type of candidate to verify. 15 | [PublicAPI] 16 | public interface INegatableLinqSpecification : 17 | INegatableSpecification 18 | { 19 | /// 20 | /// Gets typed lambda Linq Expression with candidate object verification. 21 | /// 22 | /// Strongly typed lambda Linq Expression. 23 | [PublicAPI] 24 | [NotNull] 25 | Expression> GetNegationExpression(); 26 | } 27 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Abstractions/Generic/INegatableSpecification.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace FluentSpecification.Abstractions.Generic 4 | { 5 | /// 6 | /// Base negation of generic Specification interface. 7 | /// 8 | /// 9 | /// Default "handler" for all kinds of negation generic Specifications. 10 | /// Used for negation check, for example object is not null or not equal. 11 | /// 12 | /// Type of candidate to verify. 13 | [PublicAPI] 14 | public interface INegatableSpecification : ISpecification 15 | { 16 | /// 17 | /// Checks if Specification is NOT satisfied by object. 18 | /// 19 | /// Candidate object to verification. 20 | /// 21 | /// true - Specification is NOT satisfied by . 22 | /// false - is satisfied. 23 | /// 24 | [PublicAPI] 25 | bool IsNotSatisfiedBy([CanBeNull] T candidate); 26 | } 27 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Abstractions/Generic/INegatableValidationSpecification.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Abstractions.Validation; 2 | using JetBrains.Annotations; 3 | 4 | namespace FluentSpecification.Abstractions.Generic 5 | { 6 | /// 7 | /// Base negation of generic Specification interface for 8 | /// single objects validation scenarios. 9 | /// 10 | /// 11 | /// Default "handler" for all kinds of negation generic Specifications for objects validation. 12 | /// 13 | /// Can be used for negation check, where single objects needs verification with errors prepared for e.g. UI, 14 | /// like DTOs. 15 | /// 16 | /// 17 | /// Type of candidate to verify. 18 | [PublicAPI] 19 | public interface INegatableValidationSpecification : 20 | INegatableSpecification 21 | { 22 | /// 23 | /// Checks if Specification is NOT satisfied by object. 24 | /// Returns validation no matter is satisfied or not. 25 | /// 26 | /// Candidate object to verification. 27 | /// 28 | /// Contains validation summary - errors, types of all executed Specifications 29 | /// and trace message in the style of Boole algebra. 30 | /// 31 | /// 32 | /// true - Specification is NOT satisfied by . 33 | /// false - is satisfied. should contains errors. 34 | /// 35 | [PublicAPI] 36 | bool IsNotSatisfiedBy([CanBeNull] T candidate, [NotNull] out SpecificationResult result); 37 | } 38 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Abstractions/Generic/ISpecification.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace FluentSpecification.Abstractions.Generic 4 | { 5 | /// 6 | /// Base generic Specification interface. 7 | /// 8 | /// 9 | /// Default "handler" for all kinds of generic Specifications. 10 | /// 11 | /// Type of candidate to verify. 12 | [PublicAPI] 13 | public interface ISpecification : ISpecification 14 | { 15 | /// 16 | /// Checks if Specification is satisfied by object. 17 | /// 18 | /// Candidate object to verification. 19 | /// 20 | /// true - Specification is satisfied by . 21 | /// false - is not. 22 | /// 23 | [PublicAPI] 24 | bool IsSatisfiedBy([CanBeNull] T candidate); 25 | } 26 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Abstractions/Generic/IValidationSpecification.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Abstractions.Validation; 2 | using JetBrains.Annotations; 3 | 4 | namespace FluentSpecification.Abstractions.Generic 5 | { 6 | /// 7 | /// Base generic Specification interface for 8 | /// single objects validation scenarios. 9 | /// 10 | /// 11 | /// Default "handler" for all kinds of generic Specifications for objects validation. 12 | /// 13 | /// Can be used for scenarios, where single objects needs verification with errors prepared for e.g. UI, like 14 | /// DTOs. 15 | /// 16 | /// 17 | /// Type of candidate to verify. 18 | [PublicAPI] 19 | public interface IValidationSpecification : 20 | ISpecification 21 | { 22 | /// 23 | /// Checks if Specification is satisfied by object. 24 | /// Returns validation no matter is satisfied or not. 25 | /// 26 | /// Candidate object to verification. 27 | /// 28 | /// Contains validation summary - errors, types of all executed Specifications 29 | /// and trace message in the style of Boole algebra. 30 | /// 31 | /// 32 | /// true - Specification is satisfied by . 33 | /// false - is not. should contains errors. 34 | /// 35 | [PublicAPI] 36 | bool IsSatisfiedBy([CanBeNull] T candidate, [NotNull] out SpecificationResult result); 37 | } 38 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Abstractions/ILinqSpecification.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | using JetBrains.Annotations; 3 | 4 | namespace FluentSpecification.Abstractions 5 | { 6 | /// 7 | /// Base Linq Specification interface. 8 | /// 9 | /// 10 | /// Default "handler" for all kinds of Linq Specifications. 11 | /// 12 | [PublicAPI] 13 | public interface ILinqSpecification : ISpecification 14 | { 15 | /// 16 | /// Gets Linq Expression with candidate object verification. 17 | /// 18 | /// Linq expression. 19 | [NotNull] 20 | Expression GetExpression(); 21 | } 22 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Abstractions/ISpecification.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace FluentSpecification.Abstractions 4 | { 5 | /// 6 | /// Base Specification interface. 7 | /// 8 | /// 9 | /// Default "handler" for all kinds of Specifications. 10 | /// 11 | [PublicAPI] 12 | public interface ISpecification 13 | { 14 | } 15 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Api/SpecificationTests.WithMessage.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Abstractions.Generic; 2 | using FluentSpecification.Core.Tests.Mocks; 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | using Xunit; 6 | 7 | namespace FluentSpecification.Core.Tests.Api 8 | { 9 | public partial class SpecificationTests 10 | { 11 | public class WithMessage 12 | { 13 | [Fact] 14 | public void CorrectSpecification_ReturnTrasnlatableSpecificationObject() 15 | { 16 | var sut = MockSpecification.True(); 17 | 18 | var withMessage = sut.WithMessage("Message"); 19 | 20 | Assert.NotNull(withMessage); 21 | Assert.IsType>(withMessage); 22 | } 23 | 24 | [Fact] 25 | [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] 26 | public void NullSelfSpecification_ArgumentNullException() 27 | { 28 | var exception = Record.Exception(() => ((ISpecification)null).WithMessage("Message")); 29 | 30 | Assert.NotNull(exception); 31 | Assert.IsType(exception); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/ComplexSpecificationTests.GetNegationExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Tests.Data; 2 | using FluentSpecification.Core.Tests.Mocks; 3 | using FluentSpecification.Tests.Sdk; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Core.Tests 7 | { 8 | public partial class ComplexSpecificationTests 9 | { 10 | public class GetNegationExpression 11 | { 12 | [Theory] 13 | [CorrectData(typeof(ComplexData), AsNegation = true)] 14 | public void InvokeCorrectData_ReturnTrue(T candidate) 15 | { 16 | var sut = new MockCommonSpecification(); 17 | 18 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 19 | 20 | Assert.True(result); 21 | } 22 | 23 | [Theory] 24 | [IncorrectData(typeof(ComplexData), AsNegation = true)] 25 | public void InvokeIncorrectData_ReturnFalse(T candidate) 26 | { 27 | var sut = new MockCommonSpecification(); 28 | 29 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 30 | 31 | Assert.False(result); 32 | } 33 | 34 | [Fact] 35 | public void InvokeNull_NotRaiseException() 36 | { 37 | var sut = new MockCommonSpecification(); 38 | 39 | var exception = Record.Exception(() => sut.GetNegationExpression().Compile().Invoke(null)); 40 | 41 | Assert.Null(exception); 42 | } 43 | 44 | [Fact] 45 | public void InvokeNullable_ReturnTrue() 46 | { 47 | var sut = new MockCommonSpecification(); 48 | 49 | var result = sut.GetNegationExpression().Compile().Invoke(0); 50 | 51 | Assert.True(result); 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Data/AdapterData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Tests.Mocks; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | 4 | namespace FluentSpecification.Core.Tests.Data 5 | { 6 | public class AdapterData : SpecificationData 7 | { 8 | public AdapterData() 9 | { 10 | Valid(false) 11 | .Result("TrueMockSpecification") 12 | .NegationResult("NotTrueMockSpecification+Failed", c => c 13 | .FailedSpecification(typeof(TrueMockSpecification), 14 | "Specification [TrueMockSpecification] is satisfied by candidate")); 15 | Valid(true) 16 | .Result("TrueMockValidationSpecification") 17 | .NegationResult("FailedNotTrueMockNegatableValidationSpecification", c => c 18 | .FailedSpecification(typeof(TrueMockNegatableValidationSpecification), 19 | "NotMockNegatableValidationSpecification is satisfied") 20 | .AddParameter("Result", true)); 21 | 22 | Invalid(false) 23 | .Result("FalseMockSpecification+Failed", c => c 24 | .FailedSpecification(typeof(FalseMockSpecification), 25 | "Specification [FalseMockSpecification] is not satisfied by candidate")) 26 | .NegationResult("NotFalseMockSpecification"); 27 | Invalid(true) 28 | .Result("FailedFalseMockValidationSpecification", c => c 29 | .FailedSpecification(typeof(FalseMockValidationSpecification), 30 | "MockValidationSpecification is not satisfied") 31 | .AddParameter("Result", false)) 32 | .NegationResult("NotFalseMockNegatableValidationSpecification"); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Data/AndData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Tests.Mocks; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | 4 | namespace FluentSpecification.Core.Tests.Data 5 | { 6 | public class AndData : SpecificationData 7 | { 8 | public AndData() 9 | { 10 | Valid(true, true) 11 | .Result(2, "TrueMockValidationSpecification And TrueMockValidationSpecification"); 12 | 13 | Invalid(false, true) 14 | .Result(2, "FailedFalseMockValidationSpecification And TrueMockValidationSpecification", c => 15 | c.FailedSpecification(typeof(FalseMockValidationSpecification), 16 | "MockValidationSpecification is not satisfied") 17 | .AddParameter("Result", false)); 18 | Invalid(true, false) 19 | .Result(2, "TrueMockValidationSpecification And FailedFalseMockValidationSpecification", c => 20 | c.FailedSpecification(typeof(FalseMockValidationSpecification), 21 | "MockValidationSpecification is not satisfied") 22 | .AddParameter("Result", false)); 23 | Invalid(false, false) 24 | .Result(2, "FailedFalseMockValidationSpecification And FailedFalseMockValidationSpecification", c => 25 | { 26 | c.FailedSpecification(typeof(FalseMockValidationSpecification), 27 | "MockValidationSpecification is not satisfied") 28 | .AddParameter("Result", false); 29 | c.FailedSpecification(typeof(FalseMockValidationSpecification), 30 | "MockValidationSpecification is not satisfied") 31 | .AddParameter("Result", false); 32 | }); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Data/NotData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Tests.Mocks; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | 4 | namespace FluentSpecification.Core.Tests.Data 5 | { 6 | public class NotData : SpecificationData 7 | { 8 | public NotData() 9 | { 10 | Valid(false) 11 | .Result("Not(FailedFalseMockComplexSpecification)"); 12 | Valid(true) 13 | .Result("NotFalseMockNegatableComplexSpecification"); 14 | 15 | Invalid(false) 16 | .Result("Not(TrueMockComplexSpecification)"); 17 | Invalid(true) 18 | .Result("FailedNotTrueMockNegatableComplexSpecification", c => 19 | { 20 | c.FailedSpecification(typeof(TrueMockNegatableComplexSpecification), 21 | "NotMockNegatableValidationSpecification is satisfied") 22 | .AddParameter("Result", true); 23 | }); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Data/OrData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Tests.Mocks; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | 4 | namespace FluentSpecification.Core.Tests.Data 5 | { 6 | public class OrData : SpecificationData 7 | { 8 | public OrData() 9 | { 10 | Valid(true, true) 11 | .Result(2, "TrueMockValidationSpecification Or TrueMockValidationSpecification"); 12 | Valid(false, true) 13 | .Result(2, "FailedFalseMockValidationSpecification Or TrueMockValidationSpecification"); 14 | Valid(true, false) 15 | .Result(2, "TrueMockValidationSpecification Or FailedFalseMockValidationSpecification"); 16 | 17 | Invalid(false, false) 18 | .Result(2, "FailedFalseMockValidationSpecification Or FailedFalseMockValidationSpecification", c => 19 | { 20 | c.FailedSpecification(typeof(FalseMockValidationSpecification), 21 | "MockValidationSpecification is not satisfied") 22 | .AddParameter("Result", false); 23 | c.FailedSpecification(typeof(FalseMockValidationSpecification), 24 | "MockValidationSpecification is not satisfied") 25 | .AddParameter("Result", false); 26 | }); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Data/PropertyExpressionData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using FluentSpecification.Core.Tests.Mocks; 4 | using FluentSpecification.Tests.Sdk.Data; 5 | 6 | namespace FluentSpecification.Core.Tests.Data 7 | { 8 | public class PropertyExpressionData : SpecificationData 9 | { 10 | public PropertyExpressionData() 11 | { 12 | var candidate = new FakeType 13 | { 14 | First = 1, 15 | Second = "2", 16 | Inter = new InterFakeType() 17 | }; 18 | var empty = new FakeType(); 19 | var ftSecondSelector = (Expression>) (ft => ft.Second); 20 | var ftThirdSelector = (Expression>) (ft => ft.Inter.Third); 21 | 22 | AddValid(candidate, ftSecondSelector); 23 | AddValid(candidate, ftThirdSelector); 24 | AddValid(empty, ftSecondSelector); 25 | 26 | AddInvalid(candidate, ftSecondSelector); 27 | AddInvalid(candidate, ftThirdSelector); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Data/PropertySelectorData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using FluentSpecification.Core.Tests.Mocks; 4 | using FluentSpecification.Tests.Sdk.Data; 5 | 6 | namespace FluentSpecification.Core.Tests.Data 7 | { 8 | public class PropertySelectorData : SpecificationData 9 | { 10 | public PropertySelectorData() 11 | { 12 | AddInvalid((Expression>) (ft => true)); 13 | AddInvalid((Expression>) (ft => new object())); 14 | AddInvalid((Expression>) (ft => "")); 15 | AddInvalid((Expression>) (ft => null)); 16 | AddInvalid((Expression>) (ft => "Property")); 17 | AddInvalid((Expression>) (ft => ft.Second + "")); 18 | AddInvalid((Expression>) (ft => 1)); 19 | AddInvalid((Expression>) (ft => ft.First + 0)); 20 | AddInvalid((Expression>) (ft => ft.Inter.Third && false)); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Data/TranslatableCompositeData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Tests.Mocks; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | 4 | namespace FluentSpecification.Core.Tests.Data 5 | { 6 | public class TranslatableCompositeData : SpecificationData 7 | { 8 | public TranslatableCompositeData() 9 | { 10 | Valid("Custom mock message") 11 | .Result(2, "MockCompositeInternalSpecification Mock MockCompositeInternalSpecification"); 12 | 13 | Invalid("Custom mock message") 14 | .Result(2, "FailedMockCompositeInternalSpecification Mock FailedMockCompositeInternalSpecification", c => 15 | { 16 | c.CustomError("Custom mock message"); 17 | c.FailedSpecification(typeof(FalseMockCompositeSpecification.InternalSpecification), 18 | "MockValidationSpecification is not satisfied") 19 | .AddParameter("Result", false) 20 | .AddParameter("External", 0); 21 | c.FailedSpecification(typeof(FalseMockCompositeSpecification.InternalSpecification), 22 | "MockValidationSpecification is not satisfied") 23 | .AddParameter("Result", false) 24 | .AddParameter("External", 0); 25 | }); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Data/TranslatableData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Tests.Mocks; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | 4 | namespace FluentSpecification.Core.Tests.Data 5 | { 6 | public class TranslatableData : SpecificationData 7 | { 8 | public TranslatableData() 9 | { 10 | Valid("Custom mock message") 11 | .Result("TrueMockValidationSpecification"); 12 | 13 | Invalid("Custom mock message") 14 | .Result("FailedFalseMockValidationSpecification", c => c 15 | .CustomError("Custom mock message") 16 | .FailedSpecification(typeof(FalseMockValidationSpecification), 17 | "MockValidationSpecification is not satisfied") 18 | .AddParameter("Result", false)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Data/TranslatableWithFactoryAndParametersData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Tests.Mocks; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace FluentSpecification.Core.Tests.Data 7 | { 8 | public class TranslatableWithFactoryAndParametersData : 9 | SpecificationData, string>> 10 | { 11 | public TranslatableWithFactoryAndParametersData() 12 | { 13 | Invalid(145, (c, p) => $"Passed value = {c}; With parameter = {p["Result"]}") 14 | .Result("FailedFalseMockValidationSpecification[System.Int32]", c => c 15 | .CustomError("Passed value = 145; With parameter = False") 16 | .FailedSpecification(typeof(FalseMockValidationSpecification), 17 | "MockValidationSpecification is not satisfied") 18 | .Candidate(145) 19 | .AddParameter("Result", false)); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Data/TranslatableWithFactoryData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Tests.Mocks; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | using System; 4 | 5 | namespace FluentSpecification.Core.Tests.Data 6 | { 7 | public class TranslatableWithFactoryData : SpecificationData> 8 | { 9 | public TranslatableWithFactoryData() 10 | { 11 | Invalid(145, c => $"Passed value = {c}") 12 | .Result("FailedFalseMockValidationSpecification[System.Int32]", c => c 13 | .CustomError("Passed value = 145") 14 | .FailedSpecification(typeof(FalseMockValidationSpecification), 15 | "MockValidationSpecification is not satisfied") 16 | .Candidate(145) 17 | .AddParameter("Result", false)); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/ChildComparableFakeType.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | public class ChildComparableFakeType : ComparableFakeType 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/ChildFakeType.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | public class ChildFakeType : FakeType 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/FakeType.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace FluentSpecification.Core.Tests.Mocks 6 | { 7 | public class FakeType : IEnumerable 8 | { 9 | public int First { get; set; } 10 | public string Second { get; set; } 11 | public InterFakeType Inter { get; set; } 12 | 13 | public IEnumerator GetEnumerator() 14 | { 15 | return (Second ?? Enumerable.Empty()).GetEnumerator(); 16 | } 17 | 18 | IEnumerator IEnumerable.GetEnumerator() 19 | { 20 | return GetEnumerator(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/FalseMockComplexSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | internal class FalseMockComplexSpecification : MockComplexSpecification 4 | { 5 | public FalseMockComplexSpecification() : base(false) 6 | { 7 | TraceMessage = $"FalseMockComplexSpecification[{typeof(T)}]"; 8 | } 9 | 10 | protected override string TraceMessage { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/FalseMockNegatableComplexSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | internal class FalseMockNegatableComplexSpecification : MockNegatableComplexSpecification 4 | { 5 | public FalseMockNegatableComplexSpecification() : base(false) 6 | { 7 | TraceMessage = $"FalseMockNegatableComplexSpecification[{typeof(T)}]"; 8 | } 9 | 10 | protected override string TraceMessage { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/FalseMockNegatableSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | internal class FalseMockNegatableSpecification : MockNegatableSpecification 4 | { 5 | public FalseMockNegatableSpecification() : base(false) 6 | { 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/FalseMockNegatableValidationSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | internal class FalseMockNegatableValidationSpecification : MockNegatableValidationSpecification 4 | { 5 | public FalseMockNegatableValidationSpecification() : base(false) 6 | { 7 | TraceMessage = $"FalseMockNegatableValidationSpecification[{typeof(T)}]"; 8 | } 9 | 10 | protected override string TraceMessage { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/FalseMockSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | internal class FalseMockSpecification : MockSpecification 4 | { 5 | public FalseMockSpecification() : base(false) 6 | { 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/FalseMockValidationSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | internal class FalseMockValidationSpecification : MockValidationSpecification 4 | { 5 | public FalseMockValidationSpecification() : base(false) 6 | { 7 | TraceMessage = $"FalseMockValidationSpecification[{typeof(T)}]"; 8 | } 9 | 10 | protected override string TraceMessage { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/InterFakeType.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | public class InterFakeType 4 | { 5 | public bool Third { get; } = false; 6 | } 7 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/MockCommonSpecification.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | 3 | namespace FluentSpecification.Core.Tests.Mocks 4 | { 5 | internal class MockCommonSpecification : ComplexSpecification 6 | { 7 | protected override string CreateFailedMessage(T candidate) 8 | { 9 | return "Not match"; 10 | } 11 | 12 | protected override string CreateNegationFailedMessage(T candidate) 13 | { 14 | return "Match"; 15 | } 16 | 17 | protected override Expression BuildValueTypeExpressionBody(Expression parameter) 18 | { 19 | return Expression.Constant(true); 20 | } 21 | 22 | protected override Expression BuildExpressionBody(Expression parameter) 23 | { 24 | return Expression.Constant(false); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/MockLinqSpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using FluentSpecification.Abstractions; 4 | using FluentSpecification.Abstractions.Generic; 5 | 6 | namespace FluentSpecification.Core.Tests.Mocks 7 | { 8 | internal sealed class MockLinqSpecification : 9 | ILinqSpecification, 10 | INegatableLinqSpecification 11 | { 12 | private readonly Expression> _expression; 13 | private readonly Expression> _negation; 14 | 15 | public MockLinqSpecification(Expression> expression, Expression> negation = null) 16 | { 17 | _expression = expression; 18 | _negation = negation; 19 | } 20 | 21 | public Expression> GetExpression() 22 | { 23 | return _expression; 24 | } 25 | 26 | Expression ILinqSpecification.GetExpression() 27 | { 28 | return GetExpression(); 29 | } 30 | 31 | public bool IsSatisfiedBy(T candidate) 32 | { 33 | return GetExpression().Compile().Invoke(candidate); 34 | } 35 | 36 | public Expression> GetNegationExpression() 37 | { 38 | return _negation; 39 | } 40 | 41 | public bool IsNotSatisfiedBy(T candidate) 42 | { 43 | return GetNegationExpression().Compile().Invoke(candidate); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/MockSpecification.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Abstractions.Generic; 2 | 3 | namespace FluentSpecification.Core.Tests.Mocks 4 | { 5 | internal abstract class MockSpecification : ISpecification 6 | { 7 | protected readonly bool Result; 8 | 9 | protected MockSpecification(bool result) 10 | { 11 | Result = result; 12 | } 13 | 14 | public bool IsSatisfiedBy(T candidate) 15 | { 16 | return Result; 17 | } 18 | 19 | public static ISpecification Create(bool result) 20 | { 21 | return result ? True() : False(); 22 | } 23 | 24 | public static ISpecification True() 25 | { 26 | return new TrueMockSpecification(); 27 | } 28 | 29 | public static ISpecification False() 30 | { 31 | return new FalseMockSpecification(); 32 | } 33 | } 34 | 35 | internal abstract class MockSpecification : MockSpecification 36 | { 37 | protected MockSpecification(bool result) 38 | : base(result) 39 | { 40 | } 41 | 42 | public new static ISpecification Create(bool result) 43 | { 44 | return result ? True() : False(); 45 | } 46 | 47 | public new static ISpecification True() 48 | { 49 | return new TrueMockSpecification(); 50 | } 51 | 52 | public new static ISpecification False() 53 | { 54 | return new FalseMockSpecification(); 55 | } 56 | } 57 | 58 | internal class TrueMockSpecification : MockSpecification 59 | { 60 | public TrueMockSpecification() : base(true) 61 | { 62 | } 63 | } 64 | 65 | internal class FalseMockSpecification : MockSpecification 66 | { 67 | public FalseMockSpecification() : base(false) 68 | { 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/TrueMockComplexSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | internal class TrueMockComplexSpecification : MockComplexSpecification 4 | { 5 | public TrueMockComplexSpecification() : base(true) 6 | { 7 | TraceMessage = $"TrueMockComplexSpecification[{typeof(T)}]"; 8 | } 9 | 10 | protected override string TraceMessage { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/TrueMockNegatableComplexSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | internal class TrueMockNegatableComplexSpecification : MockNegatableComplexSpecification 4 | { 5 | public TrueMockNegatableComplexSpecification() : base(true) 6 | { 7 | TraceMessage = $"TrueMockNegatableComplexSpecification[{typeof(T)}]"; 8 | } 9 | 10 | protected override string TraceMessage { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/TrueMockNegatableSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | internal class TrueMockNegatableSpecification : MockNegatableSpecification 4 | { 5 | public TrueMockNegatableSpecification() : base(true) 6 | { 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/TrueMockNegatableValidationSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | internal class TrueMockNegatableValidationSpecification : MockNegatableValidationSpecification 4 | { 5 | public TrueMockNegatableValidationSpecification() : base(true) 6 | { 7 | TraceMessage = $"TrueMockNegatableValidationSpecification[{typeof(T)}]"; 8 | } 9 | 10 | protected override string TraceMessage { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/TrueMockSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | internal class TrueMockSpecification : MockSpecification 4 | { 5 | public TrueMockSpecification() : base(true) 6 | { 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Mocks/TrueMockValidationSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Core.Tests.Mocks 2 | { 3 | internal class TrueMockValidationSpecification : MockValidationSpecification 4 | { 5 | public TrueMockValidationSpecification() : base(true) 6 | { 7 | TraceMessage = $"TrueMockValidationSpecification[{typeof(T)}]"; 8 | } 9 | 10 | protected override string TraceMessage { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/TranslatableSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Tests.Mocks; 2 | using JetBrains.Annotations; 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | using Xunit; 6 | 7 | namespace FluentSpecification.Core.Tests 8 | { 9 | [UsedImplicitly] 10 | public partial class TranslatableSpecificationTests 11 | { 12 | public class Constructor 13 | { 14 | [Fact] 15 | [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] 16 | public void NullSpecification_ArgumentNullException() 17 | { 18 | var exception = Record.Exception(() => 19 | new TranslatableSpecification(null, "Custom error message")); 20 | 21 | Assert.NotNull(exception); 22 | Assert.IsType(exception); 23 | } 24 | 25 | [Theory] 26 | [InlineData(null)] 27 | [InlineData("")] 28 | public void IncorrectMessage_ArgumentException(string message) 29 | { 30 | var exception = Record.Exception(() => 31 | new TranslatableSpecification(new MockCommonSpecification(), message)); 32 | 33 | Assert.NotNull(exception); 34 | Assert.IsType(exception); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/FluentSpecification.Core.Tests/Utils/TypeExtensionsTests.IsNumericType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using FluentSpecification.Core.Tests.Data; 4 | using FluentSpecification.Core.Utils; 5 | using FluentSpecification.Tests.Sdk; 6 | using Xunit; 7 | 8 | namespace FluentSpecification.Core.Tests.Utils 9 | { 10 | public partial class TypeExtensionsTests 11 | { 12 | public class IsNumericType 13 | { 14 | [Theory] 15 | [CorrectData(typeof(TypeNumericData))] 16 | public void NumericType_ReturnTrue(Type sut) 17 | { 18 | var result = sut.IsNumericType(); 19 | 20 | Assert.True(result); 21 | } 22 | 23 | [Theory] 24 | [IncorrectData(typeof(TypeNumericData))] 25 | public void NonNumericType_ReturnFalse(Type sut) 26 | { 27 | var result = sut.IsNumericType(); 28 | 29 | Assert.False(result); 30 | } 31 | 32 | [Fact] 33 | [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] 34 | public void Null_Exception() 35 | { 36 | var exception = Record.Exception(() => ((Type) null).IsNumericType()); 37 | 38 | Assert.NotNull(exception); 39 | Assert.IsType(exception); 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core/Composite/AndSpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using FluentSpecification.Abstractions.Generic; 4 | using JetBrains.Annotations; 5 | 6 | namespace FluentSpecification.Core.Composite 7 | { 8 | /// 9 | /// Join Specifications as logical AND. 10 | /// 11 | /// Type of candidate to verify. 12 | [PublicAPI] 13 | public sealed class AndSpecification : CompositeSpecification 14 | { 15 | /// 16 | /// Creates composite object. 17 | /// 18 | /// Base first Expression. 19 | /// Base second Expression. 20 | /// Thrown when or is null. 21 | [PublicAPI] 22 | public AndSpecification([NotNull] ISpecification left, [NotNull] ISpecification right) 23 | : base(left, right, Expression.AndAlso) 24 | { 25 | } 26 | 27 | /// 28 | [PublicAPI] 29 | protected override string TraceMessageConnector { get; } = "And"; 30 | 31 | /// 32 | [PublicAPI] 33 | protected override bool Merge(bool left, bool right) 34 | { 35 | return left && right; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core/Composite/OrSpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using FluentSpecification.Abstractions.Generic; 4 | using JetBrains.Annotations; 5 | 6 | namespace FluentSpecification.Core.Composite 7 | { 8 | /// 9 | /// Join Specifications as logical OR. 10 | /// 11 | /// Type of candidate to verify. 12 | [PublicAPI] 13 | public sealed class OrSpecification : CompositeSpecification 14 | { 15 | /// 16 | /// Creates composite object. 17 | /// 18 | /// Base first Expression. 19 | /// Base second Expression. 20 | /// Thrown when or is null. 21 | [PublicAPI] 22 | public OrSpecification(ISpecification left, ISpecification right) 23 | : base(left, right, Expression.OrElse) 24 | { 25 | } 26 | 27 | /// 28 | [PublicAPI] 29 | protected override string TraceMessageConnector { get; } = "Or"; 30 | 31 | /// 32 | [PublicAPI] 33 | protected override bool Merge(bool left, bool right) 34 | { 35 | return left || right; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core/FluentSpecification.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | FluentSpecification.Core 5 | 6 | 7 | 8 | Core implementation of Specification design pattern. Contains: 9 | - Specifications composition (And, Or, AndNot, OrNot) 10 | - Specifications negation with validation handling (error message negation, linq negation etc.) 11 | - Error handling for validation scenarios 12 | - Linq expressions composing 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/FluentSpecification.Core/Properties/AssemblyInfo.PublicKey.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("FluentSpecification.Core.Tests, PublicKey=" + 4 | "0024000004800000940000000602000000240000525341310004000001000100b953b57c8d43da" + 5 | "9a3acbd21dc190f3080922944ad2ff8703368dfad824235e6821f6a16a0d2905ce0e3be5d56d26" + 6 | "6ab47ee0174ecffc6bbc60264b3964bf23e076906cb2e23bbaba6665e2c156d15386ade4ec60ac" + 7 | "647c1e7d484246db938009279d50fe9288a7afd5129954890c6a561a07f699d84889bb87bdc875" + 8 | "cf5dc4ca")] 9 | -------------------------------------------------------------------------------- /src/FluentSpecification.Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("FluentSpecification.Core.Tests")] 4 | -------------------------------------------------------------------------------- /src/FluentSpecification.Core/Utils/AndFluentProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Abstractions.Generic; 3 | using FluentSpecification.Core.Composite; 4 | using JetBrains.Annotations; 5 | 6 | namespace FluentSpecification.Core.Utils 7 | { 8 | /// 9 | /// Used for composing Specifications by fluent API. 10 | /// Join Specifications by logical AND. 11 | /// 12 | /// Type of candidate to verify. 13 | internal sealed class AndFluentProxy : 14 | ICompositeSpecification 15 | { 16 | /// 17 | /// Create Proxy object 18 | /// 19 | /// "Left" Specification, ready for compose. 20 | public AndFluentProxy([NotNull] ISpecification baseSpecification) 21 | { 22 | BaseSpecification = baseSpecification ?? throw new ArgumentNullException(nameof(baseSpecification)); 23 | } 24 | 25 | /// 26 | public ISpecification BaseSpecification { get; } 27 | 28 | /// 29 | public IComplexSpecification Compose(ISpecification other) 30 | { 31 | return new AndSpecification(BaseSpecification, other); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core/Utils/AndNotFluentProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Abstractions.Generic; 3 | using FluentSpecification.Core.Composite; 4 | using JetBrains.Annotations; 5 | 6 | namespace FluentSpecification.Core.Utils 7 | { 8 | /// 9 | /// Used for composing Specifications by fluent API. 10 | /// Join Specifications by logical AND. Second Specifications is negated. 11 | /// 12 | /// Type of candidate to verify. 13 | internal sealed class AndNotFluentProxy : 14 | ICompositeSpecification 15 | { 16 | /// 17 | /// Create Proxy object 18 | /// 19 | /// "Left" Specification, ready for compose. 20 | public AndNotFluentProxy([NotNull] ISpecification baseSpecification) 21 | { 22 | BaseSpecification = baseSpecification ?? throw new ArgumentNullException(nameof(baseSpecification)); 23 | } 24 | 25 | /// 26 | public ISpecification BaseSpecification { get; } 27 | 28 | /// 29 | public IComplexSpecification Compose(ISpecification other) 30 | { 31 | return new AndSpecification(BaseSpecification, other.Not()); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core/Utils/OrFluentProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Abstractions.Generic; 3 | using FluentSpecification.Core.Composite; 4 | using JetBrains.Annotations; 5 | 6 | namespace FluentSpecification.Core.Utils 7 | { 8 | /// 9 | /// Used for composing Specifications by fluent API. 10 | /// Join Specifications by logical OR. 11 | /// 12 | /// Type of candidate to verify. 13 | internal sealed class OrFluentProxy : 14 | ICompositeSpecification 15 | { 16 | /// 17 | /// Create Proxy object 18 | /// 19 | /// "Left" Specification, ready for compose. 20 | public OrFluentProxy([NotNull] ISpecification baseSpecification) 21 | { 22 | BaseSpecification = baseSpecification ?? throw new ArgumentNullException(nameof(baseSpecification)); 23 | } 24 | 25 | /// 26 | public ISpecification BaseSpecification { get; } 27 | 28 | /// 29 | public IComplexSpecification Compose(ISpecification other) 30 | { 31 | return new OrSpecification(BaseSpecification, other); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Core/Utils/OrNotFluentProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Abstractions.Generic; 3 | using FluentSpecification.Core.Composite; 4 | using JetBrains.Annotations; 5 | 6 | namespace FluentSpecification.Core.Utils 7 | { 8 | /// 9 | /// Used for composing Specifications by fluent API. 10 | /// Join Specifications by logical OR. Second Specifications is negated. 11 | /// 12 | /// Type of candidate to verify. 13 | internal sealed class OrNotFluentProxy : 14 | ICompositeSpecification 15 | { 16 | /// 17 | /// Create Proxy object 18 | /// 19 | /// "Left" Specification, ready for compose. 20 | public OrNotFluentProxy([NotNull] ISpecification baseSpecification) 21 | { 22 | BaseSpecification = baseSpecification ?? throw new ArgumentNullException(nameof(baseSpecification)); 23 | } 24 | 25 | /// 26 | public ISpecification BaseSpecification { get; } 27 | 28 | /// 29 | public IComplexSpecification Compose(ISpecification other) 30 | { 31 | return new OrSpecification(BaseSpecification, other.Not()); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/BaseEfCoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Integration.Tests.Data; 3 | using Xunit; 4 | 5 | namespace FluentSpecification.Integration.Tests 6 | { 7 | [Collection("EF Collection")] 8 | public abstract class BaseEfCoreTests : IDisposable 9 | { 10 | protected BaseEfCoreTests(DatabaseFixture fixture) 11 | { 12 | Context = new EfCoreDbContext(fixture.Options); 13 | } 14 | 15 | internal EfCoreDbContext Context { get; } 16 | 17 | public void Dispose() 18 | { 19 | Context.Dispose(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/BaseEfTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Integration.Tests.Data; 3 | using Xunit; 4 | 5 | namespace FluentSpecification.Integration.Tests 6 | { 7 | [Collection("EF Collection")] 8 | public abstract class BaseEfTests : IDisposable 9 | { 10 | protected BaseEfTests(DatabaseFixture fixture) 11 | { 12 | Context = new EfDbContext(fixture.Connection); 13 | } 14 | 15 | internal EfDbContext Context { get; } 16 | 17 | public virtual void Dispose() 18 | { 19 | Context.Dispose(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/CollectionIntegrationTests.Validation.cs: -------------------------------------------------------------------------------- 1 | namespace FluentSpecification.Integration.Tests 2 | { 3 | public partial class CollectionIntegrationTests 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/CollectionIntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using FluentSpecification.Abstractions.Generic; 3 | using FluentSpecification.Integration.Tests.Data; 4 | using FluentSpecification.Tests.Sdk; 5 | using Xunit; 6 | 7 | namespace FluentSpecification.Integration.Tests 8 | { 9 | public partial class CollectionIntegrationTests 10 | { 11 | private readonly DataFixture _fixture; 12 | 13 | public CollectionIntegrationTests() 14 | { 15 | _fixture = new DataFixture(); 16 | } 17 | 18 | [Theory] 19 | [CorrectData(typeof(IntegrationData))] 20 | public void AllSpecifications_ReturnOneCustomer(ISpecification sut, int expectedId) 21 | { 22 | var result = _fixture.Customers 23 | .Where(sut.IsSatisfiedBy).ToList(); 24 | 25 | Assert.Single(result); 26 | Assert.Equal(expectedId, result.First().CustomerId); 27 | } 28 | 29 | [Theory] 30 | [IncorrectData(typeof(IntegrationData))] 31 | public void AllSpecificationsNegation_ReturnTwoCustomers(ISpecification sut, int first, int second) 32 | { 33 | var result = _fixture.Customers 34 | .Where(sut.IsSatisfiedBy).ToList(); 35 | 36 | Assert.Equal(2, result.Count); 37 | Assert.Equal(first, result.First().CustomerId); 38 | Assert.Equal(second, result.Last().CustomerId); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/Data/CreditCard.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentSpecification.Integration.Tests.Data 4 | { 5 | public class CreditCard : IEquatable 6 | { 7 | public int CustomerId { get; set; } 8 | public string CardNumber { get; set; } 9 | public DateTime ValidityDate { get; set; } 10 | 11 | public virtual Customer Customer { get; set; } 12 | 13 | public bool Equals(CreditCard other) 14 | { 15 | if (ReferenceEquals(null, other)) return false; 16 | if (ReferenceEquals(this, other)) return true; 17 | return CustomerId == other.CustomerId; 18 | } 19 | 20 | public override bool Equals(object obj) 21 | { 22 | if (ReferenceEquals(null, obj)) return false; 23 | if (ReferenceEquals(this, obj)) return true; 24 | if (obj.GetType() != GetType()) return false; 25 | return Equals((CreditCard) obj); 26 | } 27 | 28 | public override int GetHashCode() 29 | { 30 | // ReSharper disable once NonReadonlyMemberInGetHashCode 31 | return CustomerId; 32 | } 33 | 34 | public static bool operator ==(CreditCard left, CreditCard right) 35 | { 36 | return Equals(left, right); 37 | } 38 | 39 | public static bool operator !=(CreditCard left, CreditCard right) 40 | { 41 | return !Equals(left, right); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/Data/Customer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using JetBrains.Annotations; 4 | 5 | namespace FluentSpecification.Integration.Tests.Data 6 | { 7 | public class Customer : IEquatable 8 | { 9 | public Customer() 10 | { 11 | // ReSharper disable once VirtualMemberCallInConstructor 12 | Items = new List(); 13 | } 14 | 15 | public int CustomerId { get; set; } 16 | 17 | [UsedImplicitly] public string FirstName { get; set; } 18 | 19 | public string LastName { get; set; } 20 | public string Email { get; set; } 21 | public string Comments { get; set; } 22 | public bool IsActive { get; set; } 23 | 24 | public int? CaretakerId { get; set; } 25 | 26 | public virtual Customer Caretaker { get; set; } 27 | public virtual CreditCard CreditCard { get; set; } 28 | public virtual ICollection Items { get; set; } 29 | 30 | public bool Equals(Customer other) 31 | { 32 | if (ReferenceEquals(null, other)) return false; 33 | if (ReferenceEquals(this, other)) return true; 34 | return CustomerId == other.CustomerId; 35 | } 36 | 37 | public override bool Equals(object obj) 38 | { 39 | if (ReferenceEquals(null, obj)) return false; 40 | if (ReferenceEquals(this, obj)) return true; 41 | if (obj.GetType() != GetType()) return false; 42 | return Equals((Customer) obj); 43 | } 44 | 45 | public override int GetHashCode() 46 | { 47 | // ReSharper disable once NonReadonlyMemberInGetHashCode 48 | return CustomerId; 49 | } 50 | 51 | public static bool operator ==(Customer left, Customer right) 52 | { 53 | return Equals(left, right); 54 | } 55 | 56 | public static bool operator !=(Customer left, Customer right) 57 | { 58 | return !Equals(left, right); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/Data/DatabaseFixtureCollection.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace FluentSpecification.Integration.Tests.Data 4 | { 5 | [CollectionDefinition("EF Collection")] 6 | public class DatabaseFixtureCollection : ICollectionFixture 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/Data/Event.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentSpecification.Integration.Tests.Data 4 | { 5 | public class Event : IEquatable 6 | { 7 | public int Id { get; set; } 8 | public string Code { get; set; } 9 | public string Name { get; set; } 10 | public bool IsArchival { get; set; } 11 | 12 | public bool Equals(Event other) 13 | { 14 | if (ReferenceEquals(null, other)) return false; 15 | if (ReferenceEquals(this, other)) return true; 16 | return Id == other.Id; 17 | } 18 | 19 | public override bool Equals(object obj) 20 | { 21 | if (ReferenceEquals(null, obj)) return false; 22 | if (ReferenceEquals(this, obj)) return true; 23 | if (obj.GetType() != GetType()) return false; 24 | return Equals((Event) obj); 25 | } 26 | 27 | public override int GetHashCode() 28 | { 29 | // ReSharper disable once NonReadonlyMemberInGetHashCode 30 | return Id; 31 | } 32 | 33 | public static bool operator ==(Event left, Event right) 34 | { 35 | return Equals(left, right); 36 | } 37 | 38 | public static bool operator !=(Event left, Event right) 39 | { 40 | return !Equals(left, right); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/Data/Item.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentSpecification.Integration.Tests.Data 4 | { 5 | public class Item : IEquatable 6 | { 7 | public int ItemId { get; set; } 8 | public string Name { get; set; } 9 | public double Price { get; set; } 10 | public bool Paid { get; set; } 11 | 12 | public int CustomerId { get; set; } 13 | 14 | public virtual Customer Customer { get; set; } 15 | 16 | public bool Equals(Item other) 17 | { 18 | if (ReferenceEquals(null, other)) return false; 19 | if (ReferenceEquals(this, other)) return true; 20 | return ItemId == other.ItemId; 21 | } 22 | 23 | public override bool Equals(object obj) 24 | { 25 | if (ReferenceEquals(null, obj)) return false; 26 | if (ReferenceEquals(this, obj)) return true; 27 | if (obj.GetType() != GetType()) return false; 28 | return Equals((Item) obj); 29 | } 30 | 31 | public override int GetHashCode() 32 | { 33 | // ReSharper disable once NonReadonlyMemberInGetHashCode 34 | return ItemId; 35 | } 36 | 37 | public static bool operator ==(Item left, Item right) 38 | { 39 | return Equals(left, right); 40 | } 41 | 42 | public static bool operator !=(Item left, Item right) 43 | { 44 | return !Equals(left, right); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/EfCoreIntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using FluentSpecification.Abstractions.Generic; 3 | using FluentSpecification.Integration.Tests.Data; 4 | using FluentSpecification.Tests.Sdk; 5 | using Xunit; 6 | 7 | namespace FluentSpecification.Integration.Tests 8 | { 9 | [Collection("EF Collection")] 10 | public class EfCoreIntegrationTests : BaseEfCoreTests 11 | { 12 | public EfCoreIntegrationTests(DatabaseFixture fixture) 13 | : base(fixture) 14 | { 15 | } 16 | 17 | [Theory] 18 | [CorrectData(typeof(EfCoreIntegrationData))] 19 | public void AllSpecifications_ReturnOneCustomer(ILinqSpecification specification, int expectedId) 20 | { 21 | var sut = specification.GetExpression(); 22 | 23 | var result = Context.Customers 24 | .Where(sut).ToList(); 25 | 26 | Assert.Single(result); 27 | Assert.Equal(expectedId, result.First().CustomerId); 28 | } 29 | 30 | [Theory] 31 | [IncorrectData(typeof(EfCoreIntegrationData))] 32 | public void AllSpecificationsNegation_ReturnTwoCustomers(ILinqSpecification specification, int first, 33 | int second) 34 | { 35 | var sut = specification.GetExpression(); 36 | 37 | var result = Context.Customers 38 | .Where(sut).ToList(); 39 | 40 | Assert.Equal(2, result.Count); 41 | Assert.Equal(first, result.First().CustomerId); 42 | Assert.Equal(second, result.Last().CustomerId); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/EfIntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using FluentSpecification.Abstractions.Generic; 3 | using FluentSpecification.Integration.Tests.Data; 4 | using FluentSpecification.Tests.Sdk; 5 | using Xunit; 6 | 7 | namespace FluentSpecification.Integration.Tests 8 | { 9 | [Collection("EF Collection")] 10 | public class EfIntegrationTests : BaseEfTests 11 | { 12 | public EfIntegrationTests(DatabaseFixture fixture) 13 | : base(fixture) 14 | { 15 | } 16 | 17 | [Theory] 18 | [CorrectData(typeof(SqlToEntitiesIntegrationData))] 19 | public void AllSpecifications_ReturnOneCustomer(ILinqSpecification specification, int expectedId) 20 | { 21 | var sut = specification.GetExpression(); 22 | 23 | var result = Context.Customers 24 | .Where(sut).ToList(); 25 | 26 | Assert.Single(result); 27 | Assert.Equal(expectedId, result.First().CustomerId); 28 | } 29 | 30 | [Theory] 31 | [IncorrectData(typeof(SqlToEntitiesIntegrationData))] 32 | public void AllSpecificationsNegation_ReturnTwoCustomers(ILinqSpecification specification, int first, 33 | int second) 34 | { 35 | var sut = specification.GetExpression(); 36 | 37 | var result = Context.Customers 38 | .Where(sut).ToList(); 39 | 40 | Assert.Equal(2, result.Count); 41 | Assert.Equal(first, result.First().CustomerId); 42 | Assert.Equal(second, result.Last().CustomerId); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/Logic/ActiveItemsSpecification.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | using FluentSpecification.Core; 4 | using FluentSpecification.Integration.Tests.Data; 5 | 6 | namespace FluentSpecification.Integration.Tests.Logic 7 | { 8 | internal sealed class ActiveItemsSpecification 9 | : ComplexSpecification 10 | { 11 | protected override string CreateFailedMessage(Event candidate) 12 | { 13 | return $"Item is archival: [{candidate?.Id}]"; 14 | } 15 | 16 | protected override string CreateNegationFailedMessage(Event candidate) 17 | { 18 | return $"Item is not archival: [{candidate?.Id}]"; 19 | } 20 | 21 | protected override IReadOnlyDictionary GetParameters() 22 | { 23 | return null; 24 | } 25 | 26 | protected override Expression BuildExpressionBody(Expression arg) 27 | { 28 | return Expression.Not(Expression.Property(arg, nameof(Event.IsArchival))); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/Logic/CodeStartsWithSpecification.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | using FluentSpecification.Core; 5 | using FluentSpecification.Integration.Tests.Data; 6 | 7 | namespace FluentSpecification.Integration.Tests.Logic 8 | { 9 | internal sealed class CodeStartsWithSpecification : 10 | ComplexSpecification 11 | { 12 | private readonly string _code; 13 | private readonly MethodInfo _startsWith; 14 | 15 | public CodeStartsWithSpecification(string code) 16 | { 17 | _code = code; 18 | 19 | _startsWith = typeof(string).GetMethod(nameof(string.StartsWith), new[] {typeof(string)}); 20 | } 21 | 22 | protected override string CreateFailedMessage(Event candidate) 23 | { 24 | return $"Item code [{candidate?.Code}] is not starts by [{_code}]"; 25 | } 26 | 27 | protected override string CreateNegationFailedMessage(Event candidate) 28 | { 29 | return $"Item code [{candidate?.Code}] starts by [{_code}]"; 30 | } 31 | 32 | protected override IReadOnlyDictionary GetParameters() 33 | { 34 | return new Dictionary 35 | { 36 | {"Code", _code} 37 | }; 38 | } 39 | 40 | protected override Expression BuildExpressionBody(Expression arg) 41 | { 42 | return Expression.Call(Expression.Property(arg, nameof(Event.Code)), _startsWith, 43 | Expression.Constant(_code, typeof(string))); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/Logic/CustomerIsNotChildSpecification.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Abstractions.Generic; 2 | using FluentSpecification.Integration.Tests.Data; 3 | 4 | namespace FluentSpecification.Integration.Tests.Logic 5 | { 6 | internal class CustomerIsNotChildSpecification : ISpecification 7 | { 8 | public bool IsSatisfiedBy(Customer candidate) 9 | { 10 | return candidate != null && candidate.CaretakerId == null; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/Logic/FavouriteItemSpecification.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Abstractions.Generic; 2 | using FluentSpecification.Integration.Tests.Data; 3 | 4 | namespace FluentSpecification.Integration.Tests.Logic 5 | { 6 | internal sealed class FavoriteItemSpecification : ISpecification 7 | { 8 | public bool IsSatisfiedBy(Event candidate) 9 | { 10 | return candidate != null && !candidate.IsArchival && 11 | (candidate.Code.Contains("-") || candidate.Code.Contains(".")) && 12 | !candidate.Name.ToUpper().Contains("BULLET") && candidate.Id < 5; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/Logic/ItemBigIdSpecification.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | using FluentSpecification.Core; 4 | using FluentSpecification.Integration.Tests.Data; 5 | 6 | namespace FluentSpecification.Integration.Tests.Logic 7 | { 8 | internal sealed class ItemBigIdSpecification 9 | : ComplexSpecification 10 | { 11 | private const int BaseId = 100; 12 | 13 | protected override string CreateFailedMessage(Event candidate) 14 | { 15 | return $"Item Id is lower than {BaseId}"; 16 | } 17 | 18 | protected override string CreateNegationFailedMessage(Event candidate) 19 | { 20 | return $"Item Id is greater than or equal to {BaseId}"; 21 | } 22 | 23 | protected override IReadOnlyDictionary GetParameters() 24 | { 25 | return null; 26 | } 27 | 28 | protected override Expression BuildExpressionBody(Expression arg) 29 | { 30 | return Expression.GreaterThanOrEqual(Expression.Property(arg, nameof(Event.Id)), 31 | Expression.Constant(BaseId)); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/Logic/NameContainsSpecification.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | using FluentSpecification.Core; 5 | using FluentSpecification.Integration.Tests.Data; 6 | 7 | namespace FluentSpecification.Integration.Tests.Logic 8 | { 9 | internal sealed class NameContainsSpecification : 10 | ComplexSpecification 11 | { 12 | private readonly MethodInfo _contains; 13 | private readonly string _filter; 14 | 15 | public NameContainsSpecification(string filter) 16 | { 17 | _filter = filter; 18 | _contains = typeof(string).GetMethod(nameof(string.Contains), new[] {typeof(string)}); 19 | } 20 | 21 | protected override string CreateFailedMessage(Event candidate) 22 | { 23 | return $"Item [{candidate?.Name}] not contains: [{_filter}]"; 24 | } 25 | 26 | protected override string CreateNegationFailedMessage(Event candidate) 27 | { 28 | return $"Item [{candidate?.Name}] contains: [{_filter}]"; 29 | } 30 | 31 | protected override IReadOnlyDictionary GetParameters() 32 | { 33 | return new Dictionary 34 | { 35 | {"Filter", _filter} 36 | }; 37 | } 38 | 39 | protected override Expression BuildExpressionBody(Expression arg) 40 | { 41 | return Expression.Call(Expression.Property(arg, nameof(Event.Name)), _contains, 42 | Expression.Constant(_filter, typeof(string))); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Integration.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("FluentSpecification.Integration.Tests")] 5 | [assembly: AssemblyDescription("")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("")] 8 | [assembly: AssemblyTrademark("")] 9 | [assembly: AssemblyCulture("")] 10 | 11 | [assembly: ComVisible(false)] 12 | 13 | [assembly: Guid("e815b284-0266-4552-98ce-deacb09b0177")] -------------------------------------------------------------------------------- /src/FluentSpecification.Tests.Sdk/CorrectDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | 6 | namespace FluentSpecification.Tests.Sdk 7 | { 8 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 9 | [PublicAPI] 10 | public class CorrectDataAttribute : SpecificationDataAttribute 11 | { 12 | public CorrectDataAttribute(Type @class) : 13 | base(@class) 14 | { 15 | } 16 | 17 | public override IEnumerable GetData(MethodInfo testMethod) 18 | { 19 | return GetSpecificationData().GetValidData(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests.Sdk/CorrectValidationDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | 6 | namespace FluentSpecification.Tests.Sdk 7 | { 8 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 9 | [PublicAPI] 10 | public class CorrectValidationDataAttribute : SpecificationDataAttribute 11 | { 12 | public CorrectValidationDataAttribute(Type @class) : 13 | base(@class) 14 | { 15 | } 16 | 17 | public override IEnumerable GetData(MethodInfo testMethod) 18 | { 19 | return GetSpecificationData().GetValidData(false); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests.Sdk/Data/FailedSpecificationDataRow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using FluentSpecification.Abstractions.Validation; 4 | using JetBrains.Annotations; 5 | 6 | namespace FluentSpecification.Tests.Sdk.Data 7 | { 8 | [PublicAPI] 9 | public class FailedSpecificationDataRow 10 | { 11 | private readonly string[] _errors; 12 | private readonly Dictionary _parameters = new Dictionary(); 13 | private readonly Type _specType; 14 | private object _candidate; 15 | 16 | public FailedSpecificationDataRow(Type specificationType, string[] errors) 17 | { 18 | _specType = specificationType; 19 | _errors = errors; 20 | } 21 | 22 | public FailedSpecificationDataRow Candidate(object candidate) 23 | { 24 | _candidate = candidate; 25 | return this; 26 | } 27 | 28 | public FailedSpecificationDataRow AddParameter(string key, object value) 29 | { 30 | _parameters.Add(key, value); 31 | return this; 32 | } 33 | 34 | public FailedSpecification CreateFailedSpecification() 35 | { 36 | return new FailedSpecification(_specType, _parameters, _candidate, _errors); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests.Sdk/Data/ISpecificationResultCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentSpecification.Tests.Sdk.Data 4 | { 5 | public interface ISpecificationResultCreator 6 | { 7 | FailedSpecificationDataRow FailedSpecification(Type specificationType, params string[] errors); 8 | ISpecificationResultCreator CustomError(string error); 9 | } 10 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests.Sdk/FailedSpecificationComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using FluentSpecification.Abstractions.Validation; 6 | using JetBrains.Annotations; 7 | using Xunit; 8 | 9 | namespace FluentSpecification.Tests.Sdk 10 | { 11 | [PublicAPI] 12 | public class FailedSpecificationComparer : IEqualityComparer 13 | { 14 | public object Candidate { get; set; } 15 | 16 | public IReadOnlyDictionary Parameters { get; set; } = new Dictionary(); 17 | 18 | public bool Equals(FailedSpecification x, FailedSpecification y) 19 | { 20 | if (ReferenceEquals(x, y)) 21 | return true; 22 | Assert.NotNull(x); 23 | Assert.NotNull(y); 24 | 25 | Assert.Equal(x.SpecificationType, y.SpecificationType); 26 | if (Candidate is DateTime) 27 | for (var i = 0; i < x.Errors.Count; i++) 28 | Assert.Matches(x.Errors[i], y.Errors[i]); 29 | else 30 | Assert.Equal(x.Errors, y.Errors.Select(e => Regex.Replace(e, "(?<=[0-9]),(?=[0-9])", ".")).ToArray()); 31 | Assert.Equal(x.Candidate ?? Candidate, y.Candidate); 32 | 33 | var expectedParameters = x.Parameters.ToDictionary(k => k.Key, v => v.Value); 34 | if (Parameters.Count > 0) 35 | foreach (var subs in Parameters) 36 | if (expectedParameters.ContainsKey(subs.Key)) 37 | expectedParameters[subs.Key] = subs.Value; 38 | 39 | Assert.Equal(expectedParameters, y.Parameters); 40 | 41 | return true; 42 | } 43 | 44 | public int GetHashCode(FailedSpecification obj) 45 | { 46 | return obj.SpecificationType.GetHashCode() ^ 47 | obj.Parameters.GetHashCode() ^ 48 | obj.Errors.GetHashCode(); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests.Sdk/FluentSpecification.Tests.Sdk.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | FluentSpecification.Tests.Sdk 5 | netstandard1.6;netstandard2.0;net452 6 | false 7 | True 8 | False 9 | 10 | 11 | 12 | false 13 | false 14 | false 15 | false 16 | false 17 | false 18 | false 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Properties\SolutionInfo.cs 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/FluentSpecification.Tests.Sdk/IncorrectDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | 6 | namespace FluentSpecification.Tests.Sdk 7 | { 8 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 9 | [PublicAPI] 10 | public class IncorrectDataAttribute : SpecificationDataAttribute 11 | { 12 | public IncorrectDataAttribute(Type @class) : 13 | base(@class) 14 | { 15 | } 16 | 17 | public override IEnumerable GetData(MethodInfo testMethod) 18 | { 19 | return GetSpecificationData().GetInvalidData(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests.Sdk/IncorrectValidationDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | 6 | namespace FluentSpecification.Tests.Sdk 7 | { 8 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 9 | [PublicAPI] 10 | public class IncorrectValidationDataAttribute : SpecificationDataAttribute 11 | { 12 | public IncorrectValidationDataAttribute(Type @class) : 13 | base(@class) 14 | { 15 | } 16 | 17 | public override IEnumerable GetData(MethodInfo testMethod) 18 | { 19 | return GetSpecificationData().GetInvalidData(false); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests.Sdk/SpecificationDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using FluentSpecification.Tests.Sdk.Data; 4 | using JetBrains.Annotations; 5 | using Xunit.Sdk; 6 | 7 | namespace FluentSpecification.Tests.Sdk 8 | { 9 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 10 | [PublicAPI] 11 | public abstract class SpecificationDataAttribute : DataAttribute 12 | { 13 | protected SpecificationDataAttribute(Type @class) 14 | { 15 | if (!typeof(SpecificationData).GetTypeInfo().IsAssignableFrom(@class)) 16 | throw new ArgumentException("Incorrect SpecificationData class", nameof(@class)); 17 | Class = @class; 18 | } 19 | 20 | public Type Class { get; } 21 | 22 | public bool AsNegation { get; set; } 23 | 24 | protected SpecificationData GetSpecificationData() 25 | { 26 | var result = (SpecificationData) Activator.CreateInstance(Class); 27 | result.AsNegation = AsNegation; 28 | 29 | return result; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.And.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Composite; 2 | using FluentSpecification.Tests.Mocks; 3 | using Xunit; 4 | 5 | namespace FluentSpecification.Tests.Api 6 | { 7 | public partial class SpecificationTests 8 | { 9 | [Fact] 10 | public void InvokeAnd_ReturnAndSpecification() 11 | { 12 | var left = MockComplexSpecification.True(); 13 | var right = MockComplexSpecification.False(); 14 | var expected = new AndSpecification(left, right); 15 | 16 | var sut = Specification.And(left, right); 17 | 18 | Assert.Equal(expected, sut, new SpecificationComparer()); 19 | } 20 | 21 | [Fact] 22 | public void InvokeAndProperty_ReturnPropertySpecification() 23 | { 24 | var left = MockComplexSpecification.True(); 25 | var right = MockComplexSpecification.False(); 26 | var expected = new PropertySpecification( 27 | ft => ft.First, new AndSpecification(left, right)); 28 | 29 | var sut = Specification.And( 30 | ft => ft.First, left, right); 31 | 32 | Assert.Equal(expected, sut, new SpecificationComparer()); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.AndNot.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeAndNot_ReturnAndSpecification() 12 | { 13 | var left = MockComplexSpecification.True(); 14 | var right = MockComplexSpecification.True(); 15 | var expected = new AndSpecification(left, right.Not()); 16 | 17 | var sut = Specification.AndNot(left, right); 18 | 19 | Assert.Equal(expected, sut, new SpecificationComparer()); 20 | } 21 | 22 | [Fact] 23 | public void InvokeAndNotProperty_ReturnPropertySpecification() 24 | { 25 | var left = MockComplexSpecification.True(); 26 | var right = MockComplexSpecification.True(); 27 | var expected = new PropertySpecification( 28 | ft => ft.First, new AndSpecification(left, right.Not())); 29 | 30 | var sut = Specification.AndNot( 31 | ft => ft.First, left, right); 32 | 33 | Assert.Equal(expected, sut, new SpecificationComparer()); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.CreditCard.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeCompositeCreditCard_ReturnCreditCardSpecification() 12 | { 13 | var expected = new CreditCardSpecification(); 14 | 15 | var sut = new MockCompositeSpecification().CreditCard(); 16 | 17 | Assert.Equal(expected, sut, new SpecificationComparer()); 18 | } 19 | 20 | [Fact] 21 | public void InvokeCompositeCreditCardProperty_ReturnPropertySpecification() 22 | { 23 | var expected = new PropertySpecification( 24 | ft => ft.Second, new CreditCardSpecification()); 25 | 26 | var sut = new MockCompositeSpecification().CreditCard( 27 | ft => ft.Second); 28 | 29 | Assert.Equal(expected, sut, new SpecificationComparer()); 30 | } 31 | 32 | [Fact] 33 | public void InvokeCreditCard_ReturnCreditCardSpecification() 34 | { 35 | var expected = new CreditCardSpecification(); 36 | 37 | var sut = Specification.CreditCard(); 38 | 39 | Assert.Equal(expected, sut, new SpecificationComparer()); 40 | } 41 | 42 | [Fact] 43 | public void InvokeCreditCardProperty_ReturnPropertySpecification() 44 | { 45 | var expected = new PropertySpecification( 46 | ft => ft.Second, new CreditCardSpecification()); 47 | 48 | var sut = Specification.CreditCard( 49 | ft => ft.Second); 50 | 51 | Assert.Equal(expected, sut, new SpecificationComparer()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.Email.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeCompositeEmail_ReturnEmailSpecification() 12 | { 13 | var expected = new EmailSpecification(); 14 | 15 | var sut = new MockCompositeSpecification().Email(); 16 | 17 | Assert.Equal(expected, sut, new SpecificationComparer()); 18 | } 19 | 20 | [Fact] 21 | public void InvokeCompositeEmailProperty_ReturnPropertySpecification() 22 | { 23 | var expected = new PropertySpecification( 24 | ft => ft.Second, new EmailSpecification()); 25 | 26 | var sut = new MockCompositeSpecification().Email( 27 | ft => ft.Second); 28 | 29 | Assert.Equal(expected, sut, new SpecificationComparer()); 30 | } 31 | 32 | [Fact] 33 | public void InvokeEmail_ReturnEmailSpecification() 34 | { 35 | var expected = new EmailSpecification(); 36 | 37 | var sut = Specification.Email(); 38 | 39 | Assert.Equal(expected, sut, new SpecificationComparer()); 40 | } 41 | 42 | [Fact] 43 | public void InvokeEmailProperty_ReturnPropertySpecification() 44 | { 45 | var expected = new PropertySpecification( 46 | ft => ft.Second, new EmailSpecification()); 47 | 48 | var sut = Specification.Email( 49 | ft => ft.Second); 50 | 51 | Assert.Equal(expected, sut, new SpecificationComparer()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.Empty.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeCompositeEmpty_ReturnEmptySpecification() 12 | { 13 | var expected = new EmptySpecification(true); 14 | 15 | var sut = new MockCompositeSpecification().Empty(); 16 | 17 | Assert.Equal(expected, sut, new SpecificationComparer()); 18 | } 19 | 20 | [Fact] 21 | public void InvokeCompositeEmptyProperty_ReturnPropertySpecification() 22 | { 23 | var expected = new PropertySpecification( 24 | ft => ft.Second, new EmptySpecification(true)); 25 | 26 | var sut = new MockCompositeSpecification().Empty( 27 | ft => ft.Second); 28 | 29 | Assert.Equal(expected, sut, new SpecificationComparer()); 30 | } 31 | 32 | [Fact] 33 | public void InvokeEmpty_ReturnEmptySpecification() 34 | { 35 | var expected = new EmptySpecification(true); 36 | 37 | var sut = Specification.Empty(); 38 | 39 | Assert.Equal(expected, sut, new SpecificationComparer()); 40 | } 41 | 42 | [Fact] 43 | public void InvokeEmptyProperty_ReturnPropertySpecification() 44 | { 45 | var expected = new PropertySpecification( 46 | ft => ft.Second, new EmptySpecification(true)); 47 | 48 | var sut = Specification.Empty( 49 | ft => ft.Second); 50 | 51 | Assert.Equal(expected, sut, new SpecificationComparer()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.Expression.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeCompositeExpression_ReturnExpressionSpecification() 12 | { 13 | var expected = new ExpressionSpecification(ft => true); 14 | 15 | var sut = new MockCompositeSpecification().Expression(ft => true); 16 | 17 | Assert.Equal(expected, sut, new SpecificationComparer()); 18 | } 19 | 20 | [Fact] 21 | public void InvokeCompositeExpressionProperty_ReturnPropertySpecification() 22 | { 23 | var expected = new PropertySpecification( 24 | ft => ft.First, new ExpressionSpecification(ft => true)); 25 | 26 | var sut = new MockCompositeSpecification().Expression( 27 | ft => ft.First, ft => true); 28 | 29 | Assert.Equal(expected, sut, new SpecificationComparer()); 30 | } 31 | 32 | [Fact] 33 | public void InvokeExpression_ReturnExpressionSpecification() 34 | { 35 | var expected = new ExpressionSpecification(ft => true); 36 | 37 | var sut = Specification.Expression(ft => true); 38 | 39 | Assert.Equal(expected, sut, new SpecificationComparer()); 40 | } 41 | 42 | [Fact] 43 | public void InvokeExpressionProperty_ReturnPropertySpecification() 44 | { 45 | var expected = new PropertySpecification( 46 | ft => ft.First, new ExpressionSpecification(ft => true)); 47 | 48 | var sut = Specification.Expression( 49 | ft => ft.First, ft => true); 50 | 51 | Assert.Equal(expected, sut, new SpecificationComparer()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.False.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeCompositeFalse_ReturnFalseSpecification() 12 | { 13 | var expected = new FalseSpecification(); 14 | 15 | var sut = new MockCompositeSpecification().False(); 16 | 17 | Assert.Equal(expected, sut, new SpecificationComparer()); 18 | } 19 | 20 | [Fact] 21 | public void InvokeCompositeFalseProperty_ReturnPropertySpecification() 22 | { 23 | var expected = new PropertySpecification( 24 | ft => ft.Inter.Third, new FalseSpecification()); 25 | 26 | var sut = new MockCompositeSpecification().False( 27 | ft => ft.Inter.Third); 28 | 29 | Assert.Equal(expected, sut, new SpecificationComparer()); 30 | } 31 | 32 | [Fact] 33 | public void InvokeFalse_ReturnFalseSpecification() 34 | { 35 | var expected = new FalseSpecification(); 36 | 37 | var sut = Specification.False(); 38 | 39 | Assert.Equal(expected, sut, new SpecificationComparer()); 40 | } 41 | 42 | [Fact] 43 | public void InvokeFalseProperty_ReturnPropertySpecification() 44 | { 45 | var expected = new PropertySpecification( 46 | ft => ft.Inter.Third, new FalseSpecification()); 47 | 48 | var sut = Specification.False( 49 | ft => ft.Inter.Third); 50 | 51 | Assert.Equal(expected, sut, new SpecificationComparer()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.ForProperty.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Composite; 2 | using FluentSpecification.Tests.Mocks; 3 | using Xunit; 4 | 5 | namespace FluentSpecification.Tests.Api 6 | { 7 | public partial class SpecificationTests 8 | { 9 | [Fact] 10 | public void InvokeCompositeForProperty_ReturnPropertySpecification() 11 | { 12 | var expected = new PropertySpecification(ft => ft.Second, 13 | MockComplexSpecification.True()); 14 | 15 | var sut = new MockCompositeSpecification().ForProperty( 16 | ft => ft.Second, MockComplexSpecification.True()); 17 | 18 | Assert.Equal(expected, sut, new SpecificationComparer()); 19 | } 20 | 21 | [Fact] 22 | public void InvokeForProperty_ReturnPropertySpecification() 23 | { 24 | var expected = new PropertySpecification(ft => ft.Second, 25 | MockComplexSpecification.True()); 26 | 27 | var sut = Specification.ForProperty( 28 | ft => ft.Second, MockComplexSpecification.True()); 29 | 30 | Assert.Equal(expected, sut, new SpecificationComparer()); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.IsType.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeCompositeIsType_ReturnIsTypeSpecification() 12 | { 13 | var expected = new IsTypeSpecification(typeof(FakeType)); 14 | 15 | var sut = new MockCompositeSpecification().IsType(typeof(FakeType)); 16 | 17 | Assert.Equal(expected, sut, new SpecificationComparer()); 18 | } 19 | 20 | [Fact] 21 | public void InvokeCompositeIsTypeProperty_ReturnPropertySpecification() 22 | { 23 | var expected = new PropertySpecification( 24 | ft => ft.First, new IsTypeSpecification(typeof(FakeType))); 25 | 26 | var sut = new MockCompositeSpecification().IsType( 27 | ft => ft.First, typeof(FakeType)); 28 | 29 | Assert.Equal(expected, sut, new SpecificationComparer()); 30 | } 31 | 32 | [Fact] 33 | public void InvokeIsType_ReturnIsTypeSpecification() 34 | { 35 | var expected = new IsTypeSpecification(typeof(FakeType)); 36 | 37 | var sut = Specification.IsType(typeof(FakeType)); 38 | 39 | Assert.Equal(expected, sut, new SpecificationComparer()); 40 | } 41 | 42 | [Fact] 43 | public void InvokeIsTypeProperty_ReturnPropertySpecification() 44 | { 45 | var expected = new PropertySpecification( 46 | ft => ft.First, new IsTypeSpecification(typeof(FakeType))); 47 | 48 | var sut = Specification.IsType( 49 | ft => ft.First, typeof(FakeType)); 50 | 51 | Assert.Equal(expected, sut, new SpecificationComparer()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.Length.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeCompositeLength_ReturnLengthSpecification() 12 | { 13 | var expected = new LengthSpecification(0, true); 14 | 15 | var sut = new MockCompositeSpecification().Length(0); 16 | 17 | Assert.Equal(expected, sut, new SpecificationComparer()); 18 | } 19 | 20 | [Fact] 21 | public void InvokeCompositeLengthProperty_ReturnPropertySpecification() 22 | { 23 | var expected = new PropertySpecification( 24 | ft => ft.Second, new LengthSpecification(0, true)); 25 | 26 | var sut = new MockCompositeSpecification().Length( 27 | ft => ft.Second, 0); 28 | 29 | Assert.Equal(expected, sut, new SpecificationComparer()); 30 | } 31 | 32 | [Fact] 33 | public void InvokeLength_ReturnLengthSpecification() 34 | { 35 | var expected = new LengthSpecification(0, true); 36 | 37 | var sut = Specification.Length(0); 38 | 39 | Assert.Equal(expected, sut, new SpecificationComparer()); 40 | } 41 | 42 | [Fact] 43 | public void InvokeLengthProperty_ReturnPropertySpecification() 44 | { 45 | var expected = new PropertySpecification( 46 | ft => ft.Second, new LengthSpecification(0, true)); 47 | 48 | var sut = Specification.Length( 49 | ft => ft.Second, 0); 50 | 51 | Assert.Equal(expected, sut, new SpecificationComparer()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.MaxLength.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeCompositeMaxLength_ReturnMaxLengthSpecification() 12 | { 13 | var expected = new MaxLengthSpecification(0, true); 14 | 15 | var sut = new MockCompositeSpecification().MaxLength(0); 16 | 17 | Assert.Equal(expected, sut, new SpecificationComparer()); 18 | } 19 | 20 | [Fact] 21 | public void InvokeCompositeMaxLengthProperty_ReturnPropertySpecification() 22 | { 23 | var expected = new PropertySpecification( 24 | ft => ft.Second, new MaxLengthSpecification(0, true)); 25 | 26 | var sut = new MockCompositeSpecification().MaxLength( 27 | ft => ft.Second, 0); 28 | 29 | Assert.Equal(expected, sut, new SpecificationComparer()); 30 | } 31 | 32 | [Fact] 33 | public void InvokeMaxLength_ReturnMaxLengthSpecification() 34 | { 35 | var expected = new MaxLengthSpecification(0, true); 36 | 37 | var sut = Specification.MaxLength(0); 38 | 39 | Assert.Equal(expected, sut, new SpecificationComparer()); 40 | } 41 | 42 | [Fact] 43 | public void InvokeMaxLengthProperty_ReturnPropertySpecification() 44 | { 45 | var expected = new PropertySpecification( 46 | ft => ft.Second, new MaxLengthSpecification(0, true)); 47 | 48 | var sut = Specification.MaxLength( 49 | ft => ft.Second, 0); 50 | 51 | Assert.Equal(expected, sut, new SpecificationComparer()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.MinLength.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeCompositeMinLength_ReturnMinLengthSpecification() 12 | { 13 | var expected = new MinLengthSpecification(0, true); 14 | 15 | var sut = new MockCompositeSpecification().MinLength(0); 16 | 17 | Assert.Equal(expected, sut, new SpecificationComparer()); 18 | } 19 | 20 | [Fact] 21 | public void InvokeCompositeMinLengthProperty_ReturnPropertySpecification() 22 | { 23 | var expected = new PropertySpecification( 24 | ft => ft.Second, new MinLengthSpecification(0, true)); 25 | 26 | var sut = new MockCompositeSpecification().MinLength( 27 | ft => ft.Second, 0); 28 | 29 | Assert.Equal(expected, sut, new SpecificationComparer()); 30 | } 31 | 32 | [Fact] 33 | public void InvokeMinLength_ReturnMinLengthSpecification() 34 | { 35 | var expected = new MinLengthSpecification(0, true); 36 | 37 | var sut = Specification.MinLength(0); 38 | 39 | Assert.Equal(expected, sut, new SpecificationComparer()); 40 | } 41 | 42 | [Fact] 43 | public void InvokeMinLengthProperty_ReturnPropertySpecification() 44 | { 45 | var expected = new PropertySpecification( 46 | ft => ft.Second, new MinLengthSpecification(0, true)); 47 | 48 | var sut = Specification.MinLength( 49 | ft => ft.Second, 0); 50 | 51 | Assert.Equal(expected, sut, new SpecificationComparer()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.Null.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeCompositeNull_ReturnNullSpecification() 12 | { 13 | var expected = new NullSpecification(); 14 | 15 | var sut = new MockCompositeSpecification().Null(); 16 | 17 | Assert.Equal(expected, sut, new SpecificationComparer()); 18 | } 19 | 20 | [Fact] 21 | public void InvokeCompositeNullProperty_ReturnPropertySpecification() 22 | { 23 | var expected = new PropertySpecification( 24 | ft => ft.Second, new NullSpecification()); 25 | 26 | var sut = new MockCompositeSpecification().Null( 27 | ft => ft.Second); 28 | 29 | Assert.Equal(expected, sut, new SpecificationComparer()); 30 | } 31 | 32 | [Fact] 33 | public void InvokeNull_ReturnNullSpecification() 34 | { 35 | var expected = new NullSpecification(); 36 | 37 | var sut = Specification.Null(); 38 | 39 | Assert.Equal(expected, sut, new SpecificationComparer()); 40 | } 41 | 42 | [Fact] 43 | public void InvokeNullProperty_ReturnPropertySpecification() 44 | { 45 | var expected = new PropertySpecification( 46 | ft => ft.Second, new NullSpecification()); 47 | 48 | var sut = Specification.Null( 49 | ft => ft.Second); 50 | 51 | Assert.Equal(expected, sut, new SpecificationComparer()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.Or.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Composite; 2 | using FluentSpecification.Tests.Mocks; 3 | using Xunit; 4 | 5 | namespace FluentSpecification.Tests.Api 6 | { 7 | public partial class SpecificationTests 8 | { 9 | [Fact] 10 | public void InvokeOr_ReturnOrSpecification() 11 | { 12 | var left = MockComplexSpecification.True(); 13 | var right = MockComplexSpecification.True(); 14 | var expected = new OrSpecification(left, right); 15 | 16 | var sut = Specification.Or(left, right); 17 | 18 | Assert.Equal(expected, sut, new SpecificationComparer()); 19 | } 20 | 21 | [Fact] 22 | public void InvokeOrProperty_ReturnPropertySpecification() 23 | { 24 | var left = MockComplexSpecification.True(); 25 | var right = MockComplexSpecification.True(); 26 | var expected = new PropertySpecification( 27 | ft => ft.First, new OrSpecification(left, right)); 28 | 29 | var sut = Specification.Or( 30 | ft => ft.First, left, right); 31 | 32 | Assert.Equal(expected, sut, new SpecificationComparer()); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.OrNot.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeOrNot_ReturnOrSpecification() 12 | { 13 | var left = MockComplexSpecification.True(); 14 | var right = MockComplexSpecification.True(); 15 | var expected = new OrSpecification(left, right.Not()); 16 | 17 | var sut = Specification.OrNot(left, right); 18 | 19 | Assert.Equal(expected, sut, new SpecificationComparer()); 20 | } 21 | 22 | [Fact] 23 | public void InvokeOrNotProperty_ReturnPropertySpecification() 24 | { 25 | var left = MockComplexSpecification.True(); 26 | var right = MockComplexSpecification.True(); 27 | var expected = new PropertySpecification( 28 | ft => ft.First, new OrSpecification(left, right.Not())); 29 | 30 | var sut = Specification.OrNot( 31 | ft => ft.First, left, right); 32 | 33 | Assert.Equal(expected, sut, new SpecificationComparer()); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.StringContains.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeCompositeStringContains_ReturnStringContainsSpecification() 12 | { 13 | var expected = new ContainsSpecification(" "); 14 | 15 | var sut = new MockCompositeSpecification().Contains(" "); 16 | 17 | Assert.Equal(expected, sut, new SpecificationComparer()); 18 | } 19 | 20 | [Fact] 21 | public void InvokeCompositeStringContainsProperty_ReturnPropertySpecification() 22 | { 23 | var expected = new PropertySpecification( 24 | ft => ft.Second, new ContainsSpecification(" ")); 25 | 26 | var sut = new MockCompositeSpecification().Contains( 27 | ft => ft.Second, " "); 28 | 29 | Assert.Equal(expected, sut, new SpecificationComparer()); 30 | } 31 | 32 | [Fact] 33 | public void InvokeStringContains_ReturnStringContainsSpecification() 34 | { 35 | var expected = new ContainsSpecification(" "); 36 | 37 | var sut = Specification.Contains(" "); 38 | 39 | Assert.Equal(expected, sut, new SpecificationComparer()); 40 | } 41 | 42 | [Fact] 43 | public void InvokeStringContainsProperty_ReturnPropertySpecification() 44 | { 45 | var expected = new PropertySpecification( 46 | ft => ft.Second, new ContainsSpecification(" ")); 47 | 48 | var sut = Specification.Contains( 49 | ft => ft.Second, " "); 50 | 51 | Assert.Equal(expected, sut, new SpecificationComparer()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.True.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Core.Composite; 3 | using FluentSpecification.Tests.Mocks; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Api 7 | { 8 | public partial class SpecificationTests 9 | { 10 | [Fact] 11 | public void InvokeCompositeTrue_ReturnTrueSpecification() 12 | { 13 | var expected = new TrueSpecification(); 14 | 15 | var sut = new MockCompositeSpecification().True(); 16 | 17 | Assert.Equal(expected, sut, new SpecificationComparer()); 18 | } 19 | 20 | [Fact] 21 | public void InvokeCompositeTrueProperty_ReturnPropertySpecification() 22 | { 23 | var expected = new PropertySpecification( 24 | ft => ft.Inter.Third, new TrueSpecification()); 25 | 26 | var sut = new MockCompositeSpecification().True( 27 | ft => ft.Inter.Third); 28 | 29 | Assert.Equal(expected, sut, new SpecificationComparer()); 30 | } 31 | 32 | [Fact] 33 | public void InvokeTrue_ReturnTrueSpecification() 34 | { 35 | var expected = new TrueSpecification(); 36 | 37 | var sut = Specification.True(); 38 | 39 | Assert.Equal(expected, sut, new SpecificationComparer()); 40 | } 41 | 42 | [Fact] 43 | public void InvokeTrueProperty_ReturnPropertySpecification() 44 | { 45 | var expected = new PropertySpecification( 46 | ft => ft.Inter.Third, new TrueSpecification()); 47 | 48 | var sut = Specification.True( 49 | ft => ft.Inter.Third); 50 | 51 | Assert.Equal(expected, sut, new SpecificationComparer()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Api/SpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentSpecification.Tests.Api 4 | { 5 | public partial class SpecificationTests : IDisposable 6 | { 7 | public SpecificationTests() 8 | { 9 | Specification.LinqToEntities = true; 10 | } 11 | 12 | public void Dispose() 13 | { 14 | Specification.LinqToEntities = false; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/CreditCardSpecificationTests.GetNegationExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Tests.Data; 3 | using FluentSpecification.Tests.Sdk; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Common 7 | { 8 | public partial class CreditCardSpecificationTests 9 | { 10 | public class GetNegationExpression 11 | { 12 | [Theory] 13 | [CorrectData(typeof(CreditCardData), AsNegation = true)] 14 | public void InvokeValidCandidate_ReturnTrue(string candidate) 15 | { 16 | var sut = new CreditCardSpecification(); 17 | 18 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 19 | 20 | Assert.True(result); 21 | } 22 | 23 | [Theory] 24 | [IncorrectData(typeof(CreditCardData), AsNegation = true)] 25 | public void InvokeInvalidCandidate_ReturnFalse(string candidate) 26 | { 27 | var sut = new CreditCardSpecification(); 28 | 29 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 30 | 31 | Assert.False(result); 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/CreditCardSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Abstractions; 2 | using FluentSpecification.Common; 3 | using FluentSpecification.Tests.Data; 4 | using FluentSpecification.Tests.Sdk; 5 | using JetBrains.Annotations; 6 | using Xunit; 7 | 8 | namespace FluentSpecification.Tests.Common 9 | { 10 | [UsedImplicitly] 11 | public partial class CreditCardSpecificationTests 12 | { 13 | public class GetExpression 14 | { 15 | [Theory] 16 | [CorrectData(typeof(CreditCardData))] 17 | public void InvokeValidCandidate_ReturnTrue(string candidate) 18 | { 19 | var sut = new CreditCardSpecification(); 20 | 21 | var result = sut.GetExpression().Compile().Invoke(candidate); 22 | 23 | Assert.True(result); 24 | } 25 | 26 | [Theory] 27 | [IncorrectData(typeof(CreditCardData))] 28 | public void InvokeInvalidCandidate_ReturnFalse(string candidate) 29 | { 30 | var sut = new CreditCardSpecification(); 31 | 32 | var result = sut.GetExpression().Compile().Invoke(candidate); 33 | 34 | Assert.False(result); 35 | } 36 | 37 | [Fact] 38 | public void NonGenericILinqSpecification_ReturnExpressionAsAbstractExpression() 39 | { 40 | var sut = new CreditCardSpecification(); 41 | 42 | var expected = sut.GetExpression().ToString(); 43 | var sutExpression = ((ILinqSpecification) sut).GetExpression(); 44 | var result = sutExpression.ToString(); 45 | 46 | Assert.Equal(expected, result); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/EmailSpecificationTests.GetNegationExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Tests.Data; 3 | using FluentSpecification.Tests.Sdk; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Common 7 | { 8 | public partial class EmailSpecificationTests 9 | { 10 | public class GetNegationExpression 11 | { 12 | [Theory] 13 | [CorrectData(typeof(EmailData), AsNegation = true)] 14 | public void InvokeValidCandidate_ReturnTrue(string candidate) 15 | { 16 | var sut = new EmailSpecification(); 17 | 18 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 19 | 20 | Assert.True(result); 21 | } 22 | 23 | [Theory] 24 | [IncorrectData(typeof(EmailData), AsNegation = true)] 25 | public void InvokeInvalidCandidate_ReturnFalse(string candidate) 26 | { 27 | var sut = new EmailSpecification(); 28 | 29 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 30 | 31 | Assert.False(result); 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/EmailSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Abstractions; 2 | using FluentSpecification.Common; 3 | using FluentSpecification.Tests.Data; 4 | using FluentSpecification.Tests.Sdk; 5 | using JetBrains.Annotations; 6 | using Xunit; 7 | 8 | namespace FluentSpecification.Tests.Common 9 | { 10 | [UsedImplicitly] 11 | public partial class EmailSpecificationTests 12 | { 13 | public class GetExpression 14 | { 15 | [Theory] 16 | [CorrectData(typeof(EmailData))] 17 | public void InvokeValidCandidate_ReturnTrue(string candidate) 18 | { 19 | var sut = new EmailSpecification(); 20 | 21 | var result = sut.GetExpression().Compile().Invoke(candidate); 22 | 23 | Assert.True(result); 24 | } 25 | 26 | [Theory] 27 | [IncorrectData(typeof(EmailData))] 28 | public void InvokeInvalidCandidate_ReturnFalse(string candidate) 29 | { 30 | var sut = new EmailSpecification(); 31 | 32 | var result = sut.GetExpression().Compile().Invoke(candidate); 33 | 34 | Assert.False(result); 35 | } 36 | 37 | [Fact] 38 | public void NonGenericILinqSpecification_ReturnExpressionAsAbstractExpression() 39 | { 40 | var sut = new EmailSpecification(); 41 | 42 | var expected = sut.GetExpression().ToString(); 43 | var sutExpression = ((ILinqSpecification) sut).GetExpression(); 44 | var result = sutExpression.ToString(); 45 | 46 | Assert.Equal(expected, result); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/ExclusiveBetweenSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Common; 3 | using FluentSpecification.Tests.Data; 4 | using FluentSpecification.Tests.Mocks; 5 | using FluentSpecification.Tests.Sdk; 6 | using JetBrains.Annotations; 7 | using Xunit; 8 | 9 | namespace FluentSpecification.Tests.Common 10 | { 11 | [UsedImplicitly] 12 | public partial class ExclusiveBetweenSpecificationTests 13 | { 14 | public class Constructor 15 | { 16 | [Theory] 17 | [IncorrectData(typeof(ExclusiveBetweenConstructorData))] 18 | public void FromGreaterThanTo_ArgumentException(T from, T to) 19 | { 20 | var exception = Record.Exception(() => new ExclusiveBetweenSpecification(from, to)); 21 | 22 | Assert.NotNull(exception); 23 | Assert.IsType(exception); 24 | } 25 | 26 | [Fact] 27 | public void NotComparableType_Exception() 28 | { 29 | var exception = Record.Exception(() => 30 | new ExclusiveBetweenSpecification(new FakeType(), new FakeType())); 31 | 32 | Assert.NotNull(exception); 33 | Assert.IsType(exception); 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/FalseSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Abstractions; 2 | using FluentSpecification.Common; 3 | using JetBrains.Annotations; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Common 7 | { 8 | [UsedImplicitly] 9 | public partial class FalseSpecificationTests 10 | { 11 | public class GetExpression 12 | { 13 | [Fact] 14 | public void InvokeInvalidCandidate_ReturnTrue() 15 | { 16 | var sut = new FalseSpecification(); 17 | 18 | var result = sut.GetExpression().Compile().Invoke(false); 19 | 20 | Assert.True(result); 21 | } 22 | 23 | [Fact] 24 | public void InvokeValidCandidate_ReturnFalse() 25 | { 26 | var sut = new FalseSpecification(); 27 | 28 | var result = sut.GetExpression().Compile().Invoke(true); 29 | 30 | Assert.False(result); 31 | } 32 | 33 | [Fact] 34 | public void NonGenericILinqSpecification_ReturnExpressionAsAbstractExpression() 35 | { 36 | var sut = new FalseSpecification(); 37 | 38 | var expected = sut.GetExpression().ToString(); 39 | var sutExpression = ((ILinqSpecification) sut).GetExpression(); 40 | var result = sutExpression.ToString(); 41 | 42 | Assert.Equal(expected, result); 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/GreaterThanOrEqualSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Common; 3 | using FluentSpecification.Tests.Mocks; 4 | using JetBrains.Annotations; 5 | using Xunit; 6 | 7 | namespace FluentSpecification.Tests.Common 8 | { 9 | [UsedImplicitly] 10 | public partial class GreaterThanOrEqualSpecificationTests 11 | { 12 | public class Constructor 13 | { 14 | [Fact] 15 | public void NotComparableType_Exception() 16 | { 17 | var exception = Record.Exception(() => new GreaterThanOrEqualSpecification(new FakeType())); 18 | 19 | Assert.NotNull(exception); 20 | Assert.IsType(exception); 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/GreaterThanSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Common; 3 | using FluentSpecification.Tests.Mocks; 4 | using JetBrains.Annotations; 5 | using Xunit; 6 | 7 | namespace FluentSpecification.Tests.Common 8 | { 9 | [UsedImplicitly] 10 | public partial class GreaterThanSpecificationTests 11 | { 12 | public class Constructor 13 | { 14 | [Fact] 15 | public void NotComparableType_Exception() 16 | { 17 | var exception = Record.Exception(() => new GreaterThanSpecification(new FakeType())); 18 | 19 | Assert.NotNull(exception); 20 | Assert.IsType(exception); 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/InclusiveBetweenSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Common; 3 | using FluentSpecification.Tests.Data; 4 | using FluentSpecification.Tests.Mocks; 5 | using FluentSpecification.Tests.Sdk; 6 | using JetBrains.Annotations; 7 | using Xunit; 8 | 9 | namespace FluentSpecification.Tests.Common 10 | { 11 | [UsedImplicitly] 12 | public partial class InclusiveBetweenSpecificationTests 13 | { 14 | public class Constructor 15 | { 16 | [Theory] 17 | [IncorrectData(typeof(InclusiveBetweenConstructorData))] 18 | public void FromGreaterThanTo_ArgumentException(T from, T to) 19 | { 20 | var exception = Record.Exception(() => new InclusiveBetweenSpecification(from, to)); 21 | 22 | Assert.NotNull(exception); 23 | Assert.IsType(exception); 24 | } 25 | 26 | [Fact] 27 | public void NotComparableType_Exception() 28 | { 29 | var exception = Record.Exception(() => 30 | new InclusiveBetweenSpecification(new FakeType(), new FakeType())); 31 | 32 | Assert.NotNull(exception); 33 | Assert.IsType(exception); 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/IsTypeSpecificationTests.GetExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Abstractions; 3 | using FluentSpecification.Common; 4 | using FluentSpecification.Tests.Data; 5 | using FluentSpecification.Tests.Mocks; 6 | using FluentSpecification.Tests.Sdk; 7 | using Xunit; 8 | 9 | namespace FluentSpecification.Tests.Common 10 | { 11 | public partial class IsTypeSpecificationTests 12 | { 13 | public class GetExpression 14 | { 15 | [Theory] 16 | [CorrectData(typeof(IsTypeData))] 17 | public void InvokeValidCandidate_ReturnTrue(T candidate, Type expected) 18 | { 19 | var sut = new IsTypeSpecification(expected); 20 | 21 | var result = sut.GetExpression().Compile().Invoke(candidate); 22 | 23 | Assert.True(result); 24 | } 25 | 26 | [Theory] 27 | [IncorrectData(typeof(IsTypeData))] 28 | public void InvokeInvalidCandidate_ReturnFalse(T candidate, Type expected) 29 | { 30 | var sut = new IsTypeSpecification(expected); 31 | 32 | var result = sut.GetExpression().Compile().Invoke(candidate); 33 | 34 | Assert.False(result); 35 | } 36 | 37 | [Fact] 38 | public void NonGenericILinqSpecification_ReturnExpressionAsAbstractExpression() 39 | { 40 | var sut = new IsTypeSpecification(typeof(object)); 41 | 42 | var expected = sut.GetExpression().ToString(); 43 | var sutExpression = ((ILinqSpecification) sut).GetExpression(); 44 | var result = sutExpression.ToString(); 45 | 46 | Assert.Equal(expected, result); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/IsTypeSpecificationTests.GetNegationExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Common; 3 | using FluentSpecification.Tests.Data; 4 | using FluentSpecification.Tests.Sdk; 5 | using Xunit; 6 | 7 | namespace FluentSpecification.Tests.Common 8 | { 9 | public partial class IsTypeSpecificationTests 10 | { 11 | public class GetNegationExpression 12 | { 13 | [Theory] 14 | [CorrectData(typeof(IsTypeData), AsNegation = true)] 15 | public void InvokeValidCandidate_ReturnTrue(T candidate, Type expected) 16 | { 17 | var sut = new IsTypeSpecification(expected); 18 | 19 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 20 | 21 | Assert.True(result); 22 | } 23 | 24 | [Theory] 25 | [IncorrectData(typeof(IsTypeData), AsNegation = true)] 26 | public void InvokeInvalidCandidate_ReturnFalse(T candidate, Type expected) 27 | { 28 | var sut = new IsTypeSpecification(expected); 29 | 30 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 31 | 32 | Assert.False(result); 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/IsTypeSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using FluentSpecification.Common; 4 | using FluentSpecification.Tests.Mocks; 5 | using JetBrains.Annotations; 6 | using Xunit; 7 | 8 | namespace FluentSpecification.Tests.Common 9 | { 10 | [UsedImplicitly] 11 | public partial class IsTypeSpecificationTests 12 | { 13 | public class Constructor 14 | { 15 | [Fact] 16 | [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] 17 | public void NullExpected_Exception() 18 | { 19 | var exception = Record.Exception(() => new IsTypeSpecification(null)); 20 | 21 | Assert.NotNull(exception); 22 | Assert.IsType(exception); 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/LengthBetweenSpecificationTests.GetNegationExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using FluentSpecification.Common; 4 | using FluentSpecification.Tests.Data; 5 | using FluentSpecification.Tests.Sdk; 6 | using Xunit; 7 | 8 | namespace FluentSpecification.Tests.Common 9 | { 10 | public partial class LengthBetweenSpecificationTests 11 | { 12 | public class GetNegationExpression 13 | { 14 | [Theory] 15 | [CorrectData(typeof(LengthBetweenData), AsNegation = true)] 16 | public void InvokeValidCandidate_ReturnTrue(T candidate, int minLength, int maxLength) 17 | where T : IEnumerable 18 | { 19 | candidate = candidate?.ToString() != "null" ? candidate : default; 20 | var sut = new LengthBetweenSpecification(minLength, maxLength); 21 | 22 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 23 | 24 | Assert.True(result); 25 | } 26 | 27 | [Theory] 28 | [IncorrectData(typeof(LengthBetweenData), AsNegation = true)] 29 | public void InvokeInvalidCandidate_ReturnFalse(T candidate, int minLength, int maxLength) 30 | where T : IEnumerable 31 | { 32 | var sut = new LengthBetweenSpecification(minLength, maxLength); 33 | 34 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 35 | 36 | Assert.False(result); 37 | } 38 | 39 | [Fact] 40 | public void InvokeNullCollectionLinqToEntities_Exception() 41 | { 42 | var sut = new LengthBetweenSpecification(0, 0, true); 43 | var exception = Record.Exception(() => sut.GetNegationExpression().Compile().Invoke(null)); 44 | 45 | Assert.NotNull(exception); 46 | Assert.IsType(exception); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/LengthBetweenSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Common; 3 | using JetBrains.Annotations; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Common 7 | { 8 | [UsedImplicitly] 9 | public partial class LengthBetweenSpecificationTests 10 | { 11 | public class Constructor 12 | { 13 | [Fact] 14 | public void MinGreaterThanMax_ArgumentException() 15 | { 16 | var expression = Record.Exception(() => new LengthBetweenSpecification(5, 1)); 17 | 18 | Assert.NotNull(expression); 19 | Assert.IsType(expression); 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/LengthSpecificationTests.GetNegationExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using FluentSpecification.Common; 4 | using FluentSpecification.Tests.Data; 5 | using FluentSpecification.Tests.Sdk; 6 | using Xunit; 7 | 8 | namespace FluentSpecification.Tests.Common 9 | { 10 | public partial class LengthSpecificationTests 11 | { 12 | public class GetNegationExpression 13 | { 14 | [Theory] 15 | [CorrectData(typeof(LengthData), AsNegation = true)] 16 | public void InvokeValidCandidate_ReturnTrue(T candidate, int length) 17 | where T : IEnumerable 18 | { 19 | candidate = candidate?.ToString() != "null" ? candidate : default; 20 | var sut = new LengthSpecification(length); 21 | 22 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 23 | 24 | Assert.True(result); 25 | } 26 | 27 | [Theory] 28 | [IncorrectData(typeof(LengthData), AsNegation = true)] 29 | public void InvokeInvalidCandidate_ReturnFalse(T candidate, int length) 30 | where T : IEnumerable 31 | { 32 | var sut = new LengthSpecification(length); 33 | 34 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 35 | 36 | Assert.False(result); 37 | } 38 | 39 | [Fact] 40 | public void InvokeNullCollectionLinqToEntities_Exception() 41 | { 42 | var sut = new LengthSpecification(0, true); 43 | var exception = Record.Exception(() => sut.GetNegationExpression().Compile().Invoke(null)); 44 | 45 | Assert.NotNull(exception); 46 | Assert.IsType(exception); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/LessThanOrEqualSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Common; 3 | using FluentSpecification.Tests.Mocks; 4 | using JetBrains.Annotations; 5 | using Xunit; 6 | 7 | namespace FluentSpecification.Tests.Common 8 | { 9 | [UsedImplicitly] 10 | public partial class LessThanOrEqualSpecificationTests 11 | { 12 | public class Constructor 13 | { 14 | [Fact] 15 | public void NotComparableType_Exception() 16 | { 17 | var exception = Record.Exception(() => new LessThanOrEqualSpecification(new FakeType())); 18 | 19 | Assert.NotNull(exception); 20 | Assert.IsType(exception); 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/LessThanSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Common; 3 | using FluentSpecification.Tests.Mocks; 4 | using JetBrains.Annotations; 5 | using Xunit; 6 | 7 | namespace FluentSpecification.Tests.Common 8 | { 9 | [UsedImplicitly] 10 | public partial class LessThanSpecificationTests 11 | { 12 | public class Constructor 13 | { 14 | [Fact] 15 | public void NotComparableType_Exception() 16 | { 17 | var exception = Record.Exception(() => new LessThanSpecification(new FakeType())); 18 | 19 | Assert.NotNull(exception); 20 | Assert.IsType(exception); 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/MatchSpecificationTests.GetNegationExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using Xunit; 3 | 4 | namespace FluentSpecification.Tests.Common 5 | { 6 | public partial class MatchSpecificationTests 7 | { 8 | public class GetNegationExpression 9 | { 10 | [Fact] 11 | public void InvokeInvalidCandidate_ReturnFalse() 12 | { 13 | var pattern = "^[1-9][0-9]{3}-[0-9]{2}-[0-9]{2}$"; 14 | var candidate = "2019-02-26"; 15 | var sut = new MatchSpecification(pattern); 16 | 17 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 18 | 19 | Assert.False(result); 20 | } 21 | 22 | [Fact] 23 | public void InvokeNullCandidate_ReturnTrue() 24 | { 25 | var pattern = "^[1-9][0-9]{3}-[0-9]{2}-[0-9]{2}$"; 26 | var sut = new MatchSpecification(pattern); 27 | 28 | var result = sut.GetNegationExpression().Compile().Invoke(null); 29 | 30 | Assert.True(result); 31 | } 32 | 33 | [Fact] 34 | public void InvokeValidCandidate_ReturnTrue() 35 | { 36 | var pattern = "^[1-9][0-9]{3}-[0-9]{2}-[0-9]{2}$"; 37 | var candidate = "2019-02-261"; 38 | var sut = new MatchSpecification(pattern); 39 | 40 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 41 | 42 | Assert.True(result); 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/MatchSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Common; 3 | using JetBrains.Annotations; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Common 7 | { 8 | [UsedImplicitly] 9 | public partial class MatchSpecificationTests 10 | { 11 | public class Constructor 12 | { 13 | [Theory] 14 | [InlineData(null)] 15 | [InlineData("")] 16 | public void IncorrectPattern_ArgumentException(string pattern) 17 | { 18 | var exception = Record.Exception(() => 19 | new MatchSpecification(pattern)); 20 | 21 | Assert.NotNull(exception); 22 | Assert.IsType(exception); 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/MaxLengthSpecificationTests.GetNegationExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using FluentSpecification.Common; 4 | using FluentSpecification.Tests.Data; 5 | using FluentSpecification.Tests.Sdk; 6 | using Xunit; 7 | 8 | namespace FluentSpecification.Tests.Common 9 | { 10 | public partial class MaxLengthSpecificationTests 11 | { 12 | public class GetNegationExpression 13 | { 14 | [Theory] 15 | [CorrectData(typeof(MaxLengthData), AsNegation = true)] 16 | public void InvokeValidCandidate_ReturnTrue(T candidate, int maxLength) 17 | where T : IEnumerable 18 | { 19 | candidate = candidate?.ToString() != "null" ? candidate : default; 20 | var sut = new MaxLengthSpecification(maxLength); 21 | 22 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 23 | 24 | Assert.True(result); 25 | } 26 | 27 | [Theory] 28 | [IncorrectData(typeof(MaxLengthData), AsNegation = true)] 29 | public void InvokeInvalidCandidate_ReturnFalse(T candidate, int maxLength) 30 | where T : IEnumerable 31 | { 32 | var sut = new MaxLengthSpecification(maxLength); 33 | 34 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 35 | 36 | Assert.False(result); 37 | } 38 | 39 | [Fact] 40 | public void InvokeNullCollectionLinqToEntities_Exception() 41 | { 42 | var sut = new MaxLengthSpecification(0, true); 43 | var exception = Record.Exception(() => sut.GetNegationExpression().Compile().Invoke(null)); 44 | 45 | Assert.NotNull(exception); 46 | Assert.IsType(exception); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/MinLengthSpecificationTests.GetNegationExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using FluentSpecification.Common; 4 | using FluentSpecification.Tests.Data; 5 | using FluentSpecification.Tests.Sdk; 6 | using Xunit; 7 | 8 | namespace FluentSpecification.Tests.Common 9 | { 10 | public partial class MinLengthSpecificationTests 11 | { 12 | public class GetNegationExpression 13 | { 14 | [Theory] 15 | [CorrectData(typeof(MinLengthData), AsNegation = true)] 16 | public void InvokeValidCandidate_ReturnTrue(T candidate, int minLength) 17 | where T : IEnumerable 18 | { 19 | candidate = candidate?.ToString() != "null" ? candidate : default; 20 | var sut = new MinLengthSpecification(minLength); 21 | 22 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 23 | 24 | Assert.True(result); 25 | } 26 | 27 | [Theory] 28 | [IncorrectData(typeof(MinLengthData), AsNegation = true)] 29 | public void InvokeInvalidCandidate_ReturnFalse(T candidate, int minLength) 30 | where T : IEnumerable 31 | { 32 | var sut = new MinLengthSpecification(minLength); 33 | 34 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 35 | 36 | Assert.False(result); 37 | } 38 | 39 | [Fact] 40 | public void InvokeNullCollectionLinqToEntities_Exception() 41 | { 42 | var sut = new MinLengthSpecification(0, true); 43 | var exception = Record.Exception(() => sut.GetNegationExpression().Compile().Invoke(null)); 44 | 45 | Assert.NotNull(exception); 46 | Assert.IsType(exception); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/NullSpecificationTests.GetNegationExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using Xunit; 3 | 4 | namespace FluentSpecification.Tests.Common 5 | { 6 | public partial class NullSpecificationTests 7 | { 8 | public class GetNegationExpression 9 | { 10 | [Fact] 11 | public void InvokeNotNullCandidate_ReturnTrue() 12 | { 13 | var sut = new NullSpecification(); 14 | 15 | var result = sut.GetNegationExpression().Compile().Invoke(""); 16 | 17 | Assert.True(result); 18 | } 19 | 20 | [Fact] 21 | public void InvokeNullableCandidate_ReturnFalse() 22 | { 23 | var sut = new NullSpecification(); 24 | 25 | var result = sut.GetNegationExpression().Compile().Invoke(null); 26 | 27 | Assert.False(result); 28 | } 29 | 30 | [Fact] 31 | public void InvokeNullCandidate_ReturnFalse() 32 | { 33 | var sut = new NullSpecification(); 34 | 35 | var result = sut.GetNegationExpression().Compile().Invoke(null); 36 | 37 | Assert.False(result); 38 | } 39 | 40 | [Fact] 41 | public void InvokeValueTypeCandidate_ReturnTrue() 42 | { 43 | var sut = new NullSpecification(); 44 | 45 | var result = sut.GetNegationExpression().Compile().Invoke(0); 46 | 47 | Assert.True(result); 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/StringContainsSpecificationTests.GetExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Abstractions; 2 | using FluentSpecification.Common; 3 | using FluentSpecification.Tests.Data; 4 | using FluentSpecification.Tests.Sdk; 5 | using Xunit; 6 | 7 | namespace FluentSpecification.Tests.Common 8 | { 9 | public partial class StringContainsSpecificationTests 10 | { 11 | public class GetExpression 12 | { 13 | [Theory] 14 | [CorrectData(typeof(StringContainsData))] 15 | public void InvokeValidCandidate_ReturnTrue(string candidate, string expected) 16 | { 17 | var sut = new ContainsSpecification(expected); 18 | 19 | var result = sut.GetExpression().Compile().Invoke(candidate); 20 | 21 | Assert.True(result); 22 | } 23 | 24 | [Theory] 25 | [IncorrectData(typeof(StringContainsData))] 26 | public void InvokeInvalidCandidate_ReturnFalse(string candidate, string expected) 27 | { 28 | var sut = new ContainsSpecification(expected); 29 | 30 | var result = sut.GetExpression().Compile().Invoke(candidate); 31 | 32 | Assert.False(result); 33 | } 34 | 35 | [Fact] 36 | public void NonGenericILinqSpecification_ReturnExpressionAsAbstractExpression() 37 | { 38 | var sut = new ContainsSpecification(" "); 39 | 40 | var expected = sut.GetExpression().ToString(); 41 | var sutExpression = ((ILinqSpecification) sut).GetExpression(); 42 | var result = sutExpression.ToString(); 43 | 44 | Assert.Equal(expected, result); 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/StringContainsSpecificationTests.GetNegationExpression.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Tests.Data; 3 | using FluentSpecification.Tests.Sdk; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Common 7 | { 8 | public partial class StringContainsSpecificationTests 9 | { 10 | public class GetNegationExpression 11 | { 12 | [Theory] 13 | [CorrectData(typeof(StringContainsData), AsNegation = true)] 14 | public void InvokeValidCandidate_ReturnTrue(string candidate, string expected) 15 | { 16 | var sut = new ContainsSpecification(expected); 17 | 18 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 19 | 20 | Assert.True(result); 21 | } 22 | 23 | [Theory] 24 | [IncorrectData(typeof(StringContainsData), AsNegation = true)] 25 | public void InvokeInvalidCandidate_ReturnFalse(string candidate, string expected) 26 | { 27 | var sut = new ContainsSpecification(expected); 28 | 29 | var result = sut.GetNegationExpression().Compile().Invoke(candidate); 30 | 31 | Assert.False(result); 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/StringContainsSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Common; 3 | using JetBrains.Annotations; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Common 7 | { 8 | [UsedImplicitly] 9 | public partial class StringContainsSpecificationTests 10 | { 11 | public class Constructor 12 | { 13 | [Theory] 14 | [InlineData("")] 15 | [InlineData(null)] 16 | public void EmptyExpected_Exception(string expected) 17 | { 18 | var exception = Record.Exception(() => new ContainsSpecification(expected)); 19 | 20 | Assert.NotNull(exception); 21 | Assert.IsType(exception); 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Common/TrueSpecificationTests.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Abstractions; 2 | using FluentSpecification.Common; 3 | using JetBrains.Annotations; 4 | using Xunit; 5 | 6 | namespace FluentSpecification.Tests.Common 7 | { 8 | [UsedImplicitly] 9 | public partial class TrueSpecificationTests 10 | { 11 | public class GetExpression 12 | { 13 | [Fact] 14 | public void InvokeInvalidCandidate_ReturnFalse() 15 | { 16 | var sut = new TrueSpecification(); 17 | 18 | var result = sut.GetExpression().Compile().Invoke(false); 19 | 20 | Assert.False(result); 21 | } 22 | 23 | [Fact] 24 | public void InvokeValidCandidate_ReturnTrue() 25 | { 26 | var sut = new TrueSpecification(); 27 | 28 | var result = sut.GetExpression().Compile().Invoke(true); 29 | 30 | Assert.True(result); 31 | } 32 | 33 | [Fact] 34 | public void NonGenericILinqSpecification_ReturnExpressionAsAbstractExpression() 35 | { 36 | var sut = new TrueSpecification(); 37 | 38 | var expected = sut.GetExpression().ToString(); 39 | var sutExpression = ((ILinqSpecification) sut).GetExpression(); 40 | var result = sutExpression.ToString(); 41 | 42 | Assert.Equal(expected, result); 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Data/EqualNullableData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | 4 | namespace FluentSpecification.Tests.Data 5 | { 6 | public class EqualNullableData : SpecificationData 7 | { 8 | public EqualNullableData() 9 | { 10 | Valid(null, null) 11 | .Result("EqualSpecification>") 12 | .NegationResult("NotEqualSpecification>+Failed", c => c 13 | .FailedSpecification(typeof(EqualSpecification), "Object is equal to [null]") 14 | .Candidate(null) 15 | .AddParameter("Expected", null)); 16 | 17 | Invalid(0, null) 18 | .NegationResult("NotEqualSpecification>") 19 | .Result("EqualSpecification>+Failed", c => c 20 | .FailedSpecification(typeof(EqualSpecification), "Object is not equal to [null]") 21 | .Candidate(0) 22 | .AddParameter("Expected", null)); 23 | Invalid(null, 0) 24 | .NegationResult("NotEqualSpecification>") 25 | .Result("EqualSpecification>+Failed", c => c 26 | .FailedSpecification(typeof(EqualSpecification), "Object is not equal to [0]") 27 | .Candidate(null) 28 | .AddParameter("Expected", 0)); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Data/ExclusiveBetweenConstructorData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Tests.Mocks; 3 | using FluentSpecification.Tests.Sdk.Data; 4 | 5 | namespace FluentSpecification.Tests.Data 6 | { 7 | public class ExclusiveBetweenConstructorData : SpecificationData 8 | { 9 | public ExclusiveBetweenConstructorData() 10 | { 11 | AddInvalid(5, 1); 12 | AddInvalid(-3, -4); 13 | AddInvalid(5, -1); 14 | AddInvalid(5.1, 5.0); 15 | AddInvalid("123", "122"); 16 | AddInvalid("123", null); 17 | AddInvalid(true, false); 18 | AddInvalid(DateTime.Parse("2019-01-01"), DateTime.Parse("2018-12-31")); 19 | AddInvalid(new ComparableFakeType {First = 1}, new ComparableFakeType()); 20 | AddInvalid(new ComparableInterFakeType {Third = true}, new ComparableInterFakeType()); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Data/GreaterThanNullableData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | 4 | namespace FluentSpecification.Tests.Data 5 | { 6 | public class GreaterThanNullableData : SpecificationData 7 | { 8 | public GreaterThanNullableData() 9 | { 10 | Valid(0, null) 11 | .Result("GreaterThanSpecification>") 12 | .NegationResult("NotGreaterThanSpecification>+Failed", c => c 13 | .FailedSpecification(typeof(GreaterThanSpecification), "Object is greater than [null]") 14 | .Candidate(0) 15 | .AddParameter("GreaterThan", null)); 16 | 17 | Invalid(null, 0) 18 | .NegationResult("NotGreaterThanSpecification>") 19 | .Result("GreaterThanSpecification>+Failed", c => c 20 | .FailedSpecification(typeof(GreaterThanSpecification), "Object is lower than or equal to [0]") 21 | .Candidate(null) 22 | .AddParameter("GreaterThan", 0)); 23 | Invalid(null, null) 24 | .NegationResult("NotGreaterThanSpecification>") 25 | .Result("GreaterThanSpecification>+Failed", c => c 26 | .FailedSpecification(typeof(GreaterThanSpecification), 27 | "Object is lower than or equal to [null]") 28 | .Candidate(null) 29 | .AddParameter("GreaterThan", null)); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Data/GreaterThanOrEqualNullableData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | 4 | namespace FluentSpecification.Tests.Data 5 | { 6 | public class GreaterThanOrEqualNullableData : SpecificationData 7 | { 8 | public GreaterThanOrEqualNullableData() 9 | { 10 | Valid(0, null) 11 | .Result("GreaterThanOrEqualSpecification>") 12 | .NegationResult("NotGreaterThanOrEqualSpecification>+Failed", c => c 13 | .FailedSpecification(typeof(GreaterThanOrEqualSpecification), 14 | "Object is greater than or equal to [null]") 15 | .Candidate(0) 16 | .AddParameter("GreaterThan", null)); 17 | Valid(null, null) 18 | .Result("GreaterThanOrEqualSpecification>") 19 | .NegationResult("NotGreaterThanOrEqualSpecification>+Failed", c => c 20 | .FailedSpecification(typeof(GreaterThanOrEqualSpecification), 21 | "Object is greater than or equal to [null]") 22 | .Candidate(null) 23 | .AddParameter("GreaterThan", null)); 24 | 25 | Invalid(null, 0) 26 | .NegationResult("NotGreaterThanOrEqualSpecification>") 27 | .Result("GreaterThanOrEqualSpecification>+Failed", c => c 28 | .FailedSpecification(typeof(GreaterThanOrEqualSpecification), "Object is lower than [0]") 29 | .Candidate(null) 30 | .AddParameter("GreaterThan", 0)); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Data/InclusiveBetweenConstructorData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentSpecification.Tests.Mocks; 3 | using FluentSpecification.Tests.Sdk.Data; 4 | 5 | namespace FluentSpecification.Tests.Data 6 | { 7 | public class InclusiveBetweenConstructorData : SpecificationData 8 | { 9 | public InclusiveBetweenConstructorData() 10 | { 11 | AddInvalid(5, 1); 12 | AddInvalid(-3, -4); 13 | AddInvalid(5, -1); 14 | AddInvalid(5.1, 5.0); 15 | AddInvalid("123", "122"); 16 | AddInvalid("123", null); 17 | AddInvalid(true, false); 18 | AddInvalid(DateTime.Parse("2019-01-01"), DateTime.Parse("2018-12-31")); 19 | AddInvalid(new ComparableFakeType {First = 1}, new ComparableFakeType()); 20 | AddInvalid(new ComparableInterFakeType {Third = true}, new ComparableInterFakeType()); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Data/LessThanNullableData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | 4 | namespace FluentSpecification.Tests.Data 5 | { 6 | public class LessThanNullableData : SpecificationData 7 | { 8 | public LessThanNullableData() 9 | { 10 | Valid(null, 0) 11 | .Result("LessThanSpecification>") 12 | .NegationResult("NotLessThanSpecification>+Failed", c => c 13 | .FailedSpecification(typeof(LessThanSpecification), "Object is lower than [0]") 14 | .Candidate(null) 15 | .AddParameter("LessThan", 0)); 16 | 17 | Invalid(0, null) 18 | .NegationResult("NotLessThanSpecification>") 19 | .Result("LessThanSpecification>+Failed", c => c 20 | .FailedSpecification(typeof(LessThanSpecification), 21 | "Object is greater than or equal to [null]") 22 | .Candidate(0) 23 | .AddParameter("LessThan", null)); 24 | Invalid(null, null) 25 | .NegationResult("NotLessThanSpecification>") 26 | .Result("LessThanSpecification>+Failed", c => c 27 | .FailedSpecification(typeof(LessThanSpecification), 28 | "Object is greater than or equal to [null]") 29 | .Candidate(null) 30 | .AddParameter("LessThan", null)); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Data/LessThanOrEqualNullableData.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Common; 2 | using FluentSpecification.Tests.Sdk.Data; 3 | 4 | namespace FluentSpecification.Tests.Data 5 | { 6 | public class LessThanOrEqualNullableData : SpecificationData 7 | { 8 | public LessThanOrEqualNullableData() 9 | { 10 | Valid(null, 0) 11 | .Result("LessThanOrEqualSpecification>") 12 | .NegationResult("NotLessThanOrEqualSpecification>+Failed", c => c 13 | .FailedSpecification(typeof(LessThanOrEqualSpecification), 14 | "Object is lower than or equal to [0]") 15 | .Candidate(null) 16 | .AddParameter("LessThan", 0)); 17 | Valid(null, null) 18 | .Result("LessThanOrEqualSpecification>") 19 | .NegationResult("NotLessThanOrEqualSpecification>+Failed", c => c 20 | .FailedSpecification(typeof(LessThanOrEqualSpecification), 21 | "Object is lower than or equal to [null]") 22 | .Candidate(null) 23 | .AddParameter("LessThan", null)); 24 | 25 | Invalid(0, null) 26 | .NegationResult("NotLessThanOrEqualSpecification>") 27 | .Result("LessThanOrEqualSpecification>+Failed", c => c 28 | .FailedSpecification(typeof(LessThanOrEqualSpecification), "Object is greater than [null]") 29 | .Candidate(0) 30 | .AddParameter("LessThan", null)); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Mocks/ComparableFakeType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentSpecification.Tests.Mocks 4 | { 5 | public class ComparableFakeType : FakeType, 6 | IComparable 7 | { 8 | public int CompareTo(ComparableFakeType other) 9 | { 10 | if (ReferenceEquals(this, other)) return 0; 11 | if (ReferenceEquals(null, other)) return 1; 12 | return First.CompareTo(other.First); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Mocks/ComparableInterFakeType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentSpecification.Tests.Mocks 4 | { 5 | public class ComparableInterFakeType : InterFakeType, 6 | IComparable 7 | { 8 | public int CompareTo(object obj) 9 | { 10 | if (ReferenceEquals(null, obj)) return 1; 11 | if (ReferenceEquals(this, obj)) return 0; 12 | if (obj is ComparableInterFakeType other) 13 | return Third.CompareTo(other.Third); 14 | return 1; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Mocks/EquatableFakeType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentSpecification.Tests.Mocks 4 | { 5 | public class EquatableFakeType : FakeType, 6 | IEquatable 7 | { 8 | public bool Equals(EquatableFakeType other) 9 | { 10 | if (ReferenceEquals(null, other)) return false; 11 | if (ReferenceEquals(this, other)) return true; 12 | return First == other.First; 13 | } 14 | 15 | public override bool Equals(object obj) 16 | { 17 | if (ReferenceEquals(null, obj)) return false; 18 | if (ReferenceEquals(this, obj)) return true; 19 | return obj is EquatableFakeType other && Equals(other); 20 | } 21 | 22 | public override int GetHashCode() 23 | { 24 | // ReSharper disable once NonReadonlyMemberInGetHashCode 25 | return First; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Mocks/FakeIntComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace FluentSpecification.Tests.Mocks 4 | { 5 | internal class FakeIntComparer : IComparer, IEqualityComparer 6 | { 7 | public int Compare(int x, int y) 8 | { 9 | return x.CompareTo(y); 10 | } 11 | 12 | public bool Equals(int x, int y) 13 | { 14 | return x.Equals(y); 15 | } 16 | 17 | public int GetHashCode(int obj) 18 | { 19 | return obj.GetHashCode(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Mocks/FakeType.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace FluentSpecification.Tests.Mocks 6 | { 7 | public class FakeType : 8 | IEnumerable 9 | { 10 | public int First { get; set; } 11 | public string Second { get; set; } 12 | public InterFakeType Inter { get; } = null; 13 | public IEnumerable Fourth { get; set; } 14 | 15 | public IEnumerator GetEnumerator() 16 | { 17 | return (Fourth ?? Enumerable.Empty()).GetEnumerator(); 18 | } 19 | 20 | IEnumerator IEnumerable.GetEnumerator() 21 | { 22 | return GetEnumerator(); 23 | } 24 | 25 | public override string ToString() 26 | { 27 | return Second ?? $"Fake({First})"; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Mocks/FalseMockComplexSpecification.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Validation; 2 | 3 | namespace FluentSpecification.Tests.Mocks 4 | { 5 | internal class FalseMockComplexSpecification : MockComplexSpecification 6 | { 7 | public FalseMockComplexSpecification() : base(candidate => false) 8 | { 9 | TraceMessage = $"FalseMockComplexSpecification[{SpecificationResultGenerator.GetTypeShortName(typeof(T))}]"; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Mocks/InterFakeType.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace FluentSpecification.Tests.Mocks 4 | { 5 | public class InterFakeType : IEnumerable 6 | { 7 | public bool Third { get; set; } 8 | 9 | public IEnumerator GetEnumerator() 10 | { 11 | return new[] {Third}.GetEnumerator(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Mocks/MockCompositeSpecification.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Abstractions.Generic; 2 | using FluentSpecification.Core; 3 | 4 | namespace FluentSpecification.Tests.Mocks 5 | { 6 | internal class MockCompositeSpecification : ICompositeSpecification 7 | { 8 | public ISpecification BaseSpecification { get; } = MockComplexSpecification.True(); 9 | 10 | public IComplexSpecification Compose(ISpecification other) 11 | { 12 | return other.AsComplexSpecification(); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/FluentSpecification.Tests/Mocks/TrueMockComplexSpecification.cs: -------------------------------------------------------------------------------- 1 | using FluentSpecification.Core.Validation; 2 | 3 | namespace FluentSpecification.Tests.Mocks 4 | { 5 | internal class TrueMockComplexSpecification : MockComplexSpecification 6 | { 7 | public TrueMockComplexSpecification() : base(candidate => true) 8 | { 9 | TraceMessage = $"TrueMockComplexSpecification[{SpecificationResultGenerator.GetTypeShortName(typeof(T))}]"; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/FluentSpecification.snk.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michalkowal/FluentSpecification/96be7fde4e0eb445a512f153816f290e3143ccec/src/FluentSpecification.snk.enc -------------------------------------------------------------------------------- /src/FluentSpecification/Common/FalseSpecification.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | using FluentSpecification.Core; 4 | using JetBrains.Annotations; 5 | 6 | namespace FluentSpecification.Common 7 | { 8 | /// 9 | /// Checks if candidate is False. 10 | /// 11 | [PublicAPI] 12 | public sealed class FalseSpecification 13 | : ComplexSpecification 14 | { 15 | /// 16 | [PublicAPI] 17 | protected override string CreateFailedMessage(bool candidate) 18 | { 19 | return "Value is True"; 20 | } 21 | 22 | /// 23 | [PublicAPI] 24 | protected override string CreateNegationFailedMessage(bool candidate) 25 | { 26 | return "Value is False"; 27 | } 28 | 29 | /// 30 | [PublicAPI] 31 | protected override IReadOnlyDictionary GetParameters() 32 | { 33 | return null; 34 | } 35 | 36 | /// 37 | [PublicAPI] 38 | protected override Expression BuildExpressionBody(Expression arg) 39 | { 40 | var expression = Expression.Not(arg); 41 | return expression; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/FluentSpecification/Common/NullSpecification.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | using FluentSpecification.Core; 4 | using JetBrains.Annotations; 5 | 6 | namespace FluentSpecification.Common 7 | { 8 | /// 9 | /// Checks if candidate is null. 10 | /// 11 | /// Type of candidate to verify. 12 | [PublicAPI] 13 | public sealed class NullSpecification : 14 | ComplexSpecification 15 | { 16 | /// 17 | [PublicAPI] 18 | protected override string CreateFailedMessage(T candidate) 19 | { 20 | return "Object is not null"; 21 | } 22 | 23 | /// 24 | [PublicAPI] 25 | protected override string CreateNegationFailedMessage(T candidate) 26 | { 27 | return "Object is null"; 28 | } 29 | 30 | /// 31 | [PublicAPI] 32 | protected override IReadOnlyDictionary GetParameters() 33 | { 34 | return null; 35 | } 36 | 37 | /// 38 | [PublicAPI] 39 | protected override Expression BuildValueTypeExpressionBody(Expression arg) 40 | { 41 | return Expression.Constant(false); 42 | } 43 | 44 | /// 45 | [PublicAPI] 46 | protected override Expression BuildExpressionBody(Expression arg) 47 | { 48 | var expression = Expression.Equal(arg, Expression.Constant(null, typeof(T))); 49 | return expression; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/FluentSpecification/Common/TrueSpecification.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | using FluentSpecification.Core; 4 | using JetBrains.Annotations; 5 | 6 | namespace FluentSpecification.Common 7 | { 8 | /// 9 | /// Checks if candidate is True. 10 | /// 11 | [PublicAPI] 12 | public sealed class TrueSpecification 13 | : ComplexSpecification 14 | { 15 | /// 16 | [PublicAPI] 17 | protected override string CreateFailedMessage(bool candidate) 18 | { 19 | return "Value is False"; 20 | } 21 | 22 | /// 23 | [PublicAPI] 24 | protected override string CreateNegationFailedMessage(bool candidate) 25 | { 26 | return "Value is True"; 27 | } 28 | 29 | /// 30 | [PublicAPI] 31 | protected override IReadOnlyDictionary GetParameters() 32 | { 33 | return null; 34 | } 35 | 36 | /// 37 | [PublicAPI] 38 | protected override Expression BuildExpressionBody(Expression arg) 39 | { 40 | var expression = arg; 41 | return expression; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/FluentSpecification/FluentSpecification.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FluentSpecification 5 | 6 | 7 | 8 | Common implementation of small reusable Specifications. All Specifications are based on Specification design pattern. 9 | Specifications support validation scenarios and also can be used like Linq expressions, 10 | because they are designed and implemented especially for Entity Framework Core support and partially for Entity Framework 6 and tested with these frameworks. 11 | Contains Specifications: 12 | - Null, NotNull 13 | - Empty, NotEmpty 14 | - MinLength, MaxLength 15 | - LessThan, GreaterThan 16 | - Contains, NotContains 17 | - Equals, NotEquals 18 | - And more... 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/SolutionInfo.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michalkowal/FluentSpecification/96be7fde4e0eb445a512f153816f290e3143ccec/src/SolutionInfo.cs -------------------------------------------------------------------------------- /src/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "3.1.102", 4 | "rollForward": "latestFeature" 5 | } 6 | } -------------------------------------------------------------------------------- /src/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michalkowal/FluentSpecification/96be7fde4e0eb445a512f153816f290e3143ccec/src/images/icon.png -------------------------------------------------------------------------------- /tools/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | --------------------------------------------------------------------------------