From efede0d85c0500687a7fe8ad2627326e7738b187 Mon Sep 17 00:00:00 2001 From: Luke Bailey Date: Tue, 18 Jan 2022 06:18:31 +0000 Subject: [PATCH] Update to net6.0, moved to GitHub actions, removed deprecations (#15) --- .github/workflows/build.yml | 83 ++ .travis.yml | 21 - Directory.Build.props | 4 +- LICENSE | 2 +- README.md | 16 +- Winton.DomainModelling.Abstractions.sln | 2 - appveyor.yml | 27 - .../AsyncResultExtensions.cs | 1083 ++++++++--------- .../ConflictError.cs | 27 +- .../DomainException.cs | 24 - .../Entity.cs | 161 ++- .../EntityNotFoundException.cs | 34 - .../Error.cs | 67 +- .../Failure.cs | 267 ++-- .../NotFoundError.cs | 27 +- .../Result.cs | 593 +++++---- .../Success.cs | 311 +++-- .../UnauthorizedError.cs | 27 +- .../UnauthorizedException.cs | 24 - .../Unit.cs | 191 ++- ...Winton.DomainModelling.Abstractions.csproj | 2 +- .../AsyncResultExtensionsTests.cs | 273 +++-- .../DomainExceptionTests.cs | 35 - .../EntityNotFoundExceptionTests.cs | 41 - .../EntityTests.cs | 199 ++- .../FailureTests.cs | 437 ++++--- .../SuccessTests.cs | 459 ++++--- .../UnauthorizedExceptionTests.cs | 34 - .../UnitTests.cs | 127 +- ....DomainModelling.Abstractions.Tests.csproj | 2 +- 30 files changed, 2214 insertions(+), 2386 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .travis.yml delete mode 100644 appveyor.yml delete mode 100644 src/Winton.DomainModelling.Abstractions/DomainException.cs delete mode 100644 src/Winton.DomainModelling.Abstractions/EntityNotFoundException.cs delete mode 100644 src/Winton.DomainModelling.Abstractions/UnauthorizedException.cs delete mode 100644 test/Winton.DomainModelling.Abstractions.Tests/DomainExceptionTests.cs delete mode 100644 test/Winton.DomainModelling.Abstractions.Tests/EntityNotFoundExceptionTests.cs delete mode 100644 test/Winton.DomainModelling.Abstractions.Tests/UnauthorizedExceptionTests.cs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..9048b8f --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,83 @@ +name: Build +on: + push: + branches: + - master + tags: + - "*" + pull_request: +jobs: + calculate-version: + runs-on: ubuntu-20.04 + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout code + uses: actions/checkout@master + with: + fetch-depth: 0 + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0.9.11 + with: + versionSpec: "5.8.1" + - name: Run GitVersion + id: gitversion + uses: gittools/actions/gitversion/execute@v0.9.11 + - name: Version + id: version + run: | + version=${{ steps.gitversion.outputs.nuGetVersionV2 }} + if [ "${{ github.event_name }}" == "pull_request" ] + then + version=${version}-${{ steps.gitversion.outputs.shortSha }} + fi + echo "::set-output name=version::${version}" + build: + runs-on: ${{ matrix.os }} + needs: [calculate-version] + strategy: + matrix: + include: + - os: ubuntu-20.04 + nugetPush: false + - os: windows-2019 + nugetPush: true + - os: macos-10.15 + nugetPush: false + steps: + - name: Checkout code + uses: actions/checkout@master + with: + fetch-depth: 0 + submodules: recursive + - name: Setup dotnet SDK + uses: actions/setup-dotnet@v1 + with: + dotnet-version: "6.0.101" + - name: Build + run: | + dotnet build -c Release -p:Version=${{ needs.calculate-version.outputs.version }} + shell: bash + - name: Test + run: dotnet test -c Release --no-build + shell: bash + - name: Archive NuGet Packages + uses: actions/upload-artifact@v2 + if: ${{ matrix.nugetPush }} + with: + name: packages + path: | + **/*.nupkg + **/*.snupkg + retention-days: 1 + nuget-push: + runs-on: ubuntu-20.04 + needs: [build] + if: github.event_name != 'pull_request' + steps: + - name: Download NuGet Packages + uses: actions/download-artifact@v2 + with: + name: packages + - name: NuGet Push + run: dotnet nuget push **/*.nupkg -s https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d925c91..0000000 --- a/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -language: csharp -branches: - except: - - /^[0-9]/ -env: - global: - - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - - DOTNET_CLI_TELEMETRY_OPTOUT: 1 -jobs: - include: - - os: linux - dist: bionic - dotnet: 3.1 - - os: osx - osx_image: xcode11.2 - dotnet: 3.1.301 - before_install: - - ulimit -n 4096 -script: - - dotnet build -c Release - - dotnet test -c Release --no-build diff --git a/Directory.Build.props b/Directory.Build.props index 35c21b0..cddbb5f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,9 +2,9 @@ Winton Winton - Copyright 2020 Winton + Copyright 2022 Winton $(MSBuildThisFileDirectory)Rules.ruleset - 8.0 + 10.0 enable True diff --git a/LICENSE b/LICENSE index 963067d..087418a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2018 Winton +Copyright 2022 Winton Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index a712b99..c4c9bf6 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # Winton.DomainModelling.Abstractions -[![Appveyor](https://ci.appveyor.com/api/projects/status/7mba8m947ed603r1?svg=true)](https://ci.appveyor.com/project/wintoncode/winton-domainmodelling-abstractions/branch/master) -[![Travis CI](https://travis-ci.com/wintoncode/Winton.DomainModelling.Abstractions.svg?branch=master)](https://travis-ci.com/wintoncode/Winton.DomainModelling.Abstractions) -[![NuGet version](https://img.shields.io/nuget/v/Winton.DomainModelling.Abstractions.svg)](https://www.nuget.org/packages/Winton.DomainModelling.Abstractions) -[![NuGet version](https://img.shields.io/nuget/vpre/Winton.DomainModelling.Abstractions.svg)](https://www.nuget.org/packages/Winton.DomainModelling.Abstractions) - Abstractions useful for modelling a domain. +[![NuGet Badge](https://buildstats.info/nuget/Winton.DomainModelling.Abstractions)](https://www.nuget.org/packages/Winton.DomainModelling.Abstractions/) + +[![Build history](https://buildstats.info/github/chart/wintoncode/Winton.DomainModelling.Abstractions?branch=master)](https://github.com/wintoncode/Winton.DomainModelling.Abstractions/actions) + ## Building blocks ### `Entity` @@ -16,7 +15,7 @@ Instances are equal if their IDs are equal. Any equatable ID type can be used. ## Results -### `Result` +### `Result` Represents the result of a domain operation that returns data of type `TData`. It is an abstract type with exactly two concretions: `Success` and `Failure`. It is a specialisation of the more generic `Either` type found in functional programming and is inspired by [Scott Wlaschin's Railway Oriented Programming](https://fsharpforfunandprofit.com/rop/) in F#. @@ -41,6 +40,7 @@ public Person GetAdult(int id) ``` This implementation has two major drawbacks: + 1) From a client's perspective, the API is not expressive enough. The method signature gives no indication that it might throw, so the client would need to peek inside to find that out. 2) From an implementer's perspective, the error checking, whilst simple enough in this example, can often grow quite complex. This makes the implementation of the method hard to follow due to the number of conditional branches. We may try factoring out the condition checking blocks into separate methods to solve this problem. This would also allow us to share some of this logic with other parts of the code base. These factored-out methods would then have a signature like `void CheckPersonExists(Person person)`. Again, this signature tells us nothing about the fact that the method might throw an exception. Currently, the compiler is also not able to do the flow analysis necessary to determine that the `person` is not `null` after calling such a method and so we may be left with warnings in the original call site about possible null references, even though we know we've checked for that condition. @@ -66,7 +66,8 @@ Now we have a much more expressive method signature, which indicates that we mig If the operation has no data to return then a `Result` can be used. `Unit` is a special type that indicates the absence of a value, because `void` is not a valid type in C#. -Some recommendations on using `Result` types: +Some recommendations on using `Result` types: + * Make all public domain methods return a `Result`. Most domain operations will have a failure case that the client should be informed about, but even if they don't, by returning `Result` now it can be easily added later without breaking the public API. * Once an operation is in "result space", keep it there for as long as possible. `Result` has a fluent API to facilitate this. This is similar to how, once one operation becomes `async` it is best to make all surrounding operations `async` too. This can be re-phrased as, don't match on the result until the last possible moment. For example, in a web API this would mean only unwrapping the result in the Controller. @@ -83,6 +84,7 @@ Represents a failed `Result`. When constructed it takes an `Error` which contain Like exceptions, errors form a hierarchy, with all errors deriving from the base `Error` type. This library defines a few common domain error types, which are listed below, but it is expected that more specific errors will be defined on a per-domain basis. Some recommendations on designing errors: + * Try not to create custom errors that are too granular. Model them as you would entities and use the language of the domain model to guide their creation. The concept should make sense to a domain expert. * The title should be the same for all instances of the error. The details are where instance specific information can be provided. If you are creating a custom error, make the title static and only let clients customise the details. See implementations of errors in this library for examples. * Only use them for domain errors. Exceptions should still be used for system failures, such as network requests, and programming errors. diff --git a/Winton.DomainModelling.Abstractions.sln b/Winton.DomainModelling.Abstractions.sln index 5667d46..d74fa12 100644 --- a/Winton.DomainModelling.Abstractions.sln +++ b/Winton.DomainModelling.Abstractions.sln @@ -6,8 +6,6 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{859C9252-D63B-4ECB-9AB5-0BA8415C76AE}" ProjectSection(SolutionItems) = preProject .gitignore = .gitignore - .travis.yml = .travis.yml - appveyor.yml = appveyor.yml CONTRIBUTING.md = CONTRIBUTING.md Directory.Build.props = Directory.Build.props GitVersion.yml = GitVersion.yml diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index e4f78c0..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,27 +0,0 @@ -environment: - CLI_VERSION: latest - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true -image: Visual Studio 2019 -configuration: - - Release -before_build: - - dotnet tool install -g GitVersion.Tool - - dotnet gitversion /l console /output buildserver -build_script: - - dotnet build -c Release -p:Version=%GitVersion_NuGetVersion% -test_script: - - dotnet test -c Release --no-build -artifacts: - - name: NuGet - path: .\**\*.nupkg - - name: Symbols - path: .\**\*.snupkg -deploy: - - provider: NuGet - api_key: - secure: +CD+4G+gMInD/BjNvO24NW9u1udGdsx0fUi6bh0muNezQ9fP0sHnS1f96f0OXUy6 - on: - branch: - - master - - /release\/[0-9]\.[0-9]/ \ No newline at end of file diff --git a/src/Winton.DomainModelling.Abstractions/AsyncResultExtensions.cs b/src/Winton.DomainModelling.Abstractions/AsyncResultExtensions.cs index 8341480..32deee7 100644 --- a/src/Winton.DomainModelling.Abstractions/AsyncResultExtensions.cs +++ b/src/Winton.DomainModelling.Abstractions/AsyncResultExtensions.cs @@ -4,569 +4,568 @@ using System; using System.Threading.Tasks; -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +/// +/// Extension methods for asynchronous results which make it possible to chain asynchronous results together +/// using a fluent API in the same way as synchronous results. +/// +public static class AsyncResultExtensions { /// - /// Extension methods for asynchronous results which make it possible to chain asynchronous results together - /// using a fluent API in the same way as synchronous results. + /// Invokes another result generating function which takes as input the error of this result + /// if it is a failure after it has been awaited. /// - public static class AsyncResultExtensions + /// + /// If this result is a success then this is a no-op and the original success is retained. + /// This is useful for handling errors. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The function that is invoked if this result is a failure. + /// + /// + /// If this result is a failure, then the result of onFailure function; + /// otherwise the original error. + /// + public static async Task> Catch( + this Task> resultTask, + Func> onFailure) { - /// - /// Invokes another result generating function which takes as input the error of this result - /// if it is a failure after it has been awaited. - /// - /// - /// If this result is a success then this is a no-op and the original success is retained. - /// This is useful for handling errors. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The function that is invoked if this result is a failure. - /// - /// - /// If this result is a failure, then the result of onFailure function; - /// otherwise the original error. - /// - public static async Task> Catch( - this Task> resultTask, - Func> onFailure) - { - Result result = await resultTask; - return result.Catch(onFailure); - } + Result result = await resultTask; + return result.Catch(onFailure); + } - /// - /// Invokes another result generating function which takes as input the error of this result - /// if it is a failure after it has been awaited. - /// - /// - /// If this result is a success then this is a no-op and the original success is retained. - /// This is useful for handling errors. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The asynchronous function that is invoked if this result is a failure. - /// - /// - /// If this result is a failure, then the result of onFailure function; - /// otherwise the original error. - /// - public static async Task> Catch( - this Task> resultTask, - Func>> onFailure) - { - Result result = await resultTask; - return await result.Catch(onFailure); - } + /// + /// Invokes another result generating function which takes as input the error of this result + /// if it is a failure after it has been awaited. + /// + /// + /// If this result is a success then this is a no-op and the original success is retained. + /// This is useful for handling errors. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The asynchronous function that is invoked if this result is a failure. + /// + /// + /// If this result is a failure, then the result of onFailure function; + /// otherwise the original error. + /// + public static async Task> Catch( + this Task> resultTask, + Func>> onFailure) + { + Result result = await resultTask; + return await result.Catch(onFailure); + } - /// - /// Combines this result with another. - /// If both are successful then combineData is invoked; - /// else if either is a failure then combineErrors is invoked. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The type of data in the other result. - /// - /// - /// The type of data in the combined result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The other result to be combined with this one. - /// - /// - /// The function that is invoked to combine the data when both of the results are successful. - /// - /// - /// The function that is invoked to combine the errors when either of the results is a failure. - /// - /// - /// A new . - /// - public static async Task> Combine( - this Task> resultTask, - Result other, - Func combineData, - Func combineErrors) - { - Result result = await resultTask; - return result.Combine(other, combineData, combineErrors); - } + /// + /// Combines this result with another. + /// If both are successful then combineData is invoked; + /// else if either is a failure then combineErrors is invoked. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The type of data in the other result. + /// + /// + /// The type of data in the combined result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The other result to be combined with this one. + /// + /// + /// The function that is invoked to combine the data when both of the results are successful. + /// + /// + /// The function that is invoked to combine the errors when either of the results is a failure. + /// + /// + /// A new . + /// + public static async Task> Combine( + this Task> resultTask, + Result other, + Func combineData, + Func combineErrors) + { + Result result = await resultTask; + return result.Combine(other, combineData, combineErrors); + } - /// - /// Combines this result with another. - /// If both are successful then combineData is invoked; - /// else if either is a failure then combineErrors is invoked. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The type of data in the other result. - /// - /// - /// The type of data in the combined result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The other asynchronous result to be combined with this one. - /// - /// - /// The function that is invoked to combine the data when both of the results are successful. - /// - /// - /// The function that is invoked to combine the errors when either of the results is a failure. - /// - /// - /// A new . - /// - public static async Task> Combine( - this Task> resultTask, - Task> otherResultTask, - Func combineData, - Func combineErrors) - { - Result result = await resultTask; - Result otherResult = await otherResultTask; - return result.Combine(otherResult, combineData, combineErrors); - } + /// + /// Combines this result with another. + /// If both are successful then combineData is invoked; + /// else if either is a failure then combineErrors is invoked. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The type of data in the other result. + /// + /// + /// The type of data in the combined result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The other asynchronous result to be combined with this one. + /// + /// + /// The function that is invoked to combine the data when both of the results are successful. + /// + /// + /// The function that is invoked to combine the errors when either of the results is a failure. + /// + /// + /// A new . + /// + public static async Task> Combine( + this Task> resultTask, + Task> otherResultTask, + Func combineData, + Func combineErrors) + { + Result result = await resultTask; + Result otherResult = await otherResultTask; + return result.Combine(otherResult, combineData, combineErrors); + } - /// - /// Used to match on whether this result is a success or a failure. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The type that is returned. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The function that is invoked if this result represents a success. - /// - /// - /// The function that is invoked if this result represents a failure. - /// - /// - /// A value that is mapped from either the data or the error. - /// - public static async Task Match( - this Task> resultTask, - Func onSuccess, - Func onFailure) - { - Result result = await resultTask; - return result.Match(onSuccess, onFailure); - } + /// + /// Used to match on whether this result is a success or a failure. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The type that is returned. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The function that is invoked if this result represents a success. + /// + /// + /// The function that is invoked if this result represents a failure. + /// + /// + /// A value that is mapped from either the data or the error. + /// + public static async Task Match( + this Task> resultTask, + Func onSuccess, + Func onFailure) + { + Result result = await resultTask; + return result.Match(onSuccess, onFailure); + } - /// - /// Invokes the specified action if the result is a failure and returns the original result. - /// - /// - /// If this result is a success then this is a no-op and the original success is retained. - /// This is useful for publishing domain model notifications when an operation fails. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The action that will be invoked if this result is a failure. - /// - /// - /// The original result. - /// - public static async Task> OnFailure( - this Task> resultTask, - Action onFailure) - { - return await resultTask.OnFailure(_ => onFailure()); - } + /// + /// Invokes the specified action if the result is a failure and returns the original result. + /// + /// + /// If this result is a success then this is a no-op and the original success is retained. + /// This is useful for publishing domain model notifications when an operation fails. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The action that will be invoked if this result is a failure. + /// + /// + /// The original result. + /// + public static async Task> OnFailure( + this Task> resultTask, + Action onFailure) + { + return await resultTask.OnFailure(_ => onFailure()); + } - /// - /// Invokes the specified action if the result is a failure and returns the original result. - /// - /// - /// If this result is a success then this is a no-op and the original success is retained. - /// This is useful for publishing domain model notifications when an operation fails. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The action that will be invoked if this result is a failure. - /// - /// - /// The original result. - /// - public static async Task> OnFailure( - this Task> resultTask, - Action onFailure) - { - Result result = await resultTask; - return result.OnFailure(onFailure); - } + /// + /// Invokes the specified action if the result is a failure and returns the original result. + /// + /// + /// If this result is a success then this is a no-op and the original success is retained. + /// This is useful for publishing domain model notifications when an operation fails. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The action that will be invoked if this result is a failure. + /// + /// + /// The original result. + /// + public static async Task> OnFailure( + this Task> resultTask, + Action onFailure) + { + Result result = await resultTask; + return result.OnFailure(onFailure); + } - /// - /// Invokes the specified action if the result is a failure and returns the original result. - /// - /// - /// If this result is a success then this is a no-op and the original success is retained. - /// This is useful for publishing domain model notifications when an operation fails. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The asynchronous action that will be invoked if this result is a failure. - /// - /// - /// The original result. - /// - public static async Task> OnFailure( - this Task> resultTask, - Func onFailure) - { - return await resultTask.OnFailure(_ => onFailure()); - } + /// + /// Invokes the specified action if the result is a failure and returns the original result. + /// + /// + /// If this result is a success then this is a no-op and the original success is retained. + /// This is useful for publishing domain model notifications when an operation fails. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The asynchronous action that will be invoked if this result is a failure. + /// + /// + /// The original result. + /// + public static async Task> OnFailure( + this Task> resultTask, + Func onFailure) + { + return await resultTask.OnFailure(_ => onFailure()); + } - /// - /// Invokes the specified action if the result is a failure and returns the original result. - /// - /// - /// If this result is a success then this is a no-op and the original success is retained. - /// This is useful for publishing domain model notifications when an operation fails. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The asynchronous action that will be invoked if this result is a failure. - /// - /// - /// The original result. - /// - public static async Task> OnFailure( - this Task> resultTask, - Func onFailure) - { - Result result = await resultTask; - return await result.OnFailure(onFailure); - } + /// + /// Invokes the specified action if the result is a failure and returns the original result. + /// + /// + /// If this result is a success then this is a no-op and the original success is retained. + /// This is useful for publishing domain model notifications when an operation fails. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The asynchronous action that will be invoked if this result is a failure. + /// + /// + /// The original result. + /// + public static async Task> OnFailure( + this Task> resultTask, + Func onFailure) + { + Result result = await resultTask; + return await result.OnFailure(onFailure); + } - /// - /// Invokes the specified action if the result is a success and returns the original result. - /// - /// - /// If this result is a failure then this is a no-op and the original failure is retained. - /// This is useful for publishing domain model notifications when an operation succeeds. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The action that will be invoked if this result is a success. - /// - /// - /// The original result. - /// - public static async Task> OnSuccess( - this Task> resultTask, - Action onSuccess) - { - return await resultTask.OnSuccess(data => onSuccess()); - } + /// + /// Invokes the specified action if the result is a success and returns the original result. + /// + /// + /// If this result is a failure then this is a no-op and the original failure is retained. + /// This is useful for publishing domain model notifications when an operation succeeds. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The action that will be invoked if this result is a success. + /// + /// + /// The original result. + /// + public static async Task> OnSuccess( + this Task> resultTask, + Action onSuccess) + { + return await resultTask.OnSuccess(data => onSuccess()); + } - /// - /// Invokes the specified action if the result is a success and returns the original result. - /// - /// - /// If this result is a failure then this is a no-op and the original failure is retained. - /// This is useful for publishing domain model notifications when an operation succeeds. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The action that will be invoked if this result is a success. - /// - /// - /// The original result. - /// - public static async Task> OnSuccess( - this Task> resultTask, - Action onSuccess) - { - Result result = await resultTask; - return result.OnSuccess(onSuccess); - } + /// + /// Invokes the specified action if the result is a success and returns the original result. + /// + /// + /// If this result is a failure then this is a no-op and the original failure is retained. + /// This is useful for publishing domain model notifications when an operation succeeds. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The action that will be invoked if this result is a success. + /// + /// + /// The original result. + /// + public static async Task> OnSuccess( + this Task> resultTask, + Action onSuccess) + { + Result result = await resultTask; + return result.OnSuccess(onSuccess); + } - /// - /// Invokes the specified action if the result is a success and returns the original result. - /// - /// - /// If this result is a failure then this is a no-op and the original failure is retained. - /// This is useful for publishing domain model notifications when an operation succeeds. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The asynchronous action that will be invoked if this result is a success. - /// - /// - /// The original result. - /// - public static async Task> OnSuccess( - this Task> resultTask, - Func onSuccess) - { - return await resultTask.OnSuccess(data => onSuccess()); - } + /// + /// Invokes the specified action if the result is a success and returns the original result. + /// + /// + /// If this result is a failure then this is a no-op and the original failure is retained. + /// This is useful for publishing domain model notifications when an operation succeeds. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The asynchronous action that will be invoked if this result is a success. + /// + /// + /// The original result. + /// + public static async Task> OnSuccess( + this Task> resultTask, + Func onSuccess) + { + return await resultTask.OnSuccess(data => onSuccess()); + } - /// - /// Invokes the specified action if the result is a success and returns the original result. - /// - /// - /// If this result is a failure then this is a no-op and the original failure is retained. - /// This is useful for publishing domain model notifications when an operation succeeds. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The asynchronous action that will be invoked if this result is a success. - /// - /// - /// The original result. - /// - public static async Task> OnSuccess( - this Task> resultTask, - Func onSuccess) - { - Result result = await resultTask; - return await result.OnSuccess(onSuccess); - } + /// + /// Invokes the specified action if the result is a success and returns the original result. + /// + /// + /// If this result is a failure then this is a no-op and the original failure is retained. + /// This is useful for publishing domain model notifications when an operation succeeds. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The asynchronous action that will be invoked if this result is a success. + /// + /// + /// The original result. + /// + public static async Task> OnSuccess( + this Task> resultTask, + Func onSuccess) + { + Result result = await resultTask; + return await result.OnSuccess(onSuccess); + } - /// - /// Projects a successful result's data from one type to another. - /// - /// - /// If this result is a failure then this is a no-op. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The type of data in the new result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The function that is invoked to select the data. - /// - /// - /// A new result containing either; the output of the selector function - /// if this result is a success, otherwise the original failure. - /// - public static async Task> Select( - this Task> resultTask, - Func selector) - { - Result result = await resultTask; - return result.Select(selector); - } + /// + /// Projects a successful result's data from one type to another. + /// + /// + /// If this result is a failure then this is a no-op. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The type of data in the new result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The function that is invoked to select the data. + /// + /// + /// A new result containing either; the output of the selector function + /// if this result is a success, otherwise the original failure. + /// + public static async Task> Select( + this Task> resultTask, + Func selector) + { + Result result = await resultTask; + return result.Select(selector); + } - /// - /// Projects a successful result's data from one type to another. - /// - /// - /// If this result is a failure then this is a no-op. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The type of data in the new result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The asynchronous function that is invoked to select the data. - /// - /// - /// A new result containing either; the output of the selector function - /// if this result is a success, otherwise the original failure. - /// - public static async Task> Select( - this Task> resultTask, - Func> selector) - { - Result result = await resultTask; - return await result.Select(selector); - } + /// + /// Projects a successful result's data from one type to another. + /// + /// + /// If this result is a failure then this is a no-op. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The type of data in the new result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The asynchronous function that is invoked to select the data. + /// + /// + /// A new result containing either; the output of the selector function + /// if this result is a success, otherwise the original failure. + /// + public static async Task> Select( + this Task> resultTask, + Func> selector) + { + Result result = await resultTask; + return await result.Select(selector); + } - /// - /// Projects a failed result's data from one type to another. - /// - /// - /// If this result is a success then this is a no-op. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The function that is invoked to select the error. - /// - /// - /// A new result containing either; the output of the selector function - /// if this result is a failure, otherwise the original success. - /// - public static async Task> SelectError( - this Task> resultTask, - Func selector) - { - Result result = await resultTask; - return result.SelectError(selector); - } + /// + /// Projects a failed result's data from one type to another. + /// + /// + /// If this result is a success then this is a no-op. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The function that is invoked to select the error. + /// + /// + /// A new result containing either; the output of the selector function + /// if this result is a failure, otherwise the original success. + /// + public static async Task> SelectError( + this Task> resultTask, + Func selector) + { + Result result = await resultTask; + return result.SelectError(selector); + } - /// - /// Projects a failed result's error from one type to another. - /// - /// - /// If this result is a success then this is a no-op. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The asynchronous function that is invoked to select the error. - /// - /// - /// A new result containing either; the output of the selector function - /// if this result is a failure, otherwise the original success. - /// - public static async Task> SelectError( - this Task> resultTask, - Func> selector) - { - Result result = await resultTask; - return await result.SelectError(selector); - } + /// + /// Projects a failed result's error from one type to another. + /// + /// + /// If this result is a success then this is a no-op. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The asynchronous function that is invoked to select the error. + /// + /// + /// A new result containing either; the output of the selector function + /// if this result is a failure, otherwise the original success. + /// + public static async Task> SelectError( + this Task> resultTask, + Func> selector) + { + Result result = await resultTask; + return await result.SelectError(selector); + } - /// - /// Invokes another result generating function which takes as input the data of this result - /// if it is a success after it has been awaited. - /// - /// - /// If this result is a failure then this is a no-op and the original failure is retained. - /// This is useful for chaining serial operations together that return results. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The type of data in the new result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The function that is invoked if this result is a success. - /// - /// - /// If this result is a success, then the result of onSuccess function; - /// otherwise the original error. - /// - public static async Task> Then( - this Task> resultTask, - Func> onSuccess) - { - Result result = await resultTask; - return result.Then(onSuccess); - } + /// + /// Invokes another result generating function which takes as input the data of this result + /// if it is a success after it has been awaited. + /// + /// + /// If this result is a failure then this is a no-op and the original failure is retained. + /// This is useful for chaining serial operations together that return results. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The type of data in the new result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The function that is invoked if this result is a success. + /// + /// + /// If this result is a success, then the result of onSuccess function; + /// otherwise the original error. + /// + public static async Task> Then( + this Task> resultTask, + Func> onSuccess) + { + Result result = await resultTask; + return result.Then(onSuccess); + } - /// - /// Invokes another result generating function which takes as input the data of this result - /// if it is a success after it has been awaited. - /// - /// - /// If this result is a failure then this is a no-op and the original failure is retained. - /// This is useful for chaining serial operations together that return results. - /// - /// - /// The type of data encapsulated by the result. - /// - /// - /// The type of data in the new result. - /// - /// - /// The asynchronous result that this extension method is invoked on. - /// - /// - /// The asynchronous function that is invoked if this result is a success. - /// - /// - /// If this result is a success, then the result of onSuccess function; - /// otherwise the original error. - /// - public static async Task> Then( - this Task> resultTask, - Func>> onSuccess) - { - Result result = await resultTask; - return await result.Then(onSuccess); - } + /// + /// Invokes another result generating function which takes as input the data of this result + /// if it is a success after it has been awaited. + /// + /// + /// If this result is a failure then this is a no-op and the original failure is retained. + /// This is useful for chaining serial operations together that return results. + /// + /// + /// The type of data encapsulated by the result. + /// + /// + /// The type of data in the new result. + /// + /// + /// The asynchronous result that this extension method is invoked on. + /// + /// + /// The asynchronous function that is invoked if this result is a success. + /// + /// + /// If this result is a success, then the result of onSuccess function; + /// otherwise the original error. + /// + public static async Task> Then( + this Task> resultTask, + Func>> onSuccess) + { + Result result = await resultTask; + return await result.Then(onSuccess); } } \ No newline at end of file diff --git a/src/Winton.DomainModelling.Abstractions/ConflictError.cs b/src/Winton.DomainModelling.Abstractions/ConflictError.cs index 18d4458..0d2d54b 100644 --- a/src/Winton.DomainModelling.Abstractions/ConflictError.cs +++ b/src/Winton.DomainModelling.Abstractions/ConflictError.cs @@ -1,22 +1,21 @@ // Copyright (c) Winton. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +/// +/// +/// An error indicating that a conflicting entity exists. +/// +public class ConflictError : Error { - /// /// - /// An error indicating that a conflicting entity exists. + /// Initializes a new instance of the class. /// - public class ConflictError : Error + /// The detail that describes the error. + /// A new instance of . + public ConflictError(string detail) + : base("Conflict", detail) { - /// - /// Initializes a new instance of the class. - /// - /// The detail that describes the error. - /// A new instance of . - public ConflictError(string detail) - : base("Conflict", detail) - { - } } -} +} \ No newline at end of file diff --git a/src/Winton.DomainModelling.Abstractions/DomainException.cs b/src/Winton.DomainModelling.Abstractions/DomainException.cs deleted file mode 100644 index 04312dc..0000000 --- a/src/Winton.DomainModelling.Abstractions/DomainException.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Winton. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - -using System; - -namespace Winton.DomainModelling -{ - /// - /// - /// Represents domain errors. - /// - [Obsolete("Prefer to return results with an Error instead.", false)] - public class DomainException : Exception - { - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public DomainException(string message) - : base(message) - { - } - } -} \ No newline at end of file diff --git a/src/Winton.DomainModelling.Abstractions/Entity.cs b/src/Winton.DomainModelling.Abstractions/Entity.cs index b602e82..4ac0c7c 100644 --- a/src/Winton.DomainModelling.Abstractions/Entity.cs +++ b/src/Winton.DomainModelling.Abstractions/Entity.cs @@ -3,101 +3,100 @@ using System; -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +/// +/// +/// A base class to implement entity types, which are defined by their identity rather than their attributes. +/// +/// The ID type for the entity type. +public abstract class Entity : IEquatable> + where TEntityId : IEquatable { - /// /// - /// A base class to implement entity types, which are defined by their identity rather than their attributes. + /// Initializes a new instance of the class. /// - /// The ID type for the entity type. - public abstract class Entity : IEquatable> - where TEntityId : IEquatable + /// The ID for the entity. + protected Entity(TEntityId id) { - /// - /// Initializes a new instance of the class. - /// - /// The ID for the entity. - protected Entity(TEntityId id) - { - Id = id; - } + Id = id; + } - /// - /// Gets the ID for the entity. - /// - public TEntityId Id { get; } + /// + /// Gets the ID for the entity. + /// + public TEntityId Id { get; } - /// - /// Indicates whether two entities are equal. - /// - /// The first entity. - /// The second entity. - /// if the entities have the same ID; otherwise, . - public static bool operator ==(Entity? left, Entity? right) - { - return Equals(left, right); - } + /// + /// Indicates whether two entities are equal. + /// + /// The first entity. + /// The second entity. + /// if the entities have the same ID; otherwise, . + public static bool operator ==(Entity? left, Entity? right) + { + return Equals(left, right); + } - /// - /// Indicates whether two entities are unequal. - /// - /// The first entity. - /// The second entity. - /// if the entities have different IDs; otherwise, . - public static bool operator !=(Entity? left, Entity? right) - { - return !Equals(left, right); - } + /// + /// Indicates whether two entities are unequal. + /// + /// The first entity. + /// The second entity. + /// if the entities have different IDs; otherwise, . + public static bool operator !=(Entity? left, Entity? right) + { + return !Equals(left, right); + } - /// - /// - /// Indicates whether the current entity is equal to another entity. - /// - /// An entity to compare with this entity. - /// - /// if the current entity has the same ID as the entity; - /// otherwise, . - /// - public bool Equals(Entity? other) + /// + /// + /// Indicates whether the current entity is equal to another entity. + /// + /// An entity to compare with this entity. + /// + /// if the current entity has the same ID as the entity; + /// otherwise, . + /// + public bool Equals(Entity? other) + { + if (other is null) { - if (other is null) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - if (GetType() != other.GetType()) - { - return false; - } - - return !Id.Equals(default!) && !other.Id.Equals(default!) && Id.Equals(other.Id); + return false; } - /// - /// Determines whether the specified object is equal to the current object. - /// - /// The object to compare with the current object. - /// - /// if the specified object is equal to the current object; otherwise, - /// . - /// - public override bool Equals(object? obj) + if (ReferenceEquals(this, other)) { - return Equals(obj as Entity); + return true; } - /// - /// Serves as the default hash function. - /// - /// A hash code for the current object. - public override int GetHashCode() + if (GetType() != other.GetType()) { - return Id.GetHashCode(); + return false; } + + return !Id.Equals(default!) && !other.Id.Equals(default!) && Id.Equals(other.Id); + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// The object to compare with the current object. + /// + /// if the specified object is equal to the current object; otherwise, + /// . + /// + public override bool Equals(object? obj) + { + return Equals(obj as Entity); + } + + /// + /// Serves as the default hash function. + /// + /// A hash code for the current object. + public override int GetHashCode() + { + return Id.GetHashCode(); } -} \ No newline at end of file +} diff --git a/src/Winton.DomainModelling.Abstractions/EntityNotFoundException.cs b/src/Winton.DomainModelling.Abstractions/EntityNotFoundException.cs deleted file mode 100644 index 1bae265..0000000 --- a/src/Winton.DomainModelling.Abstractions/EntityNotFoundException.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Winton. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - -using System; - -namespace Winton.DomainModelling -{ - /// - /// - /// An error indicating that an entity could not be found. - /// - [Obsolete("Prefer to return results with a NotFoundError instead.", false)] - public class EntityNotFoundException : DomainException - { - private EntityNotFoundException(string message) - : base(message) - { - } - - /// - /// Creates a new instance of the class using the generic constraint to generate - /// the message. - /// - /// The type of the entity. - /// The type of the entity id. - /// A new instance of . - public static EntityNotFoundException Create() - where TEntity : Entity - where TEntityId : IEquatable - { - return new EntityNotFoundException($"The specified {typeof(TEntity).Name} could not be found."); - } - } -} \ No newline at end of file diff --git a/src/Winton.DomainModelling.Abstractions/Error.cs b/src/Winton.DomainModelling.Abstractions/Error.cs index 1d43d4f..f4502ab 100644 --- a/src/Winton.DomainModelling.Abstractions/Error.cs +++ b/src/Winton.DomainModelling.Abstractions/Error.cs @@ -1,43 +1,42 @@ // Copyright (c) Winton. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +/// +/// The base class of all errors in the domain model. +/// +/// +/// In a domain model it is better to explicitly model errors rather than throw exceptions. +/// Errors are legitimate outcomes of operations in a domain model where an operation is +/// not guaranteed to succeed. Errors can occur for several reasons, such as not being able +/// to find a requested entity or because the inputs received were invalid. +/// On the other hand, exceptions should be used in exceptional circumstances that have +/// occurred due to factors outside of the domain model's control, e.g. network failures. +/// In general, problems in the domain model should be modelled as errors and exceptions should +/// be reserved for cases where the user cannot be given information on how to correct the problem. +/// +public class Error { /// - /// The base class of all errors in the domain model. + /// Initializes a new instance of the class. /// - /// - /// In a domain model it is better to explicitly model errors rather than throw exceptions. - /// Errors are legitimate outcomes of operations in a domain model where an operation is - /// not guaranteed to succeed. Errors can occur for several reasons, such as not being able - /// to find a requested entity or because the inputs received were invalid. - /// On the other hand, exceptions should be used in exceptional circumstances that have - /// occurred due to factors outside of the domain model's control, e.g. network failures. - /// In general, problems in the domain model should be modelled as errors and exceptions should - /// be reserved for cases where the user cannot be given information on how to correct the problem. - /// - public class Error + /// The title for this type of error. + /// The detail that describes the error. + /// A new instance of . + public Error(string title, string detail) { - /// - /// Initializes a new instance of the class. - /// - /// The title for this type of error. - /// The detail that describes the error. - /// A new instance of . - public Error(string title, string detail) - { - Title = title; - Detail = detail; - } + Title = title; + Detail = detail; + } - /// - /// Gets the detail that describes the error. - /// - public string Detail { get; } + /// + /// Gets the detail that describes the error. + /// + public string Detail { get; } - /// - /// Gets the title of the error. This should be the same for all instances of the same error type. - /// - public string Title { get; } - } -} \ No newline at end of file + /// + /// Gets the title of the error. This should be the same for all instances of the same error type. + /// + public string Title { get; } +} diff --git a/src/Winton.DomainModelling.Abstractions/Failure.cs b/src/Winton.DomainModelling.Abstractions/Failure.cs index cc1f8fa..1559b52 100644 --- a/src/Winton.DomainModelling.Abstractions/Failure.cs +++ b/src/Winton.DomainModelling.Abstractions/Failure.cs @@ -4,142 +4,141 @@ using System; using System.Threading.Tasks; -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +/// +/// +/// A result indicating a failure. +/// +public sealed class Failure : Result { - /// /// - /// A result indicating a failure. + /// Initializes a new instance of the class. + /// + /// The error that caused the result to be a failure. + /// A new instance of . + public Failure(Error error) + { + Error = error; + } + + /// + /// Gets the error that caused the failure. /// - public sealed class Failure : Result - { - /// - /// Initializes a new instance of the class. - /// - /// The error that caused the result to be a failure. - /// A new instance of . - public Failure(Error error) - { - Error = error; - } - - /// - /// Gets the error that caused the failure. - /// - public Error Error { get; } - - /// - public override Result Catch(Func> onFailure) - { - return onFailure(Error); - } - - /// - public override Task> Catch(Func>> onFailure) - { - return onFailure(Error); - } - - /// - public override Result Combine( - Result other, - Func combineData, - Func combineErrors) - { - return other.Match>( - otherData => new Failure(Error), - otherError => new Failure(combineErrors(Error, otherError))); - } - - /// - public override T Match(Func onSuccess, Func onFailure) - { - return onFailure(Error); - } - - /// - public override Result OnFailure(Action onFailure) - { - return OnFailure(_ => onFailure()); - } - - /// - public override Result OnFailure(Action onFailure) - { - onFailure(Error); - return this; - } - - /// - public override Task> OnFailure(Func onFailure) - { - return OnFailure(_ => onFailure()); - } - - /// - public override async Task> OnFailure(Func onFailure) - { - await onFailure(Error); - return this; - } - - /// - public override Result OnSuccess(Action onSuccess) - { - return this; - } - - /// - public override Result OnSuccess(Action onSuccess) - { - return this; - } - - /// - public override Task> OnSuccess(Func onSuccess) - { - return Task.FromResult>(this); - } - - /// - public override Task> OnSuccess(Func onSuccess) - { - return Task.FromResult>(this); - } - - /// - public override Result Select(Func selector) - { - return new Failure(Error); - } - - /// - public override Task> Select(Func> selector) - { - return Task.FromResult>(new Failure(Error)); - } - - /// - public override Result SelectError(Func selector) - { - return new Failure(selector(Error)); - } - - /// - public override async Task> SelectError(Func> selector) - { - return new Failure(await selector(Error)); - } - - /// - public override Result Then(Func> onSuccess) - { - return new Failure(Error); - } - - /// - public override Task> Then(Func>> onSuccess) - { - return Task.FromResult>(new Failure(Error)); - } + public Error Error { get; } + + /// + public override Result Catch(Func> onFailure) + { + return onFailure(Error); + } + + /// + public override Task> Catch(Func>> onFailure) + { + return onFailure(Error); + } + + /// + public override Result Combine( + Result other, + Func combineData, + Func combineErrors) + { + return other.Match>( + otherData => new Failure(Error), + otherError => new Failure(combineErrors(Error, otherError))); + } + + /// + public override T Match(Func onSuccess, Func onFailure) + { + return onFailure(Error); + } + + /// + public override Result OnFailure(Action onFailure) + { + return OnFailure(_ => onFailure()); + } + + /// + public override Result OnFailure(Action onFailure) + { + onFailure(Error); + return this; + } + + /// + public override Task> OnFailure(Func onFailure) + { + return OnFailure(_ => onFailure()); + } + + /// + public override async Task> OnFailure(Func onFailure) + { + await onFailure(Error); + return this; + } + + /// + public override Result OnSuccess(Action onSuccess) + { + return this; + } + + /// + public override Result OnSuccess(Action onSuccess) + { + return this; + } + + /// + public override Task> OnSuccess(Func onSuccess) + { + return Task.FromResult>(this); + } + + /// + public override Task> OnSuccess(Func onSuccess) + { + return Task.FromResult>(this); + } + + /// + public override Result Select(Func selector) + { + return new Failure(Error); + } + + /// + public override Task> Select(Func> selector) + { + return Task.FromResult>(new Failure(Error)); + } + + /// + public override Result SelectError(Func selector) + { + return new Failure(selector(Error)); + } + + /// + public override async Task> SelectError(Func> selector) + { + return new Failure(await selector(Error)); + } + + /// + public override Result Then(Func> onSuccess) + { + return new Failure(Error); + } + + /// + public override Task> Then(Func>> onSuccess) + { + return Task.FromResult>(new Failure(Error)); } } \ No newline at end of file diff --git a/src/Winton.DomainModelling.Abstractions/NotFoundError.cs b/src/Winton.DomainModelling.Abstractions/NotFoundError.cs index cbde01d..90b8645 100644 --- a/src/Winton.DomainModelling.Abstractions/NotFoundError.cs +++ b/src/Winton.DomainModelling.Abstractions/NotFoundError.cs @@ -1,22 +1,21 @@ // Copyright (c) Winton. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +/// +/// +/// An error indicating that an entity could not be found. +/// +public class NotFoundError : Error { - /// /// - /// An error indicating that an entity could not be found. + /// Initializes a new instance of the class. /// - public class NotFoundError : Error + /// The detail that describes the error. + /// A new instance of . + public NotFoundError(string detail) + : base("Not found", detail) { - /// - /// Initializes a new instance of the class. - /// - /// The detail that describes the error. - /// A new instance of . - public NotFoundError(string detail) - : base("Not found", detail) - { - } } -} \ No newline at end of file +} diff --git a/src/Winton.DomainModelling.Abstractions/Result.cs b/src/Winton.DomainModelling.Abstractions/Result.cs index 8745385..d75aa14 100644 --- a/src/Winton.DomainModelling.Abstractions/Result.cs +++ b/src/Winton.DomainModelling.Abstractions/Result.cs @@ -4,322 +4,321 @@ using System; using System.Threading.Tasks; -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +/// +/// Represents the result of an operation which may succeed or fail. +/// +/// +/// Use Result<Unit> for operations that would otherwise be void. +/// +/// +/// The type of data encapsulated by the result. +/// +public abstract class Result { /// - /// Represents the result of an operation which may succeed or fail. + /// Invokes another result generating function which takes as input the error of this result + /// if it is a failure. /// /// - /// Use Result<Unit> for operations that would otherwise be void. + /// If this result is a success then this is a no-op and the original success is retained. + /// This is useful for handling errors. /// - /// - /// The type of data encapsulated by the result. - /// - public abstract class Result - { - /// - /// Invokes another result generating function which takes as input the error of this result - /// if it is a failure. - /// - /// - /// If this result is a success then this is a no-op and the original success is retained. - /// This is useful for handling errors. - /// - /// - /// The function that is invoked if this result is a failure. - /// - /// - /// If this result is a failure, then the result of the onFailure function; - /// otherwise the original error. - /// - public abstract Result Catch(Func> onFailure); + /// + /// The function that is invoked if this result is a failure. + /// + /// + /// If this result is a failure, then the result of the onFailure function; + /// otherwise the original error. + /// + public abstract Result Catch(Func> onFailure); - /// - /// Invokes another result generating function which takes as input the error of this result - /// if it is a failure. - /// - /// - /// If this result is a success then this is a no-op and the original success is retained. - /// This is useful for handling errors. - /// - /// - /// The function that is invoked if this result is a failure. - /// - /// - /// If this result is a failure, then the result of the onFailure function; - /// otherwise the original error. - /// - public abstract Task> Catch(Func>> onFailure); + /// + /// Invokes another result generating function which takes as input the error of this result + /// if it is a failure. + /// + /// + /// If this result is a success then this is a no-op and the original success is retained. + /// This is useful for handling errors. + /// + /// + /// The function that is invoked if this result is a failure. + /// + /// + /// If this result is a failure, then the result of the onFailure function; + /// otherwise the original error. + /// + public abstract Task> Catch(Func>> onFailure); - /// - /// Combines this result with another. - /// If both are successful then combineData is invoked; - /// else if either is a failure then combineErrors is invoked. - /// - /// - /// The type of data in the other result. - /// - /// - /// The type of data in the combined result. - /// - /// - /// The other result to be combined with this one. - /// - /// - /// The function that is invoked to combine the data when both of the results are successful. - /// - /// - /// The function that is invoked to combine the errors when either of the results is a failure. - /// - /// - /// A new . - /// - public abstract Result Combine( - Result other, - Func combineData, - Func combineErrors); + /// + /// Combines this result with another. + /// If both are successful then combineData is invoked; + /// else if either is a failure then combineErrors is invoked. + /// + /// + /// The type of data in the other result. + /// + /// + /// The type of data in the combined result. + /// + /// + /// The other result to be combined with this one. + /// + /// + /// The function that is invoked to combine the data when both of the results are successful. + /// + /// + /// The function that is invoked to combine the errors when either of the results is a failure. + /// + /// + /// A new . + /// + public abstract Result Combine( + Result other, + Func combineData, + Func combineErrors); - /// - /// Used to match on whether this result is a success or a failure. - /// - /// - /// The type that is returned. - /// - /// - /// The function that is invoked if this result represents a success. - /// - /// - /// The function that is invoked if this result represents a failure. - /// - /// - /// A value that is mapped from either the data or the error. - /// - public abstract T Match(Func onSuccess, Func onFailure); + /// + /// Used to match on whether this result is a success or a failure. + /// + /// + /// The type that is returned. + /// + /// + /// The function that is invoked if this result represents a success. + /// + /// + /// The function that is invoked if this result represents a failure. + /// + /// + /// A value that is mapped from either the data or the error. + /// + public abstract T Match(Func onSuccess, Func onFailure); - /// - /// Invokes the specified action if the result is a failure and returns the original result. - /// - /// - /// If this result is a success then this is a no-op and the original success is retained. - /// This is useful for publishing domain model notifications when an operation fails. - /// - /// - /// The action that will be invoked if this result is a failure. - /// - /// - /// The original result. - /// - public abstract Result OnFailure(Action onFailure); + /// + /// Invokes the specified action if the result is a failure and returns the original result. + /// + /// + /// If this result is a success then this is a no-op and the original success is retained. + /// This is useful for publishing domain model notifications when an operation fails. + /// + /// + /// The action that will be invoked if this result is a failure. + /// + /// + /// The original result. + /// + public abstract Result OnFailure(Action onFailure); - /// - /// Invokes the specified action if the result is a failure and returns the original result. - /// - /// - /// If this result is a success then this is a no-op and the original success is retained. - /// This is useful for publishing domain model notifications when an operation fails. - /// - /// - /// The action that will be invoked if this result is a failure. - /// - /// - /// The original result. - /// - public abstract Result OnFailure(Action onFailure); + /// + /// Invokes the specified action if the result is a failure and returns the original result. + /// + /// + /// If this result is a success then this is a no-op and the original success is retained. + /// This is useful for publishing domain model notifications when an operation fails. + /// + /// + /// The action that will be invoked if this result is a failure. + /// + /// + /// The original result. + /// + public abstract Result OnFailure(Action onFailure); - /// - /// Invokes the specified action if the result is a failure and returns the original result. - /// - /// - /// If this result is a success then this is a no-op and the original success is retained. - /// This is useful for publishing domain model notifications when an operation fails. - /// - /// - /// The asynchronous action that will be invoked if this result is a failure. - /// - /// - /// The original result. - /// - public abstract Task> OnFailure(Func onFailure); + /// + /// Invokes the specified action if the result is a failure and returns the original result. + /// + /// + /// If this result is a success then this is a no-op and the original success is retained. + /// This is useful for publishing domain model notifications when an operation fails. + /// + /// + /// The asynchronous action that will be invoked if this result is a failure. + /// + /// + /// The original result. + /// + public abstract Task> OnFailure(Func onFailure); - /// - /// Invokes the specified action if the result is a failure and returns the original result. - /// - /// - /// If this result is a success then this is a no-op and the original success is retained. - /// This is useful for publishing domain model notifications when an operation fails. - /// - /// - /// The asynchronous action that will be invoked if this result is a failure. - /// - /// - /// The original result. - /// - public abstract Task> OnFailure(Func onFailure); + /// + /// Invokes the specified action if the result is a failure and returns the original result. + /// + /// + /// If this result is a success then this is a no-op and the original success is retained. + /// This is useful for publishing domain model notifications when an operation fails. + /// + /// + /// The asynchronous action that will be invoked if this result is a failure. + /// + /// + /// The original result. + /// + public abstract Task> OnFailure(Func onFailure); - /// - /// Invokes the specified action if the result is a success and returns the original result. - /// - /// - /// If this result is a failure then this is a no-op and the original failure is retained. - /// This is useful for publishing domain model notifications when an operation succeeds. - /// - /// - /// The action that will be invoked if this result represents a success. - /// - /// - /// The original result. - /// - public abstract Result OnSuccess(Action onSuccess); + /// + /// Invokes the specified action if the result is a success and returns the original result. + /// + /// + /// If this result is a failure then this is a no-op and the original failure is retained. + /// This is useful for publishing domain model notifications when an operation succeeds. + /// + /// + /// The action that will be invoked if this result represents a success. + /// + /// + /// The original result. + /// + public abstract Result OnSuccess(Action onSuccess); - /// - /// Invokes the specified action if the result is a success and returns the original result. - /// - /// - /// If this result is a failure then this is a no-op and the original failure is retained. - /// This is useful for publishing domain model notifications when an operation succeeds. - /// - /// - /// The action that will be invoked if this result represents a success. - /// - /// - /// The original result. - /// - public abstract Result OnSuccess(Action onSuccess); + /// + /// Invokes the specified action if the result is a success and returns the original result. + /// + /// + /// If this result is a failure then this is a no-op and the original failure is retained. + /// This is useful for publishing domain model notifications when an operation succeeds. + /// + /// + /// The action that will be invoked if this result represents a success. + /// + /// + /// The original result. + /// + public abstract Result OnSuccess(Action onSuccess); - /// - /// Invokes the specified action if the result is a success and returns the original result. - /// - /// - /// If this result is a failure then this is a no-op and the original failure is retained. - /// This is useful for publishing domain model notifications when an operation succeeds. - /// - /// - /// The asynchronous action that will be invoked if this result represents a success. - /// - /// - /// The original result. - /// - public abstract Task> OnSuccess(Func onSuccess); + /// + /// Invokes the specified action if the result is a success and returns the original result. + /// + /// + /// If this result is a failure then this is a no-op and the original failure is retained. + /// This is useful for publishing domain model notifications when an operation succeeds. + /// + /// + /// The asynchronous action that will be invoked if this result represents a success. + /// + /// + /// The original result. + /// + public abstract Task> OnSuccess(Func onSuccess); - /// - /// Invokes the specified action if the result is a success and returns the original result. - /// - /// - /// If this result is a failure then this is a no-op and the original failure is retained. - /// This is useful for publishing domain model notifications when an operation succeeds. - /// - /// - /// The asynchronous action that will be invoked if this result represents a success. - /// - /// - /// The original result. - /// - public abstract Task> OnSuccess(Func onSuccess); + /// + /// Invokes the specified action if the result is a success and returns the original result. + /// + /// + /// If this result is a failure then this is a no-op and the original failure is retained. + /// This is useful for publishing domain model notifications when an operation succeeds. + /// + /// + /// The asynchronous action that will be invoked if this result represents a success. + /// + /// + /// The original result. + /// + public abstract Task> OnSuccess(Func onSuccess); - /// - /// Projects a successful result's data from one type to another. - /// - /// - /// If this result is a failure then this is a no-op. - /// - /// - /// The type of data in the new result. - /// - /// - /// The function that is invoked to select the data. - /// - /// - /// A new result containing either; the output of the selector function - /// if this result is a success, otherwise the original failure. - /// - public abstract Result Select(Func selector); + /// + /// Projects a successful result's data from one type to another. + /// + /// + /// If this result is a failure then this is a no-op. + /// + /// + /// The type of data in the new result. + /// + /// + /// The function that is invoked to select the data. + /// + /// + /// A new result containing either; the output of the selector function + /// if this result is a success, otherwise the original failure. + /// + public abstract Result Select(Func selector); - /// - /// Projects a successful result's data from one type to another. - /// - /// - /// If this result is a failure then this is a no-op. - /// - /// - /// The type of data in the new result. - /// - /// - /// The asynchronous function that is invoked to select the data. - /// - /// - /// A new result containing either; the output of the selector function - /// if this result is a success, otherwise the original failure. - /// - public abstract Task> Select(Func> selector); + /// + /// Projects a successful result's data from one type to another. + /// + /// + /// If this result is a failure then this is a no-op. + /// + /// + /// The type of data in the new result. + /// + /// + /// The asynchronous function that is invoked to select the data. + /// + /// + /// A new result containing either; the output of the selector function + /// if this result is a success, otherwise the original failure. + /// + public abstract Task> Select(Func> selector); - /// - /// Projects a failed result's error from one type to another. - /// - /// - /// If this result is a success then this is a no-op. - /// - /// - /// The function that is invoked to select the error. - /// - /// - /// A new result containing either; the output of the selector function - /// if this result is a failure, otherwise the original success. - /// - public abstract Result SelectError(Func selector); + /// + /// Projects a failed result's error from one type to another. + /// + /// + /// If this result is a success then this is a no-op. + /// + /// + /// The function that is invoked to select the error. + /// + /// + /// A new result containing either; the output of the selector function + /// if this result is a failure, otherwise the original success. + /// + public abstract Result SelectError(Func selector); - /// - /// Projects a failed result's error from one type to another. - /// - /// - /// If this result is a success then this is a no-op. - /// - /// - /// The asynchronous function that is invoked to select the error. - /// - /// - /// A new result containing either; the output of the selector function - /// if this result is a failure, otherwise the original success. - /// - public abstract Task> SelectError(Func> selector); + /// + /// Projects a failed result's error from one type to another. + /// + /// + /// If this result is a success then this is a no-op. + /// + /// + /// The asynchronous function that is invoked to select the error. + /// + /// + /// A new result containing either; the output of the selector function + /// if this result is a failure, otherwise the original success. + /// + public abstract Task> SelectError(Func> selector); - /// - /// Invokes another result generating function which takes as input the data of this result - /// if it is a success. - /// - /// - /// If this result is a failure then this is a no-op and the original failure is retained. - /// This is useful for chaining serial operations together that return results. - /// - /// - /// The type of data in the new result. - /// - /// - /// The function that is invoked if this result is a success. - /// - /// - /// If this result is a success, then the result of onSuccess function; - /// otherwise the original error. - /// - public abstract Result Then(Func> onSuccess); + /// + /// Invokes another result generating function which takes as input the data of this result + /// if it is a success. + /// + /// + /// If this result is a failure then this is a no-op and the original failure is retained. + /// This is useful for chaining serial operations together that return results. + /// + /// + /// The type of data in the new result. + /// + /// + /// The function that is invoked if this result is a success. + /// + /// + /// If this result is a success, then the result of onSuccess function; + /// otherwise the original error. + /// + public abstract Result Then(Func> onSuccess); - /// - /// Invokes another result generating function which takes as input the data of this result - /// if it is a success. - /// - /// - /// If this result is a failure then this is a no-op and the original failure is retained. - /// This is useful for chaining serial operations together that return results. - /// - /// - /// The type of data in the new result. - /// - /// - /// The asynchronous function that is invoked if this result is a success. - /// - /// - /// If this result is a success, then the result of onSuccess function; - /// otherwise the original error. - /// - public abstract Task> Then(Func>> onSuccess); - } -} \ No newline at end of file + /// + /// Invokes another result generating function which takes as input the data of this result + /// if it is a success. + /// + /// + /// If this result is a failure then this is a no-op and the original failure is retained. + /// This is useful for chaining serial operations together that return results. + /// + /// + /// The type of data in the new result. + /// + /// + /// The asynchronous function that is invoked if this result is a success. + /// + /// + /// If this result is a success, then the result of onSuccess function; + /// otherwise the original error. + /// + public abstract Task> Then(Func>> onSuccess); +} diff --git a/src/Winton.DomainModelling.Abstractions/Success.cs b/src/Winton.DomainModelling.Abstractions/Success.cs index 35e56ca..09f5d1a 100644 --- a/src/Winton.DomainModelling.Abstractions/Success.cs +++ b/src/Winton.DomainModelling.Abstractions/Success.cs @@ -5,168 +5,167 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +/// +/// Contains static methods to make working with void results easier. +/// +[SuppressMessage( + "StyleCop.CSharp.MaintainabilityRules", + "SA1402:FileMayOnlyContainASingleType", + Justification = "Generic and non-generic version of the same class.")] +public static class Success { /// - /// Contains static methods to make working with void results easier. + /// Creates a to represent a successful result + /// that does not contain any data. /// - [SuppressMessage( - "StyleCop.CSharp.MaintainabilityRules", - "SA1402:FileMayOnlyContainASingleType", - Justification = "Generic and non-generic version of the same class.")] - public static class Success - { - /// - /// Creates a to represent a successful result - /// that does not contain any data. - /// - /// - /// A new . - /// - public static Success Unit() - { - return new Success(DomainModelling.Unit.Value); - } + /// + /// A new . + /// + public static Success Unit() + { + return new Success(DomainModelling.Unit.Value); + } +} + +/// +/// +/// A result indicating a success. +/// +[SuppressMessage( + "StyleCop.CSharp.MaintainabilityRules", + "SA1402:FileMayOnlyContainASingleType", + Justification = "Generic and non-generic version of the same class.")] +public sealed class Success : Result +{ + /// + /// Initializes a new instance of the class. + /// + /// The data encapsulated by the result. + /// A new instance of . + public Success(TData data) + { + Data = data; } - /// /// - /// A result indicating a success. + /// Gets the data. /// - [SuppressMessage( - "StyleCop.CSharp.MaintainabilityRules", - "SA1402:FileMayOnlyContainASingleType", - Justification = "Generic and non-generic version of the same class.")] - public sealed class Success : Result - { - /// - /// Initializes a new instance of the class. - /// - /// The data encapsulated by the result. - /// A new instance of . - public Success(TData data) - { - Data = data; - } - - /// - /// Gets the data. - /// - public TData Data { get; } - - /// - public override Result Catch(Func> onFailure) - { - return new Success(Data); - } - - /// - public override Task> Catch(Func>> onFailure) - { - return Task.FromResult>(new Success(Data)); - } - - /// - public override Result Combine( - Result other, - Func combineData, - Func combineErrors) - { - return other.Match>( - otherData => new Success(combineData(Data, otherData)), - otherError => new Failure(otherError)); - } - - /// - public override T Match(Func onSuccess, Func onFailure) - { - return onSuccess(Data); - } - - /// - public override Result OnFailure(Action onFailure) - { - return this; - } - - /// - public override Result OnFailure(Action onFailure) - { - return this; - } - - /// - public override Task> OnFailure(Func onFailure) - { - return Task.FromResult>(this); - } - - /// - public override Task> OnFailure(Func onFailure) - { - return Task.FromResult>(this); - } - - /// - public override Result OnSuccess(Action onSuccess) - { - return OnSuccess(_ => onSuccess()); - } - - /// - public override Result OnSuccess(Action onSuccess) - { - onSuccess(Data); - return this; - } - - /// - public override async Task> OnSuccess(Func onSuccess) - { - return await OnSuccess(_ => onSuccess()); - } - - /// - public override async Task> OnSuccess(Func onSuccess) - { - await onSuccess(Data); - return this; - } - - /// - public override Result Select(Func selector) - { - return new Success(selector(Data)); - } - - /// - public override async Task> Select(Func> selector) - { - return new Success(await selector(Data)); - } - - /// - public override Result SelectError(Func selector) - { - return new Success(Data); - } - - /// - public override Task> SelectError(Func> selector) - { - return Task.FromResult>(new Success(Data)); - } - - /// - public override Result Then(Func> onSuccess) - { - return onSuccess(Data); - } - - /// - public override Task> Then(Func>> onSuccess) - { - return onSuccess(Data); - } + public TData Data { get; } + + /// + public override Result Catch(Func> onFailure) + { + return new Success(Data); + } + + /// + public override Task> Catch(Func>> onFailure) + { + return Task.FromResult>(new Success(Data)); + } + + /// + public override Result Combine( + Result other, + Func combineData, + Func combineErrors) + { + return other.Match>( + otherData => new Success(combineData(Data, otherData)), + otherError => new Failure(otherError)); + } + + /// + public override T Match(Func onSuccess, Func onFailure) + { + return onSuccess(Data); + } + + /// + public override Result OnFailure(Action onFailure) + { + return this; + } + + /// + public override Result OnFailure(Action onFailure) + { + return this; + } + + /// + public override Task> OnFailure(Func onFailure) + { + return Task.FromResult>(this); + } + + /// + public override Task> OnFailure(Func onFailure) + { + return Task.FromResult>(this); + } + + /// + public override Result OnSuccess(Action onSuccess) + { + return OnSuccess(_ => onSuccess()); + } + + /// + public override Result OnSuccess(Action onSuccess) + { + onSuccess(Data); + return this; + } + + /// + public override async Task> OnSuccess(Func onSuccess) + { + return await OnSuccess(_ => onSuccess()); + } + + /// + public override async Task> OnSuccess(Func onSuccess) + { + await onSuccess(Data); + return this; + } + + /// + public override Result Select(Func selector) + { + return new Success(selector(Data)); + } + + /// + public override async Task> Select(Func> selector) + { + return new Success(await selector(Data)); + } + + /// + public override Result SelectError(Func selector) + { + return new Success(Data); + } + + /// + public override Task> SelectError(Func> selector) + { + return Task.FromResult>(new Success(Data)); + } + + /// + public override Result Then(Func> onSuccess) + { + return onSuccess(Data); + } + + /// + public override Task> Then(Func>> onSuccess) + { + return onSuccess(Data); } } \ No newline at end of file diff --git a/src/Winton.DomainModelling.Abstractions/UnauthorizedError.cs b/src/Winton.DomainModelling.Abstractions/UnauthorizedError.cs index 9bbde3e..6e37ba3 100644 --- a/src/Winton.DomainModelling.Abstractions/UnauthorizedError.cs +++ b/src/Winton.DomainModelling.Abstractions/UnauthorizedError.cs @@ -1,22 +1,21 @@ // Copyright (c) Winton. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +/// +/// +/// An error indicating that the action being performed is not authorized. +/// +public class UnauthorizedError : Error { - /// /// - /// An error indicating that the action being performed is not authorized. + /// Initializes a new instance of the class. /// - public class UnauthorizedError : Error + /// The detail that describes the error. + /// A new instance of . + public UnauthorizedError(string detail) + : base("Unauthorized", detail) { - /// - /// Initializes a new instance of the class. - /// - /// The detail that describes the error. - /// A new instance of . - public UnauthorizedError(string detail) - : base("Unauthorized", detail) - { - } } -} \ No newline at end of file +} diff --git a/src/Winton.DomainModelling.Abstractions/UnauthorizedException.cs b/src/Winton.DomainModelling.Abstractions/UnauthorizedException.cs deleted file mode 100644 index 6824131..0000000 --- a/src/Winton.DomainModelling.Abstractions/UnauthorizedException.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Winton. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - -using System; - -namespace Winton.DomainModelling -{ - /// - /// - /// An error indicating that the action being performed is not authorized. - /// - [Obsolete("Prefer to return results with an UnauthorizedError instead.", false)] - public class UnauthorizedException : DomainException - { - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public UnauthorizedException(string message) - : base(message) - { - } - } -} \ No newline at end of file diff --git a/src/Winton.DomainModelling.Abstractions/Unit.cs b/src/Winton.DomainModelling.Abstractions/Unit.cs index d28d312..ddd9b94 100644 --- a/src/Winton.DomainModelling.Abstractions/Unit.cs +++ b/src/Winton.DomainModelling.Abstractions/Unit.cs @@ -3,110 +3,109 @@ using System; -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +/// +/// +/// /// +/// +/// Represents a void type, since is not a valid return type in C#. +/// +public struct Unit : IEquatable, IComparable, IComparable { - /// - /// - /// /// /// - /// Represents a void type, since is not a valid return type in C#. + /// Gets the default and only value of the type. /// - public struct Unit : IEquatable, IComparable, IComparable - { - /// - /// Gets the default and only value of the type. - /// - public static Unit Value { get; } = default; + public static Unit Value { get; } = default; - /// - /// Determines whether the first is equal to the second . - /// - /// - /// The first . - /// - /// - /// The second . - /// - /// - /// true, as only has a single value and all instances are therefore equal. - /// - public static bool operator ==(Unit first, Unit second) - { - return true; - } + /// + /// Determines whether the first is equal to the second . + /// + /// + /// The first . + /// + /// + /// The second . + /// + /// + /// true, as only has a single value and all instances are therefore equal. + /// + public static bool operator ==(Unit first, Unit second) + { + return true; + } - /// - /// Determines whether the first is not equal to the second . - /// - /// - /// The first . - /// - /// - /// The second . - /// - /// - /// false, as only has a single value and all instances are therefore equal. - /// - public static bool operator !=(Unit first, Unit second) - { - return false; - } + /// + /// Determines whether the first is not equal to the second . + /// + /// + /// The first . + /// + /// + /// The second . + /// + /// + /// false, as only has a single value and all instances are therefore equal. + /// + public static bool operator !=(Unit first, Unit second) + { + return false; + } - /// - /// - /// Compares the current object with another object of the same type. - /// - /// - /// An object to compare with this object. - /// - /// - /// Zero, as only has a single value and all instances are therefore equal. - /// - public int CompareTo(Unit other) - { - return 0; - } + /// + /// + /// Compares the current object with another object of the same type. + /// + /// + /// An object to compare with this object. + /// + /// + /// Zero, as only has a single value and all instances are therefore equal. + /// + public int CompareTo(Unit other) + { + return 0; + } - /// - /// - /// Determines whether this is equal to another . - /// - /// - /// A to compare with this . - /// - /// - /// true, as only has a single value and all instances are therefore equal. - /// - public bool Equals(Unit other) - { - return true; - } + /// + /// + /// Determines whether this is equal to another . + /// + /// + /// A to compare with this . + /// + /// + /// true, as only has a single value and all instances are therefore equal. + /// + public bool Equals(Unit other) + { + return true; + } - /// - public override bool Equals(object obj) - { - return obj is Unit; - } + /// + public override bool Equals(object? obj) + { + return obj is Unit; + } - /// - public override int GetHashCode() - { - return 0; - } + /// + public override int GetHashCode() + { + return 0; + } - /// - /// - /// Compares the current object with another object. - /// - /// - /// A to compare with this . - /// - /// - /// Zero, as only has a single value and all instances are therefore equal. - /// - int IComparable.CompareTo(object obj) - { - return 0; - } + /// + /// + /// Compares the current object with another object. + /// + /// + /// A to compare with this . + /// + /// + /// Zero, as only has a single value and all instances are therefore equal. + /// + int IComparable.CompareTo(object? obj) + { + return 0; } -} \ No newline at end of file +} diff --git a/src/Winton.DomainModelling.Abstractions/Winton.DomainModelling.Abstractions.csproj b/src/Winton.DomainModelling.Abstractions/Winton.DomainModelling.Abstractions.csproj index c99f9f0..b8d8baa 100644 --- a/src/Winton.DomainModelling.Abstractions/Winton.DomainModelling.Abstractions.csproj +++ b/src/Winton.DomainModelling.Abstractions/Winton.DomainModelling.Abstractions.csproj @@ -15,7 +15,7 @@ https://github.com/wintoncode/Winton.DomainModelling.Abstractions Winton.DomainModelling snupkg - netstandard1.0 + netstandard2.1 Winton Domain Modelling Abstractions diff --git a/test/Winton.DomainModelling.Abstractions.Tests/AsyncResultExtensionsTests.cs b/test/Winton.DomainModelling.Abstractions.Tests/AsyncResultExtensionsTests.cs index 33465ec..bd6314e 100644 --- a/test/Winton.DomainModelling.Abstractions.Tests/AsyncResultExtensionsTests.cs +++ b/test/Winton.DomainModelling.Abstractions.Tests/AsyncResultExtensionsTests.cs @@ -7,13 +7,13 @@ using FluentAssertions; using Xunit; -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +public class AsyncResultExtensionsTests { - public class AsyncResultExtensionsTests + public sealed class Combine : AsyncResultExtensionsTests { - public sealed class Combine : AsyncResultExtensionsTests - { - public static IEnumerable TestCases => new List + public static IEnumerable TestCases => new List { new object[] { @@ -41,40 +41,40 @@ public sealed class Combine : AsyncResultExtensionsTests } }; - [Theory] - [MemberData(nameof(TestCases))] - private async Task ShouldAwaitTheResultAndCallCombine( - Task> resultTask, - Result other, - Result expected) - { - Result combined = await resultTask.Combine( - other, - (i, j) => i + j, - (error, otherError) => new Error("Error", $"{error.Detail}-{otherError.Detail}")); + [Theory] + [MemberData(nameof(TestCases))] + private async Task ShouldAwaitTheResultAndCallCombine( + Task> resultTask, + Result other, + Result expected) + { + Result combined = await resultTask.Combine( + other, + (i, j) => i + j, + (error, otherError) => new Error("Error", $"{error.Detail}-{otherError.Detail}")); - combined.Should().BeEquivalentTo(expected, options => options.RespectingRuntimeTypes()); - } + combined.Should().BeEquivalentTo(expected, options => options.RespectingRuntimeTypes()); + } - [Theory] - [MemberData(nameof(TestCases))] - private async Task ShouldAwaitTheResultAndTheOtherResultAndCallCombine( - Task> resultTask, - Result other, - Result expected) - { - Result combined = await resultTask.Combine( - Task.FromResult(other), - (i, j) => i + j, - (error, otherError) => new Error("Error", $"{error.Detail}-{otherError.Detail}")); + [Theory] + [MemberData(nameof(TestCases))] + private async Task ShouldAwaitTheResultAndTheOtherResultAndCallCombine( + Task> resultTask, + Result other, + Result expected) + { + Result combined = await resultTask.Combine( + Task.FromResult(other), + (i, j) => i + j, + (error, otherError) => new Error("Error", $"{error.Detail}-{otherError.Detail}")); - combined.Should().BeEquivalentTo(expected, options => options.RespectingRuntimeTypes()); - } + combined.Should().BeEquivalentTo(expected, options => options.RespectingRuntimeTypes()); } + } - public sealed class Match : AsyncResultExtensionsTests - { - public static IEnumerable TestCases => new List + public sealed class Match : AsyncResultExtensionsTests + { + public static IEnumerable TestCases => new List { new object[] { @@ -88,19 +88,19 @@ public sealed class Match : AsyncResultExtensionsTests } }; - [Theory] - [MemberData(nameof(TestCases))] - private async Task ShouldAwaitTheResultAndCallMatch(Task> resultTask, bool expected) - { - bool isSuccess = await resultTask.Match(_ => true, _ => false); + [Theory] + [MemberData(nameof(TestCases))] + private async Task ShouldAwaitTheResultAndCallMatch(Task> resultTask, bool expected) + { + bool isSuccess = await resultTask.Match(_ => true, _ => false); - isSuccess.Should().Be(expected); - } + isSuccess.Should().Be(expected); } + } - public sealed class OnSuccess : AsyncResultExtensionsTests - { - public static IEnumerable TestCases => new List + public sealed class OnSuccess : AsyncResultExtensionsTests + { + public static IEnumerable TestCases => new List { new object[] { @@ -114,70 +114,70 @@ public sealed class OnSuccess : AsyncResultExtensionsTests } }; - [Theory] - [MemberData(nameof(TestCases))] - private async Task ShouldAwaitTheResultAndCallAsyncOnSuccess(Task> resultTask, bool expected) + [Theory] + [MemberData(nameof(TestCases))] + private async Task ShouldAwaitTheResultAndCallAsyncOnSuccess(Task> resultTask, bool expected) + { + var invoked = false; + + async Task OnSuccess() { - var invoked = false; + await Task.Yield(); + invoked = true; + } - async Task OnSuccess() - { - await Task.Yield(); - invoked = true; - } + await resultTask.OnSuccess(OnSuccess); - await resultTask.OnSuccess(OnSuccess); + invoked.Should().Be(expected); + } - invoked.Should().Be(expected); - } + [Theory] + [MemberData(nameof(TestCases))] + private async Task ShouldAwaitTheResultAndCallAsyncOnSuccessWithParameter( + Task> resultTask, + bool expected) + { + var invoked = false; - [Theory] - [MemberData(nameof(TestCases))] - private async Task ShouldAwaitTheResultAndCallAsyncOnSuccessWithParameter( - Task> resultTask, - bool expected) + async Task OnSuccess(int i) { - var invoked = false; - - async Task OnSuccess(int i) - { - await Task.Yield(); - invoked = true; - } + await Task.Yield(); + invoked = true; + } - await resultTask.OnSuccess(OnSuccess); + await resultTask.OnSuccess(OnSuccess); - invoked.Should().Be(expected); - } + invoked.Should().Be(expected); + } - [Theory] - [MemberData(nameof(TestCases))] - private async Task ShouldAwaitTheResultAndCallOnSuccess(Task> resultTask, bool expected) - { - var invoked = false; + [Theory] + [MemberData(nameof(TestCases))] + private async Task ShouldAwaitTheResultAndCallOnSuccess(Task> resultTask, bool expected) + { + var invoked = false; - await resultTask.OnSuccess(() => invoked = true); + await resultTask.OnSuccess(() => invoked = true); - invoked.Should().Be(expected); - } + invoked.Should().Be(expected); + } - [Theory] - [MemberData(nameof(TestCases))] - private async Task ShouldAwaitTheResultAndCallOnSuccessWithParameter( - Task> resultTask, - bool expected) - { - var invoked = false; + [Theory] + [MemberData(nameof(TestCases))] + private async Task ShouldAwaitTheResultAndCallOnSuccessWithParameter( + Task> resultTask, + bool expected) + { + var invoked = false; - await resultTask.OnSuccess(i => invoked = true); + await resultTask.OnSuccess(i => invoked = true); - invoked.Should().Be(expected); - } + invoked.Should().Be(expected); } + } - public sealed class Select : AsyncResultExtensionsTests - { - public static IEnumerable AsyncTestCases => new List + public sealed class Select : AsyncResultExtensionsTests + { + public static IEnumerable AsyncTestCases => new List { new object[] { @@ -193,7 +193,7 @@ public sealed class Select : AsyncResultExtensionsTests } }; - public static IEnumerable TestCases => new List + public static IEnumerable TestCases => new List { new object[] { @@ -209,34 +209,34 @@ public sealed class Select : AsyncResultExtensionsTests } }; - [Theory] - [MemberData(nameof(TestCases))] - private async Task ShouldAwaitTheResultAndCallSelect( - Task> resultTask, - Func selectData, - Result expected) - { - Result result = await resultTask.Select(selectData); + [Theory] + [MemberData(nameof(TestCases))] + private async Task ShouldAwaitTheResultAndCallSelect( + Task> resultTask, + Func selectData, + Result expected) + { + Result result = await resultTask.Select(selectData); - result.Should().BeEquivalentTo(expected, options => options.RespectingRuntimeTypes()); - } + result.Should().BeEquivalentTo(expected, options => options.RespectingRuntimeTypes()); + } - [Theory] - [MemberData(nameof(AsyncTestCases))] - private async Task ShouldAwaitTheResultAndCallSelectWithAsynchronousFunc( - Task> resultTask, - Func> selectData, - Result expected) - { - Result result = await resultTask.Select(selectData); + [Theory] + [MemberData(nameof(AsyncTestCases))] + private async Task ShouldAwaitTheResultAndCallSelectWithAsynchronousFunc( + Task> resultTask, + Func> selectData, + Result expected) + { + Result result = await resultTask.Select(selectData); - result.Should().BeEquivalentTo(expected, options => options.RespectingRuntimeTypes()); - } + result.Should().BeEquivalentTo(expected, options => options.RespectingRuntimeTypes()); } + } - public sealed class Then : AsyncResultExtensionsTests - { - public static IEnumerable AsyncTestCases => new List + public sealed class Then : AsyncResultExtensionsTests + { + public static IEnumerable AsyncTestCases => new List { new object[] { @@ -252,7 +252,7 @@ public sealed class Then : AsyncResultExtensionsTests } }; - public static IEnumerable TestCases => new List + public static IEnumerable TestCases => new List { new object[] { @@ -268,29 +268,28 @@ public sealed class Then : AsyncResultExtensionsTests } }; - [Theory] - [MemberData(nameof(TestCases))] - private async Task ShouldAwaitTheResultAndCallThen( - Task> resultTask, - Func> onSuccess, - Result expected) - { - Result result = await resultTask.Then(onSuccess); + [Theory] + [MemberData(nameof(TestCases))] + private async Task ShouldAwaitTheResultAndCallThen( + Task> resultTask, + Func> onSuccess, + Result expected) + { + Result result = await resultTask.Then(onSuccess); - result.Should().BeEquivalentTo(expected, options => options.RespectingRuntimeTypes()); - } + result.Should().BeEquivalentTo(expected, options => options.RespectingRuntimeTypes()); + } - [Theory] - [MemberData(nameof(AsyncTestCases))] - private async Task ShouldAwaitTheResultAndCallThenWithAsynchronousFunc( - Task> resultTask, - Func>> onSuccess, - Result expected) - { - Result result = await resultTask.Then(onSuccess); + [Theory] + [MemberData(nameof(AsyncTestCases))] + private async Task ShouldAwaitTheResultAndCallThenWithAsynchronousFunc( + Task> resultTask, + Func>> onSuccess, + Result expected) + { + Result result = await resultTask.Then(onSuccess); - result.Should().BeEquivalentTo(expected, options => options.RespectingRuntimeTypes()); - } + result.Should().BeEquivalentTo(expected, options => options.RespectingRuntimeTypes()); } } } \ No newline at end of file diff --git a/test/Winton.DomainModelling.Abstractions.Tests/DomainExceptionTests.cs b/test/Winton.DomainModelling.Abstractions.Tests/DomainExceptionTests.cs deleted file mode 100644 index 8d1fdbe..0000000 --- a/test/Winton.DomainModelling.Abstractions.Tests/DomainExceptionTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Winton. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - -using System; -using System.Diagnostics.CodeAnalysis; -using FluentAssertions; -using Xunit; -#pragma warning disable 618 - -namespace Winton.DomainModelling -{ - public class DomainExceptionTests - { - public sealed class Message : DomainExceptionTests - { - [Fact] - private void ShouldBeObsolete() - { - typeof(DomainException) - .Should() - .BeDecoratedWith(a => a.Message == "Prefer to return results with an Error instead."); - } - - [Theory] - [InlineData("Test")] - [InlineData("Foo")] - private void ShouldSetMessageFromConstructor(string message) - { - var exception = new DomainException(message); - - exception.Message.Should().Be(message); - } - } - } -} \ No newline at end of file diff --git a/test/Winton.DomainModelling.Abstractions.Tests/EntityNotFoundExceptionTests.cs b/test/Winton.DomainModelling.Abstractions.Tests/EntityNotFoundExceptionTests.cs deleted file mode 100644 index ef86b01..0000000 --- a/test/Winton.DomainModelling.Abstractions.Tests/EntityNotFoundExceptionTests.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Winton. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - -using System; -using FluentAssertions; -using Xunit; -#pragma warning disable 618 - -namespace Winton.DomainModelling -{ - public class EntityNotFoundExceptionTests - { - [Fact] - private void ShouldBeObsolete() - { - typeof(EntityNotFoundException) - .Should() - .BeDecoratedWith(a => a.Message == "Prefer to return results with a NotFoundError instead."); - } - - public sealed class Message : EntityNotFoundExceptionTests - { - [Fact] - private void ShouldSetMessageBasedOnTypeOfEntity() - { - EntityNotFoundException exception = EntityNotFoundException.Create(); - - exception.Message.Should().Be("The specified TestEntity could not be found."); - } - - // ReSharper disable once ClassNeverInstantiated.Local - private sealed class TestEntity : Entity - { - public TestEntity(int id) - : base(id) - { - } - } - } - } -} \ No newline at end of file diff --git a/test/Winton.DomainModelling.Abstractions.Tests/EntityTests.cs b/test/Winton.DomainModelling.Abstractions.Tests/EntityTests.cs index 7c01567..e1c1830 100644 --- a/test/Winton.DomainModelling.Abstractions.Tests/EntityTests.cs +++ b/test/Winton.DomainModelling.Abstractions.Tests/EntityTests.cs @@ -4,148 +4,147 @@ using FluentAssertions; using Xunit; -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +public class EntityTests { - public class EntityTests + public sealed class Equality : EntityTests { - public sealed class Equality : EntityTests + [Fact] + private void ShouldBeFalseWhenIdHasDefaultValue() { - [Fact] - private void ShouldBeFalseWhenIdHasDefaultValue() - { - var entityA1 = new EntityA(default); - var entityA2 = new EntityA(default); + var entityA1 = new EntityA(default); + var entityA2 = new EntityA(default); - bool equal = entityA1.Equals(entityA2); + bool equal = entityA1.Equals(entityA2); - equal.Should().BeFalse(); - } + equal.Should().BeFalse(); + } - [Fact] - private void ShouldBeFalseWhenIdsAreDifferentForDifferentReferences() - { - var entityA1 = new EntityA(1); - var entityA2 = new EntityA(2); + [Fact] + private void ShouldBeFalseWhenIdsAreDifferentForDifferentReferences() + { + var entityA1 = new EntityA(1); + var entityA2 = new EntityA(2); - bool equal = entityA1.Equals(entityA2); + bool equal = entityA1.Equals(entityA2); - equal.Should().BeFalse(); - } + equal.Should().BeFalse(); + } - [Fact] - private void ShouldBeFalseWhenOtherIsDifferentTypeOfEntity() - { - var entityA = new EntityA(1); - var entityB = new EntityB(1); + [Fact] + private void ShouldBeFalseWhenOtherIsDifferentTypeOfEntity() + { + var entityA = new EntityA(1); + var entityB = new EntityB(1); - bool equal = entityA.Equals(entityB); + bool equal = entityA.Equals(entityB); - equal.Should().BeFalse(); - } + equal.Should().BeFalse(); + } - [Fact] - private void ShouldBeFalseWhenOtherIsNull() - { - var entityA1 = new EntityA(1); + [Fact] + private void ShouldBeFalseWhenOtherIsNull() + { + var entityA1 = new EntityA(1); - bool equal = entityA1.Equals(null); + bool equal = entityA1.Equals(null); - equal.Should().BeFalse(); - } + equal.Should().BeFalse(); + } - [Fact] - private void ShouldBeTrueWhenIdsAreSameForDifferentReferences() - { - var entityA1 = new EntityA(1); - var entityA2 = new EntityA(1); + [Fact] + private void ShouldBeTrueWhenIdsAreSameForDifferentReferences() + { + var entityA1 = new EntityA(1); + var entityA2 = new EntityA(1); - bool equal = entityA1.Equals(entityA2); + bool equal = entityA1.Equals(entityA2); - equal.Should().BeTrue(); - } + equal.Should().BeTrue(); + } - [Fact] - private void ShouldBeTrueWhenReferencesAreSame() - { - var entityA1 = new EntityA(1); + [Fact] + private void ShouldBeTrueWhenReferencesAreSame() + { + var entityA1 = new EntityA(1); - bool equal = entityA1.Equals(entityA1); + bool equal = entityA1.Equals(entityA1); - equal.Should().BeTrue(); - } + equal.Should().BeTrue(); } + } - public sealed class ObjectEquality : EntityTests + public sealed class ObjectEquality : EntityTests + { + [Fact] + private void ShouldBeFalseWhenIdsAreDifferentForDifferentReferences() { - [Fact] - private void ShouldBeFalseWhenIdsAreDifferentForDifferentReferences() - { - var entityA1 = new EntityA(1); - var entityA2 = new EntityA(2); + var entityA1 = new EntityA(1); + var entityA2 = new EntityA(2); - bool equal = entityA1.Equals((object)entityA2); + bool equal = entityA1.Equals((object)entityA2); - equal.Should().BeFalse(); - } + equal.Should().BeFalse(); + } - [Fact] - private void ShouldBeFalseWhenOtherIsDifferentTypeOfEntity() - { - var entityA = new EntityA(1); - var entityB = new EntityB(1); + [Fact] + private void ShouldBeFalseWhenOtherIsDifferentTypeOfEntity() + { + var entityA = new EntityA(1); + var entityB = new EntityB(1); - // ReSharper disable once SuspiciousTypeConversion.Global - bool equal = entityA.Equals((object)entityB); + // ReSharper disable once SuspiciousTypeConversion.Global + bool equal = entityA.Equals((object)entityB); - equal.Should().BeFalse(); - } + equal.Should().BeFalse(); + } - [Fact] - private void ShouldBeFalseWhenOtherIsNull() - { - var entityA1 = new EntityA(1); + [Fact] + private void ShouldBeFalseWhenOtherIsNull() + { + var entityA1 = new EntityA(1); - bool equal = entityA1.Equals((object?)null); + bool equal = entityA1.Equals((object?)null); - equal.Should().BeFalse(); - } + equal.Should().BeFalse(); + } - [Fact] - private void ShouldBeTrueWhenIdsAreSameForDifferentReferences() - { - var entityA1 = new EntityA(1); - var entityA2 = new EntityA(1); + [Fact] + private void ShouldBeTrueWhenIdsAreSameForDifferentReferences() + { + var entityA1 = new EntityA(1); + var entityA2 = new EntityA(1); - bool equal = entityA1.Equals((object)entityA2); + bool equal = entityA1.Equals((object)entityA2); - equal.Should().BeTrue(); - } + equal.Should().BeTrue(); + } - [Fact] - private void ShouldBeTrueWhenReferencesAreSame() - { - var entityA1 = new EntityA(1); + [Fact] + private void ShouldBeTrueWhenReferencesAreSame() + { + var entityA1 = new EntityA(1); - bool equal = entityA1.Equals((object)entityA1); + bool equal = entityA1.Equals((object)entityA1); - equal.Should().BeTrue(); - } + equal.Should().BeTrue(); } + } - private sealed class EntityA : Entity + private sealed class EntityA : Entity + { + public EntityA(int id) + : base(id) { - public EntityA(int id) - : base(id) - { - } } + } - private sealed class EntityB : Entity + private sealed class EntityB : Entity + { + public EntityB(int id) + : base(id) { - public EntityB(int id) - : base(id) - { - } } } -} \ No newline at end of file +} diff --git a/test/Winton.DomainModelling.Abstractions.Tests/FailureTests.cs b/test/Winton.DomainModelling.Abstractions.Tests/FailureTests.cs index 7427f2b..9ff0b19 100644 --- a/test/Winton.DomainModelling.Abstractions.Tests/FailureTests.cs +++ b/test/Winton.DomainModelling.Abstractions.Tests/FailureTests.cs @@ -5,328 +5,327 @@ using FluentAssertions; using Xunit; -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +public class FailureTests { - public class FailureTests + public sealed class Catch : FailureTests { - public sealed class Catch : FailureTests + [Fact] + private void ShouldInvokeOnFailureFunc() { - [Fact] - private void ShouldInvokeOnFailureFunc() + Result OnFailure(Error e) { - Result OnFailure(Error e) - { - return new Failure(new Error(e.Title, $"Ka-{e.Detail}")); - } + return new Failure(new Error(e.Title, $"Ka-{e.Detail}")); + } - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - Result result = failure.Catch(e => OnFailure(e)); + Result result = failure.Catch(e => OnFailure(e)); - result.Should().BeEquivalentTo(new Failure(new Error("Error", "Ka-Boom!"))); - } + result.Should().BeEquivalentTo(new Failure(new Error("Error", "Ka-Boom!"))); + } - [Fact] - private async Task ShouldInvokeOnFailureFuncAsynchronously() + [Fact] + private async Task ShouldInvokeOnFailureFuncAsynchronously() + { + async Task> OnFailure(Error e) { - async Task> OnFailure(Error e) - { - await Task.Yield(); - return new Failure(new Error(e.Title, $"Ka-{e.Detail}")); - } + await Task.Yield(); + return new Failure(new Error(e.Title, $"Ka-{e.Detail}")); + } - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - Result result = await failure.Catch(async e => await OnFailure(e)); + Result result = await failure.Catch(async e => await OnFailure(e)); - result.Should().BeEquivalentTo(new Failure(new Error("Error", "Ka-Boom!"))); - } + result.Should().BeEquivalentTo(new Failure(new Error("Error", "Ka-Boom!"))); } + } - public sealed class Combine : FailureTests + public sealed class Combine : FailureTests + { + [Fact] + private void ShouldCombineErrorsIfTheOtherResultIsAFailure() { - [Fact] - private void ShouldCombineErrorsIfTheOtherResultIsAFailure() - { - var failure = new Failure(new Error("Error", "Ka")); + var failure = new Failure(new Error("Error", "Ka")); - Result combined = failure.Combine( - new Failure(new Error("Error", "Boom!")), - (i, j) => i + j, - (error, otherError) => new Error("Error", $"{error.Detail}-{otherError.Detail}")); + Result combined = failure.Combine( + new Failure(new Error("Error", "Boom!")), + (i, j) => i + j, + (error, otherError) => new Error("Error", $"{error.Detail}-{otherError.Detail}")); - combined.Should().BeEquivalentTo(new Failure(new Error("Error", "Ka-Boom!"))); - } + combined.Should().BeEquivalentTo(new Failure(new Error("Error", "Ka-Boom!"))); + } - [Fact] - private void ShouldReturnFailureWithOriginalErrorIfOtherIsASuccess() - { - var failure = new Failure(new Error("Error", "Ka")); + [Fact] + private void ShouldReturnFailureWithOriginalErrorIfOtherIsASuccess() + { + var failure = new Failure(new Error("Error", "Ka")); - Result combined = failure.Combine( - new Success(2), - (i, j) => i + j, - (error, otherError) => new Error("Error", $"{error.Detail}-{otherError.Detail}")); + Result combined = failure.Combine( + new Success(2), + (i, j) => i + j, + (error, otherError) => new Error("Error", $"{error.Detail}-{otherError.Detail}")); - combined.Should().BeEquivalentTo(failure); - } + combined.Should().BeEquivalentTo(failure); } + } - public sealed class Match : FailureTests + public sealed class Match : FailureTests + { + [Fact] + private void ShouldInvokeOnFailureFunc() { - [Fact] - private void ShouldInvokeOnFailureFunc() - { - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - bool matchedFailure = failure.Match(_ => false, _ => true); + bool matchedFailure = failure.Match(_ => false, _ => true); - matchedFailure.Should().BeTrue(); - } + matchedFailure.Should().BeTrue(); } + } - public sealed class OnFailure : FailureTests + public sealed class OnFailure : FailureTests + { + [Fact] + private void ShouldInvokeAction() { - [Fact] - private void ShouldInvokeAction() - { - var invoked = false; - var failure = new Failure(new Error("Error", "Boom!")); + var invoked = false; + var failure = new Failure(new Error("Error", "Boom!")); - failure.OnFailure(() => invoked = true); + failure.OnFailure(() => invoked = true); - invoked.Should().BeTrue(); - } + invoked.Should().BeTrue(); + } - [Fact] - private void ShouldNotInvokeActionWithParameters() - { - var invoked = false; - var failure = new Failure(new Error("Error", "Boom!")); + [Fact] + private void ShouldNotInvokeActionWithParameters() + { + var invoked = false; + var failure = new Failure(new Error("Error", "Boom!")); - failure.OnFailure(i => invoked = true); + failure.OnFailure(i => invoked = true); - invoked.Should().BeTrue(); - } + invoked.Should().BeTrue(); + } - [Fact] - private async Task ShouldNotInvokeAsyncAction() + [Fact] + private async Task ShouldNotInvokeAsyncAction() + { + var invoked = false; + async Task OnFailure() { - var invoked = false; - async Task OnFailure() - { - await Task.Yield(); - invoked = true; - } + await Task.Yield(); + invoked = true; + } - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - await failure.OnFailure(OnFailure); + await failure.OnFailure(OnFailure); - invoked.Should().BeTrue(); - } + invoked.Should().BeTrue(); + } - [Fact] - private async Task ShouldNotInvokeAsyncActionWithParameters() + [Fact] + private async Task ShouldNotInvokeAsyncActionWithParameters() + { + var invoked = false; + async Task OnFailure(Error e) { - var invoked = false; - async Task OnFailure(Error e) - { - await Task.Yield(); - invoked = true; - } + await Task.Yield(); + invoked = true; + } - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - await failure.OnFailure(OnFailure); + await failure.OnFailure(OnFailure); - invoked.Should().BeTrue(); - } + invoked.Should().BeTrue(); + } - [Fact] - private void ShouldReturnOriginalResult() - { - var failure = new Failure(new Error("Error", "Boom!")); + [Fact] + private void ShouldReturnOriginalResult() + { + var failure = new Failure(new Error("Error", "Boom!")); - Result result = failure.OnFailure(() => { }); + Result result = failure.OnFailure(() => { }); - result.Should().BeSameAs(failure); - } + result.Should().BeSameAs(failure); + } - [Fact] - private async Task ShouldReturnOriginalResultWhenAsyncAction() + [Fact] + private async Task ShouldReturnOriginalResultWhenAsyncAction() + { + async Task OnFailure() { - async Task OnFailure() - { - await Task.Yield(); - } + await Task.Yield(); + } - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - Result result = await failure.OnFailure(OnFailure); + Result result = await failure.OnFailure(OnFailure); - result.Should().BeSameAs(failure); - } + result.Should().BeSameAs(failure); } + } - public sealed class OnSuccess : FailureTests + public sealed class OnSuccess : FailureTests + { + [Fact] + private void ShouldNotInvokeAction() { - [Fact] - private void ShouldNotInvokeAction() - { - var invoked = false; - var failure = new Failure(new Error("Error", "Boom!")); + var invoked = false; + var failure = new Failure(new Error("Error", "Boom!")); - failure.OnSuccess(() => invoked = true); + failure.OnSuccess(() => invoked = true); - invoked.Should().BeFalse(); - } + invoked.Should().BeFalse(); + } - [Fact] - private void ShouldNotInvokeActionWithParameters() - { - var invoked = false; - var failure = new Failure(new Error("Error", "Boom!")); + [Fact] + private void ShouldNotInvokeActionWithParameters() + { + var invoked = false; + var failure = new Failure(new Error("Error", "Boom!")); - failure.OnSuccess(i => invoked = true); + failure.OnSuccess(i => invoked = true); - invoked.Should().BeFalse(); - } + invoked.Should().BeFalse(); + } - [Fact] - private async Task ShouldNotInvokeAsyncAction() + [Fact] + private async Task ShouldNotInvokeAsyncAction() + { + var invoked = false; + async Task OnSuccess() { - var invoked = false; - async Task OnSuccess() - { - await Task.Yield(); - invoked = true; - } + await Task.Yield(); + invoked = true; + } - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - await failure.OnSuccess(OnSuccess); + await failure.OnSuccess(OnSuccess); - invoked.Should().BeFalse(); - } + invoked.Should().BeFalse(); + } - [Fact] - private async Task ShouldNotInvokeAsyncActionWithParameters() + [Fact] + private async Task ShouldNotInvokeAsyncActionWithParameters() + { + var invoked = false; + async Task OnSuccess(int i) { - var invoked = false; - async Task OnSuccess(int i) - { - await Task.Yield(); - invoked = true; - } + await Task.Yield(); + invoked = true; + } - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - await failure.OnSuccess(OnSuccess); + await failure.OnSuccess(OnSuccess); - invoked.Should().BeFalse(); - } + invoked.Should().BeFalse(); + } - [Fact] - private void ShouldReturnOriginalResult() - { - var failure = new Failure(new Error("Error", "Boom!")); + [Fact] + private void ShouldReturnOriginalResult() + { + var failure = new Failure(new Error("Error", "Boom!")); - Result result = failure.OnSuccess(() => { }); + Result result = failure.OnSuccess(() => { }); - result.Should().BeSameAs(failure); - } + result.Should().BeSameAs(failure); + } - [Fact] - private async Task ShouldReturnOriginalResultWhenAsyncAction() + [Fact] + private async Task ShouldReturnOriginalResultWhenAsyncAction() + { + async Task OnSuccess() { - async Task OnSuccess() - { - await Task.Yield(); - } + await Task.Yield(); + } - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - Result result = await failure.OnSuccess(OnSuccess); + Result result = await failure.OnSuccess(OnSuccess); - result.Should().BeSameAs(failure); - } + result.Should().BeSameAs(failure); } + } - public sealed class Select : FailureTests + public sealed class Select : FailureTests + { + [Fact] + private void ShouldReturnOriginalFailure() { - [Fact] - private void ShouldReturnOriginalFailure() - { - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - Result result = failure.Select(i => $"{i}"); + Result result = failure.Select(i => $"{i}"); - result.Should().BeEquivalentTo(failure); - } + result.Should().BeEquivalentTo(failure); + } - [Fact] - private async Task ShouldReturnOriginalFailureAsynchronously() - { - var failure = new Failure(new Error("Error", "Boom!")); + [Fact] + private async Task ShouldReturnOriginalFailureAsynchronously() + { + var failure = new Failure(new Error("Error", "Boom!")); - Result result = await failure.Select(i => Task.FromResult($"{i}")); + Result result = await failure.Select(i => Task.FromResult($"{i}")); - result.Should().BeEquivalentTo(failure); - } + result.Should().BeEquivalentTo(failure); } + } - public sealed class SelectError : SuccessTests + public sealed class SelectError : SuccessTests + { + [Fact] + private void ShouldProjectError() { - [Fact] - private void ShouldProjectError() - { - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - Result result = failure.SelectError(e => new NotFoundError(e.Detail)); + Result result = failure.SelectError(e => new NotFoundError(e.Detail)); - result.Should().BeEquivalentTo(new Failure(new NotFoundError("Boom!"))); - } + result.Should().BeEquivalentTo(new Failure(new NotFoundError("Boom!"))); + } - [Fact] - private async Task ShouldProjectErrorAsynchronously() - { - var failure = new Failure(new Error("Error", "Boom!")); + [Fact] + private async Task ShouldProjectErrorAsynchronously() + { + var failure = new Failure(new Error("Error", "Boom!")); - Result result = await failure.SelectError( - e => Task.FromResult(new NotFoundError(e.Detail))); + Result result = await failure.SelectError( + e => Task.FromResult(new NotFoundError(e.Detail))); - result.Should().BeEquivalentTo(new Failure(new NotFoundError("Boom!"))); - } + result.Should().BeEquivalentTo(new Failure(new NotFoundError("Boom!"))); } + } - public sealed class Then : FailureTests + public sealed class Then : FailureTests + { + [Fact] + private void ShouldReturnFailureWithOriginalError() { - [Fact] - private void ShouldReturnFailureWithOriginalError() - { - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - Result result = failure.Then(i => new Success(i + 1)); + Result result = failure.Then(i => new Success(i + 1)); - result.Should().BeEquivalentTo(failure); - } + result.Should().BeEquivalentTo(failure); + } - [Fact] - private async Task ShouldReturnFailureWithOriginalErrorAsynchronously() + [Fact] + private async Task ShouldReturnFailureWithOriginalErrorAsynchronously() + { + async Task> OnSuccess(int i) { - async Task> OnSuccess(int i) - { - await Task.Yield(); - return new Success(i + 1); - } + await Task.Yield(); + return new Success(i + 1); + } - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - Result result = await failure.Then(OnSuccess); + Result result = await failure.Then(OnSuccess); - result.Should().BeEquivalentTo(failure); - } + result.Should().BeEquivalentTo(failure); } } } \ No newline at end of file diff --git a/test/Winton.DomainModelling.Abstractions.Tests/SuccessTests.cs b/test/Winton.DomainModelling.Abstractions.Tests/SuccessTests.cs index 6751796..034e1e2 100644 --- a/test/Winton.DomainModelling.Abstractions.Tests/SuccessTests.cs +++ b/test/Winton.DomainModelling.Abstractions.Tests/SuccessTests.cs @@ -5,345 +5,344 @@ using FluentAssertions; using Xunit; -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +public class SuccessTests { - public class SuccessTests + public sealed class Catch : FailureTests { - public sealed class Catch : FailureTests + [Fact] + private void ShouldReturnOriginalSuccess() { - [Fact] - private void ShouldReturnOriginalSuccess() + Result OnFailure(Error e) { - Result OnFailure(Error e) - { - return new Failure(new Error(e.Title, $"Ka-{e.Detail}")); - } + return new Failure(new Error(e.Title, $"Ka-{e.Detail}")); + } - var success = new Success(1); + var success = new Success(1); - Result result = success.Catch(e => OnFailure(e)); + Result result = success.Catch(e => OnFailure(e)); - result.Should().BeEquivalentTo(success); - } + result.Should().BeEquivalentTo(success); + } - [Fact] - private async Task ShouldReturnOriginalSuccessAsynchronously() + [Fact] + private async Task ShouldReturnOriginalSuccessAsynchronously() + { + async Task> OnFailure(Error e) { - async Task> OnFailure(Error e) - { - await Task.Yield(); - return new Failure(new Error(e.Title, $"Ka-{e.Detail}")); - } + await Task.Yield(); + return new Failure(new Error(e.Title, $"Ka-{e.Detail}")); + } - var success = new Success(1); + var success = new Success(1); - Result result = await success.Catch(e => OnFailure(e)); + Result result = await success.Catch(e => OnFailure(e)); - result.Should().BeEquivalentTo(success); - } + result.Should().BeEquivalentTo(success); } + } - public sealed class Combine : SuccessTests + public sealed class Combine : SuccessTests + { + [Fact] + private void ShouldCombineDataElementsIfOtherResultIsASuccess() { - [Fact] - private void ShouldCombineDataElementsIfOtherResultIsASuccess() - { - var success = new Success(1); + var success = new Success(1); - Result combined = success.Combine( - new Success(2), - (i, j) => i + j, - (error, otherError) => new Error("Error", $"{error.Detail}-{otherError.Detail}")); + Result combined = success.Combine( + new Success(2), + (i, j) => i + j, + (error, otherError) => new Error("Error", $"{error.Detail}-{otherError.Detail}")); - combined.Should().BeEquivalentTo(new Success(3)); - } + combined.Should().BeEquivalentTo(new Success(3)); + } - [Fact] - private void ShouldReturnFailureWithOtherErrorIfTheOtherResultIsAFailure() - { - var success = new Success(1); - var otherResult = new Failure(new Error("Error", "Boom!")); + [Fact] + private void ShouldReturnFailureWithOtherErrorIfTheOtherResultIsAFailure() + { + var success = new Success(1); + var otherResult = new Failure(new Error("Error", "Boom!")); - Result combined = success.Combine( - otherResult, - (i, j) => i + j, - (error, otherError) => new Error("Error", $"{error.Detail}-{otherError.Detail}")); + Result combined = success.Combine( + otherResult, + (i, j) => i + j, + (error, otherError) => new Error("Error", $"{error.Detail}-{otherError.Detail}")); - combined.Should().BeEquivalentTo(otherResult); - } + combined.Should().BeEquivalentTo(otherResult); } + } - public sealed class Match : SuccessTests + public sealed class Match : SuccessTests + { + [Fact] + private void ShouldInvokeOnSuccessFunc() { - [Fact] - private void ShouldInvokeOnSuccessFunc() - { - var success = new Success(1); + var success = new Success(1); - bool matchedSuccess = success.Match(_ => true, _ => false); + bool matchedSuccess = success.Match(_ => true, _ => false); - matchedSuccess.Should().BeTrue(); - } + matchedSuccess.Should().BeTrue(); } + } - public sealed class OnFailure : FailureTests + public sealed class OnFailure : FailureTests + { + [Fact] + private void ShouldNotInvokeAction() { - [Fact] - private void ShouldNotInvokeAction() - { - var invoked = false; - var success = new Success(1); + var invoked = false; + var success = new Success(1); - success.OnFailure(() => invoked = true); + success.OnFailure(() => invoked = true); - invoked.Should().BeFalse(); - } + invoked.Should().BeFalse(); + } - [Fact] - private void ShouldNotInvokeActionWithParameters() - { - var invoked = false; - var success = new Success(1); + [Fact] + private void ShouldNotInvokeActionWithParameters() + { + var invoked = false; + var success = new Success(1); - success.OnFailure(i => invoked = true); + success.OnFailure(i => invoked = true); - invoked.Should().BeFalse(); - } + invoked.Should().BeFalse(); + } - [Fact] - private async Task ShouldNotInvokeAsyncAction() + [Fact] + private async Task ShouldNotInvokeAsyncAction() + { + var invoked = false; + async Task OnSuccess() { - var invoked = false; - async Task OnSuccess() - { - await Task.Yield(); - invoked = true; - } + await Task.Yield(); + invoked = true; + } - var success = new Success(1); + var success = new Success(1); - await success.OnFailure(OnSuccess); + await success.OnFailure(OnSuccess); - invoked.Should().BeFalse(); - } + invoked.Should().BeFalse(); + } - [Fact] - private async Task ShouldNotInvokeAsyncActionWithParameters() + [Fact] + private async Task ShouldNotInvokeAsyncActionWithParameters() + { + var invoked = false; + async Task OnSuccess(Error e) { - var invoked = false; - async Task OnSuccess(Error e) - { - await Task.Yield(); - invoked = true; - } + await Task.Yield(); + invoked = true; + } - var success = new Success(1); + var success = new Success(1); - await success.OnFailure(OnSuccess); + await success.OnFailure(OnSuccess); - invoked.Should().BeFalse(); - } + invoked.Should().BeFalse(); + } - [Fact] - private void ShouldReturnOriginalResult() - { - var success = new Success(1); + [Fact] + private void ShouldReturnOriginalResult() + { + var success = new Success(1); - Result result = success.OnFailure(() => { }); + Result result = success.OnFailure(() => { }); - result.Should().BeSameAs(success); - } + result.Should().BeSameAs(success); + } - [Fact] - private async Task ShouldReturnOriginalResultWhenAsyncAction() + [Fact] + private async Task ShouldReturnOriginalResultWhenAsyncAction() + { + async Task OnSuccess() { - async Task OnSuccess() - { - await Task.Yield(); - } + await Task.Yield(); + } - var failure = new Failure(new Error("Error", "Boom!")); + var failure = new Failure(new Error("Error", "Boom!")); - Result result = await failure.OnFailure(OnSuccess); + Result result = await failure.OnFailure(OnSuccess); - result.Should().BeSameAs(failure); - } + result.Should().BeSameAs(failure); } + } - public sealed class OnSuccess : FailureTests + public sealed class OnSuccess : FailureTests + { + [Fact] + private void ShouldInvokeAction() { - [Fact] - private void ShouldInvokeAction() - { - var invoked = false; - var success = new Success(1); + var invoked = false; + var success = new Success(1); - success.OnSuccess(() => invoked = true); + success.OnSuccess(() => invoked = true); - invoked.Should().BeTrue(); - } + invoked.Should().BeTrue(); + } - [Fact] - private void ShouldNotInvokeActionWithParameters() - { - var invoked = false; - var success = new Success(1); + [Fact] + private void ShouldNotInvokeActionWithParameters() + { + var invoked = false; + var success = new Success(1); - success.OnSuccess(i => invoked = true); + success.OnSuccess(i => invoked = true); - invoked.Should().BeTrue(); - } + invoked.Should().BeTrue(); + } - [Fact] - private async Task ShouldNotInvokeAsyncAction() + [Fact] + private async Task ShouldNotInvokeAsyncAction() + { + var invoked = false; + async Task OnSuccess() { - var invoked = false; - async Task OnSuccess() - { - await Task.Yield(); - invoked = true; - } + await Task.Yield(); + invoked = true; + } - var success = new Success(1); + var success = new Success(1); - await success.OnSuccess(OnSuccess); + await success.OnSuccess(OnSuccess); - invoked.Should().BeTrue(); - } + invoked.Should().BeTrue(); + } - [Fact] - private async Task ShouldNotInvokeAsyncActionWithParameters() + [Fact] + private async Task ShouldNotInvokeAsyncActionWithParameters() + { + var invoked = false; + async Task OnSuccess(int i) { - var invoked = false; - async Task OnSuccess(int i) - { - await Task.Yield(); - invoked = true; - } + await Task.Yield(); + invoked = true; + } - var success = new Success(1); + var success = new Success(1); - await success.OnSuccess(OnSuccess); + await success.OnSuccess(OnSuccess); - invoked.Should().BeTrue(); - } + invoked.Should().BeTrue(); + } - [Fact] - private void ShouldReturnOriginalResult() - { - var success = new Success(1); + [Fact] + private void ShouldReturnOriginalResult() + { + var success = new Success(1); - Result result = success.OnSuccess(() => { }); + Result result = success.OnSuccess(() => { }); - result.Should().BeSameAs(success); - } + result.Should().BeSameAs(success); + } - [Fact] - private async Task ShouldReturnOriginalResultWhenAsyncAction() + [Fact] + private async Task ShouldReturnOriginalResultWhenAsyncAction() + { + async Task OnSuccess() { - async Task OnSuccess() - { - await Task.Yield(); - } + await Task.Yield(); + } - var success = new Success(1); + var success = new Success(1); - Result result = await success.OnSuccess(OnSuccess); + Result result = await success.OnSuccess(OnSuccess); - result.Should().BeSameAs(success); - } + result.Should().BeSameAs(success); } + } - public sealed class Select : SuccessTests + public sealed class Select : SuccessTests + { + [Fact] + private void ShouldProjectDataToNewType() { - [Fact] - private void ShouldProjectDataToNewType() - { - var success = new Success(1); + var success = new Success(1); - Result result = success.Select(i => $"{i}"); + Result result = success.Select(i => $"{i}"); - result.Should().BeEquivalentTo(new Success("1")); - } + result.Should().BeEquivalentTo(new Success("1")); + } - [Fact] - private async Task ShouldProjectDataToNewTypeAsynchronously() - { - var success = new Success(1); + [Fact] + private async Task ShouldProjectDataToNewTypeAsynchronously() + { + var success = new Success(1); - Result result = await success.Select(i => Task.FromResult($"{i}")); + Result result = await success.Select(i => Task.FromResult($"{i}")); - result.Should().BeEquivalentTo(new Success("1")); - } + result.Should().BeEquivalentTo(new Success("1")); } + } - public sealed class SelectError : FailureTests + public sealed class SelectError : FailureTests + { + [Fact] + private void ShouldReturnOriginalSuccess() { - [Fact] - private void ShouldReturnOriginalSuccess() - { - var success = new Success(1); + var success = new Success(1); - Result result = success.SelectError(e => new NotFoundError(e.Detail)); + Result result = success.SelectError(e => new NotFoundError(e.Detail)); - result.Should().BeEquivalentTo(success); - } + result.Should().BeEquivalentTo(success); + } - [Fact] - private async Task ShouldReturnOriginalSuccessAsynchronously() - { - var success = new Success(1); + [Fact] + private async Task ShouldReturnOriginalSuccessAsynchronously() + { + var success = new Success(1); - Result result = await success.SelectError( - e => Task.FromResult(new NotFoundError(e.Detail))); + Result result = await success.SelectError( + e => Task.FromResult(new NotFoundError(e.Detail))); - result.Should().BeEquivalentTo(success); - } + result.Should().BeEquivalentTo(success); } + } - public sealed class Then : SuccessTests + public sealed class Then : SuccessTests + { + [Fact] + private void ShouldInvokeOnSuccessFunc() { - [Fact] - private void ShouldInvokeOnSuccessFunc() + Result OnSuccess(int i) { - Result OnSuccess(int i) - { - return new Success(i + 1); - } + return new Success(i + 1); + } - var success = new Success(1); + var success = new Success(1); - Result result = success.Then(OnSuccess); + Result result = success.Then(OnSuccess); - result.Should().BeEquivalentTo(new Success(2)); - } + result.Should().BeEquivalentTo(new Success(2)); + } - [Fact] - private async Task ShouldInvokeOnSuccessFuncAsynchronously() + [Fact] + private async Task ShouldInvokeOnSuccessFuncAsynchronously() + { + async Task> OnSuccess(int i) { - async Task> OnSuccess(int i) - { - await Task.Yield(); - return new Success(i + 1); - } + await Task.Yield(); + return new Success(i + 1); + } - var success = new Success(1); + var success = new Success(1); - Result result = await success.Then(OnSuccess); + Result result = await success.Then(OnSuccess); - result.Should().BeEquivalentTo(new Success(2)); - } + result.Should().BeEquivalentTo(new Success(2)); } + } - public sealed class Unit : SuccessTests + public sealed class Unit : SuccessTests + { + [Fact] + private void ShouldReturnSuccessWithUnitData() { - [Fact] - private void ShouldReturnSuccessWithUnitData() - { - Success success = Success.Unit(); + Success success = Success.Unit(); - success.Should().BeEquivalentTo(new Success(DomainModelling.Unit.Value)); - } + success.Should().BeEquivalentTo(new Success(DomainModelling.Unit.Value)); } } } \ No newline at end of file diff --git a/test/Winton.DomainModelling.Abstractions.Tests/UnauthorizedExceptionTests.cs b/test/Winton.DomainModelling.Abstractions.Tests/UnauthorizedExceptionTests.cs deleted file mode 100644 index 70c659b..0000000 --- a/test/Winton.DomainModelling.Abstractions.Tests/UnauthorizedExceptionTests.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Winton. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - -using System; -using FluentAssertions; -using Xunit; -#pragma warning disable 618 - -namespace Winton.DomainModelling -{ - public class UnauthorizedExceptionTests - { - [Fact] - private void ShouldBeObsolete() - { - typeof(UnauthorizedException) - .Should() - .BeDecoratedWith(a => a.Message == "Prefer to return results with an UnauthorizedError instead."); - } - - public sealed class Message : UnauthorizedExceptionTests - { - [Theory] - [InlineData("Test")] - [InlineData("Foo")] - private void ShouldSetMessageFromConstructor(string message) - { - var exception = new UnauthorizedException(message); - - exception.Message.Should().Be(message); - } - } - } -} \ No newline at end of file diff --git a/test/Winton.DomainModelling.Abstractions.Tests/UnitTests.cs b/test/Winton.DomainModelling.Abstractions.Tests/UnitTests.cs index 7d2738f..8b038ba 100644 --- a/test/Winton.DomainModelling.Abstractions.Tests/UnitTests.cs +++ b/test/Winton.DomainModelling.Abstractions.Tests/UnitTests.cs @@ -6,46 +6,46 @@ using FluentAssertions; using Xunit; -namespace Winton.DomainModelling +namespace Winton.DomainModelling; + +public class UnitTests { - public class UnitTests + public sealed class CompareTo : UnitTests { - public sealed class CompareTo : UnitTests + [Fact] + private void ShouldAlwaysReturn0BecauseAllUnitsAreEqual() { - [Fact] - private void ShouldAlwaysReturn0BecauseAllUnitsAreEqual() - { - int comparable = Unit.Value.CompareTo(Unit.Value); + int comparable = Unit.Value.CompareTo(Unit.Value); - comparable.Should().Be(0); - } + comparable.Should().Be(0); + } - [Fact] - private void ShouldAlwaysReturnZeroWhenComparedToAnyObject() - { - IComparable value = Unit.Value; + [Fact] + private void ShouldAlwaysReturnZeroWhenComparedToAnyObject() + { + IComparable value = Unit.Value; - int comparable = value.CompareTo(new object()); + int comparable = value.CompareTo(new object()); - comparable.Should().Be(0); - } + comparable.Should().Be(0); } + } - public sealed class EqualityOperator : UnitTests + public sealed class EqualityOperator : UnitTests + { + [Fact] + private void ShouldAlwaysReturnTrueBecauseThereIsOnlyOneUnitValue() { - [Fact] - private void ShouldAlwaysReturnTrueBecauseThereIsOnlyOneUnitValue() - { - // ReSharper disable once EqualExpressionComparison - bool equal = Unit.Value == Unit.Value; + // ReSharper disable once EqualExpressionComparison + bool equal = Unit.Value == Unit.Value; - equal.Should().BeTrue(); - } + equal.Should().BeTrue(); } + } - public sealed class EqualsMethod : UnitTests - { - public static IEnumerable TestCases => new List + public sealed class EqualsMethod : UnitTests + { + public static IEnumerable TestCases => new List { new object[] { @@ -64,58 +64,57 @@ public sealed class EqualsMethod : UnitTests } }; - [Fact] - private void ShouldAlwaysReturnTrueBecauseThereIsOnlyOneUnitValue() - { - bool equal = Unit.Value.Equals(Unit.Value); + [Fact] + private void ShouldAlwaysReturnTrueBecauseThereIsOnlyOneUnitValue() + { + bool equal = Unit.Value.Equals(Unit.Value); - equal.Should().BeTrue(); - } + equal.Should().BeTrue(); + } - [Theory] - [MemberData(nameof(TestCases))] - private void ShouldReturnTrueIfOtherObjectIsOfTypeUnit(object obj, bool expected) - { - bool equal = Unit.Value.Equals(obj); + [Theory] + [MemberData(nameof(TestCases))] + private void ShouldReturnTrueIfOtherObjectIsOfTypeUnit(object obj, bool expected) + { + bool equal = Unit.Value.Equals(obj); - equal.Should().Be(expected); - } + equal.Should().Be(expected); } + } - public sealed class GetHashCodeMethod : UnitTests + public sealed class GetHashCodeMethod : UnitTests + { + [Fact] + private void ShouldReturnZero() { - [Fact] - private void ShouldReturnZero() - { - Unit unit = Unit.Value; + Unit unit = Unit.Value; - int hashCode = unit.GetHashCode(); + int hashCode = unit.GetHashCode(); - hashCode.Should().Be(0); - } + hashCode.Should().Be(0); } + } - public sealed class NotEqualOperator : UnitTests + public sealed class NotEqualOperator : UnitTests + { + [Fact] + private void ShouldAlwaysReturnFalseBecauseAllUnitsAreEqual() { - [Fact] - private void ShouldAlwaysReturnFalseBecauseAllUnitsAreEqual() - { - // ReSharper disable once EqualExpressionComparison - bool notEqual = Unit.Value != Unit.Value; + // ReSharper disable once EqualExpressionComparison + bool notEqual = Unit.Value != Unit.Value; - notEqual.Should().BeFalse(); - } + notEqual.Should().BeFalse(); } + } - public sealed class Value : UnitTests + public sealed class Value : UnitTests + { + [Fact] + private void ShouldReturnADefaultUnit() { - [Fact] - private void ShouldReturnADefaultUnit() - { - Unit value = Unit.Value; + Unit value = Unit.Value; - value.Should().BeEquivalentTo(default(Unit)); - } + value.Should().BeEquivalentTo(default(Unit)); } } -} \ No newline at end of file +} diff --git a/test/Winton.DomainModelling.Abstractions.Tests/Winton.DomainModelling.Abstractions.Tests.csproj b/test/Winton.DomainModelling.Abstractions.Tests/Winton.DomainModelling.Abstractions.Tests.csproj index 6db5b37..51f865d 100644 --- a/test/Winton.DomainModelling.Abstractions.Tests/Winton.DomainModelling.Abstractions.Tests.csproj +++ b/test/Winton.DomainModelling.Abstractions.Tests/Winton.DomainModelling.Abstractions.Tests.csproj @@ -2,7 +2,7 @@ Winton.DomainModelling - netcoreapp3.1 + net6.0