├── .gitattributes
├── .gitignore
├── BREAKING_CHANGES.md
├── GitVersion.yml
├── appveyor.yml
├── azure-pipelines.yml
├── bddfy-report.png
├── build.cake
├── build.ps1
├── docs
├── concepts-context-specification.md
├── concepts-system-under-test.md
├── consistent-reporting.png
├── features-overview.md
├── github.png
├── index.md
├── nuget-install-package-specify.png
├── nuget-manage-nuget-packages-specify.png
├── twitter.png
├── using-specify-configuration.md
├── using-specify-getting-started.md
├── using-specify-scenario-lifecycle.md
├── using-specify-supported-test-frameworks.md
└── visualstudio_enablesourceserversupport.png
├── license.txt
├── mkdocs.yml
├── nuget
├── ScenarioFor.cs.pp
├── Specify.Autofac.nuspec
├── Specify.nuspec
├── SpecifyAutofacBootstrapper.cs.pp
└── SpecifyBootstrapper.cs.pp
├── readme.md
└── src
├── NuGet.config
├── Samples
├── AspNetCoreApi
│ ├── AspNetCoreApi.sln
│ ├── app
│ │ └── Api
│ │ │ ├── Api.csproj
│ │ │ ├── Application
│ │ │ ├── Common
│ │ │ │ ├── AssemblyScanner.cs
│ │ │ │ ├── Behaviours
│ │ │ │ │ ├── DbUtil.cs
│ │ │ │ │ ├── RequestPerformanceBehaviour.cs
│ │ │ │ │ ├── RequestValidationBehavior.cs
│ │ │ │ │ └── SqlDatabaseBehaviour.cs
│ │ │ │ ├── Extensions
│ │ │ │ │ ├── ObjectExtensions.cs
│ │ │ │ │ └── QueryableExtensions.cs
│ │ │ │ ├── Interfaces
│ │ │ │ │ ├── ICurrentUserService.cs
│ │ │ │ │ ├── IQueryDb.cs
│ │ │ │ │ └── IUnitOfWork.cs
│ │ │ │ ├── Mappings
│ │ │ │ │ ├── IMapFrom.cs
│ │ │ │ │ ├── IMapToAndFrom.cs
│ │ │ │ │ └── MappingProfile.cs
│ │ │ │ ├── Paging
│ │ │ │ │ ├── PagedList.cs
│ │ │ │ │ └── PagedQuery.cs
│ │ │ │ └── Validation
│ │ │ │ │ ├── CommandBase.cs
│ │ │ │ │ ├── IRequireValidation.cs
│ │ │ │ │ ├── PrimaryKeyValidator.cs
│ │ │ │ │ ├── ValidationExtensions.cs
│ │ │ │ │ └── ValidationService.cs
│ │ │ ├── DependencyInjection.cs
│ │ │ └── Features
│ │ │ │ ├── MasterFiles
│ │ │ │ ├── DisposalReasonDto.cs
│ │ │ │ ├── MasterFileDto.cs
│ │ │ │ ├── WeatherConditionDto.cs
│ │ │ │ └── WeatherTypeDto.cs
│ │ │ │ └── ToDoItems
│ │ │ │ ├── CreateTodoItemCommand.cs
│ │ │ │ ├── DeleteTodoItemCommand.cs
│ │ │ │ ├── EmailValidator.cs
│ │ │ │ ├── GetAllToDosQuery.cs
│ │ │ │ ├── GetToDoItemQuery.cs
│ │ │ │ ├── ToDoItemDetailDto.cs
│ │ │ │ ├── ToDoItemDto.cs
│ │ │ │ └── UpdateTodoItemCommand.cs
│ │ │ ├── Common
│ │ │ ├── ActionResults.cs
│ │ │ ├── AspNetResultExtensions.cs
│ │ │ ├── CustomExceptionHandlerMiddleware.cs
│ │ │ └── HostingEnvironmentExtensions.cs
│ │ │ ├── Configuration
│ │ │ ├── AppServiceProviderFactory.cs
│ │ │ └── Options
│ │ │ │ └── IdentityServerOptions.cs
│ │ │ ├── Contracts
│ │ │ ├── ApiRoutes.cs
│ │ │ └── Responses
│ │ │ │ ├── ErrorResponse.cs
│ │ │ │ ├── RecordsCreatedResponse.cs
│ │ │ │ └── SuccessResponse.cs
│ │ │ ├── Controllers
│ │ │ ├── ApiController.cs
│ │ │ ├── InfoController.cs
│ │ │ ├── MasterFiles
│ │ │ │ ├── MasterFileControllerFor.cs
│ │ │ │ ├── MasterFileControllers.cs
│ │ │ │ ├── ReadDeleteControllerFor.cs
│ │ │ │ └── WeatherConditionController.cs
│ │ │ └── ToDoController.cs
│ │ │ ├── DependencyInjection.cs
│ │ │ ├── Domain
│ │ │ ├── Common
│ │ │ │ ├── Entity.cs
│ │ │ │ ├── FluentResult
│ │ │ │ │ ├── AppError.cs
│ │ │ │ │ ├── AppWarning.cs
│ │ │ │ │ ├── IFailure.cs
│ │ │ │ │ ├── RecordsCreatedSuccess.cs
│ │ │ │ │ ├── RecordsNotFoundAppError.cs
│ │ │ │ │ ├── ResultExtensions.cs
│ │ │ │ │ ├── Resultz.cs
│ │ │ │ │ ├── TreatWarningsAsErrors.cs
│ │ │ │ │ ├── ValidationMessages.cs
│ │ │ │ │ ├── ValidationSeverity.cs
│ │ │ │ │ └── Warning.cs
│ │ │ │ ├── Guards
│ │ │ │ │ └── Ensure.cs
│ │ │ │ ├── MasterFile.cs
│ │ │ │ ├── SystemTime.cs
│ │ │ │ └── ValueObject.cs
│ │ │ ├── Model
│ │ │ │ ├── MasterFiles
│ │ │ │ │ ├── DisposalReason.cs
│ │ │ │ │ ├── WeatherCondition.cs
│ │ │ │ │ └── WeatherType.cs
│ │ │ │ ├── SharedValueObjects
│ │ │ │ │ ├── ChemicalRateQuantity.cs
│ │ │ │ │ ├── PositiveDecimal.cs
│ │ │ │ │ └── PositiveInteger.cs
│ │ │ │ └── ToDos
│ │ │ │ │ ├── Email.cs
│ │ │ │ │ └── ToDoItem.cs
│ │ │ └── Utils
│ │ │ │ ├── EnumerableExtensions.cs
│ │ │ │ └── ExceptionExtensions.cs
│ │ │ ├── Infrastructure
│ │ │ ├── DependencyInjection.cs
│ │ │ ├── HealthChecks
│ │ │ │ ├── DependencyHealthCheck.cs
│ │ │ │ ├── EndpointRouteBuilderExtensions.cs
│ │ │ │ ├── LiveHealthCheck.cs
│ │ │ │ └── ReadyHealthCheck.cs
│ │ │ ├── Identity
│ │ │ │ └── CurrentUserService.cs
│ │ │ ├── Logging
│ │ │ │ ├── LoggingHelper.cs
│ │ │ │ ├── SerilogApplicationBuilderExtensions.cs
│ │ │ │ └── SerilogLoggingActionFilter.cs
│ │ │ └── Persistence
│ │ │ │ ├── AppDbContext.cs
│ │ │ │ ├── Configurations
│ │ │ │ ├── DisposalReasonConfiguration.cs
│ │ │ │ ├── TodoItemConfiguration.cs
│ │ │ │ ├── WeatherConditionConfiguration.cs
│ │ │ │ └── WeatherTypeConfiguration.cs
│ │ │ │ ├── QueryDbContext.cs
│ │ │ │ └── ShadowProperties.cs
│ │ │ ├── Program.cs
│ │ │ ├── Properties
│ │ │ └── launchSettings.json
│ │ │ ├── README.md
│ │ │ ├── Startup.cs
│ │ │ ├── SwaggerExamples
│ │ │ ├── Requests
│ │ │ │ └── CreateTodoItemCommandExample.cs
│ │ │ └── Responses
│ │ │ │ ├── RecordCreatedResponseExample.cs
│ │ │ │ └── ToDoItemDtoExample.cs
│ │ │ ├── appsettings.Test.json
│ │ │ ├── appsettings.json
│ │ │ └── appsettings.ncrunch.json
│ └── specs
│ │ ├── Specs.Acceptance
│ │ ├── AcceptanceBootstrapper.cs
│ │ ├── Application
│ │ │ ├── Aspects
│ │ │ │ ├── DevOpsStory.cs
│ │ │ │ ├── HealthyLivenessCheck.cs
│ │ │ │ └── HealthyReadinessCheck.cs
│ │ │ ├── Features
│ │ │ │ └── MasterFiles
│ │ │ │ │ └── ReadMe.md
│ │ │ └── ReadMe.md
│ │ ├── README.md
│ │ ├── ScenarioFor.cs
│ │ ├── Specs.Acceptance.csproj
│ │ ├── _CurrentSprint
│ │ │ ├── MasterFiles
│ │ │ │ ├── DisposalReasons
│ │ │ │ │ ├── CreateMany
│ │ │ │ │ │ ├── CannotUpdateDisposalReasonWhenCreating.cs
│ │ │ │ │ │ ├── CreateMultipleDisposalReasonsWithInvalidData.cs
│ │ │ │ │ │ ├── CreateMultipleDisposalReasonsWithValidData.cs
│ │ │ │ │ │ ├── CreateSingleDisposalReasonWithInvalidData.cs
│ │ │ │ │ │ ├── CreateSingleDisposalReasonWithValidData.cs
│ │ │ │ │ │ └── EachDisposalReasonHasUniqueReason.cs
│ │ │ │ │ ├── DeleteOne
│ │ │ │ │ │ ├── DeleteExistingDisposalReason.cs
│ │ │ │ │ │ └── DeleteNonExistentDisposalReason.cs
│ │ │ │ │ ├── DisposalReasonStory.cs
│ │ │ │ │ ├── GetAll
│ │ │ │ │ │ ├── GetAllDisposalReasonsOrdering.cs
│ │ │ │ │ │ ├── GetAllDisposalReasonsPaging.cs
│ │ │ │ │ │ ├── GetAllDisposalReasonsWhenNone.cs
│ │ │ │ │ │ ├── GetAllDisposalReasonsWhenSome.cs
│ │ │ │ │ │ └── GetAllDisposalReasonsWithInvalidQuery.cs
│ │ │ │ │ ├── GetOne
│ │ │ │ │ │ ├── GetOneExistingDisposalReason.cs
│ │ │ │ │ │ └── GetOneNonExistingDisposalReason.cs
│ │ │ │ │ └── UpdateMany
│ │ │ │ │ │ ├── UpdateCannotCreateDisposalReason.cs
│ │ │ │ │ │ ├── UpdateMultipleDisposalReasonsWithInvalidData.cs
│ │ │ │ │ │ ├── UpdateMultipleDisposalReasonsWithValidData.cs
│ │ │ │ │ │ ├── UpdateNonExistentDisposalReason.cs
│ │ │ │ │ │ ├── UpdateSingleDisposalReasonWithInvalidData.cs
│ │ │ │ │ │ └── UpdateSingleDisposalReasonWithValidData.cs
│ │ │ │ ├── WeatherConditions
│ │ │ │ │ └── WeatherConditionSpecs.cs
│ │ │ │ ├── WeatherTypes
│ │ │ │ │ └── WeatherTypeSpecs.cs
│ │ │ │ └── _BaseSpecifications
│ │ │ │ │ ├── CreateManySpecs.cs
│ │ │ │ │ ├── DeleteOneSpecs.cs
│ │ │ │ │ ├── GetAllSpecs.cs
│ │ │ │ │ ├── GetOneSpecs.cs
│ │ │ │ │ ├── MasterFilesStory.cs
│ │ │ │ │ └── UpdateManySpecs.cs
│ │ │ └── ReadMe.md
│ │ └── _TemporarySampleSpecs
│ │ │ ├── ReadMe.md
│ │ │ └── ToDoItems
│ │ │ ├── Create
│ │ │ ├── CreateInvalidToDo.cs
│ │ │ └── CreateValidToDo.cs
│ │ │ ├── Delete
│ │ │ ├── DeleteNonExistingToDo.cs
│ │ │ └── ValidDelete.cs
│ │ │ ├── GetAll
│ │ │ ├── GetAllToDos.cs
│ │ │ └── GetAllToDosWhenNone.cs
│ │ │ ├── GetOne
│ │ │ ├── GetExistingToDo.cs
│ │ │ └── GetNonExistingToDo.cs
│ │ │ ├── ToDoStory.cs
│ │ │ └── Update
│ │ │ ├── InvalidUpdate.cs
│ │ │ ├── UpdateNonExistentToDo.cs
│ │ │ └── ValidUpdate.cs
│ │ ├── Specs.Integration
│ │ ├── Api
│ │ │ └── Configuration
│ │ │ │ └── AutofacConventions.cs
│ │ ├── Application
│ │ │ └── Features
│ │ │ │ └── MasterFiles
│ │ │ │ └── Validators
│ │ │ │ └── DisplayReasonValidatorSpecs.cs
│ │ ├── Infrastructure
│ │ │ └── Persistence
│ │ │ │ ├── DbResilienceSpecs.cs
│ │ │ │ ├── SqlExceptionFactory.cs
│ │ │ │ └── TransientFailureCausingCommandInterceptor.cs
│ │ ├── IntegrationBootstrapper.cs
│ │ ├── ScenarioFor.cs
│ │ ├── Specs.Integration.csproj
│ │ └── TestsFor.cs
│ │ ├── Specs.Library
│ │ ├── Builders
│ │ │ ├── Dtos
│ │ │ │ └── WeatherConditionDtoBuilder.cs
│ │ │ ├── Entities
│ │ │ │ ├── MasterFiles
│ │ │ │ │ ├── DisposalReasonBuilder.cs
│ │ │ │ │ ├── MasterFileBuilder.cs
│ │ │ │ │ └── WeatherConditionBuilder.cs
│ │ │ │ └── ToDoItemBuilder.cs
│ │ │ ├── Get.cs
│ │ │ ├── ObjectMothers
│ │ │ │ ├── SequentialMother.cs
│ │ │ │ └── Stubs.cs
│ │ │ └── ValueSuppliers
│ │ │ │ └── CodeValueSupplier.cs
│ │ ├── Data
│ │ │ ├── DatabaseApplicationAction.cs
│ │ │ ├── EntityExtensions.cs
│ │ │ ├── IDb.cs
│ │ │ ├── IDbFactory.cs
│ │ │ ├── ResetDatabaseAction.cs
│ │ │ ├── ResetSystemTimeAction.cs
│ │ │ └── SqlServer
│ │ │ │ ├── SqlDb.cs
│ │ │ │ └── SqlDbFactory.cs
│ │ ├── Drivers
│ │ │ └── Api
│ │ │ │ ├── ApiDriver.cs
│ │ │ │ ├── ApiDriverException.cs
│ │ │ │ ├── ApiResponse.cs
│ │ │ │ ├── AsyncApiDriver.cs
│ │ │ │ ├── ITestHost.cs
│ │ │ │ ├── JsonExtensions.cs
│ │ │ │ ├── MemoryHost.cs
│ │ │ │ └── WebHost.cs
│ │ ├── Extensions
│ │ │ ├── ApiResponseAssertions.cs
│ │ │ ├── ObjectExtensions.cs
│ │ │ ├── ResultSpecExtensions.cs
│ │ │ ├── ServiceCollectionExtensions.cs
│ │ │ ├── StringExtensions.cs
│ │ │ └── ValidationTestUtil.cs
│ │ ├── Helpers
│ │ │ ├── AsyncHelper.cs
│ │ │ ├── ExecuteScenarioAttribute.cs
│ │ │ ├── FluentValidationHelpers.cs
│ │ │ ├── ProjectLocation.cs
│ │ │ └── TestableSystemTime.cs
│ │ ├── Identity
│ │ │ └── TestCurrentUserService.cs
│ │ ├── README.md
│ │ ├── Specs.Library.csproj
│ │ └── TestSettings.cs
│ │ └── Specs.Unit
│ │ ├── Api
│ │ └── Common
│ │ │ └── AspNetResultExtensions
│ │ │ └── ToCreatedResultSpecs.cs
│ │ ├── Application
│ │ ├── Common
│ │ │ ├── Mappings
│ │ │ │ └── AutoMapperSpecs.cs
│ │ │ └── Validation
│ │ │ │ ├── ResultSpecs.cs
│ │ │ │ └── ValidationServiceSpecs.cs
│ │ └── Features
│ │ │ └── ToDoItems
│ │ │ └── Create
│ │ │ └── CreateToDoItemCommandValidatorSpecs.cs
│ │ ├── Domain
│ │ ├── Common
│ │ │ ├── EnsureSpecs.cs
│ │ │ └── FluentResult
│ │ │ │ ├── GetReasonOrDefaultSpecs.cs
│ │ │ │ ├── GetReasonSpecs.cs
│ │ │ │ └── ResultExtensionsSpecs.cs
│ │ └── Model
│ │ │ └── SharedValueObjects
│ │ │ └── ChemicalRateQuantitySpecs.cs
│ │ ├── Infrastructure
│ │ └── Persistence
│ │ │ └── Paging
│ │ │ └── PagedQuerySpecs.cs
│ │ ├── Samples
│ │ ├── OrderProcessing.cs
│ │ ├── OrderProcessorSpecs.cs
│ │ └── ProcessingAnOrderWithoutAutoMocking.cs
│ │ ├── ScenarioFor.cs
│ │ ├── Specs.Unit.csproj
│ │ ├── TestsFor.cs
│ │ └── UnitBootstrapper.cs
└── Specify.Samples
│ ├── Domain
│ ├── Atm
│ │ ├── Atm.cs
│ │ ├── Card.cs
│ │ └── DisplayMessage.cs
│ ├── Katas
│ │ └── PrimeNumbers
│ │ │ └── PrimeCalculator.cs
│ ├── OrderProcessing
│ │ ├── Auditer.cs
│ │ ├── IInventory.cs
│ │ ├── Order.cs
│ │ ├── OrderProcessor.cs
│ │ ├── OrderResult.cs
│ │ ├── OrderSubmitted.cs
│ │ └── Publisher.cs
│ ├── TicTacToe
│ │ └── Game.cs
│ └── TrainFares
│ │ ├── BuyerCategory.cs
│ │ ├── DayPass.cs
│ │ ├── Fare.cs
│ │ ├── Money.cs
│ │ ├── MonthlyPass.cs
│ │ ├── SingleTicket.cs
│ │ └── WeeklyPass.cs
│ ├── ScenarioFor.cs
│ ├── Specify.Samples.csproj
│ ├── SpecifyBootstrapper.cs
│ └── Specs
│ ├── ATM
│ ├── AccountHasInsufficientFunds.cs
│ ├── AccountHolderWithdrawsCash.cs
│ ├── AccountHolderWithdrawsCashStory.cs
│ ├── AtmHtmlReportConfig.cs
│ └── CardHasBeenDisabled.cs
│ ├── OrderProcessing
│ └── OrderProcessorSpecs.cs
│ ├── TrainFares
│ └── BuyingTrainFareWithExamples.cs
│ └── UseExamplesWithReflectiveApi.cs
├── Settings.StyleCop
├── Specify.sln
├── Specify.sln.GhostDoc.xml
├── _NCrunch_Specify
└── StoredText
│ └── 2acd2f59abe542e5a0767534a8ca874f
├── app
├── Specify.Autofac
│ ├── AutofacContainer.cs
│ ├── AutofacImmutableContainerException.cs
│ ├── AutofacMockRegistrationHandler.cs
│ ├── ContainerBuilderExtensions.cs
│ ├── DefaultAutofacBootstrapper.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Specify.Autofac.csproj
│ └── SpecifyAutofacConfigScanner.cs
└── Specify
│ ├── Catch.cs
│ ├── Config.cs
│ ├── Configuration
│ ├── BddfyTestEngine.cs
│ ├── BootstrapperBase.cs
│ ├── DefaultBootstrapper.cs
│ ├── Examples
│ │ ├── ExampleScope.cs
│ │ ├── IExampleScope.cs
│ │ └── NullExampleScope.cs
│ ├── ExecutableAttributes
│ │ ├── BeginTestCaseAttribute.cs
│ │ └── EndTestCaseAttribute.cs
│ ├── IBootstrapSpecify.cs
│ ├── IPerApplicationAction.cs
│ ├── IPerScenarioAction.cs
│ ├── ITestEngine.cs
│ ├── ReportConfiguration.cs
│ ├── Scanners
│ │ ├── ConfigScanner.cs
│ │ ├── ConfigScannerFactory.cs
│ │ ├── IConfigScanner.cs
│ │ └── SpecifyConfigScanner.cs
│ ├── ScenarioLoggingProcessor.cs
│ ├── ScenarioRunner.cs
│ ├── SpecifyStoryMetadataScanner.cs
│ └── StepScanners
│ │ ├── ArgumentCleaningExtensions.cs
│ │ └── SpecifyExecutableAttributeStepScanner.cs
│ ├── ContainerFor.cs
│ ├── Containers
│ ├── DryContainer.cs
│ ├── DryContainerFactory.cs
│ └── DryMockingContainer.cs
│ ├── Exceptions
│ ├── InterfaceRegistrationException.cs
│ ├── InterfaceResolutionException.cs
│ └── LoggingException.cs
│ ├── Extensions
│ ├── EnumerableExtensions.cs
│ ├── ExceptionExtensions.cs
│ ├── ObjectExtensions.cs
│ ├── ScenarioExtensions.cs
│ ├── StringExtensions.cs
│ └── TypeExtensions.cs
│ ├── Host.cs
│ ├── IContainer.cs
│ ├── IScenario.cs
│ ├── LoggingGateway.cs
│ ├── Mocks
│ ├── AutoMockerFor.cs
│ ├── FakeItEasyMockFactory.cs
│ ├── FileSystem.cs
│ ├── IFileSystem.cs
│ ├── IMockFactory.cs
│ ├── MockDetector.cs
│ ├── MockFactoryBase.cs
│ ├── MoqMockFactory.cs
│ ├── NSubstituteMockFactory.cs
│ └── NullMockFactory.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── ScenarioFor.cs
│ ├── Specify.csproj
│ ├── Stories
│ ├── SpecificationStory.cs
│ ├── Story.cs
│ ├── UserStory.cs
│ └── ValueStory.cs
│ ├── TestsFor.cs
│ └── lib
│ └── AssemblyTypeResolver.cs
└── tests
├── Specify.IntegrationTests
├── ConfigScanners
│ └── SpecifyAutofacConfigScannerTests.cs
├── ContainerFors
│ ├── AutoMocking
│ │ ├── AutoMockerContainerForTests.cs
│ │ ├── AutofacMockingContainerForTests.cs
│ │ └── DryMockingContainerForTests.cs
│ ├── ContainerForIntegrationTestsBase.cs
│ └── Ioc
│ │ ├── AutofacContainerForGetTests.cs
│ │ └── DryContainerForIntegrationTests.cs
├── Containers
│ ├── AutoMocking
│ │ ├── AutofacMockingContainerTests.cs
│ │ ├── DryMockingContainerTests.cs
│ │ └── MockingContainerTestsFor.cs
│ ├── ContainerFactory.cs
│ ├── ContainerSpecsFor.cs
│ └── Ioc
│ │ ├── Application
│ │ ├── ApplicationContainerTestsFor.cs
│ │ ├── AutofacApplicationContainerTests.cs
│ │ └── DryApplicationContainerTests.cs
│ │ ├── AutofacContainerTests.cs
│ │ ├── DryContainerTests.cs
│ │ ├── ExamplesLifecycle.cs
│ │ ├── IocContainerTestsFor.cs
│ │ └── ScenarioConstructorInjectionTests.cs
├── IntegrationBootstrapper.cs
├── Mocks
│ ├── FakeItEasyMockFactoryTests.cs
│ ├── FileSystemTests.cs
│ ├── MockFactoryTestsFor.cs
│ ├── MoqMockFactoryTests.cs
│ └── NSubstituteMockFactoryTests.cs
└── Specify.IntegrationTests.csproj
└── Specify.Tests
├── Apis
├── SemanticVersioningTests.cs
├── SemanticVersioningTests.specify_autofac_has_no_public_api_changes.approved.txt
└── SemanticVersioningTests.specify_has_no_public_api_changes.approved.txt
├── Configuration
├── BddfyTestEngineTests.cs
├── BddfyTestEngineTests.should_display_examples_on_reports.approved.txt
├── LoggingProcessorTests.LoggingConfigurationTest.approved.txt
├── LoggingProcessorTests.LoggingOutputTest.approved.txt
├── LoggingProcessorTests.cs
├── ReportTestData.cs
├── Scanners
│ ├── ConfigScannerFactoryTests.cs
│ └── SpecifyConfigScannerTests.cs
├── ScenarioRunnerTests.cs
├── SpecifyStoryMetadataScannerTests.cs
├── StubPerScenarioAction.cs
└── TemporaryNLogLogger.cs
├── Mocks
├── FakeItEasyMockFactoryTests.cs
├── MockDetectorTests.cs
├── MockFactoryTestsFor.cs
├── MoqMockFactoryTests.cs
└── NSubstituteMockFactoryTests.cs
├── Properties
└── InternalsVisibleTo.cs
├── Specify.Tests.csproj
├── SpecifyRootFolder
├── CatchSpecs.cs
├── ContainerForTests.cs
├── LoggingGatewayTests.cs
├── ScenarioExtensionsTests.cs
├── ScenarioForTests.cs
└── TypeExtensionTests.cs
├── Stories
├── UserStoryTests.cs
└── ValueStoryTests.cs
├── Stubs
├── AutoMockingStubs.cs
├── Scenarios.cs
├── TestableUnitScenario.cs
├── TestableUserStoryScenario.cs
├── UnitScenarioWithAllSupportedStepsInRandomOrder.cs
├── UnitScenarioWithAllSupportedStepsInRandomOrderWithExamples.cs
├── UserStories.cs
└── UserStoryScenarioWithAllSupportedStepsInRandomOrder.cs
├── TestsFor.cs
└── nlog-sample.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | _ReSharper.*
2 | bin/
3 | obj/
4 | *.user
5 | *.suo
6 | *~
7 | *.swp
8 | *.orig
9 | *.nupkg
10 | *.crunchproject.local.xml
11 | *.crunchsolution.local.xml
12 | *.ncrunchsolution
13 | *.ncrunchproject
14 | *.cache
15 | tools/*
16 | artifacts/*
17 | PackageBuild/*
18 | Build/*
19 | *.mdf
20 | *.ldf
21 | *.sdf
22 | *.vsdoc
23 | TestResult.xml
24 | *.DotSettings
25 |
26 | # Visual Studio 2015 cache/options directory
27 | .vs/
--------------------------------------------------------------------------------
/GitVersion.yml:
--------------------------------------------------------------------------------
1 | mode: ContinuousDelivery
2 | next-version: 2.4.0
3 | branches: {}
4 | ignore:
5 | sha: []
6 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | assembly_info:
2 | patch: false
3 |
4 | configuration:
5 | - Release
6 |
7 | build_script:
8 | - ps: .\build.ps1
9 |
10 | test: off
11 | skip_tags: true
12 |
13 | cache:
14 | - src\packages -> **\packages.config # preserve "packages" directory in the root of build folder but will reset it if packages.config is modified
15 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | # .NET Desktop
2 | # Build and run tests for .NET Desktop or Windows classic desktop solutions.
3 | # Add steps that publish symbols, save build artifacts, and more:
4 | # https://docs.microsoft.com/vsts/pipelines/apps/windows/dot-net
5 |
6 | pool:
7 | vmImage: 'VS2017-Win2016'
8 |
9 | variables:
10 | solution: '**/*.sln'
11 | buildPlatform: 'Any CPU'
12 | buildConfiguration: 'Release'
13 |
14 | steps:
15 | - task: NuGetToolInstaller@0
16 |
17 | - task: NuGetCommand@2
18 | inputs:
19 | restoreSolution: '$(solution)'
20 |
21 | - task: VSBuild@1
22 | inputs:
23 | solution: '$(solution)'
24 | platform: '$(buildPlatform)'
25 | configuration: '$(buildConfiguration)'
26 |
27 | - task: VSTest@2
28 | inputs:
29 | platform: '$(buildPlatform)'
30 | configuration: '$(buildConfiguration)'
31 |
--------------------------------------------------------------------------------
/bddfy-report.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwhelan/Specify/7a6f02a3d497ef5567976e78caa151ab334433d8/bddfy-report.png
--------------------------------------------------------------------------------
/docs/concepts-context-specification.md:
--------------------------------------------------------------------------------
1 | # Context-Specification
2 | With context-specification you have a class per scenario, with each step having its own method and state being shared between methods in fields. This means that the setup and execution only happen once (the context), and then each `Then` method is a specification that asserts against the result of the execution.
3 |
4 | ## Scenarios
5 | With functional tests, user stories can be seen as placeholders for conversations that are purposely kept terse while in the backlog (Desirements).
6 |
7 | Scenarios document the conversation that took place with the customer (Requirements). They are the product owner’s acceptance criteria for the user story.
8 |
9 | ## Scenarios are Specifications
10 | Functional test scenarios are high-level specifications of what the system should do. They document the conversation with the business.
11 |
12 | Unit test scenarios are low-level specifications of how the system works. They are a conversation amongst the developers.
13 |
14 | ## Scenarios should focus on business rules
15 | All the steps should reflect the business rules (the what), not the technical implementation (the how).
16 |
17 | One suggestion is to [pretend there is no UI](http://itsadeliverything.com/declarative-vs-imperative-gherkin-scenarios-for-cucumber).
--------------------------------------------------------------------------------
/docs/concepts-system-under-test.md:
--------------------------------------------------------------------------------
1 | This is a term from the book [xUnit Patterns](http://xunitpatterns.com/), by Gerard Meszaros.
2 |
3 | > The "system under test". It is short for "whatever thing we are testing" and is always defined from the perspective of the test. When we are writing unit tests the system under test (SUT) is whatever class (a.k.a. CUT), object (a.k.a. OUT) or method(s) (a.k.a. MUT) we are testing; when we are writing customer tests, the SUT is probably the entire application (a.k.a. AUT) or at least a major subsystem of it. The parts of the application that we are not verifying in this particular test may still be involved as a depended-on component (DOC).
4 |
5 | Also known as
6 |
7 | - Application Under Test (AUT)
8 | - Method Under Test (MUT)
9 | - Class Under Test (CUT)
10 |
11 | Erik Dietrich describes the SUT as [Target](http://www.daedtech.com/test-readability-best-of-all-worlds/).
--------------------------------------------------------------------------------
/docs/consistent-reporting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwhelan/Specify/7a6f02a3d497ef5567976e78caa151ab334433d8/docs/consistent-reporting.png
--------------------------------------------------------------------------------
/docs/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwhelan/Specify/7a6f02a3d497ef5567976e78caa151ab334433d8/docs/github.png
--------------------------------------------------------------------------------
/docs/nuget-install-package-specify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwhelan/Specify/7a6f02a3d497ef5567976e78caa151ab334433d8/docs/nuget-install-package-specify.png
--------------------------------------------------------------------------------
/docs/nuget-manage-nuget-packages-specify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwhelan/Specify/7a6f02a3d497ef5567976e78caa151ab334433d8/docs/nuget-manage-nuget-packages-specify.png
--------------------------------------------------------------------------------
/docs/twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwhelan/Specify/7a6f02a3d497ef5567976e78caa151ab334433d8/docs/twitter.png
--------------------------------------------------------------------------------
/docs/visualstudio_enablesourceserversupport.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwhelan/Specify/7a6f02a3d497ef5567976e78caa151ab334433d8/docs/visualstudio_enablesourceserversupport.png
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2011-2014 Michael Whelan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: Specify-DotNet
2 | theme: readthedocs
3 | markdown_extensions: [fenced_code]
4 | pages:
5 | - Home: index.md
6 | - Features:
7 | - Overview: features-overview.md
8 | - Using Specify:
9 | - Getting Started: using-specify-getting-started.md
10 | - Configuration: using-specify-configuration.md
11 | - Scenario Lifecycle: using-specify-scenario-lifecycle.md
12 | - Supported Test Frameworks: using-specify-supported-test-frameworks.md
13 | - Concepts:
14 | - SUT (System Under Test): concepts-system-under-test.md
15 | - Context Specification: concepts-context-specification.md
--------------------------------------------------------------------------------
/nuget/Specify.Autofac.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Specify.Autofac
5 | $version$
6 | Specify.Autofac
7 | Michael Whelan
8 | Michael Whelan
9 | http://specify-dotnet.readthedocs.org/
10 | false
11 | Specify is a .Net testing library that builds on top of BDDfy from TestStack. Specify.Autofac provides Autofac adapters for IoC and automocking.
12 | The baseline for Specify
13 | Copyright 2015 Michael Whelan
14 |
15 |
16 |
17 |
18 | https://github.com/mwhelan/Specify/blob/master/license.txt
19 | TDD BDD Testing BDDfy
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/nuget/Specify.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Specify
5 | $version$
6 | Specify
7 | Michael Whelan
8 | Michael Whelan
9 | http://specify-dotnet.readthedocs.org/
10 | false
11 | Specify is a .Net testing library that builds on top of BDDfy from TestStack
12 | The baseline for Specify
13 | Copyright 2017 Michael Whelan
14 |
15 |
16 |
17 | https://github.com/mwhelan/Specify/blob/master/license.txt
18 | TDD BDD Testing BDDfy
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/nuget/SpecifyAutofacBootstrapper.cs.pp:
--------------------------------------------------------------------------------
1 | using Autofac;
2 | using Specify.Autofac;
3 |
4 | namespace $rootnamespace$
5 | {
6 | ///
7 | /// The startup class to configure Specify with the Autofac container.
8 | /// Make any changes to the default configuration settings in this file.
9 | ///
10 | public class SpecifyAutofacBootstrapper : DefaultAutofacBootstrapper
11 | {
12 | ///
13 | /// Register any additional items into the Autofac container using the ContainerBuilder or leave it as it is.
14 | ///
15 | /// The Autofac .
16 | public override void ConfigureContainer(ContainerBuilder builder)
17 | {
18 |
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/nuget/SpecifyBootstrapper.cs.pp:
--------------------------------------------------------------------------------
1 | using Specify.Configuration;
2 | using TinyIoC;
3 |
4 | namespace $rootnamespace$
5 | {
6 | ///
7 | /// The startup class to configure Specify with the default TinyIoc container.
8 | /// Make any changes to the default configuration settings in this file.
9 | ///
10 | public class SpecifyBootstrapper : DefaultBootstrapper
11 | {
12 | ///
13 | /// Register any additional items into the TinyIoc container or leave it as it is.
14 | ///
15 | /// The container.
16 | public override void ConfigureContainer(TinyIoCContainer container)
17 | {
18 |
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/NuGet.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Common/Extensions/ObjectExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Newtonsoft.Json;
3 |
4 | namespace ApiTemplate.Api.Application.Common.Extensions
5 | {
6 | public static class ObjectExtensions
7 | {
8 | public static string ToJson(this object entity)
9 | {
10 | return JsonConvert.SerializeObject(entity);
11 | }
12 |
13 | public static List ToCollection(this T item) where T : new()
14 | {
15 | return new List{item};
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Common/Interfaces/ICurrentUserService.cs:
--------------------------------------------------------------------------------
1 | namespace ApiTemplate.Api.Application.Common.Interfaces
2 | {
3 | public interface ICurrentUserService
4 | {
5 | int UserId { get; }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Common/Interfaces/IQueryDb.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using ApiTemplate.Api.Domain.Common;
3 | using ApiTemplate.Api.Domain.Model.ToDos;
4 |
5 | namespace ApiTemplate.Api.Application.Common.Interfaces
6 | {
7 | // This is used by Query Handlers in CQRS
8 | public interface IQueryDb
9 | {
10 | IQueryable ToDoItems { get; }
11 |
12 | // This is a generic alternative to above. Team should discuss preference.
13 | IQueryable QueryFor() where T : Entity;
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Common/Interfaces/IUnitOfWork.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using ApiTemplate.Api.Domain.Model.MasterFiles;
5 | using ApiTemplate.Api.Domain.Model.ToDos;
6 | using Microsoft.EntityFrameworkCore;
7 | using Microsoft.EntityFrameworkCore.Storage;
8 |
9 | namespace ApiTemplate.Api.Application.Common.Interfaces
10 | {
11 | // This is used by Command Handlers in CQRS
12 | public interface IUnitOfWork
13 | {
14 | DbSet ToDoItems { get; set; }
15 | DbSet WeatherConditions { get; set; }
16 |
17 | Task SaveChangesAsync(CancellationToken cancellationToken);
18 | IExecutionStrategy CreateExecutionStrategy();
19 | Task BeginTransactionAsync(IsolationLevel level = IsolationLevel.ReadCommitted);
20 | Task CommitTransactionAsync();
21 | void RollbackTransaction();
22 | }
23 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Common/Mappings/IMapFrom.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 |
3 | namespace ApiTemplate.Api.Application.Common.Mappings
4 | {
5 | public interface IMapFrom
6 | {
7 | void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Common/Mappings/IMapToAndFrom.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 |
3 | namespace ApiTemplate.Api.Application.Common.Mappings
4 | {
5 | public interface IMapToAndFrom
6 | {
7 | void Mapping(Profile profile) => profile
8 | .CreateMap( GetType(), typeof(T))
9 | .ReverseMap();
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Common/Mappings/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reflection;
4 | using AutoMapper;
5 |
6 | namespace ApiTemplate.Api.Application.Common.Mappings
7 | {
8 | public class MappingProfile : Profile
9 | {
10 | private Assembly _assembly;
11 |
12 | public MappingProfile()
13 | {
14 | _assembly = Assembly.GetExecutingAssembly();
15 |
16 | ApplyMapFromMappings(typeof(IMapFrom<>), "IMapFrom`1");
17 |
18 | var type = typeof(IMapToAndFrom<>);
19 |
20 | ApplyMapFromMappings(typeof(IMapToAndFrom<>), "IMapToAndFrom`1");
21 | }
22 |
23 | private void ApplyMapFromMappings(Type interfaceType, string interfaceName)
24 | {
25 | var types = _assembly.GetExportedTypes()
26 | .Where(t => t.GetInterfaces().Any(i =>
27 | i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType))
28 | .ToList();
29 |
30 | foreach (var type in types)
31 | {
32 | var instance = Activator.CreateInstance(type);
33 |
34 | var methodInfo = type.GetMethod("Mapping")
35 | ?? type.GetInterface(interfaceName).GetMethod("Mapping");
36 |
37 | methodInfo?.Invoke(instance, new object[] { this });
38 | }
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Common/Paging/PagedList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace ApiTemplate.Api.Application.Common.Paging
6 | {
7 | public class PagedList
8 | {
9 | public int CurrentPage { get; set; }
10 | public int PageCount { get; set; }
11 | public int PageSize { get; set; }
12 | public int RowCount { get; set; }
13 | public List Results { get; set; }
14 |
15 | public PagedList() { }
16 |
17 | public PagedList(int page, int? pageSize, List data)
18 | {
19 | CurrentPage = page;
20 | RowCount = data.Count();
21 | PageSize = pageSize == null ? RowCount : (int)pageSize; // If we don't pass in a page size then we want it all
22 |
23 | var pageCount = (double)RowCount / (PageSize == 0 ? 25 : PageSize); // Default our page size to 25 if we end up with zero
24 | PageCount = (int)Math.Ceiling(pageCount);
25 | if (PageCount == 0)
26 | PageCount = 1;
27 |
28 | if (PageCount < CurrentPage)
29 | CurrentPage = PageCount;
30 |
31 | var skip = (CurrentPage - 1) * PageSize;
32 |
33 | Results = data.Skip(skip).Take(PageSize).ToList();
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Common/Paging/PagedQuery.cs:
--------------------------------------------------------------------------------
1 | using ApiTemplate.Api.Domain.Common.FluentResult;
2 | using FluentResults;
3 |
4 | namespace ApiTemplate.Api.Application.Common.Paging
5 | {
6 | public class PagedQuery
7 | {
8 | public int Page { get; set; } = 1;
9 | public int? PageSize { get; set; } = null;
10 | public string OrderBy { get; set; } = null;
11 | public bool OrderByDesc { get; set; } = false;
12 | public string FilterField { get; set; } = null;
13 | public string FilterText { get; set; } = null;
14 |
15 | public bool HasSearch => FilterField != null && FilterText != null;
16 |
17 | public Result IsValid()
18 | {
19 | if (FilterField != null && FilterText is null)
20 | {
21 | return Resultz.Error("FilterText", ValidationMessages.IsRequiredFor(nameof(FilterText)));
22 | }
23 |
24 | return Result.Ok();
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Common/Validation/CommandBase.cs:
--------------------------------------------------------------------------------
1 | using FluentResults;
2 | using MediatR;
3 |
4 | namespace ApiTemplate.Api.Application.Common.Validation
5 | {
6 | public class CommandBase : IRequest
7 | {
8 | public bool IgnoreWarnings { get; set; } = false;
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Common/Validation/IRequireValidation.cs:
--------------------------------------------------------------------------------
1 | namespace ApiTemplate.Api.Application.Common.Validation
2 | {
3 | public interface IRequireValidation
4 | {
5 | public int Id { get; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Common/Validation/PrimaryKeyValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using FluentValidation.Results;
3 |
4 | namespace ApiTemplate.Api.Application.Common.Validation
5 | {
6 | public abstract class PrimaryKeyValidator : AbstractValidator
7 | where T : IRequireValidation
8 | {
9 | public override ValidationResult Validate(ValidationContext context)
10 | {
11 | var result = base.Validate(context);
12 | InjectPrimaryKeyToValidationFailure(result, context);
13 | return result;
14 | }
15 |
16 | //public override async Task ValidateAsync(ValidationContext context, CancellationToken cancellation = new CancellationToken())
17 | //{
18 | // var result = await base.ValidateAsync(context, cancellation);
19 | // InjectPrimaryKeyToValidationFailure(result, context);
20 | // return result;
21 | //}
22 |
23 | private void InjectPrimaryKeyToValidationFailure(ValidationResult result, ValidationContext context)
24 | {
25 | foreach (var failure in result.Errors)
26 | {
27 | if (failure.CustomState == null)
28 | {
29 | failure.CustomState = context.InstanceToValidate.Id;
30 | }
31 | }
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Features/MasterFiles/DisposalReasonDto.cs:
--------------------------------------------------------------------------------
1 | using ApiTemplate.Api.Application.Common.Mappings;
2 | using ApiTemplate.Api.Application.Common.Validation;
3 | using ApiTemplate.Api.Domain.Model.MasterFiles;
4 | using FluentValidation;
5 |
6 | namespace ApiTemplate.Api.Application.Features.MasterFiles
7 | {
8 | public class DisposalReasonDto : MasterFileDto, IMapToAndFrom, IRequireValidation
9 | {
10 | public virtual string Reason { get; set; }
11 | public virtual string DisposalReasonDescription { get; set; }
12 | }
13 |
14 | public class DisposalReasonDtoValidator : PrimaryKeyValidator
15 | {
16 | public DisposalReasonDtoValidator()
17 | {
18 | RuleFor(p => p.Reason)
19 | .NotEmpty()
20 | .MaximumLength(100);
21 |
22 | RuleFor(x => x.DisposalReasonDescription)
23 | .MaximumLength(500);
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Features/MasterFiles/MasterFileDto.cs:
--------------------------------------------------------------------------------
1 | namespace ApiTemplate.Api.Application.Features.MasterFiles
2 | {
3 | public class MasterFileDto
4 | {
5 | public virtual int Id { get; set; }
6 | public virtual bool ActiveFlag { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Features/MasterFiles/WeatherConditionDto.cs:
--------------------------------------------------------------------------------
1 | using ApiTemplate.Api.Application.Common.Mappings;
2 | using ApiTemplate.Api.Application.Common.Validation;
3 | using ApiTemplate.Api.Domain.Model.MasterFiles;
4 | using FluentValidation;
5 | using Newtonsoft.Json;
6 |
7 | namespace ApiTemplate.Api.Application.Features.MasterFiles
8 | {
9 | public class WeatherConditionDto : MasterFileDto, IMapToAndFrom, IRequireValidation
10 | {
11 | public string Condition { get; set; }
12 |
13 | [JsonProperty]
14 | public WeatherTypeDto WeatherType { get; set; }
15 | }
16 |
17 | public class WeatherConditionDtoValidator : PrimaryKeyValidator
18 | {
19 | public WeatherConditionDtoValidator()
20 | {
21 | RuleFor(p => p.Condition)
22 | .NotEmpty()
23 | .MaximumLength(200);
24 |
25 | // RuleFor(p => p.WeatherTypeId).NotEmpty();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Features/MasterFiles/WeatherTypeDto.cs:
--------------------------------------------------------------------------------
1 | using ApiTemplate.Api.Application.Common.Mappings;
2 | using ApiTemplate.Api.Application.Common.Validation;
3 | using ApiTemplate.Api.Domain.Model.MasterFiles;
4 | using FluentValidation;
5 |
6 | namespace ApiTemplate.Api.Application.Features.MasterFiles
7 | {
8 | public class WeatherTypeDto : MasterFileDto, IMapToAndFrom, IRequireValidation
9 | {
10 | public virtual string WeatherTypeName { get; set; }
11 | public virtual bool RequiredFlag { get; set; }
12 | }
13 |
14 | public class WeatherTypeDtoValidator : PrimaryKeyValidator
15 | {
16 | public WeatherTypeDtoValidator()
17 | {
18 | RuleFor(p => p.WeatherTypeName)
19 | .NotEmpty()
20 | .MaximumLength(200);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Features/ToDoItems/DeleteTodoItemCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using ApiTemplate.Api.Application.Common.Interfaces;
4 | using ApiTemplate.Api.Domain.Common.FluentResult;
5 | using FluentResults;
6 | using MediatR;
7 |
8 | namespace ApiTemplate.Api.Application.Features.ToDoItems
9 | {
10 | public class DeleteTodoItemCommand : IRequest
11 | {
12 | public int Id { get; set; }
13 | }
14 |
15 | public class DeleteTodoItemCommandHandler : IRequestHandler
16 | {
17 | private readonly IUnitOfWork _context;
18 |
19 | public DeleteTodoItemCommandHandler(IUnitOfWork context)
20 | {
21 | _context = context;
22 | }
23 |
24 | public async Task Handle(DeleteTodoItemCommand request, CancellationToken cancellationToken)
25 | {
26 | var entity = await _context.ToDoItems.FindAsync(request.Id);
27 |
28 | if (entity == null)
29 | {
30 | return Resultz.RecordNotFound("Id", request.Id);
31 | }
32 |
33 | _context.ToDoItems.Remove(entity);
34 |
35 | await _context.SaveChangesAsync(cancellationToken);
36 |
37 | return Result.Ok();
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Features/ToDoItems/EmailValidator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ApiTemplate.Api.Domain.Model.ToDos;
3 | using FluentValidation.Resources;
4 | using FluentValidation.Validators;
5 |
6 | namespace ApiTemplate.Api.Application.Features.ToDoItems
7 | {
8 | public class EmailValidator : PropertyValidator
9 | {
10 | public EmailValidator(IStringSource errorMessageSource) : base(errorMessageSource)
11 | {
12 | }
13 |
14 | public EmailValidator(string errorMessage) : base(errorMessage)
15 | {
16 | }
17 |
18 | protected override bool IsValid(PropertyValidatorContext context)
19 | {
20 | var result = Email.Create(context.PropertyValue.ToString());
21 | return result.IsSuccess;
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Features/ToDoItems/GetAllToDosQuery.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using ApiTemplate.Api.Application.Common.Interfaces;
6 | using ApiTemplate.Api.Domain.Model.ToDos;
7 | using AutoMapper;
8 | using AutoMapper.QueryableExtensions;
9 | using MediatR;
10 | using Microsoft.EntityFrameworkCore;
11 |
12 | namespace ApiTemplate.Api.Application.Features.ToDoItems
13 | {
14 | public class GetAllToDosQuery : IRequest>
15 | {
16 | }
17 |
18 | public class GetAllToDosQueryHandler : IRequestHandler>
19 | {
20 | private readonly IQueryDb _db;
21 | private readonly IMapper _mapper;
22 |
23 | public GetAllToDosQueryHandler(IQueryDb db, IMapper mapper)
24 | {
25 | _db = db;
26 | _mapper = mapper;
27 | }
28 |
29 | public async Task> Handle(GetAllToDosQuery request, CancellationToken cancellationToken)
30 | {
31 | var items = await _db.QueryFor()
32 | .ProjectTo(_mapper.ConfigurationProvider)
33 | .OrderBy(t => t.Title)
34 | .ToListAsync(cancellationToken);
35 |
36 | return items;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Features/ToDoItems/ToDoItemDetailDto.cs:
--------------------------------------------------------------------------------
1 | using ApiTemplate.Api.Application.Common.Mappings;
2 | using ApiTemplate.Api.Domain.Model.ToDos;
3 |
4 | namespace ApiTemplate.Api.Application.Features.ToDoItems
5 | {
6 | public class ToDoItemDetailDto : IMapFrom
7 | {
8 | public int Id { get; set; }
9 |
10 | public string Title { get; set; }
11 |
12 | public string Description { get; set; }
13 |
14 | public string Email { get; set; }
15 |
16 | public bool IsDone { get; set; }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Application/Features/ToDoItems/ToDoItemDto.cs:
--------------------------------------------------------------------------------
1 | using ApiTemplate.Api.Application.Common.Mappings;
2 | using ApiTemplate.Api.Domain.Model.ToDos;
3 | using AutoMapper;
4 |
5 | namespace ApiTemplate.Api.Application.Features.ToDoItems
6 | {
7 | public class ToDoItemDto : IMapFrom
8 | {
9 | public int Id { get; set; }
10 |
11 | public string Title { get; set; }
12 |
13 | public string Description { get; set; }
14 |
15 | public string Email { get; set; }
16 |
17 | public bool Done { get; set; }
18 |
19 | public void Mapping(Profile profile)
20 | {
21 | profile.CreateMap()
22 | .ForMember(d => d.Done,
23 | opt => opt.MapFrom(s => s.IsDone));
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Common/ActionResults.cs:
--------------------------------------------------------------------------------
1 | using ApiTemplate.Api.Contracts.Responses;
2 | using ApiTemplate.Api.Domain.Common.FluentResult;
3 | using Microsoft.AspNetCore.Mvc;
4 |
5 | namespace ApiTemplate.Api.Common
6 | {
7 | public static class ActionResults
8 | {
9 | public static IActionResult ValidationFailure(string propertyId, string validationMessage)
10 | {
11 | var validationError = Resultz.Error(propertyId, validationMessage);
12 | return new BadRequestObjectResult(new ErrorResponse(validationError));
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Common/HostingEnvironmentExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace ApiTemplate.Api.Common
5 | {
6 | public static class HostingEnvironmentExtensions
7 | {
8 | public static bool IsTest(this IHostEnvironment environment)
9 | {
10 | return environment.IsEnvironment("Test") || Environment.GetEnvironmentVariable("NCrunch") == "1"; // || environment.IsEnvironment("TFS");
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Configuration/Options/IdentityServerOptions.cs:
--------------------------------------------------------------------------------
1 | namespace ApiTemplate.Api.Configuration.Options
2 | {
3 | public class IdentityServerOptions
4 | {
5 | public string IdentityServerUrl { get; set; }
6 | public bool RequiresHttps { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Contracts/Responses/RecordsCreatedResponse.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using ApiTemplate.Api.Domain.Common.FluentResult;
3 |
4 | namespace ApiTemplate.Api.Contracts.Responses
5 | {
6 | public class RecordsCreatedResponse
7 | {
8 | public RecordsCreatedResponse() { }
9 |
10 | public RecordsCreatedResponse(RecordsCreatedSuccess reason)
11 | {
12 | NewIds = reason.NewIds;
13 | Message = reason.Message;
14 | }
15 |
16 | public RecordsCreatedResponse(int newId)
17 | {
18 | NewIds.Add(newId);
19 | Message = $"Record created with Id {newId}";
20 | }
21 |
22 | public RecordsCreatedResponse(List newIds)
23 | {
24 | NewIds = newIds;
25 | Message = "Records created";
26 | }
27 |
28 | public List NewIds { get; set; } = new List();
29 | public string Message { get; set; }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Contracts/Responses/SuccessResponse.cs:
--------------------------------------------------------------------------------
1 | namespace ApiTemplate.Api.Contracts.Responses
2 | {
3 | public class SuccessResponse
4 | {
5 | public string Message { get; set; }
6 | public T Data { get; set; }
7 | public SuccessResponse() { }
8 |
9 | public SuccessResponse(T response, string message = "Success")
10 | {
11 | Message = message;
12 | Data = response;
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Controllers/ApiController.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.Extensions.DependencyInjection;
4 |
5 | namespace ApiTemplate.Api.Controllers
6 | {
7 | [ApiController]
8 | [Route("[controller]")]
9 | [Produces("application/json")]
10 | public abstract class ApiController : ControllerBase
11 | {
12 | private IMediator _mediator;
13 | protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService();
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Controllers/InfoController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Microsoft.Extensions.Configuration;
3 |
4 | namespace ApiTemplate.Api.Controllers
5 | {
6 | ///
7 | /// temporary controller to provide feedback while building out-of-process component tests
8 | ///
9 | public class InfoController : ApiController
10 | {
11 | private readonly IConfiguration _configuration;
12 |
13 | public InfoController(IConfiguration configuration)
14 | {
15 | _configuration = configuration;
16 | }
17 |
18 | [HttpGet]
19 | public string Get()
20 | {
21 | return _configuration.GetConnectionString("AppDb");
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Controllers/MasterFiles/MasterFileControllers.cs:
--------------------------------------------------------------------------------
1 | using ApiTemplate.Api.Application.Features.MasterFiles;
2 | using ApiTemplate.Api.Domain.Model.MasterFiles;
3 |
4 | namespace ApiTemplate.Api.Controllers.MasterFiles
5 | {
6 | public class DisposalReasonController : MasterFileControllerFor { }
7 | public class WeatherTypeController : MasterFileControllerFor { }
8 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Common/FluentResult/AppError.cs:
--------------------------------------------------------------------------------
1 | using FluentResults;
2 |
3 | namespace ApiTemplate.Api.Domain.Common.FluentResult
4 | {
5 | // Base error for all application errors.
6 | public class AppError : Error, IFailure
7 | {
8 | public string PropertyName { get; }
9 | public int RowKey { get; }
10 |
11 | public AppError(string propertyName, string message, int rowKey = int.MinValue)
12 | : this(message, rowKey)
13 | {
14 | PropertyName = propertyName;
15 | }
16 |
17 | public AppError(string message, int rowKey = int.MinValue)
18 | {
19 | RowKey = rowKey;
20 | Message = message;
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Common/FluentResult/AppWarning.cs:
--------------------------------------------------------------------------------
1 | namespace ApiTemplate.Api.Domain.Common.FluentResult
2 | {
3 | // Base warning for all application warnings.
4 | public class AppWarning : Warning, IFailure
5 | {
6 | public string PropertyName { get; }
7 | public int RowKey { get; }
8 |
9 | public AppWarning(string propertyName, string message, int rowKey = int.MinValue)
10 | : this(message, rowKey)
11 | {
12 | PropertyName = propertyName;
13 | }
14 |
15 | public AppWarning(string message, int rowKey = int.MinValue)
16 | : base(message)
17 | {
18 | RowKey = rowKey;
19 | Message = message;
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Common/FluentResult/IFailure.cs:
--------------------------------------------------------------------------------
1 | namespace ApiTemplate.Api.Domain.Common.FluentResult
2 | {
3 | public interface IFailure
4 | {
5 | string PropertyName { get; }
6 | int RowKey { get; }
7 | string Message { get; }
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Common/FluentResult/RecordsCreatedSuccess.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using FluentResults;
3 |
4 | namespace ApiTemplate.Api.Domain.Common.FluentResult
5 | {
6 | public class RecordsCreatedSuccess : Success
7 | {
8 | public List NewIds { get; }
9 |
10 | public RecordsCreatedSuccess(int id)
11 | : base("Record created")
12 | {
13 | NewIds = new List {id};
14 | }
15 |
16 | public RecordsCreatedSuccess(List newIds)
17 | : base("Records created")
18 | {
19 | NewIds = newIds;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Common/FluentResult/RecordsNotFoundAppError.cs:
--------------------------------------------------------------------------------
1 | namespace ApiTemplate.Api.Domain.Common.FluentResult
2 | {
3 | public class RecordsNotFoundAppError : AppError
4 | {
5 | public RecordsNotFoundAppError(string propertyName, int id)
6 | : base(propertyName, $"Record '{id}' not found", rowKey: id)
7 | {
8 | }
9 |
10 | public RecordsNotFoundAppError()
11 | : base("Records not found")
12 | {
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Common/FluentResult/TreatWarningsAsErrors.cs:
--------------------------------------------------------------------------------
1 | using FluentResults;
2 |
3 | namespace ApiTemplate.Api.Domain.Common.FluentResult
4 | {
5 | public class TreatWarningsAsErrors : Error
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Common/FluentResult/ValidationMessages.cs:
--------------------------------------------------------------------------------
1 | namespace ApiTemplate.Api.Domain.Common.FluentResult
2 | {
3 | public class ValidationMessages
4 | {
5 | public const string MustBeValidPostCode = " must be a valid post code.";
6 | public const string DoesNotExist = " does not exist.";
7 | public const string IsRequired = " is required.";
8 | public const string CannotBeInPast = " cannot be in the past.";
9 | public const string IsInvalid = " is invalid.";
10 | public const string MustBeGreaterThanZero = " must be > 0.";
11 | public const string MustBePositive = " must be a positive number.";
12 | public const string AlreadyAssociatedWithAnotherParent = " is already asociated with another parent.";
13 |
14 | public static string IsRequiredFor(string propertyName)
15 | {
16 | return $"'{propertyName}'{IsRequired}";
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Common/FluentResult/ValidationSeverity.cs:
--------------------------------------------------------------------------------
1 | namespace ApiTemplate.Api.Domain.Common.FluentResult
2 | {
3 | //[Flags]
4 | public enum ValidationSeverity
5 | {
6 | Error,
7 | Warning
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Common/FluentResult/Warning.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using FluentResults;
3 |
4 | namespace ApiTemplate.Api.Domain.Common.FluentResult
5 | {
6 | public class Warning : Reason
7 | {
8 | public Warning(string message)
9 | {
10 | Message = message;
11 | }
12 |
13 | public Warning WithMetadata(string metadataName, object metadataValue)
14 | {
15 | Metadata.Add(metadataName, metadataValue);
16 | return this;
17 | }
18 |
19 | public Warning WithMetadata(Dictionary metadata)
20 | {
21 | foreach (var metadataItem in metadata)
22 | {
23 | Metadata.Add(metadataItem.Key, metadataItem.Value);
24 | }
25 |
26 | return this;
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Common/MasterFile.cs:
--------------------------------------------------------------------------------
1 | namespace ApiTemplate.Api.Domain.Common
2 | {
3 | public abstract class MasterFile : Entity
4 | {
5 | public virtual bool ActiveFlag { get; protected set; }
6 |
7 | public virtual void MakeActive()
8 | {
9 | ActiveFlag = true;
10 | }
11 |
12 | public virtual void MakeInactive()
13 | {
14 | ActiveFlag = false;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Common/SystemTime.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ApiTemplate.Api.Domain.Common
4 | {
5 | public static class SystemTime
6 | {
7 | public static Func UtcNow = () => DateTime.UtcNow;
8 |
9 | public static void Reset()
10 | {
11 | UtcNow = () => DateTime.UtcNow;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Model/MasterFiles/DisposalReason.cs:
--------------------------------------------------------------------------------
1 | using ApiTemplate.Api.Domain.Common;
2 |
3 | namespace ApiTemplate.Api.Domain.Model.MasterFiles
4 | {
5 | public class DisposalReason : MasterFile
6 | {
7 | public string Reason { get; protected set; }
8 |
9 | public string DisposalReasonDescription { get; protected set; }
10 |
11 | protected DisposalReason() { }
12 |
13 | public DisposalReason(string reason, string disposalReasonDescription)
14 | {
15 | Reason = reason;
16 | DisposalReasonDescription = disposalReasonDescription;
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Model/MasterFiles/WeatherCondition.cs:
--------------------------------------------------------------------------------
1 | using ApiTemplate.Api.Domain.Common;
2 |
3 | namespace ApiTemplate.Api.Domain.Model.MasterFiles
4 | {
5 | public class WeatherCondition : MasterFile
6 | {
7 | public string Condition { get; protected set; }
8 | public virtual WeatherType WeatherType { get; set; }
9 | protected WeatherCondition() { }
10 |
11 | public WeatherCondition(string condition, WeatherType weatherType)
12 | {
13 | Condition = condition;
14 | WeatherType = weatherType;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Model/MasterFiles/WeatherType.cs:
--------------------------------------------------------------------------------
1 | using ApiTemplate.Api.Domain.Common;
2 |
3 | namespace ApiTemplate.Api.Domain.Model.MasterFiles
4 | {
5 | public class WeatherType : MasterFile
6 | {
7 | public string WeatherTypeName { get; protected set; }
8 | public bool RequiredFlag { get; protected set; }
9 | protected WeatherType() { }
10 |
11 | public WeatherType(string weatherTypeName, bool requiredFlag)
12 | {
13 | WeatherTypeName = weatherTypeName;
14 | RequiredFlag = requiredFlag;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCoreApi/app/Api/Domain/Model/SharedValueObjects/PositiveDecimal.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using ApiTemplate.Api.Domain.Common;
3 | using FluentResults;
4 |
5 | namespace ApiTemplate.Api.Domain.Model.SharedValueObjects
6 | {
7 | public class PositiveDecimal : ValueObject
8 | {
9 | public decimal Value { get; }
10 |
11 | private PositiveDecimal(decimal value)
12 | {
13 | Value = value;
14 | }
15 |
16 | public static Result Create(decimal value)
17 | {
18 | if (value <= 0)
19 | return Results.Fail("Value must greater than zero.");
20 |
21 | return Results.Ok(new PositiveDecimal(value));
22 | }
23 |
24 | public static Result CreateNullable(decimal? value)
25 | {
26 | if (value == null)
27 | return Results.Ok((PositiveDecimal?)null);
28 |
29 | return Create((decimal)value);
30 | }
31 |
32 | protected override IEnumerable