diff --git a/.build/.build.csproj b/.build/.build.csproj index 714ccc947..96921358c 100644 --- a/.build/.build.csproj +++ b/.build/.build.csproj @@ -15,6 +15,8 @@ + + diff --git a/.build/Build.CI.cs b/.build/Build.CI.cs index cc7e69d0c..1c749f97b 100644 --- a/.build/Build.CI.cs +++ b/.build/Build.CI.cs @@ -1,7 +1,6 @@ using Nuke.Common.CI.GitHubActions; using Nuke.Common.CI.GitHubActions.Configuration; using Rocket.Surgery.Nuke.ContinuousIntegration; -using Rocket.Surgery.Nuke.DotNetCore; using Rocket.Surgery.Nuke.GithubActions; #pragma warning disable CA1050 @@ -62,7 +61,7 @@ public static RocketSurgeonGitHubActionsConfiguration CiMiddleware(RocketSurgeon .Jobs.OfType() .First(z => z.Name.Equals("build", StringComparison.OrdinalIgnoreCase)); job - .UseDotNetSdks("8.0") + .UseDotNetSdks("8.0", "9.0") .ConfigureStep(step => step.FetchDepth = 0) .PublishLogs(); @@ -74,7 +73,7 @@ public static RocketSurgeonGitHubActionsConfiguration LintStagedMiddleware(Rocke configuration .Jobs.OfType() .First(z => z.Name.Equals("Build", StringComparison.OrdinalIgnoreCase)) - .UseDotNetSdks("8.0"); + .UseDotNetSdks("8.0", "9.0"); return configuration; } diff --git a/.build/Build.cs b/.build/Build.cs index 5275b85d5..9ba3ce717 100644 --- a/.build/Build.cs +++ b/.build/Build.cs @@ -23,7 +23,6 @@ public partial class Pipeline : NukeBuild, ICanTestWithDotNetCore, ICanPackWithDotNetCore, IComprehendSamples, - IHaveDataCollector, ICanClean, IHaveCommonLintTargets, // IHavePublicApis, diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 316f465cd..40578a327 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -27,9 +27,19 @@ "commands": ["jb"], "rollForward": false }, - "nukeeper": { - "version": "0.35.0", - "commands": ["nukeeper"], + "jetbrains.dotcover.commandlinetools": { + "version": "2024.3.0", + "commands": ["dotnet-dotCover"], + "rollForward": false + }, + "jetbrains.dottrace.globaltools": { + "version": "2024.3.0", + "commands": ["dottrace"], + "rollForward": false + }, + "dotnet-coverage": { + "version": "17.12.6", + "commands": ["dotnet-coverage"], "rollForward": false }, "docfx": { @@ -38,7 +48,7 @@ "rollForward": false }, "strawberryshake.tools": { - "version": "14.0.0-rc.2", + "version": "14.2.0-p.4", "commands": ["dotnet-graphql"], "rollForward": false }, @@ -56,11 +66,6 @@ "version": "0.7.1", "commands": ["husky"], "rollForward": false - }, - "liquidtestreports.cli": { - "version": "2.0.0-beta.6", - "commands": ["liquid"], - "rollForward": false } } } diff --git a/.editorconfig b/.editorconfig index 88c88224b..736e81ea1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3063,7 +3063,7 @@ dotnet_diagnostic.rcs1258.severity = suggestion dotnet_diagnostic.rcs1259.severity = suggestion # Add/remove trailing comma -dotnet_diagnostic.rcs1260.severity = suggestion +dotnet_diagnostic.rcs1260.severity = none # Options: roslynator_trailing_comma_style # Use pattern matching @@ -4677,7 +4677,7 @@ resharper_T4_wrap_lines = true resharper_toplevel_function_declaration_return_type_style = do_not_change resharper_toplevel_function_definition_return_type_style = do_not_change resharper_trailing_comma_in_multiline_lists = true -resharper_trailing_comma_in_singleline_lists = true +resharper_trailing_comma_in_singleline_lists = false resharper_use_continuous_indent_inside_initializer_braces = true resharper_use_continuous_indent_inside_parens = true resharper_use_continuous_line_indent_in_expression_braces = false diff --git a/.github/renovate.json b/.github/renovate.json index 608c717ee..43f82274c 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,7 +1,7 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["local>RocketSurgeonsGuild/.github:renovate-config"], - "ignorePaths": ["**/node_modules/**", "**/bower_components/**", "**/*.csproj"], + "ignorePaths": ["**/node_modules/**", "**/*.csproj", "Directory.Packages.support.props"], "packageRules": [ { "matchCategories": ["js"], diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6565f2d43..51faefafc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,6 +84,20 @@ permissions: jobs: build: + permissions: + actions: read + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: none + packages: none + pages: none + pull-requests: write + repository-projects: none + security-events: none + statuses: write runs-on: ubuntu-latest steps: - name: Checkout @@ -95,6 +109,10 @@ jobs: uses: actions/setup-dotnet@v4.1.0 with: dotnet-version: '8.0.x' + - name: 🔨 Use .NET Core 9.0 SDK + uses: actions/setup-dotnet@v4.1.0 + with: + dotnet-version: '9.0.x' - name: 🚒 dotnet workload restore continue-on-error: true run: | @@ -113,7 +131,7 @@ jobs: - name: 🚦 Test id: test run: | - dotnet .build/bin/Debug/.build.dll --target DotnetCoreTest Test TriggerCodeCoverageReports GenerateCodeCoverageReportCobertura GenerateCodeCoverageBadges GenerateCodeCoverageSummary GenerateCodeCoverageReport + dotnet .build/bin/Debug/.build.dll --target DotnetCoreTest Test CollectCodeCoverage GenerateCodeCoverageReportCobertura GenerateCodeCoverageBadges GenerateCodeCoverageSummary GenerateCodeCoverageReport - name: 📦 Pack id: pack run: | @@ -122,18 +140,6 @@ jobs: id: default run: | dotnet .build/bin/Debug/.build.dll --target Default --skip Restore Build Test Pack - - name: 🏺 Publish coverage data - if: always() - uses: actions/upload-artifact@v4.4.3 - with: - name: 'coverage' - path: 'coverage/' - - name: 📫 Publish Coverage - if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || ((github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && github.event.pull_request.user.login != 'renovate[bot]' && github.event.pull_request.user.login != 'dependabot[bot]') - uses: codecov/codecov-action@v5.0.7 - with: - name: 'actions-${{ matrix.os }}' - token: '${{ secrets.CODECOV_TOKEN }}' - name: 🏺 Publish logs if: always() uses: actions/upload-artifact@v4.4.3 @@ -146,6 +152,30 @@ jobs: with: name: 'test data' path: 'artifacts/test/' + - name: 📫 Publish Test Results + if: always() + uses: EnricoMi/publish-unit-test-result-action@v2 + with: + files: 'artifacts/test/**/*.trx' + - name: 🏺 Publish coverage data + if: always() + uses: actions/upload-artifact@v4.4.3 + with: + name: 'coverage' + path: 'coverage/' + - name: 📫 Publish Coverage Comment + if: github.event_name == 'pull_request' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: 'Coverage' + path: 'coverage/summary/SummaryGithub.md' + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + - name: 📫 Publish Codecov Coverage + if: (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') || ((github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && github.event.pull_request.user.login != 'renovate[bot]' && github.event.pull_request.user.login != 'dependabot[bot]') + uses: codecov/codecov-action@v5.0.7 + with: + name: 'actions' + token: '${{ secrets.CODECOV_TOKEN }}' - name: 🏺 Publish NuGet Packages if: always() uses: actions/upload-artifact@v4.4.3 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e8439ddd5..1040650bc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -63,6 +63,10 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.x' + - name: 🔨 Use .NET Core 9.0 SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.0.x' - name: 🚒 dotnet workload restore continue-on-error: true run: | diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index 81f6e1cfb..c4a2891e3 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -27,6 +27,7 @@ "Build", "Clean", "CleanWellKnownTemporaryFiles", + "CollectCodeCoverage", "Default", "DotnetCoreBuild", "DotnetCorePack", @@ -50,8 +51,7 @@ "Prettier", "RegenerateBuildConfigurations", "Restore", - "Test", - "TriggerCodeCoverageReports" + "Test" ] }, "Verbosity": { diff --git a/Directory.Build.targets b/Directory.Build.targets index eb89b3a22..8a4138072 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -2,6 +2,5 @@ - diff --git a/Directory.Packages.props b/Directory.Packages.props index 3c8658af4..e36e67626 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,166 +1,177 @@ - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + true + + + + + + + + + + + + + + + + + + + 11.0.0-rc.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Directory.Packages.support.props b/Directory.Packages.support.props index d3a7398af..40eb5a320 100644 --- a/Directory.Packages.support.props +++ b/Directory.Packages.support.props @@ -1,6 +1,24 @@  - - + + + + + + + + + + + + + + + + + + + + diff --git a/LaunchPad.sln b/LaunchPad.sln index 86ee6fb52..798a9cf76 100644 --- a/LaunchPad.sln +++ b/LaunchPad.sln @@ -184,6 +184,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rocket.Surgery.LaunchPad.An EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Analyzers.Tests.roslyn4.6", "test\Analyzers.Tests.roslyn4.6\Analyzers.Tests.roslyn4.6.csproj", "{84525664-AB7C-46C7-8DDC-ABFC1BB4F6A4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi", "src\AspNetCore.FluentValidation.OpenApi\Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi.csproj", "{721DE742-912E-47DE-98FE-A8770262E67D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore.FluentValidation.OpenApi.Tests", "test\AspNetCore.FluentValidation.OpenApi.Tests\AspNetCore.FluentValidation.OpenApi.Tests.csproj", "{F0DD1D80-6948-4F10-8789-228ACC1F412F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Minimal", "sample\Sample.Minimal\Sample.Minimal.csproj", "{75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Minimal.Tests", "test\Sample.Minimal.Tests\Sample.Minimal.Tests.csproj", "{3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -788,6 +796,54 @@ Global {84525664-AB7C-46C7-8DDC-ABFC1BB4F6A4}.Release|x64.Build.0 = Release|Any CPU {84525664-AB7C-46C7-8DDC-ABFC1BB4F6A4}.Release|x86.ActiveCfg = Release|Any CPU {84525664-AB7C-46C7-8DDC-ABFC1BB4F6A4}.Release|x86.Build.0 = Release|Any CPU + {721DE742-912E-47DE-98FE-A8770262E67D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {721DE742-912E-47DE-98FE-A8770262E67D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {721DE742-912E-47DE-98FE-A8770262E67D}.Debug|x64.ActiveCfg = Debug|Any CPU + {721DE742-912E-47DE-98FE-A8770262E67D}.Debug|x64.Build.0 = Debug|Any CPU + {721DE742-912E-47DE-98FE-A8770262E67D}.Debug|x86.ActiveCfg = Debug|Any CPU + {721DE742-912E-47DE-98FE-A8770262E67D}.Debug|x86.Build.0 = Debug|Any CPU + {721DE742-912E-47DE-98FE-A8770262E67D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {721DE742-912E-47DE-98FE-A8770262E67D}.Release|Any CPU.Build.0 = Release|Any CPU + {721DE742-912E-47DE-98FE-A8770262E67D}.Release|x64.ActiveCfg = Release|Any CPU + {721DE742-912E-47DE-98FE-A8770262E67D}.Release|x64.Build.0 = Release|Any CPU + {721DE742-912E-47DE-98FE-A8770262E67D}.Release|x86.ActiveCfg = Release|Any CPU + {721DE742-912E-47DE-98FE-A8770262E67D}.Release|x86.Build.0 = Release|Any CPU + {F0DD1D80-6948-4F10-8789-228ACC1F412F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0DD1D80-6948-4F10-8789-228ACC1F412F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0DD1D80-6948-4F10-8789-228ACC1F412F}.Debug|x64.ActiveCfg = Debug|Any CPU + {F0DD1D80-6948-4F10-8789-228ACC1F412F}.Debug|x64.Build.0 = Debug|Any CPU + {F0DD1D80-6948-4F10-8789-228ACC1F412F}.Debug|x86.ActiveCfg = Debug|Any CPU + {F0DD1D80-6948-4F10-8789-228ACC1F412F}.Debug|x86.Build.0 = Debug|Any CPU + {F0DD1D80-6948-4F10-8789-228ACC1F412F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0DD1D80-6948-4F10-8789-228ACC1F412F}.Release|Any CPU.Build.0 = Release|Any CPU + {F0DD1D80-6948-4F10-8789-228ACC1F412F}.Release|x64.ActiveCfg = Release|Any CPU + {F0DD1D80-6948-4F10-8789-228ACC1F412F}.Release|x64.Build.0 = Release|Any CPU + {F0DD1D80-6948-4F10-8789-228ACC1F412F}.Release|x86.ActiveCfg = Release|Any CPU + {F0DD1D80-6948-4F10-8789-228ACC1F412F}.Release|x86.Build.0 = Release|Any CPU + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}.Debug|x64.ActiveCfg = Debug|Any CPU + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}.Debug|x64.Build.0 = Debug|Any CPU + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}.Debug|x86.ActiveCfg = Debug|Any CPU + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}.Debug|x86.Build.0 = Debug|Any CPU + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}.Release|Any CPU.Build.0 = Release|Any CPU + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}.Release|x64.ActiveCfg = Release|Any CPU + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}.Release|x64.Build.0 = Release|Any CPU + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}.Release|x86.ActiveCfg = Release|Any CPU + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA}.Release|x86.Build.0 = Release|Any CPU + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Debug|x64.ActiveCfg = Debug|Any CPU + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Debug|x64.Build.0 = Debug|Any CPU + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Debug|x86.ActiveCfg = Debug|Any CPU + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Debug|x86.Build.0 = Debug|Any CPU + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Release|Any CPU.Build.0 = Release|Any CPU + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Release|x64.ActiveCfg = Release|Any CPU + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Release|x64.Build.0 = Release|Any CPU + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Release|x86.ActiveCfg = Release|Any CPU + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -847,6 +903,10 @@ Global {2EB0A486-E55F-424C-9EA3-0ABBC10D0257} = {D65A8130-6A58-4693-A96C-8C1DFA3CA355} {9E08CB1A-1352-40FA-8CEB-0EBCA52689A6} = {8FFDF555-DB50-45F9-9A2D-6410F39151C3} {84525664-AB7C-46C7-8DDC-ABFC1BB4F6A4} = {DF33E0FB-9790-4654-B60F-8AB22E0CC3D1} + {721DE742-912E-47DE-98FE-A8770262E67D} = {8FFDF555-DB50-45F9-9A2D-6410F39151C3} + {F0DD1D80-6948-4F10-8789-228ACC1F412F} = {DF33E0FB-9790-4654-B60F-8AB22E0CC3D1} + {75C5361A-B1E8-4194-8BBC-CD68D4FC62CA} = {5D11C19B-E8E4-4CE3-9C8A-1D368578EBCB} + {3A72EB2F-D0C0-4AA4-9CCB-7EA04DC95C8D} = {DF33E0FB-9790-4654-B60F-8AB22E0CC3D1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {439897C2-CCBD-44FE-B2DC-A3E4670ADA59} diff --git a/global.json b/global.json index b31fd2df1..283605021 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { "rollForward": "latestMinor", - "version": "8.0.404" + "version": "9.0.100" } } diff --git a/sample/Sample.BlazorServer/Pages/Rockets/Delete.razor.cs b/sample/Sample.BlazorServer/Pages/Rockets/Delete.razor.cs index a80964b31..9121381a5 100644 --- a/sample/Sample.BlazorServer/Pages/Rockets/Delete.razor.cs +++ b/sample/Sample.BlazorServer/Pages/Rockets/Delete.razor.cs @@ -17,12 +17,12 @@ public partial class Delete : ComponentBase protected override async Task OnInitializedAsync() { - Model = await Mediator.Send(new GetRocket.Request { Id = Id }); + Model = await Mediator.Send(new GetRocket.Request(Id)); } public async Task Save() { - await Mediator.Send(new DeleteRocket.Request { Id = Id }); + await Mediator.Send(new DeleteRocket.Request(Id)); NavigationManager.NavigateTo("/rockets"); } } diff --git a/sample/Sample.BlazorServer/Pages/Rockets/Edit.razor.cs b/sample/Sample.BlazorServer/Pages/Rockets/Edit.razor.cs index e44b14bf7..28af3d7ff 100644 --- a/sample/Sample.BlazorServer/Pages/Rockets/Edit.razor.cs +++ b/sample/Sample.BlazorServer/Pages/Rockets/Edit.razor.cs @@ -20,7 +20,7 @@ public partial class Edit : ComponentBase protected override async Task OnInitializedAsync() { - Model = EditRocket.MapRequest(await Mediator.Send(new GetRocket.Request { Id = Id, })); + Model = EditRocket.MapRequest(await Mediator.Send(new GetRocket.Request(Id))); } public async Task Save() @@ -28,4 +28,4 @@ public async Task Save() await Mediator.Send(Model with { Id = Id, }); NavigationManager.NavigateTo("/rockets"); } -} \ No newline at end of file +} diff --git a/sample/Sample.BlazorServer/Pages/Rockets/View.razor.cs b/sample/Sample.BlazorServer/Pages/Rockets/View.razor.cs index 8414577df..16e297c8a 100644 --- a/sample/Sample.BlazorServer/Pages/Rockets/View.razor.cs +++ b/sample/Sample.BlazorServer/Pages/Rockets/View.razor.cs @@ -14,6 +14,6 @@ public partial class View : ComponentBase protected override async Task OnInitializedAsync() { - Model = await Mediator.Send(new GetRocket.Request { Id = Id }); + Model = await Mediator.Send(new GetRocket.Request(Id)); } } diff --git a/sample/Sample.BlazorServer/Pages/_Host.cshtml b/sample/Sample.BlazorServer/Pages/_Host.cshtml index 94527eecf..a80a334df 100644 --- a/sample/Sample.BlazorServer/Pages/_Host.cshtml +++ b/sample/Sample.BlazorServer/Pages/_Host.cshtml @@ -1,4 +1,5 @@ @page "/" +@using Microsoft.AspNetCore.Mvc.TagHelpers @namespace Sample.BlazorServer.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @{ diff --git a/sample/Sample.BlazorServer/Sample.BlazorServer.csproj b/sample/Sample.BlazorServer/Sample.BlazorServer.csproj index f786c2c18..7352eedfd 100644 --- a/sample/Sample.BlazorServer/Sample.BlazorServer.csproj +++ b/sample/Sample.BlazorServer/Sample.BlazorServer.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 diff --git a/sample/Sample.BlazorWasm/Sample.BlazorWasm.csproj b/sample/Sample.BlazorWasm/Sample.BlazorWasm.csproj index 67f02bddd..3bf6437e3 100644 --- a/sample/Sample.BlazorWasm/Sample.BlazorWasm.csproj +++ b/sample/Sample.BlazorWasm/Sample.BlazorWasm.csproj @@ -1,6 +1,6 @@ - net8.0-browser + net9.0-browser false true diff --git a/sample/Sample.Classic.Restful/Program.cs b/sample/Sample.Classic.Restful/Program.cs index 06e7104d1..5f6b7ac3c 100644 --- a/sample/Sample.Classic.Restful/Program.cs +++ b/sample/Sample.Classic.Restful/Program.cs @@ -1,34 +1,21 @@ using System.Reflection; -using Hellang.Middleware.ProblemDetails; using Rocket.Surgery.Hosting; using Rocket.Surgery.LaunchPad.AspNetCore; using Sample.Classic.Restful; -using Swashbuckle.AspNetCore.SwaggerGen; var builder = WebApplication .CreateBuilder(args); builder.Services.AddControllers().AddControllersAsServices(); -builder.Services - .Configure( - c => c.SwaggerDoc( - "v1", - new() - { - Version = typeof(Program).GetCustomAttribute()?.Version - ?? typeof(Program).GetCustomAttribute()?.Version ?? "0.1.0", - Title = "Test Application", - } - ) - ); var app = await builder.LaunchWith(RocketBooster.For(Imports.Instance)); -app.UseProblemDetails(); +app.UseExceptionHandler(); app.UseHttpsRedirection(); app.UseLaunchPadRequestLogging(); app.UseRouting(); +app.MapOpenApi(); app .UseSwaggerUI() .UseReDoc(); @@ -36,8 +23,7 @@ app.UseAuthorization(); app.MapControllers(); -app.MapSwagger(); await app.RunAsync(); -public partial class Program; \ No newline at end of file +public partial class Program; diff --git a/sample/Sample.Classic.Restful/Sample.Classic.Restful.csproj b/sample/Sample.Classic.Restful/Sample.Classic.Restful.csproj index 6ee51ac1a..b0626771e 100644 --- a/sample/Sample.Classic.Restful/Sample.Classic.Restful.csproj +++ b/sample/Sample.Classic.Restful/Sample.Classic.Restful.csproj @@ -1,6 +1,6 @@ - net8.0 + net9.0 true true true @@ -8,10 +8,13 @@ - + + + diff --git a/sample/Sample.Command/Sample.Command.csproj b/sample/Sample.Command/Sample.Command.csproj index 8d749aad6..4126d2e46 100644 --- a/sample/Sample.Command/Sample.Command.csproj +++ b/sample/Sample.Command/Sample.Command.csproj @@ -1,7 +1,7 @@ Exe - net8.0 + net8.0;net9.0 diff --git a/sample/Sample.Core/Operations/LaunchRecords/DeleteLaunchRecord.cs b/sample/Sample.Core/Operations/LaunchRecords/DeleteLaunchRecord.cs index 9fe647542..5ce3994d0 100644 --- a/sample/Sample.Core/Operations/LaunchRecords/DeleteLaunchRecord.cs +++ b/sample/Sample.Core/Operations/LaunchRecords/DeleteLaunchRecord.cs @@ -16,13 +16,8 @@ public static partial class DeleteLaunchRecord /// /// The request to delete a launch record /// - public record Request : IRequest - { - /// - /// The launch record to delete - /// - public LaunchRecordId Id { get; init; } - } + /// The id of the record to delete + public record Request(LaunchRecordId Id) : IRequest; [UsedImplicitly] private class Validator : AbstractValidator @@ -51,4 +46,4 @@ public async Task Handle(Request request, CancellationToken cancellationToken) await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } } -} \ No newline at end of file +} diff --git a/sample/Sample.Core/Operations/LaunchRecords/EditLaunchRecord.cs b/sample/Sample.Core/Operations/LaunchRecords/EditLaunchRecord.cs index ec460961f..806b0b686 100644 --- a/sample/Sample.Core/Operations/LaunchRecords/EditLaunchRecord.cs +++ b/sample/Sample.Core/Operations/LaunchRecords/EditLaunchRecord.cs @@ -108,6 +108,39 @@ public Validator() } } + private class PatchRequestValidator : AbstractValidator + { + public PatchRequestValidator() + { + RuleFor(x => x.Id) + .NotEmpty() + .NotNull(); + + RuleFor(x => x.Partner.Value) + .NotEmpty() + .NotNull() + .When(x => x.Partner.HasValue); + + RuleFor(x => x.RocketId.Value) + .NotEmpty() + .NotNull() + .When(x => x.RocketId.HasValue); + + RuleFor(x => x.Payload.Value) + .NotEmpty() + .NotNull() + .When(x => x.Payload.HasValue); + + RuleFor(x => x.ScheduledLaunchDate.Value) + .NotNull() + .When(x => x.ScheduledLaunchDate.HasValue); + + RuleFor(x => x.PayloadWeightKg.Value) + .GreaterThanOrEqualTo(0d) + .When(x => x.PayloadWeightKg.HasValue); + } + } + private class Handler(RocketDbContext dbContext, IMediator mediator) : PatchRequestHandler(mediator), IRequestHandler { @@ -137,4 +170,4 @@ public async Task Handle(Request request, CancellationToken c return ModelMapper.Map(rocket); } } -} \ No newline at end of file +} diff --git a/sample/Sample.Core/Operations/LaunchRecords/GetLaunchRecord.cs b/sample/Sample.Core/Operations/LaunchRecords/GetLaunchRecord.cs index 87eeff864..f6f6f2011 100644 --- a/sample/Sample.Core/Operations/LaunchRecords/GetLaunchRecord.cs +++ b/sample/Sample.Core/Operations/LaunchRecords/GetLaunchRecord.cs @@ -17,13 +17,8 @@ public static partial class GetLaunchRecord /// /// The request to get a launch record /// - public record Request : IRequest - { - /// - /// The launch record to find - /// - public LaunchRecordId Id { get; init; } - } + /// The id of the launch record + public record Request(LaunchRecordId Id) : IRequest; private class Validator : AbstractValidator { @@ -48,4 +43,4 @@ public async Task Handle(Request request, CancellationToken c return ModelMapper.Map(rocket); } } -} \ No newline at end of file +} diff --git a/sample/Sample.Core/Operations/Rockets/DeleteRocket.cs b/sample/Sample.Core/Operations/Rockets/DeleteRocket.cs index 9920edf9c..5222e2e48 100644 --- a/sample/Sample.Core/Operations/Rockets/DeleteRocket.cs +++ b/sample/Sample.Core/Operations/Rockets/DeleteRocket.cs @@ -16,13 +16,8 @@ public static partial class DeleteRocket /// /// The request to remove a rocket from the system /// - public record Request : IRequest - { - /// - /// The rocket id - /// - public RocketId Id { get; init; } - } + /// The id of the rocket to remove + public record Request(RocketId Id) : IRequest; private class Validator : AbstractValidator { @@ -45,4 +40,4 @@ public async Task Handle(Request request, CancellationToken cancellationToken) await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } } -} \ No newline at end of file +} diff --git a/sample/Sample.Core/Operations/Rockets/EditRocket.cs b/sample/Sample.Core/Operations/Rockets/EditRocket.cs index 828f250d1..dbf3c767e 100644 --- a/sample/Sample.Core/Operations/Rockets/EditRocket.cs +++ b/sample/Sample.Core/Operations/Rockets/EditRocket.cs @@ -82,6 +82,27 @@ public RequestValidator() } } + private class PatchRequestValidator : AbstractValidator + { + public PatchRequestValidator() + { + RuleFor(x => x.Id) + .NotEmpty() + .NotNull(); + + RuleFor(x => x.Type.Value) + .NotNull() + .IsInEnum() + .When(z => z.Type.HasValue); + + RuleFor(x => x.SerialNumber.Value) + .NotNull() + .MinimumLength(10) + .MaximumLength(30) + .When(z => z.SerialNumber.HasValue); + } + } + private class RequestHandler(RocketDbContext dbContext, IMediator mediator) : PatchRequestHandler(mediator), IRequestHandler { @@ -110,4 +131,4 @@ public async Task Handle(Request request, CancellationToken cancell return ModelMapper.Map(rocket); } } -} \ No newline at end of file +} diff --git a/sample/Sample.Core/Operations/Rockets/GetRocket.cs b/sample/Sample.Core/Operations/Rockets/GetRocket.cs index af0625453..453b7e4d9 100644 --- a/sample/Sample.Core/Operations/Rockets/GetRocket.cs +++ b/sample/Sample.Core/Operations/Rockets/GetRocket.cs @@ -16,13 +16,8 @@ public static partial class GetRocket /// /// Request to fetch information about a rocket /// - public record Request : IRequest - { - /// - /// The rocket id - /// - public RocketId Id { get; set; } - } + /// The id of the rocket + public record Request(RocketId Id) : IRequest; private class Validator : AbstractValidator { @@ -41,4 +36,4 @@ public async Task Handle(Request request, CancellationToken cancell return ModelMapper.Map(await dbContext.Rockets.FindAsync([request.Id,], cancellationToken).ConfigureAwait(false) ?? throw new NotFoundException()); } } -} \ No newline at end of file +} diff --git a/sample/Sample.Core/Sample.Core.csproj b/sample/Sample.Core/Sample.Core.csproj index 6d7ca0a9c..0e77c07fb 100644 --- a/sample/Sample.Core/Sample.Core.csproj +++ b/sample/Sample.Core/Sample.Core.csproj @@ -1,17 +1,17 @@ - net8.0 - true + net8.0;net9.0 + true - - - + + + diff --git a/sample/Sample.Graphql/Sample.Graphql.csproj b/sample/Sample.Graphql/Sample.Graphql.csproj index 09c6db819..6afba011e 100644 --- a/sample/Sample.Graphql/Sample.Graphql.csproj +++ b/sample/Sample.Graphql/Sample.Graphql.csproj @@ -1,11 +1,11 @@ - net8.0 - Sample.Graphql.Executable - disable - enable - enable + net9.0 + Sample.Graphql.Executable + disable + enable + enable diff --git a/sample/Sample.Graphql/Startup.cs b/sample/Sample.Graphql/Startup.cs index a2086a19f..5f1d8aa7b 100644 --- a/sample/Sample.Graphql/Startup.cs +++ b/sample/Sample.Graphql/Startup.cs @@ -1,9 +1,11 @@ -using HotChocolate.Data; +using FluentValidation; +using HotChocolate.Data; using HotChocolate.Data.Filters; using HotChocolate.Data.Sorting; using MediatR; using NetTopologySuite.Geometries; using NodaTime; +using Rocket.Surgery.LaunchPad.Foundation; using Rocket.Surgery.LaunchPad.HotChocolate; using Sample.Core.Domain; using Sample.Core.Models; @@ -15,11 +17,66 @@ namespace Sample.Graphql; public partial record EditRocketPatchRequest : IOptionalTracking { public RocketId Id { get; init; } + + [UsedImplicitly] + class Validator : AbstractValidator + { + public Validator() + { + RuleFor(x => x.Id).NotEmpty().NotNull(); + + RuleFor(x => x.Type.Value) + .NotNull() + .IsInEnum() + .When(x => x.Type.HasValue); + + RuleFor(x => x.SerialNumber.Value) + .NotNull() + .MinimumLength(10) + .MaximumLength(30) + .When(x => x.SerialNumber.HasValue); + } + } } public partial record EditLaunchRecordPatchRequest : IOptionalTracking { public LaunchRecordId Id { get; init; } + + + + private class Validator : AbstractValidator + { + public Validator() + { + RuleFor(x => x.Id) + .NotEmpty() + .NotNull(); + + RuleFor(x => x.Partner.Value) + .NotEmpty() + .NotNull() + .When(x => x.Partner.HasValue); + + RuleFor(x => x.RocketId.Value) + .NotEmpty() + .NotNull() + .When(x => x.RocketId.HasValue); + + RuleFor(x => x.Payload.Value) + .NotEmpty() + .NotNull() + .When(x => x.Payload.HasValue); + + RuleFor(x => x.ScheduledLaunchDate.Value) + .NotNull() + .When(x => x.ScheduledLaunchDate.HasValue); + + RuleFor(x => x.PayloadWeightKg.Value) + .GreaterThanOrEqualTo(0d) + .When(x => x.PayloadWeightKg.HasValue); + } + } } [ExtendObjectType(OperationTypeNames.Mutation)] @@ -27,7 +84,6 @@ public partial class RocketMutation { [UseRequestScope] public partial Task CreateRocket( - [Service] IMediator mediator, CreateRocket.Request request, CancellationToken cancellationToken @@ -292,4 +348,4 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) descriptor.AllowAnd(false).AllowOr(false); } -} \ No newline at end of file +} diff --git a/sample/Sample.Grpc/Sample.Grpc.csproj b/sample/Sample.Grpc/Sample.Grpc.csproj index e66ca39e0..7f43f6172 100644 --- a/sample/Sample.Grpc/Sample.Grpc.csproj +++ b/sample/Sample.Grpc/Sample.Grpc.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 diff --git a/sample/Sample.Minimal/CustomHostedService.cs b/sample/Sample.Minimal/CustomHostedService.cs new file mode 100644 index 000000000..7b7f090a8 --- /dev/null +++ b/sample/Sample.Minimal/CustomHostedService.cs @@ -0,0 +1,11 @@ +using Microsoft.Extensions.Options; + +internal class CustomHostedService(IOptions options) : BackgroundService +{ + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + // ReSharper disable once UnusedVariable + var v = options.Value.A; + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/sample/Sample.Minimal/CustomHostedServiceOptions.cs b/sample/Sample.Minimal/CustomHostedServiceOptions.cs new file mode 100644 index 000000000..c3c34467c --- /dev/null +++ b/sample/Sample.Minimal/CustomHostedServiceOptions.cs @@ -0,0 +1,15 @@ +using FluentValidation; + +internal class CustomHostedServiceOptions +{ + public string? A { get; set; } + + [UsedImplicitly] + private sealed class Validator : AbstractValidator + { + public Validator() + { + RuleFor(z => z.A).NotNull(); + } + } +} \ No newline at end of file diff --git a/sample/Sample.Minimal/LaunchRecordEndpoints.cs b/sample/Sample.Minimal/LaunchRecordEndpoints.cs new file mode 100644 index 000000000..35994498c --- /dev/null +++ b/sample/Sample.Minimal/LaunchRecordEndpoints.cs @@ -0,0 +1,69 @@ +using MediatR; +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; +using Sample.Core.Domain; +using Sample.Core.Models; +using Sample.Core.Operations.LaunchRecords; + +internal static partial class LaunchRecordEndpoints +{ + public static void UseLaunchRecords(this WebApplication app) + { + app.MapGet("/launch-records", ListLaunchRecords); + app.MapGet("/launch-records/{id:guid}", GetLaunchRecord); + app.MapPost("/launch-records", CreateLaunchRecord); + app.MapPut("/launch-records/{id:guid}", EditLaunchRecord); + app.MapPatch("/launch-records/{id:guid}", PatchLaunchRecord); + app.MapDelete("/launch-records/{id:guid}", DeleteLaunchRecord); + } + + [EndpointName(nameof(ListLaunchRecords))] + private static Ok> ListLaunchRecords(IMediator mediator, RocketType? rocketType) => + TypedResults.Ok(mediator.CreateStream(new ListLaunchRecords.Request(rocketType))); + + [EndpointName(nameof(GetLaunchRecord))] + private static async Task, NotFound, ProblemHttpResult>> GetLaunchRecord( + IMediator mediator, + LaunchRecordId id, + CancellationToken cancellationToken + ) => TypedResults.Ok(await mediator.Send(new GetLaunchRecord.Request(id), cancellationToken)); + + [EndpointName(nameof(CreateLaunchRecord))] + private static async Task, ProblemHttpResult, BadRequest>> CreateLaunchRecord( + IMediator mediator, + CreateLaunchRecord.Request request + ) + { + return TypedResults.CreatedAtRoute(await mediator.Send(request), nameof(GetLaunchRecord)); + } + + /// + /// Does this comment get picked up? + /// + /// + /// + /// + /// + [EndpointName(nameof(EditLaunchRecord))] + private static async Task, NotFound, ProblemHttpResult, BadRequest>> EditLaunchRecord( + IMediator mediator, + [FromRoute] + LaunchRecordId id, + EditLaunchRecord.Request model + ) => TypedResults.Ok(await mediator.Send(model with { Id = id })); + + [EndpointName(nameof(PatchLaunchRecord))] + private static async Task, NotFound, ProblemHttpResult, BadRequest>> PatchLaunchRecord( + IMediator mediator, + [FromRoute] + LaunchRecordId id, + EditLaunchRecord.PatchRequest model + ) => TypedResults.Ok(await mediator.Send(model with { Id = id })); + + [EndpointName(nameof(DeleteLaunchRecord))] + private static async Task> DeleteLaunchRecord(IMediator mediator, [FromRoute] LaunchRecordId id) + { + await mediator.Send(new DeleteLaunchRecord.Request(id)); + return TypedResults.NoContent(); + } +} diff --git a/sample/Sample.Minimal/Program.cs b/sample/Sample.Minimal/Program.cs new file mode 100644 index 000000000..e85f879d9 --- /dev/null +++ b/sample/Sample.Minimal/Program.cs @@ -0,0 +1,56 @@ +using System.Reflection; +using System.Text; +using System.Text.Json; +using Humanizer; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Rocket.Surgery.Hosting; +using Rocket.Surgery.LaunchPad.AspNetCore; +using Sample.Minimal; +using Serilog; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers().AddControllersAsServices(); +builder.Services.AddHostedService(); + +var app = await builder + .LaunchWith(RocketBooster.For(Imports.Instance)); +app.UseExceptionHandler(); +app.UseHttpsRedirection(); + +// Should this move into an extension method? +app.UseSerilogRequestLogging( + x => + { + x.GetLevel = LaunchPadHelpers.DefaultGetLevel; + x.EnrichDiagnosticContext = LaunchPadHelpers.DefaultEnrichDiagnosticContext; + } +); + +app.UseRouting(); +app.MapOpenApi(); + +app + .UseSwaggerUI() + .UseReDoc(); +app.UseLaunchRecords(); +app.UseRockets(); + +app.UseAuthorization(); +app.MapHealthChecks( + "/health", + new() + { + ResponseWriter = LaunchPadHelpers.DefaultResponseWriter, + ResultStatusCodes = new Dictionary + { + { HealthStatus.Healthy, StatusCodes.Status200OK }, + { HealthStatus.Degraded, StatusCodes.Status500InternalServerError }, + { HealthStatus.Unhealthy, StatusCodes.Status503ServiceUnavailable }, + }, + } +); + +app.Run(); + +public partial class Program; diff --git a/sample/Sample.Minimal/RocketEndpoints.cs b/sample/Sample.Minimal/RocketEndpoints.cs new file mode 100644 index 000000000..924528c8d --- /dev/null +++ b/sample/Sample.Minimal/RocketEndpoints.cs @@ -0,0 +1,58 @@ +using MediatR; +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; +using Sample.Core.Domain; +using Sample.Core.Models; +using Sample.Core.Operations.Rockets; + +internal static partial class RocketEndpoints +{ + public static void UseRockets(this WebApplication app) + { + app.MapGet("/rockets", ListRockets); + app.MapGet("/rockets/{id:guid}", GetRocket); + app.MapPost("/rockets", CreateRocket); + app.MapPut("/rockets/{id:guid}", EditRocket); + app.MapPatch("/rockets/{id:guid}", PatchRocket); + app.MapDelete("/rockets/{id:guid}", DeleteRocket); + } + + private static Ok> ListRockets(IMediator mediator, RocketType? rocketType) => + TypedResults.Ok(mediator.CreateStream(new ListRockets.Request(rocketType))); + + private static async Task, NotFound, ProblemHttpResult>> GetRocket( + IMediator mediator, + [FromRoute] + RocketId id, + CancellationToken cancellationToken + ) => TypedResults.Ok(await mediator.Send(new GetRocket.Request(id), cancellationToken)); + + private static async Task, ProblemHttpResult, BadRequest>> CreateRocket( + IMediator mediator, + CreateRocket.Request request + ) + { + return TypedResults.CreatedAtRoute(await mediator.Send(request), nameof(GetRocket)); + } + + private static async Task, NotFound, ProblemHttpResult, BadRequest>> EditRocket( + IMediator mediator, + [FromRoute] + RocketId id, + EditRocket.Request model + ) => TypedResults.Ok(await mediator.Send(model with { Id = id })); + + private static async Task, NotFound, ProblemHttpResult, BadRequest>> PatchRocket( + IMediator mediator, + [FromRoute] + RocketId id, + EditRocket.PatchRequest model + ) => TypedResults.Ok(await mediator.Send(model with { Id = id })); + + [HttpDelete("{id:guid}")] + private static async Task> DeleteRocket(IMediator mediator, [FromRoute] RocketId id) + { + await mediator.Send(new DeleteRocket.Request(id)); + return TypedResults.NoContent(); + } +} diff --git a/sample/Sample.Minimal/Sample.Minimal.csproj b/sample/Sample.Minimal/Sample.Minimal.csproj new file mode 100644 index 000000000..3ecab040b --- /dev/null +++ b/sample/Sample.Minimal/Sample.Minimal.csproj @@ -0,0 +1,22 @@ + + + net9.0 + true + true + true + true + + + + + + + + + + + diff --git a/sample/Sample.Minimal/appsettings.Development.yml b/sample/Sample.Minimal/appsettings.Development.yml new file mode 100644 index 000000000..881ec6e17 --- /dev/null +++ b/sample/Sample.Minimal/appsettings.Development.yml @@ -0,0 +1,8 @@ +Serilog: + MinimumLevel: + Default: Debug + Override: + Microsoft: 'Information' + Microsoft.AspNetCore.Hosting.Diagnostics: 'Debug' + Microsoft.Hosting.Lifetime": 'Debug' + System": 'Information' diff --git a/sample/Sample.Minimal/appsettings.yml b/sample/Sample.Minimal/appsettings.yml new file mode 100644 index 000000000..fecfd222f --- /dev/null +++ b/sample/Sample.Minimal/appsettings.yml @@ -0,0 +1,17 @@ +Serilog: + MinimumLevel: + Default: 'Information' + Override: + Microsoft: 'Warning' + Microsoft.AspNetCore.Hosting.Diagnostics: 'Information' + Microsoft.Hosting.Lifetime: 'Information' + System: 'Warning' +AllowedHosts: '*' +Metrics: + Enabled: true + Tracking: + ApdexTrackingEnabled: true + ApdexTSeconds: 0.1 + IgnoredHttpStatusCodes: [404] + IgnoredRoutesRegexPatterns: [] + OAuth2TrackingEnabled: true diff --git a/sample/Sample.Pages/Pages/Index.cshtml b/sample/Sample.Pages/Pages/Index.cshtml index 0697a99c0..f1b0fb4c7 100644 --- a/sample/Sample.Pages/Pages/Index.cshtml +++ b/sample/Sample.Pages/Pages/Index.cshtml @@ -1,4 +1,5 @@ @page +@using Microsoft.AspNetCore.Mvc.TagHelpers @model IndexModel @{ ViewData["Title"] = "Home page"; diff --git a/sample/Sample.Pages/Pages/Rockets/Create.cshtml b/sample/Sample.Pages/Pages/Rockets/Create.cshtml index 96c1edf0a..e5959bc82 100644 --- a/sample/Sample.Pages/Pages/Rockets/Create.cshtml +++ b/sample/Sample.Pages/Pages/Rockets/Create.cshtml @@ -1,4 +1,5 @@ @page +@using Microsoft.AspNetCore.Mvc.TagHelpers @using Sample.Core.Domain @model RocketCreateModel diff --git a/sample/Sample.Pages/Pages/Rockets/Delete.cshtml b/sample/Sample.Pages/Pages/Rockets/Delete.cshtml index 359e24126..cc007ecee 100644 --- a/sample/Sample.Pages/Pages/Rockets/Delete.cshtml +++ b/sample/Sample.Pages/Pages/Rockets/Delete.cshtml @@ -1,4 +1,5 @@ @page "{id:guid}" +@using Microsoft.AspNetCore.Mvc.TagHelpers @model RocketDeleteModel
diff --git a/sample/Sample.Pages/Pages/Rockets/Delete.cshtml.cs b/sample/Sample.Pages/Pages/Rockets/Delete.cshtml.cs index a298cd8d1..da11d09f3 100644 --- a/sample/Sample.Pages/Pages/Rockets/Delete.cshtml.cs +++ b/sample/Sample.Pages/Pages/Rockets/Delete.cshtml.cs @@ -7,7 +7,7 @@ public class RocketDeleteModel : RocketViewModel { public async Task OnPost() { - await Send(new DeleteRocket.Request { Id = Id }); + await Send(new DeleteRocket.Request(Id)); return RedirectToPage("Index"); } } diff --git a/sample/Sample.Pages/Pages/Rockets/Edit.cshtml b/sample/Sample.Pages/Pages/Rockets/Edit.cshtml index c37894bc7..ebfd74c90 100644 --- a/sample/Sample.Pages/Pages/Rockets/Edit.cshtml +++ b/sample/Sample.Pages/Pages/Rockets/Edit.cshtml @@ -1,4 +1,5 @@ @page "{id:guid}" +@using Microsoft.AspNetCore.Mvc.TagHelpers @using Sample.Core.Domain @model RocketEditModel diff --git a/sample/Sample.Pages/Pages/Rockets/Index.cshtml b/sample/Sample.Pages/Pages/Rockets/Index.cshtml index 070bb984a..f9aba0f68 100644 --- a/sample/Sample.Pages/Pages/Rockets/Index.cshtml +++ b/sample/Sample.Pages/Pages/Rockets/Index.cshtml @@ -1,4 +1,5 @@ @page +@using Microsoft.AspNetCore.Mvc.TagHelpers @model RocketIndexModel Create diff --git a/sample/Sample.Pages/Pages/Rockets/View.cshtml b/sample/Sample.Pages/Pages/Rockets/View.cshtml index 9e7b2030f..04fa2beaf 100644 --- a/sample/Sample.Pages/Pages/Rockets/View.cshtml +++ b/sample/Sample.Pages/Pages/Rockets/View.cshtml @@ -1,4 +1,5 @@ @page "{id:guid}" +@using Microsoft.AspNetCore.Mvc.TagHelpers @model RocketViewModel
+@using Microsoft.AspNetCore.Mvc.TagHelpers + diff --git a/sample/Sample.Pages/Program.cs b/sample/Sample.Pages/Program.cs index 078dbfab6..cce8d0756 100644 --- a/sample/Sample.Pages/Program.cs +++ b/sample/Sample.Pages/Program.cs @@ -36,7 +36,7 @@ "/health", new() { - ResponseWriter = WriteResponse, + ResponseWriter = LaunchPadHelpers.DefaultResponseWriter, ResultStatusCodes = new Dictionary { { HealthStatus.Healthy, StatusCodes.Status200OK }, @@ -125,4 +125,4 @@ static Task WriteResponse(HttpContext context, HealthReport healthReport) ); } -public partial class Program; \ No newline at end of file +public partial class Program; diff --git a/sample/Sample.Pages/Sample.Pages.csproj b/sample/Sample.Pages/Sample.Pages.csproj index d024166fb..01dfdf89a 100644 --- a/sample/Sample.Pages/Sample.Pages.csproj +++ b/sample/Sample.Pages/Sample.Pages.csproj @@ -1,6 +1,6 @@ - net8.0 + net9.0 diff --git a/sample/Sample.Restful.Client/Sample.Restful.Client.csproj b/sample/Sample.Restful.Client/Sample.Restful.Client.csproj index 123ef9988..7f4e37c76 100644 --- a/sample/Sample.Restful.Client/Sample.Restful.Client.csproj +++ b/sample/Sample.Restful.Client/Sample.Restful.Client.csproj @@ -1,6 +1,6 @@ - net8.0 + net8.0;net9.0 true @@ -11,12 +11,11 @@ /> - + - - \ No newline at end of file + diff --git a/sample/Sample.Restful/Controllers/WeatherForecastController.cs b/sample/Sample.Restful/Controllers/WeatherForecastController.cs deleted file mode 100644 index eb13fa488..000000000 --- a/sample/Sample.Restful/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Rocket.Surgery.LaunchPad.AspNetCore; - -namespace Sample.Restful.Controllers; - -[Route("[controller]")] -public class WeatherForecastController : RestfulApiController -{ - private static readonly string[] Summaries = - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - [HttpGet] - public IEnumerable Get() - { - var rng = new Random(); - return Enumerable.Range(1, 5).Select( - index => new WeatherForecast - { - Date = DateTime.Now.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - } - ) - .ToArray(); - } -} diff --git a/sample/Sample.Restful/Program.cs b/sample/Sample.Restful/Program.cs index 4394549e8..1c2a1a76f 100644 --- a/sample/Sample.Restful/Program.cs +++ b/sample/Sample.Restful/Program.cs @@ -2,7 +2,6 @@ using System.Text; using System.Text.Json; using FluentValidation; -using Hellang.Middleware.ProblemDetails; using Humanizer; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Options; @@ -10,41 +9,29 @@ using Rocket.Surgery.LaunchPad.AspNetCore; using Sample.Restful; using Serilog; -using Swashbuckle.AspNetCore.SwaggerGen; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers().AddControllersAsServices(); builder.Services.AddHostedService(); -builder.Services - .Configure( - c => c.SwaggerDoc( - "v1", - new() - { - Version = typeof(Program).GetCustomAttribute()?.Version - ?? typeof(Program).GetCustomAttribute()?.Version ?? "0.1.0", - Title = "Test Application", - } - ) - ); var app = await builder .LaunchWith(RocketBooster.For(Imports.Instance)); -app.UseProblemDetails(); +app.UseExceptionHandler(); app.UseHttpsRedirection(); // Should this move into an extension method? app.UseSerilogRequestLogging( x => { - x.GetLevel = LaunchPadLogHelpers.DefaultGetLevel; - x.EnrichDiagnosticContext = LaunchPadLogHelpers.DefaultEnrichDiagnosticContext; + x.GetLevel = LaunchPadHelpers.DefaultGetLevel; + x.EnrichDiagnosticContext = LaunchPadHelpers.DefaultEnrichDiagnosticContext; } ); app.UseRouting(); +app.MapOpenApi(); app .UseSwaggerUI() .UseReDoc(); @@ -54,7 +41,7 @@ "/health", new() { - ResponseWriter = WriteResponse, + ResponseWriter = LaunchPadHelpers.DefaultResponseWriter, ResultStatusCodes = new Dictionary { { HealthStatus.Healthy, StatusCodes.Status200OK }, @@ -66,87 +53,8 @@ app.MapControllers(); -// Should this move into an extension method? -app.MapSwagger(); - app.Run(); -static Task WriteResponse(HttpContext context, HealthReport healthReport) -{ - context.Response.ContentType = "application/json; charset=utf-8"; - - var options = new JsonWriterOptions { Indented = true, }; - - using var memoryStream = new MemoryStream(); - using (var jsonWriter = new Utf8JsonWriter(memoryStream, options)) - { - jsonWriter.WriteStartObject(); - jsonWriter.WriteString("status", healthReport.Status.ToString()); - jsonWriter.WriteStartObject("results"); - - foreach (var healthReportEntry in healthReport.Entries) - { - jsonWriter.WriteStartObject(healthReportEntry.Key); - jsonWriter.WriteString( - "status", - healthReportEntry.Value.Status.ToString() - ); - jsonWriter.WriteString( - "duration", - healthReportEntry.Value.Duration.Humanize() - ); - jsonWriter.WriteString( - "description", - healthReportEntry.Value.Description - ); - - jsonWriter.WriteStartObject("data"); - foreach (var item in healthReportEntry.Value.Data) - { - jsonWriter.WritePropertyName(item.Key); - - JsonSerializer.Serialize( - jsonWriter, - item.Value, - item.Value.GetType() - ); - } - - jsonWriter.WriteEndObject(); - - if (healthReportEntry.Value.Tags.Any()) - { - jsonWriter.WriteStartArray("tags"); - foreach (var item in healthReportEntry.Value.Tags) - { - jsonWriter.WriteStringValue(item); - } - - jsonWriter.WriteEndArray(); - } - - if (healthReportEntry.Value.Exception != null) - { - var ex = healthReportEntry.Value.Exception; - jsonWriter.WriteStartObject("exception"); - jsonWriter.WriteString("message", ex.Message); - jsonWriter.WriteString("stacktrace", ex.StackTrace); - jsonWriter.WriteString("inner", ex.InnerException?.ToString()); - jsonWriter.WriteEndObject(); - } - - jsonWriter.WriteEndObject(); - } - - jsonWriter.WriteEndObject(); - jsonWriter.WriteEndObject(); - } - - return context.Response.WriteAsync( - Encoding.UTF8.GetString(memoryStream.ToArray()) - ); -} - public partial class Program; internal class CustomHostedServiceOptions @@ -171,4 +79,4 @@ protected override Task ExecuteAsync(CancellationToken stoppingToken) var v = options.Value.A; return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/sample/Sample.Restful/Sample.Restful.csproj b/sample/Sample.Restful/Sample.Restful.csproj index b705e2f5a..5747ca074 100644 --- a/sample/Sample.Restful/Sample.Restful.csproj +++ b/sample/Sample.Restful/Sample.Restful.csproj @@ -1,6 +1,6 @@ - net8.0 + net9.0 true true true @@ -9,10 +9,13 @@ - + + + diff --git a/sample/Sample.Restful/WeatherForecast.cs b/sample/Sample.Restful/WeatherForecast.cs deleted file mode 100644 index 05ed7c985..000000000 --- a/sample/Sample.Restful/WeatherForecast.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Sample.Restful; - -public class WeatherForecast -{ - public DateTime Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)( TemperatureC / 0.5556 ); - - public string? Summary { get; set; } -} diff --git a/sample/Sample.Worker/Sample.Worker.csproj b/sample/Sample.Worker/Sample.Worker.csproj index e3bc18b8a..7ee64884a 100644 --- a/sample/Sample.Worker/Sample.Worker.csproj +++ b/sample/Sample.Worker/Sample.Worker.csproj @@ -1,6 +1,6 @@ - net8.0 + net8.0;net9.0 diff --git a/src/Analyzers.roslyn4.6/Rocket.Surgery.LaunchPad.Analyzers.roslyn4.6.csproj b/src/Analyzers.roslyn4.6/Rocket.Surgery.LaunchPad.Analyzers.roslyn4.6.csproj index bd2b9103e..210580076 100644 --- a/src/Analyzers.roslyn4.6/Rocket.Surgery.LaunchPad.Analyzers.roslyn4.6.csproj +++ b/src/Analyzers.roslyn4.6/Rocket.Surgery.LaunchPad.Analyzers.roslyn4.6.csproj @@ -12,7 +12,9 @@ - + + 4.11.0 + diff --git a/src/Analyzers/GraphqlMutationActionBodyGenerator.cs b/src/Analyzers/GraphqlMutationActionBodyGenerator.cs index 019b583bc..2a884a155 100644 --- a/src/Analyzers/GraphqlMutationActionBodyGenerator.cs +++ b/src/Analyzers/GraphqlMutationActionBodyGenerator.cs @@ -18,24 +18,24 @@ public class GraphqlMutationActionBodyGenerator : IIncrementalGenerator INamedTypeSymbol mediator, INamedTypeSymbol claimsPrincipal, INamedTypeSymbol cancellationToken, - MethodDeclarationSyntax syntax, - IMethodSymbol symbol, - IParameterSymbol parameter, + MethodDeclarationSyntax methodSyntax, + IMethodSymbol methodSymbol, + IParameterSymbol requestParameter, ITypeSymbol requestType, ExpressionSyntax requestExpression ) { - var otherParams = symbol.Parameters.Remove(parameter); + var otherParams = methodSymbol.Parameters.Remove(requestParameter); var isUnit = requestType.AllInterfaces.Any(z => z.MetadataName == "IRequest"); - var returnsMediatorUnit = symbol.ReturnType is INamedTypeSymbol { MetadataName: "Task`1", TypeArguments: [{ MetadataName: "Unit", },], }; + var returnsMediatorUnit = methodSymbol.ReturnType is INamedTypeSymbol { MetadataName: "Task`1", TypeArguments: [{ MetadataName: "Unit", },], }; isUnit = returnsMediatorUnit || isUnit; - var isStream = symbol.ReturnType.MetadataName == "IAsyncEnumerable`1"; + var isStream = methodSymbol.ReturnType.MetadataName == "IAsyncEnumerable`1"; - var newSyntax = syntax + var newSyntax = methodSyntax .WithParameterList( // ReSharper disable once NullableWarningSuppressionIsUsed - syntax.ParameterList.RemoveNodes( - syntax.ParameterList.Parameters.SelectMany(z => z.AttributeLists), + methodSyntax.ParameterList.RemoveNodes( + methodSyntax.ParameterList.Parameters.SelectMany(z => z.AttributeLists), SyntaxRemoveOptions.KeepNoTrivia )! ) @@ -47,7 +47,7 @@ ExpressionSyntax requestExpression .AddModifiers(Token(SyntaxKind.AsyncKeyword)); var block = Block(); - var resultName = parameter.Name == "result" ? "r" : "result"; + var resultName = requestParameter.Name == "result" ? "r" : "result"; var mediatorParameter = otherParams.FirstOrDefault(param => SymbolEqualityComparer.Default.Equals(mediator, param.Type)); var claimsPrincipalParameter = otherParams.FirstOrDefault(param => SymbolEqualityComparer.Default.Equals(claimsPrincipal, param.Type)); var cancellationTokenParameter = otherParams.FirstOrDefault(param => SymbolEqualityComparer.Default.Equals(cancellationToken, param.Type)); @@ -55,8 +55,8 @@ ExpressionSyntax requestExpression context.ReportDiagnostic( Diagnostic.Create( GeneratorDiagnostics.ParameterMustExist, - parameter.Locations.First(), - parameter.Locations.Skip(1), + requestParameter.Locations.First(), + requestParameter.Locations.Skip(1), mediator, requestType.Name ) @@ -79,8 +79,8 @@ ExpressionSyntax requestExpression context.ReportDiagnostic( Diagnostic.Create( GeneratorDiagnostics.ParameterMustExist, - parameter.Locations.First(), - parameter.Locations.Skip(1), + requestParameter.Locations.First(), + requestParameter.Locations.Skip(1), claimsPrincipal, requestType.Name ) @@ -95,10 +95,10 @@ ExpressionSyntax requestExpression VariableDeclaration( IdentifierName(Identifier(TriviaList(), SyntaxKind.VarKeyword, "var", "var", TriviaList())) ) - .WithVariables(SingletonSeparatedList(VariableDeclarator("_" + parameter.Name).WithInitializer(EqualsValueClause(requestExpression)))) + .WithVariables(SingletonSeparatedList(VariableDeclarator("_" + requestParameter.Name).WithInitializer(EqualsValueClause(requestExpression)))) ) ); - requestExpression = IdentifierName("_" + parameter.Name); + requestExpression = IdentifierName("_" + requestParameter.Name); } var sendRequestExpression = isStream @@ -118,7 +118,6 @@ ExpressionSyntax requestExpression ) ); } - if (requestType.IsRecord) { var expressions = new List(); @@ -167,7 +166,7 @@ ExpressionSyntax requestExpression SyntaxKind.SimpleAssignmentExpression, MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("_" + parameter.Name), + IdentifierName("_" + requestParameter.Name), IdentifierName(claimsPrincipalProperty!.Name) ), IdentifierName(claimsPrincipalParameter!.Name) @@ -201,6 +200,20 @@ ExpressionSyntax requestExpression ReturnStatement(IdentifierName(resultName)) ); +// if (compilation.GetTypeByMetadataName("FluentValidation.ValidationException") is { }) +// { +// newSyntax = newSyntax.AddAttributeLists(GetErrorAttribute("FluentValidation.ValidationException")); +// } +// +// if (compilation.GetTypeByMetadataName("Rocket.Surgery.LaunchPad.Foundation.NotFoundException") is { }) +// { +// newSyntax = newSyntax.AddAttributeLists(GetErrorAttribute("Rocket.Surgery.LaunchPad.Foundation.NotFoundException")); +// } +// +// if (compilation.GetTypeByMetadataName("Rocket.Surgery.LaunchPad.Foundation.RequestFailedException") is { }) +// { +// newSyntax = newSyntax.AddAttributeLists(GetErrorAttribute("Rocket.Surgery.LaunchPad.Foundation.RequestFailedException")); +// } return newSyntax .WithBody(block.NormalizeWhitespace()) @@ -262,6 +275,30 @@ static ExpressionSyntax streamMediatorRequest( ) .WithArgumentList(ArgumentList(SeparatedList(arguments))); } + + static AttributeListSyntax GetErrorAttribute(string exceptionType) + { + return AttributeList( + SingletonSeparatedList( + Attribute( + QualifiedName( + QualifiedName( + IdentifierName("HotChocolate"), + IdentifierName("Types") + ), + GenericName( + Identifier("Error") + ) + .WithTypeArgumentList( + TypeArgumentList( + SingletonSeparatedList(ParseName(exceptionType)) + ) + ) + ) + ) + ) + ); + } } private void GenerateMethods( @@ -435,4 +472,4 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterSourceOutput(syntaxProvider, GenerateMethods); } -} \ No newline at end of file +} diff --git a/src/Analyzers/PropertyTrackingGenerator.cs b/src/Analyzers/PropertyTrackingGenerator.cs index 1dd84ee05..a3beeb9e1 100644 --- a/src/Analyzers/PropertyTrackingGenerator.cs +++ b/src/Analyzers/PropertyTrackingGenerator.cs @@ -215,13 +215,11 @@ INamedTypeSymbol targetSymbol AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, IdentifierName(propertySymbol.Name), - InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(propertySymbol.Name), - IdentifierName("HasBeenSet") + IdentifierName("HasValue") ) - ) ) ); createMethodInitializer = createMethodInitializer.AddExpressions( @@ -405,13 +403,11 @@ bool isRecord ) { return IfStatement( - InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(propertySymbol.Name), - IdentifierName("HasBeenSet") - ) - ), + IdentifierName("HasValue") + ), Block( SingletonList( ExpressionStatement( @@ -541,4 +537,4 @@ and TypeDeclarationSyntax static (productionContext, tuple) => GeneratePropertyTracking(productionContext, tuple.syntax, tuple.symbol, tuple.targetSymbol!) ); } -} \ No newline at end of file +} diff --git a/src/AspNetCore.Blazor/Rocket.Surgery.LaunchPad.AspNetCore.Blazor.csproj b/src/AspNetCore.Blazor/Rocket.Surgery.LaunchPad.AspNetCore.Blazor.csproj index a56f57d9f..c2e5e8b02 100644 --- a/src/AspNetCore.Blazor/Rocket.Surgery.LaunchPad.AspNetCore.Blazor.csproj +++ b/src/AspNetCore.Blazor/Rocket.Surgery.LaunchPad.AspNetCore.Blazor.csproj @@ -1,6 +1,6 @@  - net8.0 + net9.0 $(PackageTags) diff --git a/src/AspNetCore.FluentValidation.OpenApi/BetweenPropertyRule.cs b/src/AspNetCore.FluentValidation.OpenApi/BetweenPropertyRule.cs new file mode 100644 index 000000000..548a12783 --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/BetweenPropertyRule.cs @@ -0,0 +1,27 @@ +using FluentValidation.Validators; + +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +[Experimental(Constants.ExperimentalId)] +public sealed class BetweenPropertyRule : IPropertyRuleHandler +{ + Task IPropertyRuleHandler.HandleAsync(OpenApiValidationContext context, CancellationToken cancellationToken) + { + if (context is not { PropertyValidator: IBetweenValidator validator }) return Task.CompletedTask; + + var schemaProperty = context.PropertySchema; + if (validator.From.IsNumeric()) + { + schemaProperty.Minimum = Convert.ToDecimal(validator.From); + if (validator.Name == "ExclusiveBetweenValidator") schemaProperty.ExclusiveMinimum = true; + } + + if (validator.To.IsNumeric()) + { + schemaProperty.Maximum = Convert.ToDecimal(validator.To); + if (validator.Name == "ExclusiveBetweenValidator") schemaProperty.ExclusiveMaximum = true; + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/AspNetCore.FluentValidation.OpenApi/ComparisonPropertyRule.cs b/src/AspNetCore.FluentValidation.OpenApi/ComparisonPropertyRule.cs new file mode 100644 index 000000000..c9709a43f --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/ComparisonPropertyRule.cs @@ -0,0 +1,47 @@ +using FluentValidation.Validators; + +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +[Experimental(Constants.ExperimentalId)] +public sealed class ComparisonPropertyRule : IPropertyRuleHandler +{ + Task IPropertyRuleHandler.HandleAsync(OpenApiValidationContext context, CancellationToken cancellationToken) + { + if (context is not { PropertyValidator: IComparisonValidator validator }) return Task.CompletedTask; + + if (!validator.ValueToCompare.IsNumeric()) return Task.CompletedTask; + var valueToCompare = Convert.ToDecimal(validator.ValueToCompare); + var schemaProperty = context.PropertySchema; + + switch (validator) + { + case { Comparison: Comparison.GreaterThanOrEqual }: + { + schemaProperty.Minimum = valueToCompare; + return Task.CompletedTask; + } + + case { Comparison: Comparison.GreaterThan }: + { + schemaProperty.Minimum = valueToCompare; + schemaProperty.ExclusiveMinimum = true; + return Task.CompletedTask; + } + + case { Comparison: Comparison.LessThanOrEqual }: + { + schemaProperty.Maximum = valueToCompare; + return Task.CompletedTask; + } + + case { Comparison: Comparison.LessThan }: + { + schemaProperty.Maximum = valueToCompare; + schemaProperty.ExclusiveMaximum = true; + return Task.CompletedTask; + } + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/AspNetCore.FluentValidation.OpenApi/Constants.cs b/src/AspNetCore.FluentValidation.OpenApi/Constants.cs new file mode 100644 index 000000000..f83d11db1 --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/Constants.cs @@ -0,0 +1,8 @@ +using System.Numerics; + +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +internal static class Constants +{ + public const string ExperimentalId = "RSGEXP"; +} diff --git a/src/AspNetCore.FluentValidation.OpenApi/EmailPropertyRule.cs b/src/AspNetCore.FluentValidation.OpenApi/EmailPropertyRule.cs new file mode 100644 index 000000000..470a01cb0 --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/EmailPropertyRule.cs @@ -0,0 +1,14 @@ +using FluentValidation.Validators; + +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +[Experimental(Constants.ExperimentalId)] +public sealed class EmailPropertyRule : IPropertyRuleHandler +{ + Task IPropertyRuleHandler.HandleAsync(OpenApiValidationContext context, CancellationToken cancellationToken) + { + if (context is not { PropertyValidator: IEmailValidator validator }) return Task.CompletedTask; + context.PropertySchema.Format = "email"; + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/AspNetCore.FluentValidation.OpenApi/Extensions.cs b/src/AspNetCore.FluentValidation.OpenApi/Extensions.cs new file mode 100644 index 000000000..f955e820b --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/Extensions.cs @@ -0,0 +1,6 @@ +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +internal static class Extensions +{ + internal static bool IsNumeric(this object value) => value is int || value is long || value is float || value is double || value is decimal; +} \ No newline at end of file diff --git a/src/AspNetCore.FluentValidation.OpenApi/FluentValidationOpenApiSchemaTransformer.cs b/src/AspNetCore.FluentValidation.OpenApi/FluentValidationOpenApiSchemaTransformer.cs new file mode 100644 index 000000000..90bd81c38 --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/FluentValidationOpenApiSchemaTransformer.cs @@ -0,0 +1,44 @@ +using FluentValidation; +using Microsoft.AspNetCore.OpenApi; +using Microsoft.OpenApi.Models; + +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +[Experimental(Constants.ExperimentalId)] +public class FluentValidationOpenApiSchemaTransformer(IEnumerable ruleDefinitionHandlers) : IOpenApiSchemaTransformer +{ + public async Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken) + { + var validatorType = typeof(IValidator<>).MakeGenericType(context.JsonTypeInfo.Type); + if (context.ApplicationServices.GetService(validatorType) is not IValidator validator) return; + + var descriptor = validator.CreateDescriptor(); + foreach (var (validators, propertyKey, property) in descriptor + .GetMembersWithValidators() + .Join( + schema.Properties, + z => z.Key, + z => z.Key, + (z, y) => ( validators: z.AsEnumerable(), property: y.Key, schema: y.Value ), + StringComparer.OrdinalIgnoreCase + )) + + { + foreach (var (propertyValidator, component) in validators) + { + foreach (var item in ruleDefinitionHandlers) + { + var ctx = new OpenApiValidationContext( + schema, + property, + context, + propertyValidator, + propertyKey, + component + ); + await item.HandleAsync(ctx, cancellationToken); + } + } + } + } +} diff --git a/src/AspNetCore.FluentValidation.OpenApi/IPropertyRuleHandler.cs b/src/AspNetCore.FluentValidation.OpenApi/IPropertyRuleHandler.cs new file mode 100644 index 000000000..d4de4498a --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/IPropertyRuleHandler.cs @@ -0,0 +1,7 @@ +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +[Experimental(Constants.ExperimentalId)] +public interface IPropertyRuleHandler +{ + Task HandleAsync(OpenApiValidationContext context, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/AspNetCore.FluentValidation.OpenApi/LengthPropertyRule.cs b/src/AspNetCore.FluentValidation.OpenApi/LengthPropertyRule.cs new file mode 100644 index 000000000..a914e0869 --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/LengthPropertyRule.cs @@ -0,0 +1,30 @@ +using FluentValidation.Validators; + +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +[Experimental(Constants.ExperimentalId)] +public sealed class LengthPropertyRule : IPropertyRuleHandler +{ + Task IPropertyRuleHandler.HandleAsync(OpenApiValidationContext context, CancellationToken cancellationToken) + { + if (context.PropertyValidator is not ILengthValidator validator) return Task.CompletedTask; + + if (context.PropertySchema.Type == "array") + { + if (validator.Max > 0) + context.PropertySchema.MaxItems = validator.Max; + + if (validator.Min > 0) + context.PropertySchema.MinItems = validator.Min; + } + else + { + if (validator.Max > 0) + context.PropertySchema.MaxLength = validator.Max; + if (validator.Min > 0) + context.PropertySchema.MinLength = validator.Min; + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/AspNetCore.FluentValidation.OpenApi/NotEmptyPropertyRule.cs b/src/AspNetCore.FluentValidation.OpenApi/NotEmptyPropertyRule.cs new file mode 100644 index 000000000..bc1f8992c --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/NotEmptyPropertyRule.cs @@ -0,0 +1,15 @@ +using FluentValidation.Validators; + +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +[Experimental(Constants.ExperimentalId)] +public sealed class NotEmptyPropertyRule : IPropertyRuleHandler +{ + Task IPropertyRuleHandler.HandleAsync(OpenApiValidationContext context, CancellationToken cancellationToken) + { + if (context is { PropertyValidator: not INotEmptyValidator } or { PropertySchema.MinLength: > 1 } or { PropertySchema.Type: not ("string" or "array") }) + return Task.CompletedTask; + context.PropertySchema.MinLength = 1; + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/AspNetCore.FluentValidation.OpenApi/OpenApiValidationContext.cs b/src/AspNetCore.FluentValidation.OpenApi/OpenApiValidationContext.cs new file mode 100644 index 000000000..4354cdb92 --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/OpenApiValidationContext.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization.Metadata; +using FluentValidation.Internal; +using FluentValidation.Validators; +using Microsoft.AspNetCore.OpenApi; +using Microsoft.OpenApi.Models; + +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +[Experimental(Constants.ExperimentalId)] +public record OpenApiValidationContext +( + OpenApiSchema TypeSchema, + OpenApiSchema PropertySchema, + OpenApiSchemaTransformerContext TransformerContext, + IPropertyValidator PropertyValidator, + string PropertyKey, + IRuleComponent RuleComponent); diff --git a/src/AspNetCore.FluentValidation.OpenApi/RegularExpressionPropertyRule.cs b/src/AspNetCore.FluentValidation.OpenApi/RegularExpressionPropertyRule.cs new file mode 100644 index 000000000..e679001fd --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/RegularExpressionPropertyRule.cs @@ -0,0 +1,26 @@ +using FluentValidation.Validators; + +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +[Experimental(Constants.ExperimentalId)] +public sealed class RegularExpressionPropertyRule : IPropertyRuleHandler +{ + Task IPropertyRuleHandler.HandleAsync(OpenApiValidationContext context, CancellationToken cancellationToken) + { + if (context is not { PropertyValidator: IRegularExpressionValidator validator }) return Task.CompletedTask; + + var anyPatterns = context.PropertySchema.AllOf.Any(schema => schema.Pattern is { }); + if (context.PropertySchema is { Pattern: { } } || anyPatterns) + { + if (!anyPatterns) context.PropertySchema.AllOf.Add(new() { Pattern = context.PropertySchema.Pattern }); + context.PropertySchema.AllOf.Add(new() { Pattern = validator.Expression }); + context.PropertySchema.Pattern = null; + } + else + { + context.PropertySchema.Pattern = validator.Expression; + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/AspNetCore.FluentValidation.OpenApi/RequiredPropertyRuleHandler.cs b/src/AspNetCore.FluentValidation.OpenApi/RequiredPropertyRuleHandler.cs new file mode 100644 index 000000000..daa223c58 --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/RequiredPropertyRuleHandler.cs @@ -0,0 +1,14 @@ +using FluentValidation.Validators; + +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +[Experimental(Constants.ExperimentalId)] +public sealed class RequiredPropertyRuleHandler : IPropertyRuleHandler +{ + Task IPropertyRuleHandler.HandleAsync(OpenApiValidationContext context, CancellationToken cancellationToken) + { + if (context.PropertyValidator is not (INotNullValidator or INotEmptyValidator)) return Task.CompletedTask; + context.TypeSchema.Required.Add(context.PropertyKey); + return Task.CompletedTask; + } +} diff --git a/src/AspNetCore.FluentValidation.OpenApi/Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi.csproj b/src/AspNetCore.FluentValidation.OpenApi/Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi.csproj new file mode 100644 index 000000000..d8be2d6d3 --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi.csproj @@ -0,0 +1,14 @@ + + + net9.0 + + The goal of this package is be a minimal and experimental implementation of open api integration of fluent validation for aspnet core + + fluentvalidation,openapi + + + + + + + diff --git a/src/AspNetCore.FluentValidation.OpenApi/ServiceCollectionExtensions.cs b/src/AspNetCore.FluentValidation.OpenApi/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..669020fbe --- /dev/null +++ b/src/AspNetCore.FluentValidation.OpenApi/ServiceCollectionExtensions.cs @@ -0,0 +1,28 @@ +using FluentValidation.AspNetCore; +using Microsoft.AspNetCore.OpenApi; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +[Experimental(Constants.ExperimentalId)] +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddFluentValidationOpenApi(this IServiceCollection services) + { + services.AddOpenApi(); + services.AddFluentValidationAutoValidation(); + services.Configure( + "v1", + options => { options.AddSchemaTransformer(); } + ); + services.TryAddEnumerable(ServiceDescriptor.Transient()); + services.TryAddEnumerable(ServiceDescriptor.Transient()); + services.TryAddEnumerable(ServiceDescriptor.Transient()); + services.TryAddEnumerable(ServiceDescriptor.Transient()); + services.TryAddEnumerable(ServiceDescriptor.Transient()); + services.TryAddEnumerable(ServiceDescriptor.Transient()); + services.TryAddEnumerable(ServiceDescriptor.Transient()); + return services; + } +} \ No newline at end of file diff --git a/src/AspNetCore.NewtonsoftJson/Rocket.Surgery.LaunchPad.AspNetCore.NewtonsoftJson.csproj b/src/AspNetCore.NewtonsoftJson/Rocket.Surgery.LaunchPad.AspNetCore.NewtonsoftJson.csproj index a4b21ce90..d84359a19 100644 --- a/src/AspNetCore.NewtonsoftJson/Rocket.Surgery.LaunchPad.AspNetCore.NewtonsoftJson.csproj +++ b/src/AspNetCore.NewtonsoftJson/Rocket.Surgery.LaunchPad.AspNetCore.NewtonsoftJson.csproj @@ -1,6 +1,6 @@  - net8.0 + net9.0 $(PackageTags) Rocket.Surgery.LaunchPad.AspNetCore diff --git a/src/AspNetCore.Spatial/Conventions/AspNetCoreSpatialConvention.cs b/src/AspNetCore.Spatial/Conventions/AspNetCoreSpatialConvention.cs index 54158f607..3daa02376 100644 --- a/src/AspNetCore.Spatial/Conventions/AspNetCoreSpatialConvention.cs +++ b/src/AspNetCore.Spatial/Conventions/AspNetCoreSpatialConvention.cs @@ -3,7 +3,6 @@ using Rocket.Surgery.Conventions; using Rocket.Surgery.Conventions.DependencyInjection; using Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; -using Swashbuckle.AspNetCore.SwaggerGen; namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions; @@ -18,6 +17,6 @@ public class AspNetCoreSpatialConvention : IServiceConvention /// public void Register(IConventionContext context, IConfiguration configuration, IServiceCollection services) { - services.Configure(o => o.ConfigureForNetTopologySuite()); +// services.Configure(o => o.ConfigureForNetTopologySuite()); } } diff --git a/src/AspNetCore.Spatial/OpenApi/NetTopologySuiteSwashbuckleExtensions.cs b/src/AspNetCore.Spatial/OpenApi/NetTopologySuiteSwashbuckleExtensions.cs index 74556cf28..edd912e2f 100644 --- a/src/AspNetCore.Spatial/OpenApi/NetTopologySuiteSwashbuckleExtensions.cs +++ b/src/AspNetCore.Spatial/OpenApi/NetTopologySuiteSwashbuckleExtensions.cs @@ -1,466 +1,465 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Interfaces; -using Microsoft.OpenApi.Models; -using NetTopologySuite.Features; -using NetTopologySuite.Geometries; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; - -[ExcludeFromCodeCoverage] -internal static class NetTopologySuiteSwashbuckleExtensions -{ - public static SwaggerGenOptions ConfigureForNetTopologySuite(this SwaggerGenOptions c) - { - c.DocumentFilter(); - c.MapType( - () => new OpenApiSchema - { - ExternalDocs = new OpenApiExternalDocs - { - Url = new Uri("http://geojson.org/geojson-spec.html#geometry-objects"), - }, - Type = "object", - Extensions = new Dictionary - { - ["clrType"] = new OpenApiString(typeof(Geometry).FullName) - }, - Description = "GeoJSon geometry", - Discriminator = new OpenApiDiscriminator - { - PropertyName = "type", - }, - Required = new HashSet { "type" }, - Properties = new Dictionary - { - ["type"] = new() - { - Type = "string", - Enum = new List - { - new OpenApiString("Point"), - new OpenApiString("LineString"), - new OpenApiString("Polygon"), - new OpenApiString("MultiPoint"), - new OpenApiString("MultiLineString"), - new OpenApiString("MultiPolygon"), - }, - Description = "the geometry type" - } - } - } - ); - - c.MapType( - () => new OpenApiSchema - { - ExternalDocs = new OpenApiExternalDocs - { - Url = new Uri("http://geojson.org/geojson-spec.html#id2"), - }, - Type = "object", - Extensions = new Dictionary - { - ["clrType"] = new OpenApiString(typeof(Point).FullName) - }, - Description = "GeoJSon geometry", - AllOf = new List - { - new() - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Geometry", - } - }, - new() - { - Properties = new Dictionary - { - ["coordinates"] = new() - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Point3D" - } - } - } - } - }, - } - ); - c.MapType( - () => new OpenApiSchema - { - ExternalDocs = new OpenApiExternalDocs - { - Url = new Uri("http://geojson.org/geojson-spec.html#id3"), - }, - Type = "object", - Extensions = new Dictionary - { - ["clrType"] = new OpenApiString(typeof(LineString).FullName) - }, - Description = "GeoJSon geometry", - AllOf = new List - { - new() - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Geometry", - } - }, - new() - { - Properties = new Dictionary - { - ["coordinates"] = new() - { - Type = "array", - Items = new OpenApiSchema - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Point3D" - } - } - } - } - } - }, - } - ); - c.MapType( - () => new OpenApiSchema - { - ExternalDocs = new OpenApiExternalDocs - { - Url = new Uri("http://geojson.org/geojson-spec.html#id4"), - }, - Type = "object", - Extensions = new Dictionary - { - ["clrType"] = new OpenApiString(typeof(Polygon).FullName) - }, - Description = "GeoJSon geometry", - AllOf = new List - { - new() - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Geometry", - } - }, - new() - { - Properties = new Dictionary - { - ["coordinates"] = new() - { - Type = "array", - Items = new OpenApiSchema - { - Type = "array", - Items = new OpenApiSchema - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Point3D" - } - } - } - } - } - } - }, - } - ); - c.MapType( - () => new OpenApiSchema - { - ExternalDocs = new OpenApiExternalDocs - { - Url = new Uri("http://geojson.org/geojson-spec.html#id4"), - }, - Type = "object", - Extensions = new Dictionary - { - ["clrType"] = new OpenApiString(typeof(MultiPoint).FullName) - }, - Description = "GeoJSon geometry", - AllOf = new List - { - new() - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Geometry", - } - }, - new() - { - Properties = new Dictionary - { - ["coordinates"] = new() - { - Type = "array", - Items = new OpenApiSchema - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Point3D" - } - } - } - } - } - }, - } - ); - c.MapType( - () => new OpenApiSchema - { - ExternalDocs = new OpenApiExternalDocs - { - Url = new Uri("http://geojson.org/geojson-spec.html#id5"), - }, - Type = "object", - Extensions = new Dictionary - { - ["clrType"] = new OpenApiString(typeof(MultiLineString).FullName) - }, - Description = "GeoJSon geometry", - AllOf = new List - { - new() - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Geometry", - } - }, - new() - { - Properties = new Dictionary - { - ["coordinates"] = new() - { - Type = "array", - Items = new OpenApiSchema - { - Type = "array", - Items = new OpenApiSchema - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Point3D" - } - } - } - } - } - } - }, - } - ); - c.MapType( - () => new OpenApiSchema - { - ExternalDocs = new OpenApiExternalDocs - { - Url = new Uri("http://geojson.org/geojson-spec.html#id6"), - }, - Type = "object", - Extensions = new Dictionary - { - ["clrType"] = new OpenApiString(typeof(MultiPolygon).FullName) - }, - Description = "GeoJSon geometry", - AllOf = new List - { - new() - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Geometry", - } - }, - new() - { - Properties = new Dictionary - { - ["coordinates"] = new() - { - Type = "array", - Items = new OpenApiSchema - { - Type = "array", - Items = new OpenApiSchema - { - Type = "array", - Items = new OpenApiSchema - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Point3D" - } - } - } - } - } - } - } - }, - } - ); - c.MapType( - () => new OpenApiSchema - { - ExternalDocs = new OpenApiExternalDocs - { - Url = new Uri("http://geojson.org/geojson-spec.html#geometrycollection"), - }, - Type = "object", - Extensions = new Dictionary - { - ["clrType"] = new OpenApiString(typeof(GeometryCollection).FullName) - }, - Required = new HashSet { "type", "geometries" }, - Description = "GeoJSon geometry collection", - Properties = new Dictionary - { - ["type"] = new() - { - Type = "string", - Enum = new List { new OpenApiString("GeometryCollection") }, - }, - ["geometries"] = new() - { - Type = "array", - Items = new OpenApiSchema - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Geometry" - } - } - } - } - } - ); - - c.MapType( - () => new OpenApiSchema - { - Type = "object", - Description = "GeoJSon Feature", - Required = new HashSet { "type", "id", "geometry" }, - ExternalDocs = new OpenApiExternalDocs - { - Url = new Uri("https://tools.ietf.org/html/rfc7946#section-3.2") - }, - Properties = new Dictionary - { - ["type"] = new() - { - Type = "string", - Enum = new List { new OpenApiString("Feature") } - }, - ["id"] = new() - { - Type = "integer", - }, - ["geometry"] = new() - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "GeometryCollection", - } - }, - ["properties"] = new() - { - Type = "object" - } - } - } - ); - - c.MapType( - () => new OpenApiSchema - { - Type = "object", - Description = "GeoJSon Feature collection", - Required = new HashSet { "type", "features" }, - ExternalDocs = new OpenApiExternalDocs - { - Url = new Uri("https://tools.ietf.org/html/rfc7946#section-3.2") - }, - Properties = new Dictionary - { - ["type"] = new() - { - Type = "string", - Enum = new List { new OpenApiString("FeatureCollection") } - }, - ["features"] = new() - { - Type = "array", - Items = new OpenApiSchema - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Feature" - } - } - } - } - } - ); - - - return c; - } - - private class DocumentFilter : IDocumentFilter - { - public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) - { - swaggerDoc.Components.Schemas.Add( - "Point3D", - new OpenApiSchema - { - Type = "array", - Description = "Point in 3D space", - ExternalDocs = new OpenApiExternalDocs - { - Url = new Uri("http://geojson.org/geojson-spec.html#id2") - }, - MinItems = 2, - MaxItems = 3, - Items = new OpenApiSchema - { - Type = "number" - } - } - ); - } - } -} +//using Microsoft.Extensions.DependencyInjection; +//using Microsoft.OpenApi.Any; +//using Microsoft.OpenApi.Interfaces; +//using Microsoft.OpenApi.Models; +//using NetTopologySuite.Features; +//using NetTopologySuite.Geometries; +// +//namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; +// +//[ExcludeFromCodeCoverage] +//internal static class NetTopologySuiteSwashbuckleExtensions +//{ +// public static SwaggerGenOptions ConfigureForNetTopologySuite(this SwaggerGenOptions c) +// { +// c.DocumentFilter(); +// c.MapType( +// () => new OpenApiSchema +// { +// ExternalDocs = new OpenApiExternalDocs +// { +// Url = new Uri("http://geojson.org/geojson-spec.html#geometry-objects"), +// }, +// Type = "object", +// Extensions = new Dictionary +// { +// ["clrType"] = new OpenApiString(typeof(Geometry).FullName) +// }, +// Description = "GeoJSon geometry", +// Discriminator = new OpenApiDiscriminator +// { +// PropertyName = "type", +// }, +// Required = new HashSet { "type" }, +// Properties = new Dictionary +// { +// ["type"] = new() +// { +// Type = "string", +// Enum = new List +// { +// new OpenApiString("Point"), +// new OpenApiString("LineString"), +// new OpenApiString("Polygon"), +// new OpenApiString("MultiPoint"), +// new OpenApiString("MultiLineString"), +// new OpenApiString("MultiPolygon"), +// }, +// Description = "the geometry type" +// } +// } +// } +// ); +// +// c.MapType( +// () => new OpenApiSchema +// { +// ExternalDocs = new OpenApiExternalDocs +// { +// Url = new Uri("http://geojson.org/geojson-spec.html#id2"), +// }, +// Type = "object", +// Extensions = new Dictionary +// { +// ["clrType"] = new OpenApiString(typeof(Point).FullName) +// }, +// Description = "GeoJSon geometry", +// AllOf = new List +// { +// new() +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Geometry", +// } +// }, +// new() +// { +// Properties = new Dictionary +// { +// ["coordinates"] = new() +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Point3D" +// } +// } +// } +// } +// }, +// } +// ); +// c.MapType( +// () => new OpenApiSchema +// { +// ExternalDocs = new OpenApiExternalDocs +// { +// Url = new Uri("http://geojson.org/geojson-spec.html#id3"), +// }, +// Type = "object", +// Extensions = new Dictionary +// { +// ["clrType"] = new OpenApiString(typeof(LineString).FullName) +// }, +// Description = "GeoJSon geometry", +// AllOf = new List +// { +// new() +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Geometry", +// } +// }, +// new() +// { +// Properties = new Dictionary +// { +// ["coordinates"] = new() +// { +// Type = "array", +// Items = new OpenApiSchema +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Point3D" +// } +// } +// } +// } +// } +// }, +// } +// ); +// c.MapType( +// () => new OpenApiSchema +// { +// ExternalDocs = new OpenApiExternalDocs +// { +// Url = new Uri("http://geojson.org/geojson-spec.html#id4"), +// }, +// Type = "object", +// Extensions = new Dictionary +// { +// ["clrType"] = new OpenApiString(typeof(Polygon).FullName) +// }, +// Description = "GeoJSon geometry", +// AllOf = new List +// { +// new() +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Geometry", +// } +// }, +// new() +// { +// Properties = new Dictionary +// { +// ["coordinates"] = new() +// { +// Type = "array", +// Items = new OpenApiSchema +// { +// Type = "array", +// Items = new OpenApiSchema +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Point3D" +// } +// } +// } +// } +// } +// } +// }, +// } +// ); +// c.MapType( +// () => new OpenApiSchema +// { +// ExternalDocs = new OpenApiExternalDocs +// { +// Url = new Uri("http://geojson.org/geojson-spec.html#id4"), +// }, +// Type = "object", +// Extensions = new Dictionary +// { +// ["clrType"] = new OpenApiString(typeof(MultiPoint).FullName) +// }, +// Description = "GeoJSon geometry", +// AllOf = new List +// { +// new() +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Geometry", +// } +// }, +// new() +// { +// Properties = new Dictionary +// { +// ["coordinates"] = new() +// { +// Type = "array", +// Items = new OpenApiSchema +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Point3D" +// } +// } +// } +// } +// } +// }, +// } +// ); +// c.MapType( +// () => new OpenApiSchema +// { +// ExternalDocs = new OpenApiExternalDocs +// { +// Url = new Uri("http://geojson.org/geojson-spec.html#id5"), +// }, +// Type = "object", +// Extensions = new Dictionary +// { +// ["clrType"] = new OpenApiString(typeof(MultiLineString).FullName) +// }, +// Description = "GeoJSon geometry", +// AllOf = new List +// { +// new() +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Geometry", +// } +// }, +// new() +// { +// Properties = new Dictionary +// { +// ["coordinates"] = new() +// { +// Type = "array", +// Items = new OpenApiSchema +// { +// Type = "array", +// Items = new OpenApiSchema +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Point3D" +// } +// } +// } +// } +// } +// } +// }, +// } +// ); +// c.MapType( +// () => new OpenApiSchema +// { +// ExternalDocs = new OpenApiExternalDocs +// { +// Url = new Uri("http://geojson.org/geojson-spec.html#id6"), +// }, +// Type = "object", +// Extensions = new Dictionary +// { +// ["clrType"] = new OpenApiString(typeof(MultiPolygon).FullName) +// }, +// Description = "GeoJSon geometry", +// AllOf = new List +// { +// new() +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Geometry", +// } +// }, +// new() +// { +// Properties = new Dictionary +// { +// ["coordinates"] = new() +// { +// Type = "array", +// Items = new OpenApiSchema +// { +// Type = "array", +// Items = new OpenApiSchema +// { +// Type = "array", +// Items = new OpenApiSchema +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Point3D" +// } +// } +// } +// } +// } +// } +// } +// }, +// } +// ); +// c.MapType( +// () => new OpenApiSchema +// { +// ExternalDocs = new OpenApiExternalDocs +// { +// Url = new Uri("http://geojson.org/geojson-spec.html#geometrycollection"), +// }, +// Type = "object", +// Extensions = new Dictionary +// { +// ["clrType"] = new OpenApiString(typeof(GeometryCollection).FullName) +// }, +// Required = new HashSet { "type", "geometries" }, +// Description = "GeoJSon geometry collection", +// Properties = new Dictionary +// { +// ["type"] = new() +// { +// Type = "string", +// Enum = new List { new OpenApiString("GeometryCollection") }, +// }, +// ["geometries"] = new() +// { +// Type = "array", +// Items = new OpenApiSchema +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Geometry" +// } +// } +// } +// } +// } +// ); +// +// c.MapType( +// () => new OpenApiSchema +// { +// Type = "object", +// Description = "GeoJSon Feature", +// Required = new HashSet { "type", "id", "geometry" }, +// ExternalDocs = new OpenApiExternalDocs +// { +// Url = new Uri("https://tools.ietf.org/html/rfc7946#section-3.2") +// }, +// Properties = new Dictionary +// { +// ["type"] = new() +// { +// Type = "string", +// Enum = new List { new OpenApiString("Feature") } +// }, +// ["id"] = new() +// { +// Type = "integer", +// }, +// ["geometry"] = new() +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "GeometryCollection", +// } +// }, +// ["properties"] = new() +// { +// Type = "object" +// } +// } +// } +// ); +// +// c.MapType( +// () => new OpenApiSchema +// { +// Type = "object", +// Description = "GeoJSon Feature collection", +// Required = new HashSet { "type", "features" }, +// ExternalDocs = new OpenApiExternalDocs +// { +// Url = new Uri("https://tools.ietf.org/html/rfc7946#section-3.2") +// }, +// Properties = new Dictionary +// { +// ["type"] = new() +// { +// Type = "string", +// Enum = new List { new OpenApiString("FeatureCollection") } +// }, +// ["features"] = new() +// { +// Type = "array", +// Items = new OpenApiSchema +// { +// Reference = new OpenApiReference +// { +// Type = ReferenceType.Schema, +// Id = "Feature" +// } +// } +// } +// } +// } +// ); +// +// +// return c; +// } +// +// private class DocumentFilter : IDocumentFilter +// { +// public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) +// { +// swaggerDoc.Components.Schemas.Add( +// "Point3D", +// new OpenApiSchema +// { +// Type = "array", +// Description = "Point in 3D space", +// ExternalDocs = new OpenApiExternalDocs +// { +// Url = new Uri("http://geojson.org/geojson-spec.html#id2") +// }, +// MinItems = 2, +// MaxItems = 3, +// Items = new OpenApiSchema +// { +// Type = "number" +// } +// } +// ); +// } +// } +//} diff --git a/src/AspNetCore.Spatial/Rocket.Surgery.LaunchPad.AspNetCore.Spatial.csproj b/src/AspNetCore.Spatial/Rocket.Surgery.LaunchPad.AspNetCore.Spatial.csproj index 058372507..c48a91fcb 100644 --- a/src/AspNetCore.Spatial/Rocket.Surgery.LaunchPad.AspNetCore.Spatial.csproj +++ b/src/AspNetCore.Spatial/Rocket.Surgery.LaunchPad.AspNetCore.Spatial.csproj @@ -1,6 +1,6 @@  - net8.0 + net9.0 $(PackageTags) Rocket.Surgery.LaunchPad.AspNetCore diff --git a/src/AspNetCore.Testing/LaunchPadExtension.cs b/src/AspNetCore.Testing/LaunchPadExtension.cs index 2f1a7e0ab..1b8b6f5cc 100644 --- a/src/AspNetCore.Testing/LaunchPadExtension.cs +++ b/src/AspNetCore.Testing/LaunchPadExtension.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Rocket.Surgery.Conventions; namespace Rocket.Surgery.LaunchPad.AspNetCore.Testing; @@ -62,4 +63,4 @@ public virtual IHostBuilder Configure(IHostBuilder builder) } } #pragma warning restore CA1816 -#pragma warning restore CA1063 \ No newline at end of file +#pragma warning restore CA1063 diff --git a/src/AspNetCore.Testing/LaunchPadWebAppFixture.cs b/src/AspNetCore.Testing/LaunchPadWebAppFixture.cs index 992007de5..de0c3a61d 100644 --- a/src/AspNetCore.Testing/LaunchPadWebAppFixture.cs +++ b/src/AspNetCore.Testing/LaunchPadWebAppFixture.cs @@ -41,7 +41,8 @@ protected LaunchPadWebAppFixture(params IAlbaExtension[] extensions) /// public async Task InitializeAsync() { - _host = await Alba.AlbaHost.For(_extensions); + _host = await Alba.AlbaHost.For([.._extensions]); + await ResetAsync(); } /// @@ -191,4 +192,4 @@ public bool IsEnabled(LogLevel logLevel) return _logger?.BeginScope(state); } } -} \ No newline at end of file +} diff --git a/src/AspNetCore.Testing/Rocket.Surgery.LaunchPad.AspNetCore.Testing.csproj b/src/AspNetCore.Testing/Rocket.Surgery.LaunchPad.AspNetCore.Testing.csproj index 1d572900d..8e7f447b5 100644 --- a/src/AspNetCore.Testing/Rocket.Surgery.LaunchPad.AspNetCore.Testing.csproj +++ b/src/AspNetCore.Testing/Rocket.Surgery.LaunchPad.AspNetCore.Testing.csproj @@ -1,6 +1,6 @@  - net8.0 + net9.0 $(PackageTags) @@ -15,8 +15,8 @@ - - - + + + diff --git a/src/AspNetCore/Composition/IRestfulApiMethodMatcher.cs b/src/AspNetCore/Composition/IRestfulApiMethodMatcher.cs index 0f752e897..a42f15528 100644 --- a/src/AspNetCore/Composition/IRestfulApiMethodMatcher.cs +++ b/src/AspNetCore/Composition/IRestfulApiMethodMatcher.cs @@ -10,6 +10,5 @@ internal interface IRestfulApiMethodMatcher string[] Names { get; } IDictionary Parameters { get; } - [RequiresUnreferencedCode("DynamicBehavior is incompatible with trimming.")] bool IsMatch(ActionModel actionModel); } diff --git a/src/AspNetCore/Composition/RestfulApiActionModelConvention.cs b/src/AspNetCore/Composition/RestfulApiActionModelConvention.cs index 4dc2e008e..1337d16eb 100644 --- a/src/AspNetCore/Composition/RestfulApiActionModelConvention.cs +++ b/src/AspNetCore/Composition/RestfulApiActionModelConvention.cs @@ -6,14 +6,14 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.OpenApi; using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using Rocket.Surgery.LaunchPad.AspNetCore.Validation; -using Swashbuckle.AspNetCore.SwaggerGen; namespace Rocket.Surgery.LaunchPad.AspNetCore.Composition; -internal class RestfulApiActionModelConvention : IActionModelConvention, ISchemaFilter +internal class RestfulApiActionModelConvention : IActionModelConvention, IOpenApiSchemaTransformer { private static string? GetHttpMethod(ActionModel action) { @@ -157,7 +157,7 @@ ActionModel actionModel } // TODO: Make a source generator for this to work without generics - [RequiresUnreferencedCode("DynamicBehavior is incompatible with trimming.")] + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "")] public void Apply(ActionModel action) { if (!typeof(RestfulApiController).IsAssignableFrom(action.Controller.ControllerType)) return; @@ -170,14 +170,18 @@ public void Apply(ActionModel action) ExtractParameterDetails(action); } - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken) { - if (_propertiesToHideFromOpenApi.TryGetValue(context.Type, out var propertiesToRemove)) - foreach (var property in propertiesToRemove - .Join(schema.Properties, z => z, z => z.Key, (_, b) => b.Key, StringComparer.OrdinalIgnoreCase) - .ToArray()) - { - schema.Properties.Remove(property); - } + if (!_propertiesToHideFromOpenApi.TryGetValue(context.JsonTypeInfo.Type, out var propertiesToRemove)) return Task.CompletedTask; + + foreach (var property in propertiesToRemove + .Join(schema.Properties, z => z, z => z.Key, (_, b) => b.Key, StringComparer.OrdinalIgnoreCase) + .ToArray() + ) + { + schema.Properties.Remove(property); + } + + return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/src/AspNetCore/Conventions/AspNetCoreConvention.cs b/src/AspNetCore/Conventions/AspNetCoreConvention.cs index 16163ebdc..ca424bc6e 100644 --- a/src/AspNetCore/Conventions/AspNetCoreConvention.cs +++ b/src/AspNetCore/Conventions/AspNetCoreConvention.cs @@ -108,7 +108,7 @@ public void Register(IConventionContext context, IConfiguration configuration, I // ReSharper disable once NullableWarningSuppressionIsUsed GetServiceFromCollection(services)!, context - .AssemblyProvider.GetAssemblies(s => s.FromAssemblyDependenciesOf(typeof(AspNetCoreConvention))) + .TypeProvider.GetAssemblies(s => s.FromAssemblyDependenciesOf(typeof(AspNetCoreConvention))) .Where(_options.AssemblyPartFilter) .SelectMany(GetApplicationPartAssemblies) ); diff --git a/src/AspNetCore/Conventions/FluentValidationConvention.cs b/src/AspNetCore/Conventions/FluentValidationConvention.cs index 2b8f1a4f9..abc3b839b 100644 --- a/src/AspNetCore/Conventions/FluentValidationConvention.cs +++ b/src/AspNetCore/Conventions/FluentValidationConvention.cs @@ -1,17 +1,10 @@ -using System.Reflection; -using FluentValidation.AspNetCore; -using FluentValidation.Validators; -using MicroElements.OpenApi.FluentValidation; -using MicroElements.Swashbuckle.FluentValidation; +using FluentValidation.AspNetCore; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Any; using Rocket.Surgery.Conventions; using Rocket.Surgery.Conventions.DependencyInjection; -using Rocket.Surgery.Extensions; using Rocket.Surgery.LaunchPad.AspNetCore.Validation; -using Rocket.Surgery.LaunchPad.Foundation.Validation; using MvcJsonOptions = Microsoft.AspNetCore.Mvc.JsonOptions; using HttpJsonOptions = Microsoft.AspNetCore.Http.Json.JsonOptions; @@ -29,95 +22,6 @@ namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions; [ConventionCategory(ConventionCategory.Application)] public partial class FluentValidationConvention : IServiceConvention { - private static void AddFluentValidationRules(IServiceCollection services) - { - services.AddSingleton( - new FluentValidationRule("NotEmpty") - .WithCondition(propertyValidator => propertyValidator is INotEmptyValidator) - .WithApply( - context => - { - var ruleContext = ( (ValidationRuleContext)context - .GetType() - .GetProperties(BindingFlags.Instance | BindingFlags.NonPublic) - .First(z => z.PropertyType == typeof(ValidationRuleContext)) - .GetValue(context)! ) - .GetReflectionContext(); - var propertyType = ruleContext?.PropertyInfo?.DeclaringType; - if (propertyType == typeof(string)) - { - ( context.Schema.Properties[context.PropertyKey] ??= new() ).MinLength = 1; - } - } - ) - ); - - services.AddSingleton( - new FluentValidationRule("ValueTypeOrEnum") - .WithApply( - context => - { - var ruleContext = ( (ValidationRuleContext)context - .GetType() - .GetProperties(BindingFlags.Instance | BindingFlags.NonPublic) - .First(z => z.PropertyType == typeof(ValidationRuleContext)) - .GetValue(context)! ) - .GetReflectionContext(); - var propertyType = ruleContext?.PropertyInfo?.DeclaringType; - if (propertyType != null && ( ( propertyType.IsValueType && Nullable.GetUnderlyingType(propertyType) == null ) || propertyType.IsEnum )) - { - context.Schema.Required.Add(context.PropertyKey); - ( context.Schema.Properties[context.PropertyKey] ??= new() ).Nullable = false; - } - } - ) - ); - - services.AddSingleton( - new FluentValidationRule("Nullable") - .WithApply( - context => - { - var ruleContext = ( (ValidationRuleContext)context - .GetType() - .GetProperties(BindingFlags.Instance | BindingFlags.NonPublic) - .First(z => z.PropertyType == typeof(ValidationRuleContext)) - .GetValue(context)! ) - .GetReflectionContext(); - ( context.Schema.Properties[context.PropertyKey] ??= new() ).Nullable = - context.PropertyValidator is not (INotNullValidator or INotEmptyValidator) - || ( ruleContext is { PropertyInfo: FieldInfo fi, } && getNullableValue(fi.GetNullability(), fi.FieldType) ) - ; - - static bool getNullableValue(Nullability nullability, Type propertyType) - { - return nullability switch - { - Nullability.Nullable => true, - Nullability.NonNullable => false, - Nullability.NotDefined => !propertyType.IsValueType || Nullable.GetUnderlyingType(propertyType) is { }, - _ => false, - }; - } - } - ) - ); - - services.AddSingleton( - new FluentValidationRule("IsOneOf") - .WithCondition(propertyValidator => propertyValidator is IStringInValidator) - .WithApply( - context => - { - var validator = context.PropertyValidator as IStringInValidator; - ( context.Schema.Properties[context.PropertyKey] ??= new() ).Enum = - // ReSharper disable once NullableWarningSuppressionIsUsed - validator!.Values.Select(x => new OpenApiString(x)).Cast().ToList(); - } - ) - ); - } - /// /// Registers the specified context. /// @@ -129,7 +33,7 @@ public void Register(IConventionContext context, IConfiguration configuration, I services.AddFluentValidationClientsideAdapters(); services .Configure(mvcOptions => mvcOptions.Filters.Insert(0, new ValidationExceptionFilter())) - .Configure(options => options.JsonSerializerOptions.Converters.Add(new ValidationProblemDetailsConverter())) + .Configure(options => options.JsonSerializerOptions.Converters.Add(new ValidationProblemDetailsConverter ())) .Configure(options => options.SerializerOptions.Converters.Add(new ValidationProblemDetailsConverter())); // AddFluentValidationRules(services); diff --git a/src/AspNetCore/Conventions/OpenApiConvention.cs b/src/AspNetCore/Conventions/OpenApiConvention.cs new file mode 100644 index 000000000..3432a6dd6 --- /dev/null +++ b/src/AspNetCore/Conventions/OpenApiConvention.cs @@ -0,0 +1,73 @@ +using System.Text; +using System.Text.Json; +using FluentValidation; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OpenApi; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Models; +using Rocket.Surgery.Conventions; +using Rocket.Surgery.Conventions.DependencyInjection; +using Rocket.Surgery.LaunchPad.AspNetCore.Composition; +using Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; +using Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; + +namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions; + +/// +/// ValidationConvention. +/// Implements the +/// +/// +/// +[PublicAPI] +[ExportConvention] +[AfterConvention(typeof(AspNetCoreConvention))] +[ConventionCategory(ConventionCategory.Application)] +public partial class OpenApiConvention : IServiceConvention +{ + [LoggerMessage( + EventId = 0, + Level = LogLevel.Debug, + Message = "Error adding XML comments from {XmlFile}" + )] + internal static partial void ErrorAddingXMLComments(ILogger logger, Exception exception, string xmlFile); + + /// + /// Registers the specified context. + /// + /// The context. + /// + /// + public void Register(IConventionContext context, IConfiguration configuration, IServiceCollection services) + { + ArgumentNullException.ThrowIfNull(context); + + services.AddOpenApi( + options => + { + options.AddSchemaTransformer(); + options.AddSchemaTransformer(); + options.AddSchemaTransformer(); + options.AddSchemaTransformer(); + options.AddOperationTransformer(); + options.AddOperationTransformer(); + options.AddOperationTransformer(); + options.AddOperationTransformer(); + + }); + services.AddFluentValidationOpenApi(); + } +} + +internal class NestedTypeSchemaFilter : IOpenApiSchemaTransformer +{ + public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken) + { + if (context is not { JsonTypeInfo.Type.DeclaringType: {} }) return Task.CompletedTask; + schema.Annotations["x-schema-id"] = $"{context.JsonTypeInfo.Type.DeclaringType.Name}{context.JsonTypeInfo.Type.Name}"; + return Task.CompletedTask; + } +} diff --git a/src/AspNetCore/Conventions/ProblemDetailsConvention.cs b/src/AspNetCore/Conventions/ProblemDetailsConvention.cs index b94437753..36d07629d 100644 --- a/src/AspNetCore/Conventions/ProblemDetailsConvention.cs +++ b/src/AspNetCore/Conventions/ProblemDetailsConvention.cs @@ -1,17 +1,15 @@ using FluentValidation; using FluentValidation.Results; -using Hellang.Middleware.ProblemDetails; -using Hellang.Middleware.ProblemDetails.Mvc; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Rocket.Surgery.Conventions; using Rocket.Surgery.Conventions.DependencyInjection; using Rocket.Surgery.LaunchPad.AspNetCore.Validation; using Rocket.Surgery.LaunchPad.Foundation; -using ProblemDetailsOptions = Hellang.Middleware.ProblemDetails.ProblemDetailsOptions; namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions; @@ -30,57 +28,88 @@ public class ProblemDetailsConvention : IServiceConvention /// public void Register(IConventionContext context, IConfiguration configuration, IServiceCollection services) { - ProblemDetailsExtensions - .AddProblemDetails(services) - .AddProblemDetailsConventions(); + services.AddProblemDetails(); + services.AddExceptionHandler( + options => + { + var old = options.StatusCodeSelector; + options.StatusCodeSelector = (exception) => exception switch + { + NotFoundException => StatusCodes.Status404NotFound, + RequestFailedException => StatusCodes.Status400BadRequest, + NotAuthorizedException => StatusCodes.Status403Forbidden, + ValidationException => StatusCodes.Status422UnprocessableEntity, + _ => old?.Invoke(exception) ?? StatusCodes.Status500InternalServerError, + }; + } + ); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); services .AddOptions() .Configure(static options => options.SuppressModelStateInvalidFilter = true); - services - .AddOptions() - .Configure>( - static (builder, apiBehaviorOptions) => - { - var currentIncludeExceptionDetails = builder.IncludeExceptionDetails; - builder.IncludeExceptionDetails = (httpContext, exception) => - exception is not IProblemDetailsData && currentIncludeExceptionDetails(httpContext, exception); - builder.OnBeforeWriteDetails = (_, problemDetails) => - { - if ( - !problemDetails.Status.HasValue - || !apiBehaviorOptions.Value.ClientErrorMapping.TryGetValue( - problemDetails.Status.Value, - out var clientErrorData - ) - ) - return; + } +} + +class OnBeforeWriteProblemDetailsWriter(IOptions apiBehaviorOptions) : IProblemDetailsWriter +{ + public ValueTask WriteAsync(ProblemDetailsContext context) => throw new NotImplementedException(); + + public bool CanWrite(ProblemDetailsContext context) + { + if (!context.ProblemDetails.Status.HasValue + || !apiBehaviorOptions.Value.ClientErrorMapping.TryGetValue(context.ProblemDetails.Status.Value, out var clientErrorData)) + return false; + + context.ProblemDetails.Title ??= clientErrorData.Title; + context.ProblemDetails.Type ??= clientErrorData.Link; + return false; + } +} + +class FluentValidationProblemDetailsWriter(IOptions apiBehaviorOptions) : IProblemDetailsWriter +{ + public ValueTask WriteAsync(ProblemDetailsContext context) + { + if (context is not { Exception: IProblemDetailsData details } + || context.HttpContext.Items[typeof(ValidationResult)] is not ValidationResult validationResult) return ValueTask.CompletedTask; + + context.ProblemDetails = new FluentValidationProblemDetails(validationResult.Errors) + { + Status = StatusCodes.Status422UnprocessableEntity, + Title = details.Title ?? context.ProblemDetails.Title, + Type = details.Link ?? context.ProblemDetails.Type, + Detail = context.ProblemDetails.Detail, + Instance = details.Instance ?? context.ProblemDetails.Instance, + Extensions = context.ProblemDetails.Extensions, + }; + return ValueTask.CompletedTask; + } - problemDetails.Title ??= clientErrorData.Title; - problemDetails.Type ??= clientErrorData.Link; - }; -// builder.MapToProblemDetailsDataException(StatusCodes.Status404NotFound); -// builder.MapToProblemDetailsDataException(StatusCodes.Status400BadRequest); -// builder.MapToProblemDetailsDataException(StatusCodes.Status403Forbidden); - builder.Map( - static exception => new FluentValidationProblemDetails(exception.Errors) - { - Status = StatusCodes.Status422UnprocessableEntity, - } - ); - builder.Map( - static (ctx, ex) => ex is not IProblemDetailsData && ctx.Items[typeof(ValidationResult)] is ValidationResult, - static (ctx, _) => - { - var result = ctx.Items[typeof(ValidationResult)] as ValidationResult; - // ReSharper disable once NullableWarningSuppressionIsUsed - return new FluentValidationProblemDetails(result!.Errors) - { - Status = StatusCodes.Status422UnprocessableEntity, - }; - } - ); - } - ); + public bool CanWrite(ProblemDetailsContext context) + { + return context.Exception is not IProblemDetailsData && context.HttpContext.Items[typeof(ValidationResult)] is ValidationResult; } } + +class ValidationExceptionProblemDetailsWriter(IOptions apiBehaviorOptions) : IProblemDetailsWriter +{ + public ValueTask WriteAsync(ProblemDetailsContext context) + { + if (context.Exception is not ValidationException validationException) return ValueTask.CompletedTask; + context.ProblemDetails = new FluentValidationProblemDetails(validationException.Errors) + { + Status = StatusCodes.Status422UnprocessableEntity, + Title = context.ProblemDetails.Title, + Type = context.ProblemDetails.Type, + Detail = context.ProblemDetails.Detail, + Instance = context.ProblemDetails.Instance, + Extensions = context.ProblemDetails.Extensions, + }; + return ValueTask.CompletedTask; + } + + public bool CanWrite(ProblemDetailsContext context) => context.Exception is ValidationException; +} diff --git a/src/AspNetCore/Conventions/ProblemDetailsOptionsExtensions.cs b/src/AspNetCore/Conventions/ProblemDetailsOptionsExtensions.cs deleted file mode 100644 index 12f7b1990..000000000 --- a/src/AspNetCore/Conventions/ProblemDetailsOptionsExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Hellang.Middleware.ProblemDetails; -using Microsoft.AspNetCore.Mvc; -using Rocket.Surgery.LaunchPad.Foundation; - -namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions; - -/// -/// Extensions to ProblemDetailsOptions -/// -public static class ProblemDetailsOptionsExtensions -{ - /// - /// Creates a mapping for an exception that implements with the given status code - /// - /// - /// - /// - public static void MapToProblemDetailsDataException(this ProblemDetailsOptions options, int statusCode) - where TException : Exception, IProblemDetailsData - { - options.Map((_, ex) => ConstructProblemDetails(ex, statusCode)); - } - - private static ProblemDetails ConstructProblemDetails(TException ex, int statusCode) where TException : Exception, IProblemDetailsData - { - var details = new ProblemDetails - { - Detail = ex.Message, - Title = ex.Title, - Type = ex.Link, - Instance = ex.Instance, - Status = statusCode - }; - foreach (var item in ex.Properties) - { - if (details.Extensions.ContainsKey(item.Key)) continue; - details.Extensions.Add(item); - } - - return details; - } -} diff --git a/src/AspNetCore/Conventions/SwashbuckleConvention.cs b/src/AspNetCore/Conventions/SwashbuckleConvention.cs deleted file mode 100644 index 22319a353..000000000 --- a/src/AspNetCore/Conventions/SwashbuckleConvention.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System.Text; -using System.Text.Json; -using FluentValidation; -using MicroElements.Swashbuckle.FluentValidation.AspNetCore; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Rocket.Surgery.Conventions; -using Rocket.Surgery.Conventions.DependencyInjection; -using Rocket.Surgery.LaunchPad.AspNetCore.Composition; -using Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace Rocket.Surgery.LaunchPad.AspNetCore.Conventions; - -/// -/// ValidationConvention. -/// Implements the -/// -/// -/// -[PublicAPI] -[ExportConvention] -[AfterConvention(typeof(AspNetCoreConvention))] -[ConventionCategory(ConventionCategory.Application)] -public partial class SwashbuckleConvention : IServiceConvention -{ - [LoggerMessage( - EventId = 0, - Level = LogLevel.Debug, - Message = "Error adding XML comments from {XmlFile}" - )] - internal static partial void ErrorAddingXMLComments(ILogger logger, Exception exception, string xmlFile); - - /// - /// Registers the specified context. - /// - /// The context. - /// - /// - public void Register(IConventionContext context, IConfiguration configuration, IServiceCollection services) - { - ArgumentNullException.ThrowIfNull(context); - - services.ConfigureOptions(); - - services.AddFluentValidationRulesToSwagger( - options => options.SetNotNullableIfMinLengthGreaterThenZero = true, - options => options.ServiceLifetime = ServiceLifetime.Singleton - ); - -// services.TryAddEnumerable(ServiceDescriptor.Transient()); - - services - .AddOptions() - .Configure>( - (options, mvcOptions) => options.ConfigureForNodaTime(mvcOptions.Value.JsonSerializerOptions) - ); - services.AddSwaggerGen( - options => - { - options.SchemaFilter(); - options.SchemaFilter(); - options.SchemaFilter(); - options.OperationFilter(); - options.OperationFilter(); - options.OperationFilter(); - options.OperationFilter(); - - options.MapType( - () => new() - { - Type = "object", - AdditionalPropertiesAllowed = true, - } - ); - options.MapType( - () => new() - { - Type = "object", - AdditionalPropertiesAllowed = true, - Nullable = true, - } - ); - - options.DocInclusionPredicate( - (_, apiDesc) => - { - if (!apiDesc.TryGetMethodInfo(out var methodInfo)) - return false; - return methodInfo.DeclaringType?.GetCustomAttributes(true).OfType().Any() == true; - } - ); - - string nestedTypeName(Type type) - { - // ReSharper disable once NullableWarningSuppressionIsUsed - return type.IsNested ? schemaIdSelector(type.DeclaringType!) + type.Name : type.Name; - } - - string schemaIdSelector(Type type) - { - if (type == typeof(Severity)) return $"Validation{nameof(Severity)}"; - if (type.IsGenericType) - { - var sb = new StringBuilder(); - var name = nestedTypeName(type); - name = name[..name.IndexOf('`', StringComparison.Ordinal)]; - sb.Append(name); - foreach (var gt in type.GetGenericArguments()) - { - sb.Append('_').Append(schemaIdSelector(gt)); - } - - return sb.ToString(); - } - - return nestedTypeName(type); - } - - options.CustomSchemaIds(schemaIdSelector); - - foreach (var item in Directory - .EnumerateFiles(AppContext.BaseDirectory, "*.xml") - .Where(x => File.Exists(Path.ChangeExtension(x, "dll")))) - { - try - { - options.IncludeXmlComments(item, true); - } - #pragma warning disable CA1031 - catch (Exception e) - #pragma warning restore CA1031 - { - ErrorAddingXMLComments(context.Logger, e, item); - } - } - } - ); - } -} diff --git a/src/AspNetCore/Conventions/SystemJsonTextConvention.cs b/src/AspNetCore/Conventions/SystemJsonTextConvention.cs index 6bbbf4490..6e2a4e0ee 100644 --- a/src/AspNetCore/Conventions/SystemJsonTextConvention.cs +++ b/src/AspNetCore/Conventions/SystemJsonTextConvention.cs @@ -1,6 +1,7 @@ using System.Text.Json; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Rocket.Surgery.Conventions; using Rocket.Surgery.Conventions.DependencyInjection; using Rocket.Surgery.LaunchPad.Foundation; @@ -44,17 +45,9 @@ public void Register(IConventionContext context, IConfiguration configuration, I services .AddOptions() - .Configure( - (options, provider) => ActivatorUtilities - .CreateInstance>(provider, options.JsonSerializerOptions) - .Create(nameof(MvcJsonOptions)) - ); + .Configure((options, provider) => ExistingValueOptions.Apply(provider, options.JsonSerializerOptions, Options.DefaultName)); services .AddOptions() - .Configure( - (options, provider) => ActivatorUtilities - .CreateInstance>(provider, options.SerializerOptions) - .Create(nameof(HttpJsonOptions)) - ); + .Configure((options, provider) => ExistingValueOptions.Apply(provider, options.SerializerOptions, Options.DefaultName)); } } diff --git a/src/AspNetCore/LaunchPadHelpers.cs b/src/AspNetCore/LaunchPadHelpers.cs index 13aee1ba8..47ee095bc 100644 --- a/src/AspNetCore/LaunchPadHelpers.cs +++ b/src/AspNetCore/LaunchPadHelpers.cs @@ -1,4 +1,7 @@ +using System.Text.Json; +using Humanizer; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Diagnostics.HealthChecks; using Serilog; using Serilog.Events; @@ -7,7 +10,7 @@ namespace Rocket.Surgery.LaunchPad.AspNetCore; /// /// Helpers for during application startup and in middleware /// -public static class LaunchPadLogHelpers +public static class LaunchPadHelpers { /// /// Setup the with default values from the request @@ -62,4 +65,87 @@ private static bool IsHealthCheckEndpoint(HttpContext ctx) // No endpoint, so not a health check endpoint return false; } -} \ No newline at end of file + + /// + /// The default response writer for health checks + /// + /// + /// + /// + public static Task DefaultResponseWriter (HttpContext context, HealthReport report) + { + context.Response.ContentType = "application/json; charset=utf-8"; + + var options = new JsonWriterOptions { Indented = true, }; + + using var memoryStream = new MemoryStream(); + using (var jsonWriter = new Utf8JsonWriter(memoryStream, options)) + { + jsonWriter.WriteStartObject(); + jsonWriter.WriteString("status", report.Status.ToString()); + jsonWriter.WriteStartObject("results"); + + foreach (var healthReportEntry in report.Entries) + { + jsonWriter.WriteStartObject(healthReportEntry.Key); + jsonWriter.WriteString( + "status", + healthReportEntry.Value.Status.ToString() + ); + jsonWriter.WriteString( + "duration", + healthReportEntry.Value.Duration.Humanize() + ); + jsonWriter.WriteString( + "description", + healthReportEntry.Value.Description + ); + + jsonWriter.WriteStartObject("data"); + foreach (var item in healthReportEntry.Value.Data) + { + jsonWriter.WritePropertyName(item.Key); + + JsonSerializer.Serialize( + jsonWriter, + item.Value, + item.Value.GetType() + ); + } + + jsonWriter.WriteEndObject(); + + if (healthReportEntry.Value.Tags.Any()) + { + jsonWriter.WriteStartArray("tags"); + foreach (var item in healthReportEntry.Value.Tags) + { + jsonWriter.WriteStringValue(item); + } + + jsonWriter.WriteEndArray(); + } + + if (healthReportEntry.Value.Exception != null) + { + var ex = healthReportEntry.Value.Exception; + jsonWriter.WriteStartObject("exception"); + jsonWriter.WriteString("message", ex.Message); + jsonWriter.WriteString("stacktrace", ex.StackTrace); + jsonWriter.WriteString("inner", ex.InnerException?.ToString()); + jsonWriter.WriteEndObject(); + } + + jsonWriter.WriteEndObject(); + } + + jsonWriter.WriteEndObject(); + jsonWriter.WriteEndObject(); + } + + return context.Response.WriteAsync( + System.Text.Encoding.UTF8.GetString(memoryStream.ToArray()) + ); + + } +} diff --git a/src/AspNetCore/OpenApi/AuthorizeFilter.cs b/src/AspNetCore/OpenApi/AuthorizeFilter.cs index 1aed4d4a1..6c50a8386 100644 --- a/src/AspNetCore/OpenApi/AuthorizeFilter.cs +++ b/src/AspNetCore/OpenApi/AuthorizeFilter.cs @@ -1,11 +1,11 @@ -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; +using Microsoft.AspNetCore.OpenApi; +using Microsoft.OpenApi.Models; namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; -internal class AuthorizeFilter : IOperationFilter +internal class AuthorizeFilter : IOpenApiOperationTransformer { - public void Apply(OpenApiOperation operation, OperationFilterContext context) + public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken) { // enhance the 401/403 with the media response if (operation.Responses.TryGetValue("401", out var value)) @@ -29,5 +29,7 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) Description = "The error details", } ); + + return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/src/AspNetCore/OpenApi/NodaTimeSwashbuckleExtensions.cs b/src/AspNetCore/OpenApi/NodaTimeSwashbuckleExtensions.cs deleted file mode 100644 index 600a56b56..000000000 --- a/src/AspNetCore/OpenApi/NodaTimeSwashbuckleExtensions.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System.Text.Json; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Interfaces; -using Microsoft.OpenApi.Models; -using NodaTime; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; - -[ExcludeFromCodeCoverage] -internal static class NodaTimeSwashbuckleExtensions -{ - public static SwaggerGenOptions ConfigureForNodaTime(this SwaggerGenOptions c, JsonSerializerOptions settings) - { - IEnumerable<(Type type, Func schema)> createStringSchema( - Type type, - object value, - string? format = null - ) - { - yield return ( type, - () => new() - { - Type = "string", - Format = format, - Example = new OpenApiString(JsonSerializer.Serialize(value, settings).Trim('"')), - Extensions = new Dictionary - { - ["clrType"] = new OpenApiString(type.FullName), - }, - } ); - if (type.IsValueType) - yield return ( typeof(Nullable<>).MakeGenericType(type), - () => new() - { - Type = "string", - Format = format, - Example = new OpenApiString( - JsonSerializer.Serialize(value, settings).Trim('"') - ), - Nullable = true, - Extensions = new Dictionary - { - ["clrType"] = new OpenApiString(type.FullName), - }, - } ); - } - - var instant = Instant.FromUnixTimeSeconds(1573000000); - var interval = new Interval( - instant, - instant - .PlusTicks(TimeSpan.TicksPerDay) - .PlusTicks(TimeSpan.TicksPerHour) - .PlusTicks(TimeSpan.TicksPerMinute) - .PlusTicks(TimeSpan.TicksPerSecond) - .PlusTicks(TimeSpan.TicksPerMillisecond) - ); - var dateTimeZone = DateTimeZoneProviders.Tzdb["America/New_York"]; - var zonedDateTime = instant.InZone(dateTimeZone); - var instantSchemas = - createStringSchema(typeof(Instant), Instant.FromUnixTimeSeconds(1573000000), "date-time").ToArray(); - var period = Period.Between( - zonedDateTime.LocalDateTime, - interval.End.InZone(dateTimeZone).LocalDateTime, - PeriodUnits.AllUnits - ); - foreach (( var type, var schema ) in instantSchemas) - { - c.MapType(type, schema); - } - - foreach (( var type, var schema ) in createStringSchema( - typeof(LocalDate), - LocalDate.FromDateTime(instant.ToDateTimeUtc()), - "date" - )) - { - c.MapType(type, schema); - } - - foreach (( var type, var schema ) in createStringSchema( - typeof(LocalTime), - LocalTime.FromSecondsSinceMidnight(86400 - 12300), - "time" - )) - { - c.MapType(type, schema); - } - - foreach (( var type, var schema ) in createStringSchema( - typeof(LocalDateTime), - LocalDateTime.FromDateTime(instant.ToDateTimeUtc()), - "date-time" - )) - { - c.MapType(type, schema); - } - - foreach (( var type, var schema ) in createStringSchema( - typeof(OffsetDateTime), - OffsetDateTime.FromDateTimeOffset(instant.ToDateTimeOffset()), - "date-time" - )) - { - c.MapType(type, schema); - } - - foreach (( var type, var schema ) in createStringSchema(typeof(ZonedDateTime), zonedDateTime, "date-time")) - { - c.MapType(type, schema); - } - - foreach (( var type, var schema ) in createStringSchema(typeof(Offset), zonedDateTime.Offset)) - { - c.MapType(type, schema); - } - - foreach (( var type, var schema ) in createStringSchema(typeof(Period), period)) - { - c.MapType(type, schema); - } - - foreach (( var type, var schema ) in createStringSchema(typeof(Duration), interval.Duration)) - { - c.MapType(type, schema); - } - - foreach (( var type, var schema ) in createStringSchema(typeof(DateTimeZone), dateTimeZone)) - { - c.MapType(type, schema); - } - - c.MapType( - () => - { - var instantSchema = instantSchemas[0].schema(); - return new() - { - Type = "object", - Nullable = false, - Properties = { ["start"] = instantSchema, ["end"] = instantSchema, }, - Extensions = new Dictionary - { - ["clrType"] = new OpenApiString(typeof(Interval).FullName), - }, - }; - } - ); - c.MapType( - () => new() - { - Type = "object", - Nullable = true, - Properties = { ["start"] = instantSchemas[0].schema(), ["end"] = instantSchemas[0].schema(), }, - - Extensions = new Dictionary - { - ["clrType"] = new OpenApiString(typeof(Interval).FullName), - }, - } - ); - - return c; - } -} \ No newline at end of file diff --git a/src/AspNetCore/OpenApi/OpenApiDefaultOptions.cs b/src/AspNetCore/OpenApi/OpenApiDefaultOptions.cs deleted file mode 100644 index 71f46448d..000000000 --- a/src/AspNetCore/OpenApi/OpenApiDefaultOptions.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.Extensions.Options; -using Swashbuckle.AspNetCore.ReDoc; -using Swashbuckle.AspNetCore.Swagger; -using Swashbuckle.AspNetCore.SwaggerGen; -using Swashbuckle.AspNetCore.SwaggerUI; - -namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; - -internal class OpenApiDefaultOptions : IConfigureOptions, - IConfigureOptions, - IConfigureOptions, - IConfigureOptions -{ - void IConfigureOptions.Configure(ReDocOptions options) - { - } - - void IConfigureOptions.Configure(SwaggerGenOptions options) - { - } - - void IConfigureOptions.Configure(SwaggerOptions options) - { - } - - void IConfigureOptions.Configure(SwaggerUIOptions options) - { - options.ConfigObject.DeepLinking = true; - options.ConfigObject.ShowExtensions = true; - options.ConfigObject.ShowCommonExtensions = true; - options.ConfigObject.Filter = string.Empty; - options.ConfigObject.DisplayRequestDuration = true; - options.ConfigObject.DisplayOperationId = true; - } -} diff --git a/src/AspNetCore/OpenApi/OperationIdFilter.cs b/src/AspNetCore/OpenApi/OperationIdFilter.cs index 97625e9ae..28fd933fc 100644 --- a/src/AspNetCore/OpenApi/OperationIdFilter.cs +++ b/src/AspNetCore/OpenApi/OperationIdFilter.cs @@ -1,12 +1,12 @@ using System.Globalization; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.OpenApi; using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; -internal class OperationIdFilter : IOperationFilter +internal class OperationIdFilter : IOpenApiOperationTransformer { /// /// By default, pascalize converts strings to UpperCamelCase also removing underscores @@ -31,14 +31,16 @@ private static string Camelize(string input) #pragma warning restore CA1308 } - public void Apply(OpenApiOperation operation, OperationFilterContext context) + public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken) { - if (string.IsNullOrWhiteSpace(operation.OperationId) && context.ApiDescription.ActionDescriptor is ControllerActionDescriptor cad) + if (string.IsNullOrWhiteSpace(operation.OperationId) && context.Description.ActionDescriptor is ControllerActionDescriptor cad) operation.OperationId = cad.ActionName; + if (operation.Parameters is null) return Task.CompletedTask; foreach (var parameter in operation.Parameters) { parameter.Name = Camelize(parameter.Name); } + return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/src/AspNetCore/OpenApi/OperationMediaTypesFilter.cs b/src/AspNetCore/OpenApi/OperationMediaTypesFilter.cs index 3aee8dd4d..3eb1d47ad 100644 --- a/src/AspNetCore/OpenApi/OperationMediaTypesFilter.cs +++ b/src/AspNetCore/OpenApi/OperationMediaTypesFilter.cs @@ -1,22 +1,24 @@ -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; +using Microsoft.AspNetCore.OpenApi; +using Microsoft.OpenApi.Models; namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; -internal class OperationMediaTypesFilter : IOperationFilter -{ - public void Apply(OpenApiOperation operation, OperationFilterContext context) + internal class OperationMediaTypesFilter : IOpenApiOperationTransformer + { + public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken) { var contentCollections = - operation.Responses.Values.Select(x => x.Content ?? new Dictionary()) - .Concat(new[] { operation.RequestBody?.Content ?? new Dictionary() }) - .Where(x => x.ContainsKey("text/plain")) - .ToArray(); + operation + .Responses.Values.Select(x => x.Content ?? new Dictionary()) + .Concat([operation.RequestBody?.Content ?? new Dictionary()]) + .Where(x => x.ContainsKey("text/plain")) + .ToArray(); var patchCollections = - operation.Responses.Values.Select(x => x.Content ?? new Dictionary()) - .Concat(new[] { operation.RequestBody?.Content ?? new Dictionary() }) - .Where(x => x.Keys.Any(z => z.Contains("patch", StringComparison.OrdinalIgnoreCase))) - .ToArray(); + operation + .Responses.Values.Select(x => x.Content ?? new Dictionary()) + .Concat([operation.RequestBody?.Content ?? new Dictionary()]) + .Where(x => x.Keys.Any(z => z.Contains("patch", StringComparison.OrdinalIgnoreCase))) + .ToArray(); foreach (var item in contentCollections) { @@ -31,5 +33,7 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) item.Remove(p.Key); } } + + return Task.CompletedTask; } } diff --git a/src/AspNetCore/OpenApi/ProblemDetailsSchemaFilter.cs b/src/AspNetCore/OpenApi/ProblemDetailsSchemaFilter.cs index fffaf3253..5854d4ff3 100644 --- a/src/AspNetCore/OpenApi/ProblemDetailsSchemaFilter.cs +++ b/src/AspNetCore/OpenApi/ProblemDetailsSchemaFilter.cs @@ -1,23 +1,23 @@ using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OpenApi; using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; -internal class ProblemDetailsSchemaFilter : ISchemaFilter +internal class ProblemDetailsSchemaFilter : IOpenApiSchemaTransformer { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken) { - if (typeof(ProblemDetails).IsAssignableFrom(context.Type)) + if (true) return Task.CompletedTask; + if (!typeof(ProblemDetails).IsAssignableFrom(context.JsonTypeInfo.Type)) return Task.CompletedTask; + schema.AdditionalPropertiesAllowed = true; + schema.Properties.Remove(nameof(ProblemDetails.Extensions)); + schema.Properties.Remove("extensions"); + if (schema.Properties.TryGetValue("validationErrors", out var v)) { - schema.AdditionalPropertiesAllowed = true; - schema.Properties.Remove(nameof(ProblemDetails.Extensions)); - schema.Properties.Remove("extensions"); - if (schema.Properties.TryGetValue("validationErrors", out var v)) - { - schema.Properties["errors"] = v; - schema.Properties.Remove("validationErrors"); - } + schema.Properties["errors"] = v; + schema.Properties.Remove("validationErrors"); } + return Task.CompletedTask; } } diff --git a/src/AspNetCore/OpenApi/StatusCode201Filter.cs b/src/AspNetCore/OpenApi/StatusCode201Filter.cs index 530a36e3d..31a82a8a4 100644 --- a/src/AspNetCore/OpenApi/StatusCode201Filter.cs +++ b/src/AspNetCore/OpenApi/StatusCode201Filter.cs @@ -1,15 +1,15 @@ -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; +using Microsoft.AspNetCore.OpenApi; +using Microsoft.OpenApi.Models; namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; -internal class StatusCode201Filter : IOperationFilter +internal class StatusCode201Filter : IOpenApiOperationTransformer { - public void Apply(OpenApiOperation operation, OperationFilterContext context) + public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken) { // enhance the 201 with the response header if (!operation.Responses.TryGetValue("201", out var value)) - return; + return Task.CompletedTask; value.Headers.Add( "location", new OpenApiHeader @@ -19,5 +19,6 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) Description = "The location of the entity that was created" } ); + return Task.CompletedTask; } } diff --git a/src/AspNetCore/OpenApi/StronglyTypedIdSchemaFilter.cs b/src/AspNetCore/OpenApi/StronglyTypedIdSchemaFilter.cs index 418aa1c89..8f0f88fe6 100644 --- a/src/AspNetCore/OpenApi/StronglyTypedIdSchemaFilter.cs +++ b/src/AspNetCore/OpenApi/StronglyTypedIdSchemaFilter.cs @@ -1,18 +1,16 @@ using System.Reflection; +using Microsoft.AspNetCore.OpenApi; using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; internal static class StronglyTypedIdHelpers { - [RequiresUnreferencedCode("DynamicBehavior is incompatible with trimming.")] public static bool IsStronglyTypedId(Type? type) { return GetStronglyTypedIdType(type) is { }; } - [RequiresUnreferencedCode("DynamicBehavior is incompatible with trimming.")] public static Type? GetStronglyTypedIdType(Type? type) { if (type?.GetMember("New", BindingFlags.Static | BindingFlags.Public).FirstOrDefault() is MethodInfo @@ -24,25 +22,12 @@ public static bool IsStronglyTypedId(Type? type) } } -internal class StronglyTypedIdSchemaFilter : ISchemaFilter +internal class StronglyTypedIdSchemaFilter : IOpenApiSchemaTransformer { - // TODO: Make a source generator for this to work without generics - [RequiresUnreferencedCode("DynamicBehavior is incompatible with trimming.")] - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken) { - if (StronglyTypedIdHelpers.GetStronglyTypedIdType(context.Type) is not { } type) return; - schema.Properties.Clear(); - var s2 = context.SchemaGenerator.GenerateSchema(type, context.SchemaRepository, context.MemberInfo, context.ParameterInfo); - schema.Format = s2.Format; - schema.Type = s2.Type; - schema.Enum = s2.Enum; - schema.Default = s2.Default; - schema.Maximum = s2.Maximum; - schema.Minimum = s2.Minimum; - schema.Reference = s2.Reference; - schema.Pattern = s2.Pattern; - schema.Nullable = s2.Nullable; - schema.Required = s2.Required; - schema.Not = s2.Not; + if (StronglyTypedIdHelpers.GetStronglyTypedIdType(context.JsonPropertyInfo?.PropertyType) is not { } type) return Task.CompletedTask; +// TODO: Solve this + return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/src/AspNetCore/OpenApi/SwashbuckleAddAllDocumentEndpoints.cs b/src/AspNetCore/OpenApi/SwashbuckleAddAllDocumentEndpoints.cs deleted file mode 100644 index 44eaf3e50..000000000 --- a/src/AspNetCore/OpenApi/SwashbuckleAddAllDocumentEndpoints.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Options; -using Swashbuckle.AspNetCore.SwaggerGen; -using Swashbuckle.AspNetCore.SwaggerUI; - -namespace Rocket.Surgery.LaunchPad.AspNetCore.OpenApi; - -internal class SwashbuckleAddAllDocumentEndpoints(IOptions options) : IConfigureOptions -{ - public void Configure(SwaggerUIOptions options1) - { - foreach (var item in options.Value.SwaggerGeneratorOptions.SwaggerDocs) - { - options1.SwaggerEndpoint($"/swagger/{item.Key}/swagger.json", item.Value.Title); - } - } -} diff --git a/src/AspNetCore/Rocket.Surgery.LaunchPad.AspNetCore.csproj b/src/AspNetCore/Rocket.Surgery.LaunchPad.AspNetCore.csproj index 5bdcd4809..28ddffcd7 100644 --- a/src/AspNetCore/Rocket.Surgery.LaunchPad.AspNetCore.csproj +++ b/src/AspNetCore/Rocket.Surgery.LaunchPad.AspNetCore.csproj @@ -1,10 +1,9 @@  - net8.0 + net9.0 $(PackageTags) false - false @@ -13,17 +12,16 @@ + + + - - - - diff --git a/src/AspNetCore/RocketSurgeryMvcCoreExtensions.cs b/src/AspNetCore/RocketSurgeryMvcCoreExtensions.cs index 68ba5cf53..2ddc72eae 100644 --- a/src/AspNetCore/RocketSurgeryMvcCoreExtensions.cs +++ b/src/AspNetCore/RocketSurgeryMvcCoreExtensions.cs @@ -40,8 +40,8 @@ public static IApplicationBuilder UseLaunchPadRequestLogging(this IApplicationBu return app.UseSerilogRequestLogging( x => { - x.GetLevel = LaunchPadLogHelpers.DefaultGetLevel; - x.EnrichDiagnosticContext = LaunchPadLogHelpers.DefaultEnrichDiagnosticContext; + x.GetLevel = LaunchPadHelpers.DefaultGetLevel; + x.EnrichDiagnosticContext = LaunchPadHelpers.DefaultEnrichDiagnosticContext; } ); } diff --git a/src/AspNetCore/Validation/ValidationExceptionFilter.cs b/src/AspNetCore/Validation/ValidationExceptionFilter.cs index 5998e0fe6..4d3348dea 100644 --- a/src/AspNetCore/Validation/ValidationExceptionFilter.cs +++ b/src/AspNetCore/Validation/ValidationExceptionFilter.cs @@ -19,7 +19,7 @@ public Task OnExceptionAsync(ExceptionContext context) /// public void OnException(ExceptionContext context) { - if (!( context.Exception is ValidationException validationException )) return; + if (context.Exception is not ValidationException validationException) return; context.ExceptionHandled = true; context.Result = new UnprocessableEntityObjectResult(new FluentValidationProblemDetails(validationException.Errors)); } diff --git a/src/EntityFramework/Rocket.Surgery.LaunchPad.EntityFramework.csproj b/src/EntityFramework/Rocket.Surgery.LaunchPad.EntityFramework.csproj index daa22425d..e19188843 100644 --- a/src/EntityFramework/Rocket.Surgery.LaunchPad.EntityFramework.csproj +++ b/src/EntityFramework/Rocket.Surgery.LaunchPad.EntityFramework.csproj @@ -1,11 +1,11 @@  - - net8.0 - - $(PackageTags) - - - - - + + net8.0;net9.0 + + $(PackageTags) + + + + + diff --git a/src/Foundation.NewtonsoftJson/Rocket.Surgery.LaunchPad.Foundation.NewtonsoftJson.csproj b/src/Foundation.NewtonsoftJson/Rocket.Surgery.LaunchPad.Foundation.NewtonsoftJson.csproj index 3f7d63226..9e4a64f50 100644 --- a/src/Foundation.NewtonsoftJson/Rocket.Surgery.LaunchPad.Foundation.NewtonsoftJson.csproj +++ b/src/Foundation.NewtonsoftJson/Rocket.Surgery.LaunchPad.Foundation.NewtonsoftJson.csproj @@ -1,6 +1,6 @@  - net8.0 + net8.0;net9.0 $(PackageTags) Rocket.Surgery.LaunchPad.Foundation diff --git a/src/Foundation/Assigned.cs b/src/Foundation/Assigned.cs index a538f7164..4417851ae 100644 --- a/src/Foundation/Assigned.cs +++ b/src/Foundation/Assigned.cs @@ -87,13 +87,9 @@ private Assigned(T? value, bool hasValue) public T? Value { get; } /// - /// true if the Assigned was explicitly set. + /// true if the Assigned was explicitly set. /// - /// - public bool HasBeenSet() - { - return _hasValue; - } + public bool HasValue => _hasValue; /// /// Provides the name string. @@ -151,4 +147,4 @@ public bool Equals(Assigned? other) return Equals(Value, other.Value); } -} \ No newline at end of file +} diff --git a/src/Foundation/Conventions/DefaultConvention.cs b/src/Foundation/Conventions/DefaultConvention.cs index 76a0422b3..d5c97cbda 100644 --- a/src/Foundation/Conventions/DefaultConvention.cs +++ b/src/Foundation/Conventions/DefaultConvention.cs @@ -27,5 +27,7 @@ public void Register(IConventionContext context, IConfiguration configuration, I .AddOptions() .AddLogging() .AddExecuteScopedServices(); + + services.AddCompiledServiceRegistrations(context.TypeProvider); } } diff --git a/src/Foundation/Conventions/FluentValidationConvention.cs b/src/Foundation/Conventions/FluentValidationConvention.cs index b8d1d4e9e..3418f3a48 100644 --- a/src/Foundation/Conventions/FluentValidationConvention.cs +++ b/src/Foundation/Conventions/FluentValidationConvention.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Options; using Rocket.Surgery.Conventions; using Rocket.Surgery.Conventions.DependencyInjection; -using Rocket.Surgery.Conventions.Reflection; +using Rocket.Surgery.DependencyInjection.Compiled; using Rocket.Surgery.LaunchPad.Foundation.Validation; namespace Rocket.Surgery.LaunchPad.Foundation.Conventions; @@ -47,7 +47,7 @@ public void Register(IConventionContext context, IConfiguration configuration, I { ArgumentNullException.ThrowIfNull(context); - var types = context.AssemblyProvider.GetTypes( + var types = context.TypeProvider.GetTypes( z => z .FromAssemblyDependenciesOf() .GetTypes( @@ -57,13 +57,17 @@ public void Register(IConventionContext context, IConfiguration configuration, I .NotInfoOf(TypeInfoFilter.GenericType) ) ); - foreach (var validator in types) - { - if (validator is not { BaseType: { IsGenericType: true, GenericTypeArguments: [var innerType,], }, }) continue; - var interfaceType = typeof(IValidator<>).MakeGenericType(innerType); - services.Add(new(interfaceType, validator, _options.ValidatorLifetime)); - services.Add(new(validator, validator, _options.ValidatorLifetime)); - } + + context.TypeProvider + .Scan( + services, + z => z + .FromAssemblyDependenciesOf() + .AddClasses(t => t.AssignableTo().NotAssignableTo(typeof(CompositeValidator<>))) + .AsSelf() + .AsImplementedInterfaces(a => a.AssignableTo()) + .WithTransientLifetime() + ); if (_options.RegisterValidationOptionsAsHealthChecks == true || ( !_options.RegisterValidationOptionsAsHealthChecks.HasValue diff --git a/src/Foundation/Conventions/HealthChecksConvention.cs b/src/Foundation/Conventions/HealthChecksConvention.cs index 6b014159a..78d3c9eb1 100644 --- a/src/Foundation/Conventions/HealthChecksConvention.cs +++ b/src/Foundation/Conventions/HealthChecksConvention.cs @@ -8,9 +8,9 @@ namespace Rocket.Surgery.LaunchPad.Foundation.Conventions; /// /// EnvironmentLoggingConvention. -/// Implements the +/// Implements the /// -/// +/// [PublicAPI] [ExportConvention] [ConventionCategory(ConventionCategory.Core)] diff --git a/src/Foundation/Conventions/MediatRConvention.cs b/src/Foundation/Conventions/MediatRConvention.cs index 1e550e367..f8b2fddda 100644 --- a/src/Foundation/Conventions/MediatRConvention.cs +++ b/src/Foundation/Conventions/MediatRConvention.cs @@ -35,9 +35,8 @@ public MediatRConvention(FoundationOptions? options = null) /// public void Register(IConventionContext context, IConfiguration configuration, IServiceCollection services) { - // TODO: Look at converting this to use the assembly type provider - var assemblies = context.AssemblyProvider.GetAssemblies(x => x.FromAssemblyDependenciesOf()).ToArray(); - if (!assemblies.Any()) return; + var assemblies = context.TypeProvider.GetAssemblies(x => x.FromAssemblyDependenciesOf()).ToArray(); + if (!assemblies.Any()) throw new ArgumentException("No assemblies found that reference MediatR"); services.AddMediatR( c => diff --git a/src/Foundation/Conventions/OptionsConvention.cs b/src/Foundation/Conventions/OptionsConvention.cs index 8bcbb05fe..424a491e8 100644 --- a/src/Foundation/Conventions/OptionsConvention.cs +++ b/src/Foundation/Conventions/OptionsConvention.cs @@ -11,6 +11,7 @@ namespace Rocket.Surgery.LaunchPad.Foundation.Conventions; /// [ExportConvention] [ConventionCategory(ConventionCategory.Core)] +[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] public class OptionsConvention : IServiceConvention { private readonly MethodInfo _configureMethod; @@ -20,22 +21,28 @@ public class OptionsConvention : IServiceConvention /// public OptionsConvention() { - _configureMethod = typeof(OptionsConfigurationServiceCollectionExtensions).GetMethod( - nameof(OptionsConfigurationServiceCollectionExtensions.Configure), - [typeof(IServiceCollection), typeof(string), typeof(IConfiguration),] - )!; + _configureMethod = GetType().GetMethod(nameof(Configure), BindingFlags.NonPublic | BindingFlags.Static)!; } /// public void Register(IConventionContext context, IConfiguration configuration, IServiceCollection services) { - var classes = context.AssemblyProvider.GetTypes( + var classes = context.TypeProvider.GetTypes( s => s.FromAssemblyDependenciesOf().GetTypes(f => f.WithAttribute()) ); foreach (var options in classes) { var attribute = options.GetCustomAttribute()!; + #pragma warning disable IL2060 _configureMethod.MakeGenericMethod(options).Invoke(null, [services, attribute.OptionsName, configuration.GetSection(attribute.ConfigurationKey),]); + #pragma warning restore IL2060 } } + + [RequiresUnreferencedCode("Calls Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure(String, IConfiguration)")] + private static IServiceCollection Configure<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TOptions>(IServiceCollection services, string? name, IConfiguration config) + where TOptions : class + { + return services.Configure(name, config); + } } diff --git a/src/Foundation/Conventions/TimeConvention.cs b/src/Foundation/Conventions/TimeConvention.cs index bb59a8d1a..3b6eceb01 100644 --- a/src/Foundation/Conventions/TimeConvention.cs +++ b/src/Foundation/Conventions/TimeConvention.cs @@ -33,7 +33,7 @@ public TimeConvention(FoundationOptions? options = null) /// public void Register(IConventionContext context, IConfiguration configuration, IServiceProvider services, LoggerConfiguration loggerConfiguration) { - loggerConfiguration.Destructure.NodaTimeTypes(services.GetRequiredService()); + loggerConfiguration.Destructure.NodaTimeTypes(); } /// @@ -46,8 +46,9 @@ public void Register(IConventionContext context, IConfiguration configuration, I { ArgumentNullException.ThrowIfNull(context); + // Try add so that unit tests can insert fakes services.TryAddSingleton(TimeProvider.System); services.TryAddSingleton(SystemClock.Instance); - services.TryAddSingleton(new DateTimeZoneCache(_options.DateTimeZoneSource)); + services.TryAddSingleton(DateTimeZoneProviders.Tzdb); } } diff --git a/src/Foundation/ExistingValueOptionsFactory.cs b/src/Foundation/ExistingValueOptionsFactory.cs index 605d1f511..125d8ea8b 100644 --- a/src/Foundation/ExistingValueOptionsFactory.cs +++ b/src/Foundation/ExistingValueOptionsFactory.cs @@ -1,7 +1,51 @@ +using System.Text.Json; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace Rocket.Surgery.LaunchPad.Foundation; +/// +/// Helper methods for creating instances. +/// +public static class ExistingValueOptions +{ + /// + /// Applys all of the , , and instances to the options instance. + /// + /// + /// + /// + /// + /// + public static void Apply(IServiceProvider serviceProvider, TOptions options, string name) where TOptions : class, new() + { + var setups = serviceProvider.GetServices>(); + var postConfigures = serviceProvider.GetServices>(); + var validations = serviceProvider.GetServices>(); + + foreach (var setup in setups) + { + if (setup is IConfigureNamedOptions namedSetup) + namedSetup.Configure(name, options); + else if (name == Options.DefaultName) setup.Configure(options); + } + + foreach (var post in postConfigures) + { + post.PostConfigure(name, options); + } + + var failures = new List(); + foreach (var validate in validations) + { + var result = validate.Validate(name, options); + if (result.Failed) failures.AddRange(result.Failures); + } + + if (failures.Count > 0) throw new OptionsValidationException(name, typeof(TOptions), failures); + } +} + /// /// Implementation of . /// @@ -53,18 +97,16 @@ public TOptions Create(string name) post.PostConfigure(name, options); } - if (_validations != null) + if (_validations is null) return options; + var failures = new List(); + foreach (var validate in _validations) { - var failures = new List(); - foreach (var validate in _validations) - { - var result = validate.Validate(name, options); - if (result.Failed) failures.AddRange(result.Failures); - } - - if (failures.Count > 0) throw new OptionsValidationException(name, typeof(TOptions), failures); + var result = validate.Validate(name, options); + if (result.Failed) failures.AddRange(result.Failures); } + if (failures.Count > 0) throw new OptionsValidationException(name, typeof(TOptions), failures); + return options; } -} \ No newline at end of file +} diff --git a/src/Foundation/FluentValidationProblemDetail.cs b/src/Foundation/FluentValidationProblemDetail.cs index e74df7522..f75093659 100644 --- a/src/Foundation/FluentValidationProblemDetail.cs +++ b/src/Foundation/FluentValidationProblemDetail.cs @@ -11,25 +11,6 @@ namespace Rocket.Surgery.LaunchPad.Foundation; [PublicAPI] public class FluentValidationProblemDetail { - /// - /// Convert the problem details into a dictionary of information in graphql - /// - /// - /// - public static implicit operator ReadOnlyDictionary(FluentValidationProblemDetail detail) - { - return new( - new Dictionary - { - ["propertyName"] = detail.PropertyName, - ["errorMessage"] = detail.ErrorMessage, - ["attemptedValue"] = detail.AttemptedValue, - ["severity"] = detail.Severity, - ["errorCode"] = detail.ErrorCode, - } - ); - } - /// /// A validation error problem /// @@ -64,15 +45,6 @@ public FluentValidationProblemDetail(ValidationFailure validationFailure) [JsonPropertyName("errorCode")] public string ErrorCode { get; set; } - /// - /// Convert the problem details into a dictionary of information in graphql - /// - /// - public ReadOnlyDictionary ToReadOnlyDictionary() - { - return this; - } - internal class Validator : AbstractValidator { public Validator() @@ -82,4 +54,4 @@ public Validator() RuleFor(x => x.ErrorMessage).NotNull(); } } -} \ No newline at end of file +} diff --git a/src/Foundation/NodaTimeLoggerConfigurationExtensions.cs b/src/Foundation/NodaTimeLoggerConfigurationExtensions.cs index c794538b2..b4d78bd2c 100644 --- a/src/Foundation/NodaTimeLoggerConfigurationExtensions.cs +++ b/src/Foundation/NodaTimeLoggerConfigurationExtensions.cs @@ -13,9 +13,8 @@ public static class NodaTimeLoggerConfigurationExtensions /// Enable destructuring of JSON.NET dynamic objects. /// /// The logger configuration to apply configuration to. - /// /// An object allowing configuration to continue. - public static LoggerConfiguration NodaTimeTypes(this LoggerDestructuringConfiguration configuration, IDateTimeZoneProvider provider) + public static LoggerConfiguration NodaTimeTypes(this LoggerDestructuringConfiguration configuration) { return configuration .AsScalar() @@ -24,4 +23,4 @@ public static LoggerConfiguration NodaTimeTypes(this LoggerDestructuringConfigur .Destructure .With(new NodaTimeDestructuringPolicy()); } -} \ No newline at end of file +} diff --git a/src/Foundation/Rocket.Surgery.LaunchPad.Foundation.csproj b/src/Foundation/Rocket.Surgery.LaunchPad.Foundation.csproj index 48e16f977..c309f5d34 100644 --- a/src/Foundation/Rocket.Surgery.LaunchPad.Foundation.csproj +++ b/src/Foundation/Rocket.Surgery.LaunchPad.Foundation.csproj @@ -1,6 +1,6 @@  - net8.0 + net8.0;net9.0 $(PackageTags) @@ -24,6 +24,10 @@ + + + + @@ -34,20 +38,8 @@ - - - + + diff --git a/src/Grpc/Rocket.Surgery.LaunchPad.Grpc.csproj b/src/Grpc/Rocket.Surgery.LaunchPad.Grpc.csproj index 0275c3a48..ab0037ac8 100644 --- a/src/Grpc/Rocket.Surgery.LaunchPad.Grpc.csproj +++ b/src/Grpc/Rocket.Surgery.LaunchPad.Grpc.csproj @@ -1,6 +1,6 @@  - net8.0 + net9.0 $(PackageTags) false diff --git a/src/Hosting/Conventions/EnvironmentLoggingConvention.cs b/src/Hosting/Conventions/EnvironmentLoggingConvention.cs index 064a1d3f2..edf18c2c7 100644 --- a/src/Hosting/Conventions/EnvironmentLoggingConvention.cs +++ b/src/Hosting/Conventions/EnvironmentLoggingConvention.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Rocket.Surgery.Conventions; using Rocket.Surgery.LaunchPad.Serilog; diff --git a/src/Hosting/Conventions/SerilogHostingConvention.cs b/src/Hosting/Conventions/SerilogHostingConvention.cs index d1b50dd30..91e5a369b 100644 --- a/src/Hosting/Conventions/SerilogHostingConvention.cs +++ b/src/Hosting/Conventions/SerilogHostingConvention.cs @@ -1,8 +1,10 @@ using App.Metrics; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Rocket.Surgery.Conventions; +using Rocket.Surgery.Conventions.DependencyInjection; using Rocket.Surgery.Conventions.Hosting; using Rocket.Surgery.Hosting; using Serilog; @@ -22,43 +24,15 @@ namespace Rocket.Surgery.LaunchPad.Hosting.Conventions; [PublicAPI] [ExportConvention] [ConventionCategory(ConventionCategory.Core)] -public class SerilogHostingConvention : IHostApplicationConvention, IHostCreatedConvention +public class SerilogHostingConvention : IServiceConvention { - private void CustomAddSerilog( - IServiceCollection collection, - Action configureLogger - ) - { - collection.AddSingleton(new LoggerProviderCollection()); - collection.AddSingleton( - services => - { - var loggerConfiguration = new LoggerConfiguration(); - loggerConfiguration.WriteTo.Providers(services.GetRequiredService()); - configureLogger(services, loggerConfiguration); - return loggerConfiguration.CreateLogger(); - } - ); - collection.AddSingleton(services => services.GetRequiredService().ForContext(new NullEnricher())); - collection.AddSingleton( - services => new SerilogLoggerFactory( - services.GetRequiredService(), - true, - services.GetRequiredService() - ) - ); - collection.AddSingleton(services => new DiagnosticContext(services.GetRequiredService())); - collection.AddSingleton(services => services.GetRequiredService()); - } - /// - public void Register(IConventionContext context, IHostApplicationBuilder builder) + public void Register(IConventionContext context, IConfiguration configuration, IServiceCollection services) { ArgumentNullException.ThrowIfNull(context); // removes default console loggers and such - foreach (var item in builder - .Services + foreach (var item in services .Where( x => { @@ -70,44 +44,12 @@ public void Register(IConventionContext context, IHostApplicationBuilder builder .ToArray() ) { - builder.Services.Remove(item); - } - - if (context.Get() is { } logger) - { - builder.Services.AddSerilog(logger); - } - else - { - CustomAddSerilog( - builder.Services, - (services, loggerConfiguration) => loggerConfiguration.ApplyConventions(context, builder.Configuration, services) - ); + services.Remove(item); } - if (context.Get() != null) - // ReSharper disable once NullableWarningSuppressionIsUsed - builder.Services.AddSingleton(context.Get()!); - } - - /// - public void Register(IConventionContext context, IHost host) - { - host - .Services - .GetServices() - .Aggregate( - host.Services.GetRequiredService(), - (factory, loggerProvider) => - { - factory.AddProvider(loggerProvider); - return factory; - } - ); - } - - private class NullEnricher : ILogEventEnricher - { - public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { } + services.ActivateSingleton(); + services.ActivateSingleton(); + services.AddSingleton(sp => new DiagnosticContext(sp.GetRequiredService())); + services.AddSingleton(sp => sp.GetRequiredService()); } } diff --git a/src/Hosting/Rocket.Surgery.LaunchPad.Hosting.csproj b/src/Hosting/Rocket.Surgery.LaunchPad.Hosting.csproj index aa3a2a342..5a1aee5e4 100644 --- a/src/Hosting/Rocket.Surgery.LaunchPad.Hosting.csproj +++ b/src/Hosting/Rocket.Surgery.LaunchPad.Hosting.csproj @@ -1,19 +1,16 @@  - net8.0 + net8.0;net9.0 $(PackageTags) - - - diff --git a/src/HotChocolate/Conventions/GraphqlConvention.cs b/src/HotChocolate/Conventions/GraphqlConvention.cs index f30fe0233..8998bbe03 100644 --- a/src/HotChocolate/Conventions/GraphqlConvention.cs +++ b/src/HotChocolate/Conventions/GraphqlConvention.cs @@ -1,11 +1,14 @@ -using MediatR; +using FairyBread; +using FluentValidation; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using MediatR; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Rocket.Surgery.Conventions; using Rocket.Surgery.Conventions.DependencyInjection; using Rocket.Surgery.LaunchPad.Foundation; -using Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; using Rocket.Surgery.LaunchPad.HotChocolate.Types; using IConventionContext = Rocket.Surgery.Conventions.IConventionContext; @@ -42,9 +45,13 @@ public void Register(IConventionContext context, IConfiguration configuration, I { var sb = context .GetOrAdd(() => services.AddGraphQL()) - .AddFairyBread() +// .AddMutationConventions() + .AddFairyBread( + options => { options.ThrowIfNoValidatorsFound = false; } + ) .AddErrorFilter() .BindRuntimeType(); + services.Replace(ServiceDescriptor.Singleton()); if (!_rocketChocolateOptions.IncludeAssemblyInfoQuery) return; @@ -52,3 +59,37 @@ public void Register(IConventionContext context, IConfiguration configuration, I sb.AddType(); } } + +//class LaunchPadValidatorProvider : IValidatorProvider +//{ +// public IEnumerable GetValidators(IMiddlewareContext context, IInputField argument) +// { +// yield break; +// } +//} + +class LaunchPadValidatorRegistry(IServiceProvider serviceProvider) : IValidatorRegistry +{ + private readonly Lazy>> _cache = new( + () => + { + var dictionary = new Dictionary>(); + var scope = serviceProvider.CreateScope(); + var validators = scope.ServiceProvider.GetServices(); + foreach (var validator in validators) + { + var type = validator.GetType().GetInterfaces().First(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IValidator<>)).GetGenericArguments()[0]; + if (!dictionary.TryGetValue(type, out var list)) + { + list = new (); + dictionary[type] = list; + } + + list.Add(new (validator.GetType())); + } + return dictionary; + } + ); + + public Dictionary> Cache => _cache.Value; +} diff --git a/src/HotChocolate/FairyBread/ArgumentValidationResult.cs b/src/HotChocolate/FairyBread/ArgumentValidationResult.cs deleted file mode 100644 index 23f1a9c43..000000000 --- a/src/HotChocolate/FairyBread/ArgumentValidationResult.cs +++ /dev/null @@ -1,33 +0,0 @@ -using FluentValidation; -using FluentValidation.Results; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -public record ArgumentValidationResult -{ - public ArgumentValidationResult( - string argumentName, - IValidator validator, - ValidationResult result - ) - { - ArgumentName = argumentName; - Validator = validator; - Result = result ?? throw new ArgumentNullException(nameof(result)); - } - - /// - /// Name of the argument this result is for. - /// - public string ArgumentName { get; } - - /// - /// The validator that caused this result. - /// - public IValidator Validator { get; } - - /// - /// The validation result. - /// - public ValidationResult Result { get; } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/DefaultFairyBreadOptions.cs b/src/HotChocolate/FairyBread/DefaultFairyBreadOptions.cs deleted file mode 100644 index 9f06a1d0d..000000000 --- a/src/HotChocolate/FairyBread/DefaultFairyBreadOptions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using HotChocolate.Types.Descriptors.Definitions; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -public class DefaultFairyBreadOptions : IFairyBreadOptions -{ - /// - public virtual bool ThrowIfNoValidatorsFound { get; set; } = true; - - /// - public Func ShouldValidateArgument { get; set; } - = (o, f, a) => true; -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/DefaultValidationErrorsHandler.cs b/src/HotChocolate/FairyBread/DefaultValidationErrorsHandler.cs deleted file mode 100644 index bf1dcab0a..000000000 --- a/src/HotChocolate/FairyBread/DefaultValidationErrorsHandler.cs +++ /dev/null @@ -1,54 +0,0 @@ -using FluentValidation; -using FluentValidation.Results; -using HotChocolate; -using HotChocolate.Resolvers; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -public class DefaultValidationErrorsHandler : IValidationErrorsHandler -{ - protected virtual IErrorBuilder CreateErrorBuilder( - IMiddlewareContext context, - string argumentName, - IValidator validator, - ValidationFailure failure - ) - { - var builder = ErrorBuilder - .New() - .SetPath(context.Path) - .SetMessage(failure.ErrorMessage) - .SetCode("FairyBread_ValidationError") - .SetExtension("argumentName", argumentName) - .SetExtension("validatorName", validator.GetType().Name) - .SetExtension("errorCode", failure.ErrorCode) - .SetExtension("errorMessage", failure.ErrorMessage) - .SetExtension("attemptedValue", failure.AttemptedValue) - .SetExtension("severity", failure.Severity) - .SetExtension("formattedMessagePlaceholderValues", failure.FormattedMessagePlaceholderValues); - - if (!string.IsNullOrWhiteSpace(failure.PropertyName)) - { - builder = builder - .SetExtension("propertyName", failure.PropertyName); - } - - return builder; - } - - public virtual void Handle( - IMiddlewareContext context, - IEnumerable invalidResults - ) - { - foreach (var invalidResult in invalidResults) - { - foreach (var failure in invalidResult.Result.Errors) - { - var errorBuilder = CreateErrorBuilder(context, invalidResult.ArgumentName, invalidResult.Validator, failure); - var error = errorBuilder.Build(); - context.ReportError(error); - } - } - } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/DefaultValidatorProvider.cs b/src/HotChocolate/FairyBread/DefaultValidatorProvider.cs deleted file mode 100644 index 6f1b38ed4..000000000 --- a/src/HotChocolate/FairyBread/DefaultValidatorProvider.cs +++ /dev/null @@ -1,45 +0,0 @@ -using FluentValidation; -using HotChocolate.Resolvers; -using HotChocolate.Types; -using Microsoft.Extensions.DependencyInjection; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -public class DefaultValidatorProvider : IValidatorProvider -{ - protected readonly IValidatorRegistry ValidatorRegistry; - - public DefaultValidatorProvider( - IValidatorRegistry validatorRegistry - ) - { - ValidatorRegistry = validatorRegistry; - } - - public virtual IEnumerable GetValidators(IMiddlewareContext context, IInputField argument) - { - if (!argument.ContextData.TryGetValue( - WellKnownContextData.ValidatorDescriptors, - out var validatorDescriptorsRaw - ) - || validatorDescriptorsRaw is not IEnumerable validatorDescriptors) - { - yield break; - } - - foreach (var validatorDescriptor in validatorDescriptors) - { - if (validatorDescriptor.RequiresOwnScope) - { - var scope = context.Services.CreateScope(); // disposed by middleware - var validator = (IValidator)scope.ServiceProvider.GetRequiredService(validatorDescriptor.ValidatorType); - yield return new(validator, scope); - } - else - { - var validator = (IValidator)context.Services.GetRequiredService(validatorDescriptor.ValidatorType); - yield return new(validator); - } - } - } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/DefaultValidatorRegistry.cs b/src/HotChocolate/FairyBread/DefaultValidatorRegistry.cs deleted file mode 100644 index 6c079db4f..000000000 --- a/src/HotChocolate/FairyBread/DefaultValidatorRegistry.cs +++ /dev/null @@ -1,34 +0,0 @@ -using FluentValidation; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -public class DefaultValidatorRegistry : IValidatorRegistry -{ - private readonly IServiceProvider _serviceProvider; - - private readonly Dictionary _cache = new(); - - public DefaultValidatorRegistry(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public bool TryGetValidator(Type type, [NotNullWhen(true)] out ValidatorDescriptor? descriptor) - { - if (_cache.TryGetValue(type, out var validator)) - { - descriptor = validator; - return true; - } - - var validatorType = typeof(IValidator<>).MakeGenericType(type); - if (_serviceProvider.GetService(typeof(IValidator<>).MakeGenericType(type)) is { }) - { - descriptor = _cache[type] = new(validatorType); - return true; - } - - descriptor = null; - return false; - } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/DontValidateAttribute.cs b/src/HotChocolate/FairyBread/DontValidateAttribute.cs deleted file mode 100644 index 653d40e37..000000000 --- a/src/HotChocolate/FairyBread/DontValidateAttribute.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using HotChocolate.Types; -using HotChocolate.Types.Descriptors; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -/// -/// Instructs FairyBread to not run any validation on the annotated argument. -/// -[AttributeUsage(AttributeTargets.Parameter)] -public class DontValidateAttribute : ArgumentDescriptorAttribute -{ - protected override void OnConfigure( - IDescriptorContext context, - IArgumentDescriptor descriptor, - ParameterInfo parameter - ) - { - descriptor.DontValidate(); - } -} - -public static class DontValidateArgumentDescriptorExtensions -{ - /// - /// Instructs FairyBread to not run any validation for this argument. - /// - public static IArgumentDescriptor DontValidate( - this IArgumentDescriptor descriptor - ) - { - descriptor.Extend().OnBeforeNaming((completionContext, argDef) => { argDef.ContextData[WellKnownContextData.DontValidate] = true; }); - - return descriptor; - } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/DontValidateImplicitlyAttribute.cs b/src/HotChocolate/FairyBread/DontValidateImplicitlyAttribute.cs deleted file mode 100644 index 05c76e389..000000000 --- a/src/HotChocolate/FairyBread/DontValidateImplicitlyAttribute.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Reflection; -using HotChocolate.Types; -using HotChocolate.Types.Descriptors; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -/// -/// Instructs FairyBread to not run any validators that -/// are implicitly associated with the annotated argument's type. -/// Explicit validators will still be run. -/// -[AttributeUsage(AttributeTargets.Parameter)] -public class DontValidateImplicitlyAttribute : ArgumentDescriptorAttribute -{ - protected override void OnConfigure( - IDescriptorContext context, - IArgumentDescriptor descriptor, - ParameterInfo parameter - ) - { - descriptor.DontValidateImplicitly(); - } -} - -public static class DontValidateImplicitlyArgumentDescriptorExtensions -{ - /// - /// Instructs FairyBread to not run any validators that - /// are implicitly associated with this argument's runtime type. - /// Explicit validators will still be run. - /// - public static IArgumentDescriptor DontValidateImplicitly( - this IArgumentDescriptor descriptor - ) - { - descriptor.Extend().OnBeforeNaming((completionContext, argDef) => { argDef.ContextData[WellKnownContextData.DontValidateImplicitly] = true; }); - - return descriptor; - } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/IExplicitUsageOnlyValidator.cs b/src/HotChocolate/FairyBread/IExplicitUsageOnlyValidator.cs deleted file mode 100644 index c4d7be924..000000000 --- a/src/HotChocolate/FairyBread/IExplicitUsageOnlyValidator.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -/// -/// Marker interface for indicating that a validator -/// should only be run by FairyBread on a field resolver argument -/// if it is explicitly assigned to that argument. -/// -public interface IExplicitUsageOnlyValidator { } \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/IFairyBreadOptions.cs b/src/HotChocolate/FairyBread/IFairyBreadOptions.cs deleted file mode 100644 index ee9c56e1c..000000000 --- a/src/HotChocolate/FairyBread/IFairyBreadOptions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using HotChocolate.Types.Descriptors.Definitions; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -public interface IFairyBreadOptions -{ - /// - /// If true, FairyBread will throw on startup if no validators are found. - /// This is true by default to avoid an accidental release with no validators - /// that continues to function silently but would obviously be very dangerous. - /// - bool ThrowIfNoValidatorsFound { get; set; } - - /// - /// A function that evaluates an argument during schema building. - /// If it returns true, validation will occur on this argument at runtime, else it won't. - /// The default implementation always returns true. - /// - Func ShouldValidateArgument { get; set; } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/IRequestExecutorBuilderExtensions.cs b/src/HotChocolate/FairyBread/IRequestExecutorBuilderExtensions.cs deleted file mode 100644 index 6df15ce53..000000000 --- a/src/HotChocolate/FairyBread/IRequestExecutorBuilderExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using HotChocolate.Execution.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -public static class IRequestExecutorBuilderExtensions -{ - public static IRequestExecutorBuilder AddFairyBread( - this IRequestExecutorBuilder requestExecutorBuilder, - Action? configureOptions = null - ) - { - // Services - var services = requestExecutorBuilder.Services; - - var options = new DefaultFairyBreadOptions(); - configureOptions?.Invoke(options); - - services.TryAddSingleton(options); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - - // Executor builder - requestExecutorBuilder.TryAddTypeInterceptor(); - - return requestExecutorBuilder; - } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/IRequiresOwnScopeValidator.cs b/src/HotChocolate/FairyBread/IRequiresOwnScopeValidator.cs deleted file mode 100644 index 061e10a07..000000000 --- a/src/HotChocolate/FairyBread/IRequiresOwnScopeValidator.cs +++ /dev/null @@ -1,9 +0,0 @@ -using FluentValidation; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -/// -/// Marker interface for validators that should be resolved -/// from the service provider in it's own scope. -/// -public interface IRequiresOwnScopeValidator : IValidator { } \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/IValidationErrorsHandler.cs b/src/HotChocolate/FairyBread/IValidationErrorsHandler.cs deleted file mode 100644 index ada9c5514..000000000 --- a/src/HotChocolate/FairyBread/IValidationErrorsHandler.cs +++ /dev/null @@ -1,11 +0,0 @@ -using HotChocolate.Resolvers; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -public interface IValidationErrorsHandler -{ - void Handle( - IMiddlewareContext context, - IEnumerable invalidResults - ); -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/IValidatorProvider.cs b/src/HotChocolate/FairyBread/IValidatorProvider.cs deleted file mode 100644 index 4cfa10ebd..000000000 --- a/src/HotChocolate/FairyBread/IValidatorProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -using HotChocolate.Resolvers; -using HotChocolate.Types; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -/// -/// Resolves validators at query execution time. -/// -public interface IValidatorProvider -{ - IEnumerable GetValidators(IMiddlewareContext context, IInputField argument); -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/IValidatorRegistry.cs b/src/HotChocolate/FairyBread/IValidatorRegistry.cs deleted file mode 100644 index ab89b1168..000000000 --- a/src/HotChocolate/FairyBread/IValidatorRegistry.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -/// -/// Maintains a registry of implicit validators -/// keyed by the target runtime type for validation. -/// -public interface IValidatorRegistry -{ - /// - /// Try and get the validator descriptor for the specified type. - /// - /// - /// - /// - bool TryGetValidator(Type type, [NotNullWhen(true)] out ValidatorDescriptor? descriptor); -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/ResolvedValidator.cs b/src/HotChocolate/FairyBread/ResolvedValidator.cs deleted file mode 100644 index 79f72ab3d..000000000 --- a/src/HotChocolate/FairyBread/ResolvedValidator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FluentValidation; -using Microsoft.Extensions.DependencyInjection; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -public struct ResolvedValidator -{ - public IValidator Validator { get; } - - public IServiceScope? Scope { get; } - - public ResolvedValidator(IValidator validator) - { - Validator = validator; - Scope = null; - } - - public ResolvedValidator(IValidator validator, IServiceScope scope) - { - Validator = validator; - Scope = scope; - } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/ValidateAttribute.cs b/src/HotChocolate/FairyBread/ValidateAttribute.cs deleted file mode 100644 index 1f059f1b2..000000000 --- a/src/HotChocolate/FairyBread/ValidateAttribute.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Reflection; -using FluentValidation; -using HotChocolate.Types; -using HotChocolate.Types.Descriptors; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -/// -/// Instructs FairyBread to add the given validator/s for the annotated argument. -/// -[AttributeUsage(AttributeTargets.Parameter)] -public class ValidateAttribute : ArgumentDescriptorAttribute -{ - public Type[] ValidatorTypes; - - public ValidateAttribute(params Type[] validatorTypes) - { - ValidatorTypes = validatorTypes; - } - - protected override void OnConfigure( - IDescriptorContext context, - IArgumentDescriptor descriptor, - ParameterInfo parameter - ) - { - if (parameter.GetCustomAttribute() is not ValidateAttribute attr) - { - return; - } - - descriptor.ValidateWith(attr.ValidatorTypes); - } -} - -public static class ValidateArgumentDescriptorExtensions -{ - /// - /// Instructs FairyBread to add the given validator to the argument. - /// - public static IArgumentDescriptor ValidateWith( - this IArgumentDescriptor descriptor - ) - where TValidator : IValidator - { - return descriptor.ValidateWith(typeof(TValidator)); - } - - /// - /// Instructs FairyBread to add the given validator/s to the argument. - /// - public static IArgumentDescriptor ValidateWith( - this IArgumentDescriptor descriptor, - params Type[] validatorTypes - ) - { - descriptor - .Extend() - .OnBeforeNaming( - (completionContext, argDef) => - { - if (!argDef.ContextData.TryGetValue(WellKnownContextData.ExplicitValidatorTypes, out var explicitValidatorTypesRaw) - || explicitValidatorTypesRaw is not List explicitValidatorTypes) - { - argDef.ContextData[WellKnownContextData.ExplicitValidatorTypes] - = new List(validatorTypes.Distinct()); - return; - } - - foreach (var validatorType in validatorTypes) - { - if (!explicitValidatorTypes.Contains(validatorType)) - { - explicitValidatorTypes.Add(validatorType); - } - } - } - ); - - return descriptor; - } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/ValidationMiddleware.cs b/src/HotChocolate/FairyBread/ValidationMiddleware.cs deleted file mode 100644 index 29251a047..000000000 --- a/src/HotChocolate/FairyBread/ValidationMiddleware.cs +++ /dev/null @@ -1,65 +0,0 @@ -using FluentValidation; -using HotChocolate.Resolvers; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -[UsedImplicitly] -internal class ValidationMiddleware -( - FieldDelegate next, - IValidatorProvider validatorProvider, - IValidationErrorsHandler validationErrorsHandler -) -{ - public async Task InvokeAsync(IMiddlewareContext context) - { - var arguments = context.Selection.Field.Arguments; - - var invalidResults = new List(); - - foreach (var argument in arguments) - { - var resolvedValidators = validatorProvider - .GetValidators(context, argument) - .ToArray(); - if (resolvedValidators.Length > 0) - try - { - var value = context.ArgumentValue(argument.Name); - if (value == null) continue; - - foreach (var resolvedValidator in resolvedValidators) - { - var validationContext = new ValidationContext(value); - var validationResult = await resolvedValidator.Validator.ValidateAsync( - validationContext, - context.RequestAborted - ); - if (validationResult is { IsValid: false, }) - invalidResults.Add( - new( - argument.Name, - resolvedValidator.Validator, - validationResult - ) - ); - } - } - finally - { - foreach (var resolvedValidator in resolvedValidators) - { - resolvedValidator.Scope?.Dispose(); - } - } - } - - if (invalidResults.Any()) - { - validationErrorsHandler.Handle(context, invalidResults); - return; - } - - await next(context); - } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/ValidationMiddlewareInjector.cs b/src/HotChocolate/FairyBread/ValidationMiddlewareInjector.cs deleted file mode 100644 index 7d3c57d77..000000000 --- a/src/HotChocolate/FairyBread/ValidationMiddlewareInjector.cs +++ /dev/null @@ -1,235 +0,0 @@ -using HotChocolate; -using HotChocolate.Configuration; -using HotChocolate.Internal; -using HotChocolate.Resolvers; -using HotChocolate.Types; -using HotChocolate.Types.Descriptors; -using HotChocolate.Types.Descriptors.Definitions; -using Microsoft.Extensions.DependencyInjection; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -internal class ValidationMiddlewareInjector : TypeInterceptor -{ - private static List DetermineValidatorsForArg( - IValidatorRegistry validatorRegistry, - ArgumentDefinition argDef - ) - { - // If validation is explicitly disabled, return none so validation middleware won't be added - if (argDef.ContextData.ContainsKey(WellKnownContextData.DontValidate)) - { - return new(0); - } - - var validators = new List(); - - // Include implicit validator/s first (if allowed) - if (!argDef.ContextData.ContainsKey(WellKnownContextData.DontValidateImplicitly) - && TryGetArgRuntimeType(argDef) is { } argRuntimeType - && validatorRegistry.TryGetValidator(argRuntimeType, out var implicitValidators)) - { - validators.Add(implicitValidators); - } - - // Include explicit validator/s (that aren't already added implicitly) - if (argDef.ContextData.TryGetValue(WellKnownContextData.ExplicitValidatorTypes, out var explicitValidatorTypesRaw) - && explicitValidatorTypesRaw is IEnumerable explicitValidatorTypes) - { - // TODO: Potentially check and throw if there's a validator being explicitly applied for the wrong runtime type - - foreach (var validatorType in explicitValidatorTypes) - { - if (validators.Any(v => v.ValidatorType == validatorType)) - { - continue; - } - - validators.Add(new(validatorType)); - } - } - - return validators; - } - - private static Type? TryGetArgRuntimeType( - ArgumentDefinition argDef - ) - { - if (argDef.Parameter?.ParameterType is { } argRuntimeType) - { - return argRuntimeType; - } - - if (argDef.Type is ExtendedTypeReference extTypeRef) - { - return TryGetRuntimeType(extTypeRef.Type); - } - - return null; - } - - private static Type? TryGetRuntimeType(IExtendedType extType) - { - // It's already a runtime type, .Type(typeof(int)) - if (extType.Kind == ExtendedTypeKind.Runtime) - { - return extType.Source; - } - - // Array (though not sure what produces this scenario as seems to always be list) - if (extType.IsArray) - { - if (extType.ElementType is null) - { - return null; - } - - var elementRuntimeType = TryGetRuntimeType(extType.ElementType); - if (elementRuntimeType is null) - { - return null; - } - - return Array.CreateInstance(elementRuntimeType, 0).GetType(); - } - - // List - if (extType.IsList) - { - if (extType.ElementType is null) - { - return null; - } - - var elementRuntimeType = TryGetRuntimeType(extType.ElementType); - if (elementRuntimeType is null) - { - return null; - } - - return typeof(List<>).MakeGenericType(elementRuntimeType); - } - - // Input object - if (typeof(InputObjectType).IsAssignableFrom(extType)) - { - var currBaseType = extType.Type.BaseType; - while (currBaseType is { } && ( !currBaseType.IsGenericType || currBaseType.GetGenericTypeDefinition() != typeof(InputObjectType<>) )) - { - currBaseType = currBaseType.BaseType; - } - - if (currBaseType is null) - { - return null; - } - - return currBaseType!.GenericTypeArguments[0]; - } - - // Singular scalar - if (typeof(ScalarType).IsAssignableFrom(extType)) - { - var currBaseType = extType.Type.BaseType; - while (currBaseType is { } && ( !currBaseType.IsGenericType || currBaseType.GetGenericTypeDefinition() != typeof(ScalarType<>) )) - { - currBaseType = currBaseType.BaseType; - } - - if (currBaseType is null) - { - return null; - } - - var argRuntimeType = currBaseType.GenericTypeArguments[0]; - if (argRuntimeType.IsValueType && extType.IsNullable) - { - return typeof(Nullable<>).MakeGenericType(argRuntimeType); - } - - return argRuntimeType; - } - - return null; - } - - private FieldMiddlewareDefinition? _validationFieldMiddlewareDef; - - public override void OnBeforeCompleteType( - ITypeCompletionContext completionContext, - DefinitionBase definition - ) - { - if (definition is not ObjectTypeDefinition objTypeDef) - { - return; - } - - var options = completionContext.Services - .GetRequiredService(); - var validatorRegistry = completionContext.Services - .GetRequiredService(); - - foreach (var fieldDef in objTypeDef.Fields) - { - // Don't add validation middleware unless: - // 1. we have args - var needsValidationMiddleware = false; - - foreach (var argDef in fieldDef.Arguments) - { - var argCoord = new SchemaCoordinate(objTypeDef.Name, fieldDef.Name, argDef.Name); - - // 2. the argument should be validated according to options func - if (!options.ShouldValidateArgument(objTypeDef, fieldDef, argDef)) - { - continue; - } - - // 3. there's validators for it - List validatorDescs; - try - { - validatorDescs = DetermineValidatorsForArg(validatorRegistry, argDef); - if (validatorDescs.Count < 1) - { - continue; - } - } - catch (Exception ex) - { - throw new( - $"Problem getting runtime type for argument '{argDef.Name}' " + $"in field '{fieldDef.Name}' on object type '{objTypeDef.Name}'.", - ex - ); - } - - // Cleanup context now we're done with these - foreach (var key in argDef.ContextData.Keys) - { - if (key.StartsWith(WellKnownContextData.Prefix)) - { - argDef.ContextData.Remove(key); - } - } - - validatorDescs.TrimExcess(); - needsValidationMiddleware = true; - argDef.ContextData[WellKnownContextData.ValidatorDescriptors] = validatorDescs.AsReadOnly(); - } - - if (needsValidationMiddleware) - { - if (_validationFieldMiddlewareDef is null) - { - _validationFieldMiddlewareDef = new( - FieldClassMiddlewareFactory.Create() - ); - } - - fieldDef.MiddlewareDefinitions.Insert(0, _validationFieldMiddlewareDef); - } - } - } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/ValidatorDescriptor.cs b/src/HotChocolate/FairyBread/ValidatorDescriptor.cs deleted file mode 100644 index 2de9b64d8..000000000 --- a/src/HotChocolate/FairyBread/ValidatorDescriptor.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -/// -/// Description of a validator configuration to be stored in the validator descriptor cache. -/// -public class ValidatorDescriptor -{ - /// - /// Instantiates a new . - /// - public ValidatorDescriptor(Type validatorType) - { - ValidatorType = validatorType; - RequiresOwnScope = WellKnownTypes.IRequiresOwnScopeValidator.IsAssignableFrom(validatorType); - ExplicitUsageOnly = WellKnownTypes.IExplicitUsageOnlyValidator.IsAssignableFrom(validatorType); - } - - /// - /// Type of the validator. - /// - public Type ValidatorType { get; } - - /// - /// Does the validator inherit ? - /// If so, this means it should be resolved from the service provider in it's own scope. - /// - public bool RequiresOwnScope { get; } - - /// - /// Does the validator inherit ? - /// If so, this means it should be only executed when explicitly set on an argument - /// (rather than implicitly given the type it can validate). - /// - public bool ExplicitUsageOnly { get; } -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/WellKnownContextData.cs b/src/HotChocolate/FairyBread/WellKnownContextData.cs deleted file mode 100644 index 1acb36ae6..000000000 --- a/src/HotChocolate/FairyBread/WellKnownContextData.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -internal static class WellKnownContextData -{ - public const string Prefix = "FairyBread"; - - public const string DontValidate = - Prefix + ".DontValidate"; - - public const string DontValidateImplicitly = - Prefix + ".DontValidateImplicitly"; - - public const string ExplicitValidatorTypes = - Prefix + ".ExplicitValidatorTypes"; - - public const string ValidatorDescriptors = - Prefix + ".Validators"; -} \ No newline at end of file diff --git a/src/HotChocolate/FairyBread/WellKnownTypes.cs b/src/HotChocolate/FairyBread/WellKnownTypes.cs deleted file mode 100644 index da6319b9f..000000000 --- a/src/HotChocolate/FairyBread/WellKnownTypes.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -internal class WellKnownTypes -{ - public static readonly Type IRequiresOwnScopeValidator = typeof(IRequiresOwnScopeValidator); - public static readonly Type IExplicitUsageOnlyValidator = typeof(IExplicitUsageOnlyValidator); -} \ No newline at end of file diff --git a/src/HotChocolate/GraphqlErrorFilter.cs b/src/HotChocolate/GraphqlErrorFilter.cs index e9e2af4ac..1b0000ed2 100644 --- a/src/HotChocolate/GraphqlErrorFilter.cs +++ b/src/HotChocolate/GraphqlErrorFilter.cs @@ -8,54 +8,74 @@ namespace Rocket.Surgery.LaunchPad.HotChocolate; internal class GraphqlErrorFilter : IErrorFilter { - protected virtual ReadOnlyDictionary FormatFailure(ValidationFailure failure) + /* + protected virtual IErrorBuilder FormatFailure(ValidationFailure failure) + { - return new FluentValidationProblemDetail(failure); + var builder = ErrorBuilder + .New() + .SetMessage(failure.ErrorMessage) + .SetCode(failure.ErrorCode) + .SetExtension("errorCode", failure.ErrorCode) + .SetExtension("errorMessage", failure.ErrorMessage) + .SetExtension("attemptedValue", failure.AttemptedValue) + .SetExtension("severity", failure.Severity); + + if (!string.IsNullOrWhiteSpace(failure.PropertyName)) + { + builder = builder + .SetExtension("field", failure.PropertyName) + .SetExtension("propertyName", failure.PropertyName); + } + + return builder; } + */ public IError OnError(IError error) { + /* if (error.Exception is ValidationException vx) { var childErrors = vx - .Errors.Select(x => new FluentValidationProblemDetail(x)) - .Select( - failure => - { - var err = new Error(failure.ErrorMessage) - .WithCode(failure.ErrorCode) - .SetExtension("attemptedValue", failure.AttemptedValue) - .SetExtension("severity", failure.Severity); - - if (!string.IsNullOrWhiteSpace(failure.PropertyName)) - err = err - .SetExtension("field", failure.PropertyName) - .SetExtension("propertyName", failure.PropertyName); - - return err; - } - ); - return new AggregateError(childErrors); + .Errors + .Select(failure => FormatFailure(failure).Build()); + error = new AggregateError(childErrors); + error.WithCode("VALIDATION"); } + */ - if (error.Exception is IProblemDetailsData ex) + var builder = ErrorBuilder.FromError(error); + + if (error.Exception is { }) { - var builder = ErrorBuilder.FromError(error); builder .SetException(error.Exception) - .SetMessage(error.Exception.Message) - .WithProblemDetails(ex); - - if (error.Exception is NotFoundException) builder.SetCode("NOTFOUND"); + .SetMessage(error.Exception?.Message ?? error.Message) + .SetCode(error.Code ?? "UNKNOWN"); + } - if (error.Exception is NotAuthorizedException) builder.SetCode("NOTAUTHORIZED"); - if (error.Exception is RequestFailedException) builder.SetCode("FAILED"); + if (error.Exception is IProblemDetailsData ex) + { + builder.WithProblemDetails(ex); + } - return builder.Build(); + switch (error.Exception) + { + case NotFoundException: + builder.SetCode("NOTFOUND"); + break; + case NotAuthorizedException: + builder.SetCode("NOTAUTHORIZED"); + break; + case RequestFailedException: + builder.SetCode("FAILED"); + break; } - return error; + return builder.Build(); + } -} \ No newline at end of file +} diff --git a/src/HotChocolate/Rocket.Surgery.LaunchPad.HotChocolate.csproj b/src/HotChocolate/Rocket.Surgery.LaunchPad.HotChocolate.csproj index c1cd71241..4810df1fc 100644 --- a/src/HotChocolate/Rocket.Surgery.LaunchPad.HotChocolate.csproj +++ b/src/HotChocolate/Rocket.Surgery.LaunchPad.HotChocolate.csproj @@ -1,26 +1,23 @@  - - net8.0 - - $(PackageTags) - - - - - - - - - - - - - - - - - - - - + + net8.0;net9.0 + + $(PackageTags) + + + + + + + + + + + + + + + + + diff --git a/src/HotChocolate/Validation/ValidationMiddleware.cs b/src/HotChocolate/Validation/ValidationMiddleware.cs deleted file mode 100644 index e8837c07f..000000000 --- a/src/HotChocolate/Validation/ValidationMiddleware.cs +++ /dev/null @@ -1,66 +0,0 @@ -using FluentValidation; -using HotChocolate.Resolvers; -using Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -namespace Rocket.Surgery.LaunchPad.HotChocolate.Validation; - -[UsedImplicitly] -internal class ValidationMiddleware -( - FieldDelegate next, - IValidatorProvider validatorProvider, - IValidationErrorsHandler validationErrorsHandler -) -{ - public async Task InvokeAsync(IMiddlewareContext context) - { - var arguments = context.Selection.Field.Arguments; - - var invalidResults = new List(); - - foreach (var argument in arguments) - { - var resolvedValidators = validatorProvider - .GetValidators(context, argument) - .ToArray(); - if (resolvedValidators.Length > 0) - try - { - var value = context.ArgumentValue(argument.Name); - if (value == null) continue; - - foreach (var resolvedValidator in resolvedValidators) - { - var validationContext = new ValidationContext(value); - var validationResult = await resolvedValidator.Validator.ValidateAsync( - validationContext, - context.RequestAborted - ); - if (validationResult is { IsValid: false, }) - invalidResults.Add( - new( - argument.Name, - resolvedValidator.Validator, - validationResult - ) - ); - } - } - finally - { - foreach (var resolvedValidator in resolvedValidators) - { - resolvedValidator.Scope?.Dispose(); - } - } - } - - if (invalidResults.Any()) - { - validationErrorsHandler.Handle(context, invalidResults); - return; - } - - await next(context); - } -} \ No newline at end of file diff --git a/src/Mapping.NewtonsoftJson/Rocket.Surgery.LaunchPad.Mapping.NewtonsoftJson.csproj b/src/Mapping.NewtonsoftJson/Rocket.Surgery.LaunchPad.Mapping.NewtonsoftJson.csproj index b1b140824..76283394c 100644 --- a/src/Mapping.NewtonsoftJson/Rocket.Surgery.LaunchPad.Mapping.NewtonsoftJson.csproj +++ b/src/Mapping.NewtonsoftJson/Rocket.Surgery.LaunchPad.Mapping.NewtonsoftJson.csproj @@ -1,6 +1,6 @@  - net8.0 + net8.0;net9.0 $(PackageTags) Rocket.Surgery.LaunchPad.Mapping diff --git a/src/Mapping/Rocket.Surgery.LaunchPad.Mapping.csproj b/src/Mapping/Rocket.Surgery.LaunchPad.Mapping.csproj index d893f12e6..b9ca1c334 100644 --- a/src/Mapping/Rocket.Surgery.LaunchPad.Mapping.csproj +++ b/src/Mapping/Rocket.Surgery.LaunchPad.Mapping.csproj @@ -1,6 +1,6 @@  - net8.0 + net8.0;net9.0 $(PackageTags) false diff --git a/src/Metadata/Rocket.Surgery.LaunchPad.Metadata.csproj b/src/Metadata/Rocket.Surgery.LaunchPad.Metadata.csproj index 8f9444ce7..524637768 100644 --- a/src/Metadata/Rocket.Surgery.LaunchPad.Metadata.csproj +++ b/src/Metadata/Rocket.Surgery.LaunchPad.Metadata.csproj @@ -1,6 +1,6 @@  - net8.0 + net8.0;net9.0 $(PackageTags) diff --git a/src/Serilog/Conventions/LoggerConvention.cs b/src/Serilog/Conventions/LoggerConvention.cs new file mode 100644 index 000000000..b925e220d --- /dev/null +++ b/src/Serilog/Conventions/LoggerConvention.cs @@ -0,0 +1,83 @@ +using App.Metrics; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Rocket.Surgery.Conventions; +using Rocket.Surgery.Conventions.DependencyInjection; +using Rocket.Surgery.Conventions.Hosting; +using Serilog; +using Serilog.Core; +using Serilog.Extensions.Logging; +using ILogger = Serilog.ILogger; + +namespace Rocket.Surgery.LaunchPad.Serilog.Conventions; + +/// +/// SerilogHostingConvention. +/// Implements the +/// +/// +[PublicAPI] +[ExportConvention] +[ConventionCategory(ConventionCategory.Core)] +public class LoggerConvention : IServiceConvention +{ + /// + public void Register(IConventionContext context, IConfiguration configuration, IServiceCollection services) + { + ArgumentNullException.ThrowIfNull(context); + + // removes default console loggers and such + foreach (var item in services + .Where( + x => + { + var type = x.IsKeyedService ? x.KeyedImplementationType : x.ImplementationType; + return type?.FullName?.StartsWith("Microsoft.Extensions.Logging", StringComparison.Ordinal) == true + && type.FullName.EndsWith("Provider", StringComparison.Ordinal); + } + ) + .ToArray() + ) + { + services.Remove(item); + } + + #pragma warning disable CA2000 + var loggerProviderCollection = new LoggerProviderCollection(); + #pragma warning restore CA2000 + var loggerConfiguration = new LoggerConfiguration(); + + if (context.Get() is { } logger) loggerConfiguration.WriteTo.Logger(logger); + services.AddSingleton( + serviceProvider => + { + loggerConfiguration.ApplyConventions(context, configuration, serviceProvider); + return loggerConfiguration.CreateLogger(); + } + ); + services.AddSingleton(serviceProvider => serviceProvider.GetRequiredService()); + services.AddSingleton( + serviceProvider => new SerilogLoggerFactory( + serviceProvider.GetRequiredService(), + true, + loggerProviderCollection + ) + ); + + services.AddSingleton( + serviceProvider => + { + var loggerProviders = serviceProvider.GetServices(); + return loggerProviders.Aggregate( + loggerProviderCollection, + (factory, loggerProvider) => + { + factory.AddProvider(loggerProvider); + return factory; + } + ); + } + ); + } +} \ No newline at end of file diff --git a/src/Hosting/Conventions/SerilogConsoleLoggingConvention.cs b/src/Serilog/Conventions/SerilogConsoleLoggingConvention.cs similarity index 91% rename from src/Hosting/Conventions/SerilogConsoleLoggingConvention.cs rename to src/Serilog/Conventions/SerilogConsoleLoggingConvention.cs index 24e7c5b12..ebd9f12a3 100644 --- a/src/Hosting/Conventions/SerilogConsoleLoggingConvention.cs +++ b/src/Serilog/Conventions/SerilogConsoleLoggingConvention.cs @@ -1,12 +1,11 @@ using System.Globalization; using Microsoft.Extensions.Configuration; using Rocket.Surgery.Conventions; -using Rocket.Surgery.LaunchPad.Serilog; using Serilog; using Serilog.Events; using Serilog.Sinks.SystemConsole.Themes; -namespace Rocket.Surgery.LaunchPad.Hosting.Conventions; +namespace Rocket.Surgery.LaunchPad.Serilog.Conventions; /// /// SerilogConsoleLoggingConvention. @@ -15,7 +14,7 @@ namespace Rocket.Surgery.LaunchPad.Hosting.Conventions; /// [PublicAPI] [ExportConvention] -[AfterConvention] +[AfterConvention] [ConventionCategory(ConventionCategory.Core)] public sealed class SerilogConsoleLoggingConvention : ISerilogConvention { diff --git a/src/Hosting/Conventions/SerilogDebugLoggingConvention.cs b/src/Serilog/Conventions/SerilogDebugLoggingConvention.cs similarity index 91% rename from src/Hosting/Conventions/SerilogDebugLoggingConvention.cs rename to src/Serilog/Conventions/SerilogDebugLoggingConvention.cs index be4d47770..6c994c388 100644 --- a/src/Hosting/Conventions/SerilogDebugLoggingConvention.cs +++ b/src/Serilog/Conventions/SerilogDebugLoggingConvention.cs @@ -1,11 +1,10 @@ using System.Globalization; using Microsoft.Extensions.Configuration; using Rocket.Surgery.Conventions; -using Rocket.Surgery.LaunchPad.Serilog; using Serilog; using Serilog.Events; -namespace Rocket.Surgery.LaunchPad.Hosting.Conventions; +namespace Rocket.Surgery.LaunchPad.Serilog.Conventions; /// /// SerilogDebugLoggingConvention. @@ -14,7 +13,7 @@ namespace Rocket.Surgery.LaunchPad.Hosting.Conventions; /// [PublicAPI] [ExportConvention] -[AfterConvention] +[AfterConvention] [ConventionCategory(ConventionCategory.Core)] public sealed class SerilogDebugLoggingConvention : ISerilogConvention { diff --git a/src/Serilog/Conventions/SerilogEnrichEnvironmentLoggingConvention.cs b/src/Serilog/Conventions/SerilogEnrichEnvironmentLoggingConvention.cs index 52646b31a..b49ba0c0f 100644 --- a/src/Serilog/Conventions/SerilogEnrichEnvironmentLoggingConvention.cs +++ b/src/Serilog/Conventions/SerilogEnrichEnvironmentLoggingConvention.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Rocket.Surgery.Conventions; using Serilog; @@ -35,6 +36,10 @@ LoggerConfiguration loggerConfiguration .Enrich.WithMachineName() .Enrich.WithProcessId() .Enrich.WithProcessName() - .Enrich.WithThreadId(); + .Enrich.WithThreadId() + .Enrich.WithAssemblyInformationalVersion() + .Enrich.WithAssemblyName() + .Enrich.WithAssemblyVersion() + ; } } diff --git a/src/Serilog/Conventions/SerilogEnrichLoggingConvention.cs b/src/Serilog/Conventions/SerilogEnrichLoggingConvention.cs index fa7779077..44520319f 100644 --- a/src/Serilog/Conventions/SerilogEnrichLoggingConvention.cs +++ b/src/Serilog/Conventions/SerilogEnrichLoggingConvention.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Rocket.Surgery.Conventions; using Serilog; using Serilog.Exceptions; @@ -33,6 +34,7 @@ LoggerConfiguration loggerConfiguration loggerConfiguration .Enrich.FromLogContext() - .Enrich.WithExceptionDetails(); + .Enrich.WithExceptionDetails() + .Enrich.WithDemystifiedStackTraces(); } } diff --git a/src/Serilog/Conventions/SerilogEnrichSpansConvention.cs b/src/Serilog/Conventions/SerilogEnrichSpansConvention.cs index d550be922..fb5f82188 100644 --- a/src/Serilog/Conventions/SerilogEnrichSpansConvention.cs +++ b/src/Serilog/Conventions/SerilogEnrichSpansConvention.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Rocket.Surgery.Conventions; using Serilog; using Serilog.Enrichers.Span; diff --git a/src/Serilog/Conventions/SerilogReadFromConfigurationConvention.cs b/src/Serilog/Conventions/SerilogReadFromConfigurationConvention.cs index 5bd7fca62..e574b2c36 100644 --- a/src/Serilog/Conventions/SerilogReadFromConfigurationConvention.cs +++ b/src/Serilog/Conventions/SerilogReadFromConfigurationConvention.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Rocket.Surgery.Conventions; using Rocket.Surgery.Conventions.Configuration; diff --git a/src/Serilog/Conventions/SerilogTraceLoggingConvention.cs b/src/Serilog/Conventions/SerilogTraceLoggingConvention.cs new file mode 100644 index 000000000..bd953b164 --- /dev/null +++ b/src/Serilog/Conventions/SerilogTraceLoggingConvention.cs @@ -0,0 +1,42 @@ +using System.Globalization; +using Microsoft.Extensions.Configuration; +using Rocket.Surgery.Conventions; +using Serilog; +using Serilog.Events; + +namespace Rocket.Surgery.LaunchPad.Serilog.Conventions; + +/// +/// SerilogDebugLoggingConvention. +/// Implements the +/// +/// +[PublicAPI] +[ExportConvention] +[AfterConvention] +[ConventionCategory(ConventionCategory.Core)] +public sealed class SerilogTraceLoggingConvention(LaunchPadLoggingOptions? options = null) : ISerilogConvention +{ + private readonly LaunchPadLoggingOptions _options = options ?? new LaunchPadLoggingOptions(); + + /// + public void Register( + IConventionContext context, + IConfiguration configuration, + IServiceProvider services, + LoggerConfiguration loggerConfiguration + ) + { + ArgumentNullException.ThrowIfNull(configuration); + + if (!_options.EnableTraceLogging) return; + + loggerConfiguration.WriteTo.Async( + c => c.Trace( + LogEventLevel.Verbose, + _options.TraceMessageTemplate, + CultureInfo.InvariantCulture + ) + ); + } +} diff --git a/src/Serilog/ISerilogConvention.cs b/src/Serilog/ISerilogConvention.cs index cd7919d26..10d290644 100644 --- a/src/Serilog/ISerilogConvention.cs +++ b/src/Serilog/ISerilogConvention.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Rocket.Surgery.Conventions; using Serilog; @@ -19,4 +20,4 @@ public interface ISerilogConvention : IConvention /// /// void Register(IConventionContext context, IConfiguration configuration, IServiceProvider services, LoggerConfiguration loggerConfiguration); -} \ No newline at end of file +} diff --git a/src/Serilog/LaunchPadLoggingOptions.cs b/src/Serilog/LaunchPadLoggingOptions.cs index 572383a94..ce6ff73da 100644 --- a/src/Serilog/LaunchPadLoggingOptions.cs +++ b/src/Serilog/LaunchPadLoggingOptions.cs @@ -27,4 +27,15 @@ public class LaunchPadLoggingOptions /// Enable or disable debug logging, defaults to enabled /// public bool EnableDebugLogging { get; set; } = true; -} \ No newline at end of file + + /// + /// The default debug message template + /// + public string TraceMessageTemplate { get; set; } = + "[{Timestamp:HH:mm:ss} {Level:w4}] {Message}{NewLine}{Exception}"; + + /// + /// Enable or disable debug logging, defaults to enabled + /// + public bool EnableTraceLogging { get; set; } = true; +} diff --git a/src/Serilog/Rocket.Surgery.LaunchPad.Serilog.csproj b/src/Serilog/Rocket.Surgery.LaunchPad.Serilog.csproj index fd08d87af..01a3e7a49 100644 --- a/src/Serilog/Rocket.Surgery.LaunchPad.Serilog.csproj +++ b/src/Serilog/Rocket.Surgery.LaunchPad.Serilog.csproj @@ -1,19 +1,24 @@  - net8.0 + net8.0;net9.0 $(PackageTags) - - - - - - - + + + + + + + + + - + + + + diff --git a/src/Serilog/RocketSurgerySerilogExtensions.cs b/src/Serilog/RocketSurgerySerilogExtensions.cs index b8c37c0e9..2759a5e38 100644 --- a/src/Serilog/RocketSurgerySerilogExtensions.cs +++ b/src/Serilog/RocketSurgerySerilogExtensions.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Rocket.Surgery.Conventions; using Rocket.Surgery.LaunchPad.Serilog; using Serilog; @@ -35,4 +36,4 @@ IServiceProvider services return configurationBuilder; } -} \ No newline at end of file +} diff --git a/src/Serilog/SerilogAbstractionsHostBuilderExtensions.cs b/src/Serilog/SerilogAbstractionsHostBuilderExtensions.cs index 1009342a1..c224f6583 100644 --- a/src/Serilog/SerilogAbstractionsHostBuilderExtensions.cs +++ b/src/Serilog/SerilogAbstractionsHostBuilderExtensions.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Rocket.Surgery.LaunchPad.Serilog; using Serilog; diff --git a/src/Serilog/SerilogConventionDelegate.cs b/src/Serilog/SerilogConventionDelegate.cs index d2ec47834..03164c081 100644 --- a/src/Serilog/SerilogConventionDelegate.cs +++ b/src/Serilog/SerilogConventionDelegate.cs @@ -17,4 +17,4 @@ public delegate void SerilogConvention( IConfiguration configuration, IServiceProvider services, LoggerConfiguration loggerConfiguration -); \ No newline at end of file +); diff --git a/src/Spatial.NewtonsoftJson/Rocket.Surgery.LaunchPad.Spatial.NewtonsoftJson.csproj b/src/Spatial.NewtonsoftJson/Rocket.Surgery.LaunchPad.Spatial.NewtonsoftJson.csproj index d1ac69456..5cb4ceef3 100644 --- a/src/Spatial.NewtonsoftJson/Rocket.Surgery.LaunchPad.Spatial.NewtonsoftJson.csproj +++ b/src/Spatial.NewtonsoftJson/Rocket.Surgery.LaunchPad.Spatial.NewtonsoftJson.csproj @@ -1,6 +1,6 @@  - net8.0 + net8.0;net9.0 $(PackageTags) Rocket.Surgery.LaunchPad.Spatial @@ -13,6 +13,6 @@ - + diff --git a/src/Spatial/Rocket.Surgery.LaunchPad.Spatial.csproj b/src/Spatial/Rocket.Surgery.LaunchPad.Spatial.csproj index 70fd7c23d..e81960b00 100644 --- a/src/Spatial/Rocket.Surgery.LaunchPad.Spatial.csproj +++ b/src/Spatial/Rocket.Surgery.LaunchPad.Spatial.csproj @@ -1,6 +1,6 @@  - net8.0 + net8.0;net9.0 $(PackageTags) @@ -11,6 +11,6 @@ - + diff --git a/src/StrawberryShake.Spatial/Rocket.Surgery.LaunchPad.StrawberryShake.Spatial.csproj b/src/StrawberryShake.Spatial/Rocket.Surgery.LaunchPad.StrawberryShake.Spatial.csproj index 07cb0c62d..c10483f91 100644 --- a/src/StrawberryShake.Spatial/Rocket.Surgery.LaunchPad.StrawberryShake.Spatial.csproj +++ b/src/StrawberryShake.Spatial/Rocket.Surgery.LaunchPad.StrawberryShake.Spatial.csproj @@ -1,13 +1,13 @@  - - net8.0 - - $(PackageTags) - - - - - - - + + net8.0;net9.0 + + $(PackageTags) + + + + + + + diff --git a/src/StrawberryShake/Rocket.Surgery.LaunchPad.StrawberryShake.csproj b/src/StrawberryShake/Rocket.Surgery.LaunchPad.StrawberryShake.csproj index f6d5febb5..bb3b20764 100644 --- a/src/StrawberryShake/Rocket.Surgery.LaunchPad.StrawberryShake.csproj +++ b/src/StrawberryShake/Rocket.Surgery.LaunchPad.StrawberryShake.csproj @@ -1,6 +1,6 @@  - net8.0 + net8.0;net9.0 $(PackageTags) false @@ -10,6 +10,6 @@ - + diff --git a/src/Telemetry/Conventions/SerilogTelemetryConvention.cs b/src/Telemetry/Conventions/SerilogTelemetryConvention.cs new file mode 100644 index 000000000..303001934 --- /dev/null +++ b/src/Telemetry/Conventions/SerilogTelemetryConvention.cs @@ -0,0 +1,56 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; +using Rocket.Surgery.Conventions; +using Rocket.Surgery.Conventions.DependencyInjection; +using Rocket.Surgery.LaunchPad.Serilog; +using Serilog; +using Serilog.Sinks.OpenTelemetry; + +namespace Rocket.Surgery.LaunchPad.Telemetry.Conventions; + +/// +/// Defines serilog telemetry configuration +/// +[PublicAPI] +[ConventionCategory(ConventionCategory.Core)] +public partial class SerilogTelemetryConvention : ISerilogConvention, IServiceConvention +{ + /// + public void Register(IConventionContext context, IConfiguration configuration, IServiceProvider services, LoggerConfiguration loggerConfiguration) + { + loggerConfiguration.WriteTo.OpenTelemetry( + options => + { + services.GetRequiredService>(); + var di = services.GetRequiredService>(); + options.BatchingOptions.BatchSizeLimit = di.CurrentValue.BatchingOptions.BatchSizeLimit; + options.BatchingOptions.BufferingTimeLimit = di.CurrentValue.BatchingOptions.BufferingTimeLimit; + options.BatchingOptions.EagerlyEmitFirstEvent = di.CurrentValue.BatchingOptions.EagerlyEmitFirstEvent; + options.BatchingOptions.QueueLimit = di.CurrentValue.BatchingOptions.QueueLimit; + options.BatchingOptions.RetryTimeLimit = di.CurrentValue.BatchingOptions.RetryTimeLimit; + options.Endpoint = di.CurrentValue.Endpoint; + options.FormatProvider = di.CurrentValue.FormatProvider; + options.Headers = di.CurrentValue.Headers; + options.HttpMessageHandler = di.CurrentValue.HttpMessageHandler; + options.IncludedData = di.CurrentValue.IncludedData; + options.LevelSwitch = di.CurrentValue.LevelSwitch; + options.LogsEndpoint = di.CurrentValue.LogsEndpoint; + options.OnBeginSuppressInstrumentation = di.CurrentValue.OnBeginSuppressInstrumentation; + options.Protocol = di.CurrentValue.Protocol; + options.ResourceAttributes = di.CurrentValue.ResourceAttributes; + options.RestrictedToMinimumLevel = di.CurrentValue.RestrictedToMinimumLevel; + options.TracesEndpoint = di.CurrentValue.TracesEndpoint; + } + ); + } + + /// + public void Register(IConventionContext context, IConfiguration configuration, IServiceCollection services) + { + services.AddOptions(); + } +} diff --git a/src/Telemetry/Conventions/TelemetryConvention.cs b/src/Telemetry/Conventions/TelemetryConvention.cs new file mode 100644 index 000000000..5ccf8dbc8 --- /dev/null +++ b/src/Telemetry/Conventions/TelemetryConvention.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Rocket.Surgery.Conventions; +using Rocket.Surgery.Conventions.Logging; + +namespace Rocket.Surgery.LaunchPad.Telemetry.Conventions; + +/// +/// Defines default telemetry convention +/// +[PublicAPI] +[ExportConvention] +[ConventionCategory(ConventionCategory.Core)] +public class TelemetryConvention : ILoggingConvention +{ + /// + public void Register(IConventionContext context, IConfiguration configuration, ILoggingBuilder builder) + { + builder.AddOpenTelemetry(); + } +} \ No newline at end of file diff --git a/src/Telemetry/IOpenTelemetryAsyncConvention.cs b/src/Telemetry/IOpenTelemetryAsyncConvention.cs index 19a59c7a4..102d6b97b 100644 --- a/src/Telemetry/IOpenTelemetryAsyncConvention.cs +++ b/src/Telemetry/IOpenTelemetryAsyncConvention.cs @@ -9,6 +9,7 @@ namespace Rocket.Surgery.LaunchPad.Telemetry; /// Implements the /// /// +[PublicAPI] public interface IOpenTelemetryAsyncConvention : IConvention { /// @@ -19,4 +20,4 @@ public interface IOpenTelemetryAsyncConvention : IConvention /// /// ValueTask Register(IConventionContext context, IConfiguration configuration, IOpenTelemetryBuilder builder, CancellationToken cancellationToken); -} \ No newline at end of file +} diff --git a/src/Telemetry/IOpenTelemetryConvention.cs b/src/Telemetry/IOpenTelemetryConvention.cs index 0a950b1af..426ae019e 100644 --- a/src/Telemetry/IOpenTelemetryConvention.cs +++ b/src/Telemetry/IOpenTelemetryConvention.cs @@ -9,6 +9,7 @@ namespace Rocket.Surgery.LaunchPad.Telemetry; /// Implements the /// /// +[PublicAPI] public interface IOpenTelemetryConvention : IConvention { /// @@ -18,4 +19,4 @@ public interface IOpenTelemetryConvention : IConvention /// /// void Register(IConventionContext context, IConfiguration configuration, IOpenTelemetryBuilder builder); -} \ No newline at end of file +} diff --git a/src/Telemetry/IOpenTelemetryMetricsConvention.cs b/src/Telemetry/IOpenTelemetryMetricsConvention.cs deleted file mode 100644 index f5d0994db..000000000 --- a/src/Telemetry/IOpenTelemetryMetricsConvention.cs +++ /dev/null @@ -1 +0,0 @@ -namespace Rocket.Surgery.LaunchPad.Telemetry; \ No newline at end of file diff --git a/src/Telemetry/OpenTelemetryAsyncConvention.cs b/src/Telemetry/OpenTelemetryAsyncConvention.cs index bb7d5336d..a724f31c4 100644 --- a/src/Telemetry/OpenTelemetryAsyncConvention.cs +++ b/src/Telemetry/OpenTelemetryAsyncConvention.cs @@ -11,9 +11,10 @@ namespace Rocket.Surgery.LaunchPad.Telemetry; /// /// /// +[PublicAPI] public delegate ValueTask OpenTelemetryAsyncConvention( IConventionContext context, IConfiguration configuration, IOpenTelemetryBuilder builder, CancellationToken cancellationToken -); \ No newline at end of file +); diff --git a/src/Telemetry/OpenTelemetryConvention.cs b/src/Telemetry/OpenTelemetryConvention.cs index b0ce5691a..a8148c274 100644 --- a/src/Telemetry/OpenTelemetryConvention.cs +++ b/src/Telemetry/OpenTelemetryConvention.cs @@ -10,4 +10,5 @@ namespace Rocket.Surgery.LaunchPad.Telemetry; /// /// /// -public delegate void OpenTelemetryConvention(IConventionContext context, IConfiguration configuration, IOpenTelemetryBuilder builder); \ No newline at end of file +[PublicAPI] +public delegate void OpenTelemetryConvention(IConventionContext context, IConfiguration configuration, IOpenTelemetryBuilder builder); diff --git a/src/Telemetry/OpenTelemetryMetricsConvention.cs b/src/Telemetry/OpenTelemetryMetricsConvention.cs index 3e9ee9ebb..abe82308b 100644 --- a/src/Telemetry/OpenTelemetryMetricsConvention.cs +++ b/src/Telemetry/OpenTelemetryMetricsConvention.cs @@ -10,4 +10,5 @@ namespace Rocket.Surgery.LaunchPad.Telemetry; /// /// /// -public delegate void OpenTelemetryMetricsConvention(IConventionContext context, IConfiguration configuration, MeterProviderBuilder builder); \ No newline at end of file +[PublicAPI] +public delegate void OpenTelemetryMetricsConvention(IConventionContext context, IConfiguration configuration, MeterProviderBuilder builder); diff --git a/src/Telemetry/OpenTelemetryTracingConvention.cs b/src/Telemetry/OpenTelemetryTracingConvention.cs index 21f4b02c4..27598c97e 100644 --- a/src/Telemetry/OpenTelemetryTracingConvention.cs +++ b/src/Telemetry/OpenTelemetryTracingConvention.cs @@ -10,4 +10,5 @@ namespace Rocket.Surgery.LaunchPad.Telemetry; /// /// /// -public delegate void OpenTelemetryTracingConvention(IConventionContext context, IConfiguration configuration, TracerProviderBuilder builder); \ No newline at end of file +[PublicAPI] +public delegate void OpenTelemetryTracingConvention(IConventionContext context, IConfiguration configuration, TracerProviderBuilder builder); diff --git a/src/Telemetry/Rocket.Surgery.LaunchPad.Telemetry.csproj b/src/Telemetry/Rocket.Surgery.LaunchPad.Telemetry.csproj index d4194ba25..8686abc7c 100644 --- a/src/Telemetry/Rocket.Surgery.LaunchPad.Telemetry.csproj +++ b/src/Telemetry/Rocket.Surgery.LaunchPad.Telemetry.csproj @@ -1,11 +1,13 @@  - net8.0 + net8.0;net9.0 $(PackageTags) + + diff --git a/src/Telemetry/RocketSurgeryOpenTelemetryExtensions.cs b/src/Telemetry/RocketSurgeryOpenTelemetryExtensions.cs index 5b55a7555..7a92d715d 100644 --- a/src/Telemetry/RocketSurgeryOpenTelemetryExtensions.cs +++ b/src/Telemetry/RocketSurgeryOpenTelemetryExtensions.cs @@ -39,10 +39,10 @@ public static async ValueTask ApplyConventionsAsync( @delegate(context, configuration, builder); break; case IOpenTelemetryAsyncConvention convention: - await convention.Register(context, configuration, builder, cancellationToken); + await convention.Register(context, configuration, builder, cancellationToken).ConfigureAwait(false); break; case OpenTelemetryAsyncConvention @delegate: - await @delegate(context, configuration, builder, cancellationToken); + await @delegate(context, configuration, builder, cancellationToken).ConfigureAwait(false); break; } } @@ -55,6 +55,8 @@ public static async ValueTask ApplyConventionsAsync( /// /// The container. /// The delegate. + /// + /// /// IConventionHostBuilder. public static ConventionContextBuilder ConfigureOpenTelemetry( this ConventionContextBuilder container, @@ -77,6 +79,8 @@ public static ConventionContextBuilder ConfigureOpenTelemetry( /// /// The container. /// The delegate. + /// + /// /// IConventionHostBuilder. public static ConventionContextBuilder ConfigureOpenTelemetry( this ConventionContextBuilder container, @@ -98,6 +102,8 @@ public static ConventionContextBuilder ConfigureOpenTelemetry( /// /// The container. /// The delegate. + /// + /// /// IConventionHostBuilder. public static ConventionContextBuilder ConfigureOpenTelemetry( this ConventionContextBuilder container, @@ -119,6 +125,8 @@ public static ConventionContextBuilder ConfigureOpenTelemetry( /// /// The container. /// The delegate. + /// + /// /// IConventionHostBuilder. public static ConventionContextBuilder ConfigureOpenTelemetry( this ConventionContextBuilder container, @@ -140,6 +148,8 @@ public static ConventionContextBuilder ConfigureOpenTelemetry( /// /// The container. /// The delegate. + /// + /// /// IConventionHostBuilder. public static ConventionContextBuilder ConfigureOpenTelemetry( this ConventionContextBuilder container, diff --git a/src/Testing/Rocket.Surgery.LaunchPad.Testing.csproj b/src/Testing/Rocket.Surgery.LaunchPad.Testing.csproj index 5690eceef..2d99c38c7 100644 --- a/src/Testing/Rocket.Surgery.LaunchPad.Testing.csproj +++ b/src/Testing/Rocket.Surgery.LaunchPad.Testing.csproj @@ -1,6 +1,6 @@  - net8.0 + net8.0;net9.0 $(PackageTags) @@ -12,6 +12,6 @@ - + diff --git a/test/Analyzers.Tests.roslyn4.6/Analyzers.Tests.roslyn4.6.csproj b/test/Analyzers.Tests.roslyn4.6/Analyzers.Tests.roslyn4.6.csproj index 2971fbe37..b7ff1889c 100644 --- a/test/Analyzers.Tests.roslyn4.6/Analyzers.Tests.roslyn4.6.csproj +++ b/test/Analyzers.Tests.roslyn4.6/Analyzers.Tests.roslyn4.6.csproj @@ -1,6 +1,6 @@ - net8.0 + net9.0 <_Analyzer_Roslyn_Version_>$(MSBuildProjectName.Replace("Analyzers.Tests.", "")) $(DefineConstants);ROSLYN4_6 Analyzers.Tests @@ -13,16 +13,13 @@ - + - + - + diff --git a/test/Analyzers.Tests/Analyzers.Tests.csproj b/test/Analyzers.Tests/Analyzers.Tests.csproj index 940637c69..f3d8bca52 100644 --- a/test/Analyzers.Tests/Analyzers.Tests.csproj +++ b/test/Analyzers.Tests/Analyzers.Tests.csproj @@ -1,6 +1,6 @@ - net8.0 + net9.0 $(DefineConstants);ROSLYN_CURRENT;ROSLYN4_8 diff --git a/test/Analyzers.Tests/ControllerActionBodyGeneratorTests.cs b/test/Analyzers.Tests/ControllerActionBodyGeneratorTests.cs index b434cb136..8eeccf43c 100644 --- a/test/Analyzers.Tests/ControllerActionBodyGeneratorTests.cs +++ b/test/Analyzers.Tests/ControllerActionBodyGeneratorTests.cs @@ -722,4 +722,4 @@ public override async Task InitializeAsync() " ); } -} \ No newline at end of file +} diff --git a/test/Analyzers.Tests/GeneratorTest.cs b/test/Analyzers.Tests/GeneratorTest.cs index aa2e35b22..09abd57a8 100644 --- a/test/Analyzers.Tests/GeneratorTest.cs +++ b/test/Analyzers.Tests/GeneratorTest.cs @@ -1,10 +1,8 @@ using System.Reflection; using System.Runtime.Loader; -using Microsoft.CodeAnalysis; +using HotChocolate.Types; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Rocket.Surgery.Conventions; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.Extensions.Testing.SourceGenerators; namespace Analyzers.Tests.Helpers; @@ -22,12 +20,8 @@ public void Dispose() } } -public abstract class GeneratorTest(ITestOutputHelper testOutputHelper) : LoggerTest(testOutputHelper, LogLevel.Trace), IAsyncLifetime +public abstract class GeneratorTest(ITestOutputHelper testOutputHelper) : LoggerTest(XUnitDefaults.CreateTestContext(testOutputHelper)), IAsyncLifetime { - private readonly HashSet _metadataReferences = new(ReferenceEqualityComparer.Instance); - private readonly HashSet _generators = new(); - private readonly List _sources = new(); - public AssemblyLoadContext AssemblyLoadContext { get; } = new CollectibleTestAssemblyLoadContext(); protected GeneratorTestContextBuilder Builder { get; set; } = null!; @@ -40,7 +34,8 @@ public virtual Task InitializeAsync() .AddReferences( typeof(ActivatorUtilities).Assembly, typeof(ConventionContext).Assembly, - typeof(IConventionContext).Assembly + typeof(IConventionContext).Assembly, + typeof(ErrorAttribute<>).Assembly ); return Task.CompletedTask; @@ -53,4 +48,4 @@ public virtual Task DisposeAsync() Disposables.Dispose(); return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/test/Analyzers.Tests/GraphqlMutationActionBodyGeneratorTests.cs b/test/Analyzers.Tests/GraphqlMutationActionBodyGeneratorTests.cs index fbcb1b6c3..33f3d6bec 100644 --- a/test/Analyzers.Tests/GraphqlMutationActionBodyGeneratorTests.cs +++ b/test/Analyzers.Tests/GraphqlMutationActionBodyGeneratorTests.cs @@ -1330,4 +1330,4 @@ public override async Task InitializeAsync() " ); } -} \ No newline at end of file +} diff --git a/test/Analyzers.Tests/GraphqlOptionalPropertyTrackingGeneratorTests.cs b/test/Analyzers.Tests/GraphqlOptionalPropertyTrackingGeneratorTests.cs index 8cc5a7793..9d6e31a1e 100644 --- a/test/Analyzers.Tests/GraphqlOptionalPropertyTrackingGeneratorTests.cs +++ b/test/Analyzers.Tests/GraphqlOptionalPropertyTrackingGeneratorTests.cs @@ -681,22 +681,22 @@ public record Changes public Changes GetChangedState() { return new Changes() - {SerialNumber = SerialNumber.HasBeenSet(), Type = Type.HasBeenSet(), PlannedDate = PlannedDate.HasBeenSet()}; + {SerialNumber = SerialNumber.HasValue, Type = Type.HasValue, PlannedDate = PlannedDate.HasValue}; } public Request ApplyChanges(Request value) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { value = value with {SerialNumber = SerialNumber}; } - if (Type.HasBeenSet()) + if (Type.HasValue) { value = value with {Type = Type}; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { value.PlannedDate = PlannedDate; } @@ -749,22 +749,22 @@ public class Changes public Changes GetChangedState() { return new Changes() - {SerialNumber = SerialNumber.HasBeenSet(), Type = Type.HasBeenSet(), PlannedDate = PlannedDate.HasBeenSet()}; + {SerialNumber = SerialNumber.HasValue, Type = Type.HasValue, PlannedDate = PlannedDate.HasValue}; } public Request ApplyChanges(Request value) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { value.SerialNumber = SerialNumber; } - if (Type.HasBeenSet()) + if (Type.HasValue) { value.Type = Type; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { value.PlannedDate = PlannedDate; } @@ -1323,4 +1323,4 @@ public class RocketModel " ); } -} \ No newline at end of file +} diff --git a/test/Analyzers.Tests/InheritFromGeneratorTests.cs b/test/Analyzers.Tests/InheritFromGeneratorTests.cs index 04dec895b..d219927c3 100644 --- a/test/Analyzers.Tests/InheritFromGeneratorTests.cs +++ b/test/Analyzers.Tests/InheritFromGeneratorTests.cs @@ -879,4 +879,4 @@ public class RocketModel " ); } -} \ No newline at end of file +} diff --git a/test/Analyzers.Tests/ModuleInitializer.cs b/test/Analyzers.Tests/ModuleInitializer.cs index 188cd0d42..57b2a8c30 100644 --- a/test/Analyzers.Tests/ModuleInitializer.cs +++ b/test/Analyzers.Tests/ModuleInitializer.cs @@ -13,7 +13,6 @@ public static class ModuleInitializer public static void Init() { VerifyGeneratorTextContext.Initialize(DiagnosticSeverity.Error, Customizers.Empty); - DiffRunner.Disabled = true; DerivePathInfo( (sourceFile, _, type, method) => @@ -42,4 +41,4 @@ static string GetTypeName(Type type) } ); } -} \ No newline at end of file +} diff --git a/test/Analyzers.Tests/MutableGeneratorTests.cs b/test/Analyzers.Tests/MutableGeneratorTests.cs index cdcf8e8f1..df2c45f7c 100644 --- a/test/Analyzers.Tests/MutableGeneratorTests.cs +++ b/test/Analyzers.Tests/MutableGeneratorTests.cs @@ -15,4 +15,4 @@ public override async Task InitializeAsync() typeof(MutableAttribute) ); } -} \ No newline at end of file +} diff --git a/test/Analyzers.Tests/PropertyTrackingGeneratorTests.cs b/test/Analyzers.Tests/PropertyTrackingGeneratorTests.cs index 9d59f79ad..5c6213355 100644 --- a/test/Analyzers.Tests/PropertyTrackingGeneratorTests.cs +++ b/test/Analyzers.Tests/PropertyTrackingGeneratorTests.cs @@ -1068,4 +1068,4 @@ public class RocketModel " ); } -} \ No newline at end of file +} diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodiesWithMultipleParameters2_sources=#Input2_GetRocketLaunchRecord_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodiesWithMultipleParameters2_sources=#Input2_GetRocketLaunchRecord_PatchRequest_PropertyTracking.verified.cs index b5c588e14..5ed01bb50 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodiesWithMultipleParameters2_sources=#Input2_GetRocketLaunchRecord_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodiesWithMultipleParameters2_sources=#Input2_GetRocketLaunchRecord_PatchRequest_PropertyTracking.verified.cs @@ -24,14 +24,14 @@ public Changes GetChangedState() { return new Changes() { - LaunchId = LaunchId.HasBeenSet() + LaunchId = LaunchId.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.GetRocketLaunchRecord.Request ApplyChanges(global::TestNamespace.GetRocketLaunchRecord.Request state) { - if (LaunchId.HasBeenSet()) + if (LaunchId.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodiesWithMultipleParameters3_sources=#Input2_GetRocketLaunchRecord_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodiesWithMultipleParameters3_sources=#Input2_GetRocketLaunchRecord_PatchRequest_PropertyTracking.verified.cs index 15a248568..fec5dfe5e 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodiesWithMultipleParameters3_sources=#Input2_GetRocketLaunchRecord_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodiesWithMultipleParameters3_sources=#Input2_GetRocketLaunchRecord_PatchRequest_PropertyTracking.verified.cs @@ -24,14 +24,14 @@ public Changes GetChangedState() { return new Changes() { - LaunchRecordId = LaunchRecordId.HasBeenSet() + LaunchRecordId = LaunchRecordId.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.GetRocketLaunchRecord.Request ApplyChanges(global::TestNamespace.GetRocketLaunchRecord.Request state) { - if (LaunchRecordId.HasBeenSet()) + if (LaunchRecordId.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodiesWithMultipleParameters4_sources=#Input2_GetRocketLaunchRecord_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodiesWithMultipleParameters4_sources=#Input2_GetRocketLaunchRecord_PatchRequest_PropertyTracking.verified.cs index 919b517fd..a3a4426c2 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodiesWithMultipleParameters4_sources=#Input2_GetRocketLaunchRecord_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodiesWithMultipleParameters4_sources=#Input2_GetRocketLaunchRecord_PatchRequest_PropertyTracking.verified.cs @@ -24,14 +24,14 @@ public Changes GetChangedState() { return new Changes() { - LaunchRecordId = LaunchRecordId.HasBeenSet() + LaunchRecordId = LaunchRecordId.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.GetRocketLaunchRecord.Request ApplyChanges(global::TestNamespace.GetRocketLaunchRecord.Request state) { - if (LaunchRecordId.HasBeenSet()) + if (LaunchRecordId.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithCancellationToken_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithCancellationToken_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs index 2f0042e8a..4bef6132e 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithCancellationToken_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithCancellationToken_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs @@ -25,14 +25,14 @@ public Changes GetChangedState() { return new Changes() { - Name = Name.HasBeenSet() + Name = Name.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.GetRocket.Request ApplyChanges(global::TestNamespace.GetRocket.Request state) { - if (Name.HasBeenSet()) + if (Name.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipal2_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipal2_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs index b95047aba..208f472a4 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipal2_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipal2_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs @@ -25,14 +25,14 @@ public Changes GetChangedState() { return new Changes() { - Name = Name.HasBeenSet() + Name = Name.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.GetRocket.Request ApplyChanges(global::TestNamespace.GetRocket.Request state) { - if (Name.HasBeenSet()) + if (Name.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipalAndCancellationToken_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipalAndCancellationToken_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs index 7d33903a9..e9487b169 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipalAndCancellationToken_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipalAndCancellationToken_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs @@ -26,14 +26,14 @@ public Changes GetChangedState() { return new Changes() { - Name = Name.HasBeenSet() + Name = Name.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.GetRocket.Request ApplyChanges(global::TestNamespace.GetRocket.Request state) { - if (Name.HasBeenSet()) + if (Name.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipalConstructor_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipalConstructor_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs index d7ed98445..5fa93dae2 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipalConstructor_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipalConstructor_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs @@ -26,14 +26,14 @@ public Changes GetChangedState() { return new Changes() { - Name = Name.HasBeenSet() + Name = Name.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.GetRocket.Request ApplyChanges(global::TestNamespace.GetRocket.Request state) { - if (Name.HasBeenSet()) + if (Name.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipal_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipal_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs index 67319712f..732cabd5e 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipal_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequestWithClaimsPrincipal_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs @@ -25,14 +25,14 @@ public Changes GetChangedState() { return new Changes() { - Name = Name.HasBeenSet() + Name = Name.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.GetRocket.Request ApplyChanges(global::TestNamespace.GetRocket.Request state) { - if (Name.HasBeenSet()) + if (Name.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequest_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequest_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs index 54b507aae..ccc279ed9 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequest_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyForRequest_sources=#Input2_GetRocket_PatchRequest_PropertyTracking.verified.cs @@ -24,14 +24,14 @@ public Changes GetChangedState() { return new Changes() { - Name = Name.HasBeenSet() + Name = Name.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.GetRocket.Request ApplyChanges(global::TestNamespace.GetRocket.Request state) { - if (Name.HasBeenSet()) + if (Name.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithCancellationTokenAndClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithCancellationTokenAndClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs index d5931f89a..1202c1bcc 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithCancellationTokenAndClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithCancellationTokenAndClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs @@ -34,16 +34,16 @@ public Changes GetChangedState() { return new Changes() { - Sn = Sn.HasBeenSet(), - ClaimsPrincipal = ClaimsPrincipal.HasBeenSet(), - Other = Other.HasBeenSet() + Sn = Sn.HasValue, + ClaimsPrincipal = ClaimsPrincipal.HasValue, + Other = Other.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.Save2Rocket.Request ApplyChanges(global::TestNamespace.Save2Rocket.Request state) { - if (Sn.HasBeenSet()) + if (Sn.HasValue) { state = state with { @@ -51,7 +51,7 @@ public Changes GetChangedState() }; } - if (ClaimsPrincipal.HasBeenSet()) + if (ClaimsPrincipal.HasValue) { state = state with { @@ -59,7 +59,7 @@ public Changes GetChangedState() }; } - if (Other.HasBeenSet()) + if (Other.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithCancellationToken_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithCancellationToken_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs index d8723e634..675f52b36 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithCancellationToken_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithCancellationToken_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs @@ -29,15 +29,15 @@ public Changes GetChangedState() { return new Changes() { - Sn = Sn.HasBeenSet(), - Other = Other.HasBeenSet() + Sn = Sn.HasValue, + Other = Other.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.Save2Rocket.Request ApplyChanges(global::TestNamespace.Save2Rocket.Request state) { - if (Sn.HasBeenSet()) + if (Sn.HasValue) { state = state with { @@ -45,7 +45,7 @@ public Changes GetChangedState() }; } - if (Other.HasBeenSet()) + if (Other.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs index 3a6aa91a3..b30ed8690 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs @@ -33,16 +33,16 @@ public Changes GetChangedState() { return new Changes() { - Sn = Sn.HasBeenSet(), - ClaimsPrincipal = ClaimsPrincipal.HasBeenSet(), - Other = Other.HasBeenSet() + Sn = Sn.HasValue, + ClaimsPrincipal = ClaimsPrincipal.HasValue, + Other = Other.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.Save2Rocket.Request ApplyChanges(global::TestNamespace.Save2Rocket.Request state) { - if (Sn.HasBeenSet()) + if (Sn.HasValue) { state = state with { @@ -50,7 +50,7 @@ public Changes GetChangedState() }; } - if (ClaimsPrincipal.HasBeenSet()) + if (ClaimsPrincipal.HasValue) { state = state with { @@ -58,7 +58,7 @@ public Changes GetChangedState() }; } - if (Other.HasBeenSet()) + if (Other.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithDifferentlyNamedCancellationToken_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithDifferentlyNamedCancellationToken_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs index d8723e634..675f52b36 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithDifferentlyNamedCancellationToken_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithDifferentlyNamedCancellationToken_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs @@ -29,15 +29,15 @@ public Changes GetChangedState() { return new Changes() { - Sn = Sn.HasBeenSet(), - Other = Other.HasBeenSet() + Sn = Sn.HasValue, + Other = Other.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.Save2Rocket.Request ApplyChanges(global::TestNamespace.Save2Rocket.Request state) { - if (Sn.HasBeenSet()) + if (Sn.HasValue) { state = state with { @@ -45,7 +45,7 @@ public Changes GetChangedState() }; } - if (Other.HasBeenSet()) + if (Other.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithDifferentlyNamedClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithDifferentlyNamedClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs index 3a6aa91a3..b30ed8690 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithDifferentlyNamedClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithDifferentlyNamedClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs @@ -33,16 +33,16 @@ public Changes GetChangedState() { return new Changes() { - Sn = Sn.HasBeenSet(), - ClaimsPrincipal = ClaimsPrincipal.HasBeenSet(), - Other = Other.HasBeenSet() + Sn = Sn.HasValue, + ClaimsPrincipal = ClaimsPrincipal.HasValue, + Other = Other.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.Save2Rocket.Request ApplyChanges(global::TestNamespace.Save2Rocket.Request state) { - if (Sn.HasBeenSet()) + if (Sn.HasValue) { state = state with { @@ -50,7 +50,7 @@ public Changes GetChangedState() }; } - if (ClaimsPrincipal.HasBeenSet()) + if (ClaimsPrincipal.HasValue) { state = state with { @@ -58,7 +58,7 @@ public Changes GetChangedState() }; } - if (Other.HasBeenSet()) + if (Other.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithoutCancellationToken_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithoutCancellationToken_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs index 09fff2a7c..7aa2356b4 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithoutCancellationToken_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithoutCancellationToken_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs @@ -28,15 +28,15 @@ public Changes GetChangedState() { return new Changes() { - Sn = Sn.HasBeenSet(), - Other = Other.HasBeenSet() + Sn = Sn.HasValue, + Other = Other.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.Save2Rocket.Request ApplyChanges(global::TestNamespace.Save2Rocket.Request state) { - if (Sn.HasBeenSet()) + if (Sn.HasValue) { state = state with { @@ -44,7 +44,7 @@ public Changes GetChangedState() }; } - if (Other.HasBeenSet()) + if (Other.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithoutClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithoutClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs index b61c81515..1fa8f7440 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithoutClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlMutationActionBodyGeneratorTests.Should_Generate_Method_Bodies_With_Optional_Tracking_key=GenerateBodyWithoutClaimsPrincipal_sources=#Input2_Save2Rocket_PatchRequest_PropertyTracking.verified.cs @@ -33,16 +33,16 @@ public Changes GetChangedState() { return new Changes() { - Sn = Sn.HasBeenSet(), - ClaimsPrincipal = ClaimsPrincipal.HasBeenSet(), - Other = Other.HasBeenSet() + Sn = Sn.HasValue, + ClaimsPrincipal = ClaimsPrincipal.HasValue, + Other = Other.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::TestNamespace.Save2Rocket.Request ApplyChanges(global::TestNamespace.Save2Rocket.Request state) { - if (Sn.HasBeenSet()) + if (Sn.HasValue) { state = state with { @@ -50,7 +50,7 @@ public Changes GetChangedState() }; } - if (ClaimsPrincipal.HasBeenSet()) + if (ClaimsPrincipal.HasValue) { state = state with { @@ -58,7 +58,7 @@ public Changes GetChangedState() }; } - if (Other.HasBeenSet()) + if (Other.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=SerialNumber_value=12345_modelType=Class_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=SerialNumber_value=12345_modelType=Class_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index 38adc21f3..eb55ee6fb 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=SerialNumber_value=12345_modelType=Class_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=SerialNumber_value=12345_modelType=Class_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,26 +31,26 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state.SerialNumber = SerialNumber!; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state.PlannedDate = PlannedDate!; } diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=SerialNumber_value=12345_modelType=Record_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=SerialNumber_value=12345_modelType=Record_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index 65c740c34..709bf8ffb 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=SerialNumber_value=12345_modelType=Record_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=SerialNumber_value=12345_modelType=Record_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,16 +31,16 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -48,7 +48,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { @@ -56,7 +56,7 @@ public Changes GetChangedState() }; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=Type_value=12345_modelType=Class_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=Type_value=12345_modelType=Class_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index 38adc21f3..eb55ee6fb 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=Type_value=12345_modelType=Class_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=Type_value=12345_modelType=Class_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,26 +31,26 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state.SerialNumber = SerialNumber!; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state.PlannedDate = PlannedDate!; } diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=Type_value=12345_modelType=Record_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=Type_value=12345_modelType=Record_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index 65c740c34..709bf8ffb 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=Type_value=12345_modelType=Record_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_And_Create_property=Type_value=12345_modelType=Record_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,16 +31,16 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -48,7 +48,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { @@ -56,7 +56,7 @@ public Changes GetChangedState() }; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator#Input1_PatchRocket_PropertyTracking.verified.cs index 47d68654a..bffe4b197 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator#Input1_PatchRocket_PropertyTracking.verified.cs @@ -29,16 +29,16 @@ public Changes GetChangedState() { return new Changes() { - PlannedDate = PlannedDate.HasBeenSet(), - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet() + PlannedDate = PlannedDate.HasValue, + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state = state with { @@ -46,7 +46,7 @@ public Changes GetChangedState() }; } - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -54,7 +54,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator.verified.txt b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator.verified.txt index 5a48cfb22..dce81d7aa 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator.verified.txt +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator.verified.txt @@ -7,6 +7,7 @@ }, References: [ HotChocolate.Abstractions.dll, + HotChocolate.Types.Errors.dll, MediatR.Contracts.dll, MediatR.dll, Microsoft.Extensions.DependencyInjection.Abstractions.dll, diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute2_propertyTracking=OtherAssembly.verified.txt b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute2_propertyTracking=OtherAssembly.verified.txt index 550da3229..8da916c5b 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute2_propertyTracking=OtherAssembly.verified.txt +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute2_propertyTracking=OtherAssembly.verified.txt @@ -6,6 +6,7 @@ }, References: [ HotChocolate.Abstractions.dll, + HotChocolate.Types.Errors.dll, MediatR.Contracts.dll, MediatR.dll, Microsoft.Extensions.DependencyInjection.Abstractions.dll, diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute2_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute2_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index 38adc21f3..eb55ee6fb 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute2_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute2_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,26 +31,26 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state.SerialNumber = SerialNumber!; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state.PlannedDate = PlannedDate!; } diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute2_propertyTracking=SameAssembly.verified.txt b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute2_propertyTracking=SameAssembly.verified.txt index 5a48cfb22..dce81d7aa 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute2_propertyTracking=SameAssembly.verified.txt +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute2_propertyTracking=SameAssembly.verified.txt @@ -7,6 +7,7 @@ }, References: [ HotChocolate.Abstractions.dll, + HotChocolate.Types.Errors.dll, MediatR.Contracts.dll, MediatR.dll, Microsoft.Extensions.DependencyInjection.Abstractions.dll, diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute_propertyTracking=OtherAssembly.verified.txt b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute_propertyTracking=OtherAssembly.verified.txt index 550da3229..8da916c5b 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute_propertyTracking=OtherAssembly.verified.txt +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute_propertyTracking=OtherAssembly.verified.txt @@ -6,6 +6,7 @@ }, References: [ HotChocolate.Abstractions.dll, + HotChocolate.Types.Errors.dll, MediatR.Contracts.dll, MediatR.dll, Microsoft.Extensions.DependencyInjection.Abstractions.dll, diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index 59ae9dfe6..9c7f1bd87 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,26 +31,26 @@ public Changes GetChangedState() { return new Changes() { - PlannedDate = PlannedDate.HasBeenSet(), - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet() + PlannedDate = PlannedDate.HasValue, + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state.PlannedDate = PlannedDate!; } - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state.SerialNumber = SerialNumber!; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute_propertyTracking=SameAssembly.verified.txt b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute_propertyTracking=SameAssembly.verified.txt index 5a48cfb22..dce81d7aa 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute_propertyTracking=SameAssembly.verified.txt +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_Attribute_propertyTracking=SameAssembly.verified.txt @@ -7,6 +7,7 @@ }, References: [ HotChocolate.Abstractions.dll, + HotChocolate.Types.Errors.dll, MediatR.Contracts.dll, MediatR.dll, Microsoft.Extensions.DependencyInjection.Abstractions.dll, diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_propertyTracking=OtherAssembly.verified.txt b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_propertyTracking=OtherAssembly.verified.txt index 9f5ebe93a..36d030f0a 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_propertyTracking=OtherAssembly.verified.txt +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_propertyTracking=OtherAssembly.verified.txt @@ -23,6 +23,7 @@ }, References: [ HotChocolate.Abstractions.dll, + HotChocolate.Types.Errors.dll, MediatR.Contracts.dll, MediatR.dll, Microsoft.Extensions.DependencyInjection.Abstractions.dll, diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index b0d20bac1..d16284242 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -27,20 +27,20 @@ public Changes GetChangedState() { return new Changes() { - PlannedDate = PlannedDate.HasBeenSet(), - Type = Type.HasBeenSet() + PlannedDate = PlannedDate.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state.PlannedDate = PlannedDate!; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_propertyTracking=SameAssembly.verified.txt b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_propertyTracking=SameAssembly.verified.txt index 5a48cfb22..dce81d7aa 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_propertyTracking=SameAssembly.verified.txt +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude_propertyTracking=SameAssembly.verified.txt @@ -7,6 +7,7 @@ }, References: [ HotChocolate.Abstractions.dll, + HotChocolate.Types.Errors.dll, MediatR.Contracts.dll, MediatR.dll, Microsoft.Extensions.DependencyInjection.Abstractions.dll, diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Create_property=SerialNumber_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Create_property=SerialNumber_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index 38adc21f3..eb55ee6fb 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Create_property=SerialNumber_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Create_property=SerialNumber_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,26 +31,26 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state.SerialNumber = SerialNumber!; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state.PlannedDate = PlannedDate!; } diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Create_property=Type_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Create_property=Type_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index 38adc21f3..eb55ee6fb 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Create_property=Type_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Create_property=Type_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,26 +31,26 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state.SerialNumber = SerialNumber!; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state.PlannedDate = PlannedDate!; } diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_IPropertyTracking_Properties_And_Create_property=SerialNumber_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_IPropertyTracking_Properties_And_Create_property=SerialNumber_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index 65c740c34..709bf8ffb 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_IPropertyTracking_Properties_And_Create_property=SerialNumber_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_IPropertyTracking_Properties_And_Create_property=SerialNumber_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,16 +31,16 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -48,7 +48,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { @@ -56,7 +56,7 @@ public Changes GetChangedState() }; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_IPropertyTracking_Properties_And_Create_property=Type_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_IPropertyTracking_Properties_And_Create_property=Type_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index 65c740c34..709bf8ffb 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_IPropertyTracking_Properties_And_Create_property=Type_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_IPropertyTracking_Properties_And_Create_property=Type_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,16 +31,16 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -48,7 +48,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { @@ -56,7 +56,7 @@ public Changes GetChangedState() }; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Create_property=SerialNumber_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Create_property=SerialNumber_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index 65c740c34..709bf8ffb 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Create_property=SerialNumber_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Create_property=SerialNumber_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,16 +31,16 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -48,7 +48,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { @@ -56,7 +56,7 @@ public Changes GetChangedState() }; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Create_property=Type_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Create_property=Type_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs index 65c740c34..709bf8ffb 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Create_property=Type_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Create_property=Type_value=12345_propertyTracking=SameAssembly#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,16 +31,16 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -48,7 +48,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { @@ -56,7 +56,7 @@ public Changes GetChangedState() }; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Require_Partial_Type_Declaration#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Require_Partial_Type_Declaration#Input1_PatchRocket_PropertyTracking.verified.cs index ccfc2fa46..532de5e59 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Require_Partial_Type_Declaration#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Require_Partial_Type_Declaration#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,26 +31,26 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Sample.Core.Operations.Rockets.Request ApplyChanges(global::Sample.Core.Operations.Rockets.Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state.SerialNumber = SerialNumber!; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state.PlannedDate = PlannedDate!; } diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Require_Same_Type_As_Class#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Require_Same_Type_As_Class#Input1_PatchRocket_PropertyTracking.verified.cs index ccfc2fa46..532de5e59 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Require_Same_Type_As_Class#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Require_Same_Type_As_Class#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,26 +31,26 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Sample.Core.Operations.Rockets.Request ApplyChanges(global::Sample.Core.Operations.Rockets.Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state.SerialNumber = SerialNumber!; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state.PlannedDate = PlannedDate!; } diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Require_Same_Type_As_Record#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Require_Same_Type_As_Record#Input1_PatchRocket_PropertyTracking.verified.cs index 9b8d68150..1baba74e7 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Require_Same_Type_As_Record#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Require_Same_Type_As_Record#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,16 +31,16 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Sample.Core.Operations.Rockets.Request ApplyChanges(global::Sample.Core.Operations.Rockets.Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -48,7 +48,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { @@ -56,7 +56,7 @@ public Changes GetChangedState() }; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Builtin_Struct_Property#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Builtin_Struct_Property#Input1_PatchRocket_PropertyTracking.verified.cs index 314e78bb6..6cda22602 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Builtin_Struct_Property#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Builtin_Struct_Property#Input1_PatchRocket_PropertyTracking.verified.cs @@ -26,15 +26,15 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Sample.Core.Operations.Rockets.Request ApplyChanges(global::Sample.Core.Operations.Rockets.Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -42,7 +42,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Class_Property#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Class_Property#Input1_PatchRocket_PropertyTracking.verified.cs index ec72d5393..32dfb06c4 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Class_Property#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Class_Property#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,16 +31,16 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Sample.Core.Operations.Rockets.Request ApplyChanges(global::Sample.Core.Operations.Rockets.Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -48,7 +48,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { @@ -56,7 +56,7 @@ public Changes GetChangedState() }; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Enum_Property#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Enum_Property#Input1_PatchRocket_PropertyTracking.verified.cs index 8bec92b73..1e87d6bd0 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Enum_Property#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Enum_Property#Input1_PatchRocket_PropertyTracking.verified.cs @@ -32,16 +32,16 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Sample.Core.Operations.Rockets.Request ApplyChanges(global::Sample.Core.Operations.Rockets.Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -49,7 +49,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { @@ -57,7 +57,7 @@ public Changes GetChangedState() }; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Struct_Property#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Struct_Property#Input1_PatchRocket_PropertyTracking.verified.cs index e946baefe..5ac23b079 100644 --- a/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Struct_Property#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/GraphqlOptionalPropertyTrackingGeneratorTests.Should_Support_Nullable_Struct_Property#Input1_PatchRocket_PropertyTracking.verified.cs @@ -31,16 +31,16 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet(), - PlannedDate = PlannedDate.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue, + PlannedDate = PlannedDate.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Sample.Core.Operations.Rockets.Request ApplyChanges(global::Sample.Core.Operations.Rockets.Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -48,7 +48,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { @@ -56,7 +56,7 @@ public Changes GetChangedState() }; } - if (PlannedDate.HasBeenSet()) + if (PlannedDate.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_And_Exclude_Properties#Input1_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_And_Exclude_Properties#Input1_PatchRequest_PropertyTracking.verified.cs index 05cc9bf59..cbacf386e 100644 --- a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_And_Exclude_Properties#Input1_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_And_Exclude_Properties#Input1_PatchRequest_PropertyTracking.verified.cs @@ -21,14 +21,14 @@ public Changes GetChangedState() { return new Changes() { - Name = Name.HasBeenSet() + Name = Name.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (Name.HasBeenSet()) + if (Name.HasValue) { state.Name = Name!; } diff --git a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator#Input1_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator#Input1_PatchRequest_PropertyTracking.verified.cs index 119ff2d64..2d8703ff6 100644 --- a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator#Input1_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator#Input1_PatchRequest_PropertyTracking.verified.cs @@ -24,20 +24,20 @@ public Changes GetChangedState() { return new Changes() { - Type = Type.HasBeenSet(), - SerialNumber = SerialNumber.HasBeenSet() + Type = Type.HasValue, + SerialNumber = SerialNumber.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state.SerialNumber = SerialNumber!; } diff --git a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude#Input1_PatchRequest_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude#Input1_PatchRequest_PropertyTracking.verified.cs index 505b8a46f..ca4c88254 100644 --- a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude#Input1_PatchRequest_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_IPropertyTracking_Properties_When_Using_InheritsFromGenerator_Exclude#Input1_PatchRequest_PropertyTracking.verified.cs @@ -24,20 +24,20 @@ public Changes GetChangedState() { return new Changes() { - Type = Type.HasBeenSet(), - Something = Something.HasBeenSet() + Type = Type.HasValue, + Something = Something.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } - if (Something.HasBeenSet()) + if (Something.HasValue) { state.Something = Something!; } diff --git a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Apply_Changes_property=SerialNumber_value=12345#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Apply_Changes_property=SerialNumber_value=12345#Input1_PatchRocket_PropertyTracking.verified.cs index 2b29c19be..3aa7895f6 100644 --- a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Apply_Changes_property=SerialNumber_value=12345#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Apply_Changes_property=SerialNumber_value=12345#Input1_PatchRocket_PropertyTracking.verified.cs @@ -24,20 +24,20 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state.SerialNumber = SerialNumber!; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } diff --git a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Apply_Changes_property=Type_value=12345#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Apply_Changes_property=Type_value=12345#Input1_PatchRocket_PropertyTracking.verified.cs index 2b29c19be..3aa7895f6 100644 --- a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Apply_Changes_property=Type_value=12345#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Apply_Changes_property=Type_value=12345#Input1_PatchRocket_PropertyTracking.verified.cs @@ -24,20 +24,20 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state.SerialNumber = SerialNumber!; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } diff --git a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Track_Changes#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Track_Changes#Input1_PatchRocket_PropertyTracking.verified.cs index b6faffe2e..345a4a89b 100644 --- a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Track_Changes#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Class_With_Underlying_Properties_And_Track_Changes#Input1_PatchRocket_PropertyTracking.verified.cs @@ -24,20 +24,20 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state.SerialNumber = SerialNumber!; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state.Type = Type!; } diff --git a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Apply_Changes_property=SerialNumber#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Apply_Changes_property=SerialNumber#Input1_PatchRocket_PropertyTracking.verified.cs index 046bad8a4..5a993a4d8 100644 --- a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Apply_Changes_property=SerialNumber#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Apply_Changes_property=SerialNumber#Input1_PatchRocket_PropertyTracking.verified.cs @@ -28,16 +28,16 @@ public Changes GetChangedState() { return new Changes() { - Id = Id.HasBeenSet(), - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet() + Id = Id.HasValue, + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (Id.HasBeenSet()) + if (Id.HasValue) { state = state with { @@ -45,7 +45,7 @@ public Changes GetChangedState() }; } - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -53,7 +53,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Apply_Changes_property=Type#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Apply_Changes_property=Type#Input1_PatchRocket_PropertyTracking.verified.cs index 046bad8a4..5a993a4d8 100644 --- a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Apply_Changes_property=Type#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Apply_Changes_property=Type#Input1_PatchRocket_PropertyTracking.verified.cs @@ -28,16 +28,16 @@ public Changes GetChangedState() { return new Changes() { - Id = Id.HasBeenSet(), - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet() + Id = Id.HasValue, + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (Id.HasBeenSet()) + if (Id.HasValue) { state = state with { @@ -45,7 +45,7 @@ public Changes GetChangedState() }; } - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -53,7 +53,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Track_Changes#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Track_Changes#Input1_PatchRocket_PropertyTracking.verified.cs index 046bad8a4..5a993a4d8 100644 --- a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Track_Changes#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Generate_Record_With_Underlying_Properties_And_Track_Changes#Input1_PatchRocket_PropertyTracking.verified.cs @@ -28,16 +28,16 @@ public Changes GetChangedState() { return new Changes() { - Id = Id.HasBeenSet(), - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet() + Id = Id.HasValue, + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Request ApplyChanges(global::Request state) { - if (Id.HasBeenSet()) + if (Id.HasValue) { state = state with { @@ -45,7 +45,7 @@ public Changes GetChangedState() }; } - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -53,7 +53,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Require_Partial_Parent_Type_Declaration#Input1_PublicClass_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Require_Partial_Parent_Type_Declaration#Input1_PublicClass_PatchRocket_PropertyTracking.verified.cs index 642b296ac..f99559fb5 100644 --- a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Require_Partial_Parent_Type_Declaration#Input1_PublicClass_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Require_Partial_Parent_Type_Declaration#Input1_PublicClass_PatchRocket_PropertyTracking.verified.cs @@ -28,15 +28,15 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Sample.Core.Operations.Rockets.Request ApplyChanges(global::Sample.Core.Operations.Rockets.Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -44,7 +44,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Support_Nullable_Class_Property#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Support_Nullable_Class_Property#Input1_PatchRocket_PropertyTracking.verified.cs index 83de36e9d..5b3f102fb 100644 --- a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Support_Nullable_Class_Property#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Support_Nullable_Class_Property#Input1_PatchRocket_PropertyTracking.verified.cs @@ -26,15 +26,15 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Sample.Core.Operations.Rockets.Request ApplyChanges(global::Sample.Core.Operations.Rockets.Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -42,7 +42,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { diff --git a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Support_Nullable_Struct_Property#Input1_PatchRocket_PropertyTracking.verified.cs b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Support_Nullable_Struct_Property#Input1_PatchRocket_PropertyTracking.verified.cs index 441abfde0..9a88b7543 100644 --- a/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Support_Nullable_Struct_Property#Input1_PatchRocket_PropertyTracking.verified.cs +++ b/test/Analyzers.Tests/snapshots/PropertyTrackingGeneratorTests.Should_Support_Nullable_Struct_Property#Input1_PatchRocket_PropertyTracking.verified.cs @@ -26,15 +26,15 @@ public Changes GetChangedState() { return new Changes() { - SerialNumber = SerialNumber.HasBeenSet(), - Type = Type.HasBeenSet() + SerialNumber = SerialNumber.HasValue, + Type = Type.HasValue }; } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage, System.CodeDom.Compiler.GeneratedCode("Rocket.Surgery.LaunchPad.Analyzers", "version"), System.Runtime.CompilerServices.CompilerGenerated] public global::Sample.Core.Operations.Rockets.Request ApplyChanges(global::Sample.Core.Operations.Rockets.Request state) { - if (SerialNumber.HasBeenSet()) + if (SerialNumber.HasValue) { state = state with { @@ -42,7 +42,7 @@ public Changes GetChangedState() }; } - if (Type.HasBeenSet()) + if (Type.HasValue) { state = state with { diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/AspNetCore.FluentValidation.OpenApi.Tests.csproj b/test/AspNetCore.FluentValidation.OpenApi.Tests/AspNetCore.FluentValidation.OpenApi.Tests.csproj new file mode 100644 index 000000000..ea5d32966 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/AspNetCore.FluentValidation.OpenApi.Tests.csproj @@ -0,0 +1,11 @@ + + + net9.0 + + + + + + + + diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-decimal-gtelt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-decimal-gtelt.verified.txt new file mode 100644 index 000000000..a9cabb5cb --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-decimal-gtelt.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + DecimalContainer: { + type: object, + properties: { + value: { + maximum: 2.2, + exclusiveMaximum: true, + minimum: 1.1, + exclusiveMinimum: true, + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-decimal-gtlte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-decimal-gtlte.verified.txt new file mode 100644 index 000000000..a9cabb5cb --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-decimal-gtlte.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + DecimalContainer: { + type: object, + properties: { + value: { + maximum: 2.2, + exclusiveMaximum: true, + minimum: 1.1, + exclusiveMinimum: true, + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-integer-gtelt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-integer-gtelt.verified.txt new file mode 100644 index 000000000..55a68e389 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-integer-gtelt.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + IntegerContainer: { + type: object, + properties: { + value: { + maximum: 11, + exclusiveMaximum: true, + minimum: 2, + exclusiveMinimum: true, + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-integer-gtlte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-integer-gtlte.verified.txt new file mode 100644 index 000000000..59093b609 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-integer-gtlte.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + IntegerContainer: { + type: object, + properties: { + value: { + maximum: 10, + exclusiveMaximum: true, + minimum: 2, + exclusiveMinimum: true, + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-nullable-decimal-gtelt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-nullable-decimal-gtelt.verified.txt new file mode 100644 index 000000000..767982839 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-nullable-decimal-gtelt.verified.txt @@ -0,0 +1,18 @@ +{ + schemas: { + NullableDecimalContainer: { + type: object, + properties: { + value: { + maximum: 2.2, + exclusiveMaximum: true, + minimum: 1.1, + exclusiveMinimum: true, + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-nullable-decimal-gtlte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-nullable-decimal-gtlte.verified.txt new file mode 100644 index 000000000..767982839 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-nullable-decimal-gtlte.verified.txt @@ -0,0 +1,18 @@ +{ + schemas: { + NullableDecimalContainer: { + type: object, + properties: { + value: { + maximum: 2.2, + exclusiveMaximum: true, + minimum: 1.1, + exclusiveMinimum: true, + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-nullable-integer-gtelt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-nullable-integer-gtelt.verified.txt new file mode 100644 index 000000000..67a622bb7 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-nullable-integer-gtelt.verified.txt @@ -0,0 +1,18 @@ +{ + schemas: { + NullableIntegerContainer: { + type: object, + properties: { + value: { + maximum: 11, + exclusiveMaximum: true, + minimum: 2, + exclusiveMinimum: true, + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-nullable-integer-gtlte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-nullable-integer-gtlte.verified.txt new file mode 100644 index 000000000..f5af261f9 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-exclusive-nullable-integer-gtlte.verified.txt @@ -0,0 +1,18 @@ +{ + schemas: { + NullableIntegerContainer: { + type: object, + properties: { + value: { + maximum: 10, + exclusiveMaximum: true, + minimum: 2, + exclusiveMinimum: true, + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-decimal-gtelt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-decimal-gtelt.verified.txt new file mode 100644 index 000000000..1d4df2571 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-decimal-gtelt.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + DecimalContainer: { + type: object, + properties: { + value: { + maximum: 2.2, + minimum: 1.1, + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-decimal-gtlte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-decimal-gtlte.verified.txt new file mode 100644 index 000000000..1d4df2571 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-decimal-gtlte.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + DecimalContainer: { + type: object, + properties: { + value: { + maximum: 2.2, + minimum: 1.1, + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-integer-gtelt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-integer-gtelt.verified.txt new file mode 100644 index 000000000..48a368bda --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-integer-gtelt.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + IntegerContainer: { + type: object, + properties: { + value: { + maximum: 11, + minimum: 2, + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-integer-gtlte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-integer-gtlte.verified.txt new file mode 100644 index 000000000..bfe19112d --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-integer-gtlte.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + IntegerContainer: { + type: object, + properties: { + value: { + maximum: 10, + minimum: 2, + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-nullable-decimal-gtelt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-nullable-decimal-gtelt.verified.txt new file mode 100644 index 000000000..da9bccb21 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-nullable-decimal-gtelt.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableDecimalContainer: { + type: object, + properties: { + value: { + maximum: 2.2, + minimum: 1.1, + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-nullable-decimal-gtlte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-nullable-decimal-gtlte.verified.txt new file mode 100644 index 000000000..da9bccb21 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-nullable-decimal-gtlte.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableDecimalContainer: { + type: object, + properties: { + value: { + maximum: 2.2, + minimum: 1.1, + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-nullable-integer-gtelt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-nullable-integer-gtelt.verified.txt new file mode 100644 index 000000000..a609342d4 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-nullable-integer-gtelt.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableIntegerContainer: { + type: object, + properties: { + value: { + maximum: 11, + minimum: 2, + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-nullable-integer-gtlte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-nullable-integer-gtlte.verified.txt new file mode 100644 index 000000000..af3bb23b4 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_between_properties_result=-comparison-inclusive-nullable-integer-gtlte.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableIntegerContainer: { + type: object, + properties: { + value: { + maximum: 10, + minimum: 2, + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-gt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-gt.verified.txt new file mode 100644 index 000000000..0411aaffa --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-gt.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + DecimalContainer: { + type: object, + properties: { + value: { + minimum: 2.1, + exclusiveMinimum: true, + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-gte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-gte.verified.txt new file mode 100644 index 000000000..eb59ec699 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-gte.verified.txt @@ -0,0 +1,14 @@ +{ + schemas: { + DecimalContainer: { + type: object, + properties: { + value: { + minimum: 2.1, + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-gtelt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-gtelt.verified.txt new file mode 100644 index 000000000..39acbe8e7 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-gtelt.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + DecimalContainer: { + type: object, + properties: { + value: { + maximum: 2.2, + exclusiveMaximum: true, + minimum: 1.1, + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-gtlte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-gtlte.verified.txt new file mode 100644 index 000000000..5fc14e3ee --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-gtlte.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + DecimalContainer: { + type: object, + properties: { + value: { + maximum: 2.2, + minimum: 1.1, + exclusiveMinimum: true, + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-lt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-lt.verified.txt new file mode 100644 index 000000000..ec08e0974 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-lt.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + DecimalContainer: { + type: object, + properties: { + value: { + maximum: 1.1, + exclusiveMaximum: true, + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-lte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-lte.verified.txt new file mode 100644 index 000000000..158fab3e9 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-decimal-lte.verified.txt @@ -0,0 +1,14 @@ +{ + schemas: { + DecimalContainer: { + type: object, + properties: { + value: { + maximum: 1.1, + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-gt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-gt.verified.txt new file mode 100644 index 000000000..5d9aa198e --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-gt.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + IntegerContainer: { + type: object, + properties: { + value: { + minimum: 2, + exclusiveMinimum: true, + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-gte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-gte.verified.txt new file mode 100644 index 000000000..3b0958705 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-gte.verified.txt @@ -0,0 +1,14 @@ +{ + schemas: { + IntegerContainer: { + type: object, + properties: { + value: { + minimum: 2, + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-gtelt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-gtelt.verified.txt new file mode 100644 index 000000000..07c461629 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-gtelt.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + IntegerContainer: { + type: object, + properties: { + value: { + maximum: 11, + exclusiveMaximum: true, + minimum: 2, + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-gtlte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-gtlte.verified.txt new file mode 100644 index 000000000..83a9d82e4 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-gtlte.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + IntegerContainer: { + type: object, + properties: { + value: { + maximum: 10, + minimum: 2, + exclusiveMinimum: true, + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-lt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-lt.verified.txt new file mode 100644 index 000000000..a47392ffa --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-lt.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + IntegerContainer: { + type: object, + properties: { + value: { + maximum: 1, + exclusiveMaximum: true, + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-lte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-lte.verified.txt new file mode 100644 index 000000000..38c81cc4f --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-integer-lte.verified.txt @@ -0,0 +1,14 @@ +{ + schemas: { + IntegerContainer: { + type: object, + properties: { + value: { + maximum: 1, + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-gt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-gt.verified.txt new file mode 100644 index 000000000..042ae3700 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-gt.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableDecimalContainer: { + type: object, + properties: { + value: { + minimum: 2.1, + exclusiveMinimum: true, + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-gte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-gte.verified.txt new file mode 100644 index 000000000..259894dcd --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-gte.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + NullableDecimalContainer: { + type: object, + properties: { + value: { + minimum: 2.1, + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-gtelt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-gtelt.verified.txt new file mode 100644 index 000000000..04a5e3c9b --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-gtelt.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableDecimalContainer: { + type: object, + properties: { + value: { + maximum: 2.2, + exclusiveMaximum: true, + minimum: 1.1, + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-gtlte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-gtlte.verified.txt new file mode 100644 index 000000000..d9a1b9311 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-gtlte.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableDecimalContainer: { + type: object, + properties: { + value: { + maximum: 2.2, + minimum: 1.1, + exclusiveMinimum: true, + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-lt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-lt.verified.txt new file mode 100644 index 000000000..78a774fa5 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-lt.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableDecimalContainer: { + type: object, + properties: { + value: { + maximum: 1.1, + exclusiveMaximum: true, + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-lte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-lte.verified.txt new file mode 100644 index 000000000..90421faff --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-decimal-lte.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + NullableDecimalContainer: { + type: object, + properties: { + value: { + maximum: 1.1, + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-gt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-gt.verified.txt new file mode 100644 index 000000000..286f20a13 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-gt.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableIntegerContainer: { + type: object, + properties: { + value: { + minimum: 2, + exclusiveMinimum: true, + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-gte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-gte.verified.txt new file mode 100644 index 000000000..9d8829309 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-gte.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + NullableIntegerContainer: { + type: object, + properties: { + value: { + minimum: 2, + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-gtelt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-gtelt.verified.txt new file mode 100644 index 000000000..827002907 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-gtelt.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableIntegerContainer: { + type: object, + properties: { + value: { + maximum: 11, + exclusiveMaximum: true, + minimum: 2, + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-gtlte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-gtlte.verified.txt new file mode 100644 index 000000000..e4be27006 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-gtlte.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableIntegerContainer: { + type: object, + properties: { + value: { + maximum: 10, + minimum: 2, + exclusiveMinimum: true, + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-lt.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-lt.verified.txt new file mode 100644 index 000000000..6158248fd --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-lt.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableIntegerContainer: { + type: object, + properties: { + value: { + maximum: 1, + exclusiveMaximum: true, + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-lte.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-lte.verified.txt new file mode 100644 index 000000000..432db3841 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_comparison_properties_result=-comparison-nullable-integer-lte.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + NullableIntegerContainer: { + type: object, + properties: { + value: { + maximum: 1, + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_email_properties_result=-email-collection.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_email_properties_result=-email-collection.verified.txt new file mode 100644 index 000000000..f27a287c9 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_email_properties_result=-email-collection.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + CollectionContainer: { + type: object, + properties: { + value: { + type: array, + items: { + type: string + }, + format: email + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_email_properties_result=-email-nullable-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_email_properties_result=-email-nullable-string.verified.txt new file mode 100644 index 000000000..655151fd3 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_email_properties_result=-email-nullable-string.verified.txt @@ -0,0 +1,14 @@ +{ + schemas: { + NullableStringContainer: { + type: object, + properties: { + value: { + type: string, + format: email, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_email_properties_result=-email-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_email_properties_result=-email-string.verified.txt new file mode 100644 index 000000000..b51764ceb --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_email_properties_result=-email-string.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + StringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: string, + format: email + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-1-100-collection.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-1-100-collection.verified.txt new file mode 100644 index 000000000..2c0397614 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-1-100-collection.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + CollectionContainer: { + type: object, + properties: { + value: { + maxItems: 50, + minItems: 5, + type: array, + items: { + type: string + } + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-1-100-nullable-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-1-100-nullable-string.verified.txt new file mode 100644 index 000000000..3fbd80e78 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-1-100-nullable-string.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + NullableStringContainer: { + type: object, + properties: { + value: { + maxLength: 100, + minLength: 1, + type: string, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-1-100-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-1-100-string.verified.txt new file mode 100644 index 000000000..846ec50a8 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-1-100-string.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + StringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + maxLength: 50, + minLength: 5, + type: string + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-5-50-collection.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-5-50-collection.verified.txt new file mode 100644 index 000000000..2c0397614 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-5-50-collection.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + CollectionContainer: { + type: object, + properties: { + value: { + maxItems: 50, + minItems: 5, + type: array, + items: { + type: string + } + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-5-50-nullable-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-5-50-nullable-string.verified.txt new file mode 100644 index 000000000..6a34e62a8 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-5-50-nullable-string.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + NullableStringContainer: { + type: object, + properties: { + value: { + maxLength: 50, + minLength: 5, + type: string, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-5-50-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-5-50-string.verified.txt new file mode 100644 index 000000000..846ec50a8 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-length-5-50-string.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + StringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + maxLength: 50, + minLength: 5, + type: string + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-100-collection.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-100-collection.verified.txt new file mode 100644 index 000000000..e741fe7c5 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-100-collection.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + CollectionContainer: { + type: object, + properties: { + value: { + maxItems: 100, + type: array, + items: { + type: string + } + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-100-nullable-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-100-nullable-string.verified.txt new file mode 100644 index 000000000..049ea477a --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-100-nullable-string.verified.txt @@ -0,0 +1,14 @@ +{ + schemas: { + NullableStringContainer: { + type: object, + properties: { + value: { + maxLength: 100, + type: string, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-100-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-100-string.verified.txt new file mode 100644 index 000000000..f593db1f5 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-100-string.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + StringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + maxLength: 100, + type: string + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-50-nullable-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-50-nullable-string.verified.txt new file mode 100644 index 000000000..a480e4758 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-50-nullable-string.verified.txt @@ -0,0 +1,14 @@ +{ + schemas: { + NullableStringContainer: { + type: object, + properties: { + value: { + maxLength: 50, + type: string, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-50-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-50-string.verified.txt new file mode 100644 index 000000000..0ac257539 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-max-length-50-string.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + StringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + maxLength: 50, + type: string + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-1-nullable-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-1-nullable-string.verified.txt new file mode 100644 index 000000000..434c83df2 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-1-nullable-string.verified.txt @@ -0,0 +1,14 @@ +{ + schemas: { + NullableStringContainer: { + type: object, + properties: { + value: { + minLength: 1, + type: string, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-1-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-1-string.verified.txt new file mode 100644 index 000000000..4e8304077 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-1-string.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + StringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + minLength: 1, + type: string + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-5-collection.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-5-collection.verified.txt new file mode 100644 index 000000000..ecb044f8f --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-5-collection.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + CollectionContainer: { + type: object, + properties: { + value: { + minItems: 5, + type: array, + items: { + type: string + } + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-5-nullable-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-5-nullable-string.verified.txt new file mode 100644 index 000000000..3b59eb5ec --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-5-nullable-string.verified.txt @@ -0,0 +1,14 @@ +{ + schemas: { + NullableStringContainer: { + type: object, + properties: { + value: { + minLength: 5, + type: string, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-5-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-5-string.verified.txt new file mode 100644 index 000000000..d96be1c3b --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_length_properties_result=-min-length-5-string.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + StringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + minLength: 5, + type: string + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_regex_properties_result=-regex-collection.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_regex_properties_result=-regex-collection.verified.txt new file mode 100644 index 000000000..3a03e364f --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_regex_properties_result=-regex-collection.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + CollectionContainer: { + type: object, + properties: { + value: { + pattern: ^[a-zA-Z0-9]*$, + type: array, + items: { + type: string + } + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_regex_properties_result=-regex-nullable-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_regex_properties_result=-regex-nullable-string.verified.txt new file mode 100644 index 000000000..427787351 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_regex_properties_result=-regex-nullable-string.verified.txt @@ -0,0 +1,14 @@ +{ + schemas: { + NullableStringContainer: { + type: object, + properties: { + value: { + pattern: ^[a-zA-Z0-9]*$, + type: string, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_regex_properties_result=-regex-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_regex_properties_result=-regex-string.verified.txt new file mode 100644 index 000000000..91b8ac0cd --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_regex_properties_result=-regex-string.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + StringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + pattern: ^[a-zA-Z0-9]*$, + type: string + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-boolean.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-boolean.verified.txt new file mode 100644 index 000000000..f4ecf85e6 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-boolean.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + BooleanContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: boolean + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-collection.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-collection.verified.txt new file mode 100644 index 000000000..df17ac39c --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-collection.verified.txt @@ -0,0 +1,19 @@ +{ + schemas: { + CollectionContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + minLength: 1, + type: array, + items: { + type: string + } + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-decimal.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-decimal.verified.txt new file mode 100644 index 000000000..5912a7f6c --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-decimal.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + DecimalContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-integer.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-integer.verified.txt new file mode 100644 index 000000000..d21c40e0e --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-integer.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + IntegerContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-nullable-boolean.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-nullable-boolean.verified.txt new file mode 100644 index 000000000..00a98546d --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-nullable-boolean.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableBooleanContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: boolean, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-nullable-decimal.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-nullable-decimal.verified.txt new file mode 100644 index 000000000..f472689b2 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-nullable-decimal.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableDecimalContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-nullable-integer.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-nullable-integer.verified.txt new file mode 100644 index 000000000..0e05f92a8 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-nullable-integer.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableIntegerContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-nullable-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-nullable-string.verified.txt new file mode 100644 index 000000000..876d1bd81 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-nullable-string.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableStringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + minLength: 1, + type: string, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-string.verified.txt new file mode 100644 index 000000000..4e8304077 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_and_min_length_result=-notempty-string.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + StringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + minLength: 1, + type: string + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-boolean.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-boolean.verified.txt new file mode 100644 index 000000000..f4ecf85e6 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-boolean.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + BooleanContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: boolean + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-collection.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-collection.verified.txt new file mode 100644 index 000000000..6ab29be53 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-collection.verified.txt @@ -0,0 +1,18 @@ +{ + schemas: { + CollectionContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: array, + items: { + type: string + } + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-decimal.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-decimal.verified.txt new file mode 100644 index 000000000..5912a7f6c --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-decimal.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + DecimalContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-integer.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-integer.verified.txt new file mode 100644 index 000000000..d21c40e0e --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-integer.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + IntegerContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-nullable-boolean.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-nullable-boolean.verified.txt new file mode 100644 index 000000000..00a98546d --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-nullable-boolean.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableBooleanContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: boolean, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-nullable-decimal.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-nullable-decimal.verified.txt new file mode 100644 index 000000000..f472689b2 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-nullable-decimal.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableDecimalContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-nullable-integer.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-nullable-integer.verified.txt new file mode 100644 index 000000000..0e05f92a8 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-nullable-integer.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableIntegerContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-nullable-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-nullable-string.verified.txt new file mode 100644 index 000000000..91f12472c --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-nullable-string.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableStringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: string, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-string.verified.txt new file mode 100644 index 000000000..d03262f16 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/PropertyRuleHandlerTests.Should_generate_required_property_result=-notnull-string.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + StringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: string + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property.verified.txt new file mode 100644 index 000000000..fe3db7e75 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property.verified.txt @@ -0,0 +1,54 @@ +{ + openapi: 3.0.1, + info: { + title: ReSharperTestRunner | v1, + version: 1.0.0 + }, + paths: { + /default/boolean: { + post: { + tags: [ + ReSharperTestRunner + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/BooleanContainer + } + } + } + }, + responses: { + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/BooleanContainer + } + } + } + } + } + } + } + }, + components: { + schemas: { + BooleanContainer: { + type: object, + properties: { + value: { + type: boolean + } + } + } + } + }, + tags: [ + { + name: ReSharperTestRunner + } + ] +} diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-boolean.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-boolean.verified.txt new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-boolean.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-collection.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-collection.verified.txt new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-collection.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-decimal.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-decimal.verified.txt new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-decimal.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-integer.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-integer.verified.txt new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-integer.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-string.verified.txt new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-default-string.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-boolean.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-boolean.verified.txt new file mode 100644 index 000000000..f4ecf85e6 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-boolean.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + BooleanContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: boolean + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-collection.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-collection.verified.txt new file mode 100644 index 000000000..df17ac39c --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-collection.verified.txt @@ -0,0 +1,19 @@ +{ + schemas: { + CollectionContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + minLength: 1, + type: array, + items: { + type: string + } + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-decimal.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-decimal.verified.txt new file mode 100644 index 000000000..5912a7f6c --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-decimal.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + DecimalContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-integer.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-integer.verified.txt new file mode 100644 index 000000000..d21c40e0e --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-integer.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + IntegerContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-nullable-boolean.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-nullable-boolean.verified.txt new file mode 100644 index 000000000..00a98546d --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-nullable-boolean.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableBooleanContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: boolean, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-nullable-decimal.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-nullable-decimal.verified.txt new file mode 100644 index 000000000..f472689b2 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-nullable-decimal.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableDecimalContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-nullable-integer.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-nullable-integer.verified.txt new file mode 100644 index 000000000..0e05f92a8 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-nullable-integer.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableIntegerContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-nullable-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-nullable-string.verified.txt new file mode 100644 index 000000000..876d1bd81 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-nullable-string.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableStringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + minLength: 1, + type: string, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-string.verified.txt new file mode 100644 index 000000000..4e8304077 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notempty-string.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + StringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + minLength: 1, + type: string + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-boolean.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-boolean.verified.txt new file mode 100644 index 000000000..f4ecf85e6 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-boolean.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + BooleanContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: boolean + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-collection.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-collection.verified.txt new file mode 100644 index 000000000..6ab29be53 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-collection.verified.txt @@ -0,0 +1,18 @@ +{ + schemas: { + CollectionContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: array, + items: { + type: string + } + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-decimal.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-decimal.verified.txt new file mode 100644 index 000000000..5912a7f6c --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-decimal.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + DecimalContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: number, + format: double + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-integer.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-integer.verified.txt new file mode 100644 index 000000000..d21c40e0e --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-integer.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + IntegerContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: integer, + format: int32 + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-nullable-boolean.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-nullable-boolean.verified.txt new file mode 100644 index 000000000..00a98546d --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-nullable-boolean.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableBooleanContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: boolean, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-nullable-decimal.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-nullable-decimal.verified.txt new file mode 100644 index 000000000..f472689b2 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-nullable-decimal.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableDecimalContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: number, + format: double, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-nullable-integer.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-nullable-integer.verified.txt new file mode 100644 index 000000000..0e05f92a8 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-nullable-integer.verified.txt @@ -0,0 +1,17 @@ +{ + schemas: { + NullableIntegerContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: integer, + format: int32, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-nullable-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-nullable-string.verified.txt new file mode 100644 index 000000000..91f12472c --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-nullable-string.verified.txt @@ -0,0 +1,16 @@ +{ + schemas: { + NullableStringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: string, + nullable: true + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-string.verified.txt b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-string.verified.txt new file mode 100644 index 000000000..d03262f16 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RequiredPropertyRuleTests.Should_generate_required_property_result=-notnull-string.verified.txt @@ -0,0 +1,15 @@ +{ + schemas: { + StringContainer: { + required: [ + value + ], + type: object, + properties: { + value: { + type: string + } + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCore.FluentValidation.OpenApi.Tests/RuleTestBase.cs b/test/AspNetCore.FluentValidation.OpenApi.Tests/RuleTestBase.cs new file mode 100644 index 000000000..e15df4b88 --- /dev/null +++ b/test/AspNetCore.FluentValidation.OpenApi.Tests/RuleTestBase.cs @@ -0,0 +1,257 @@ +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using Argon; +using DiffEngine; +using FluentValidation; +using FluentValidation.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Rocket.Surgery.LaunchPad.AspNetCore.FluentValidation.OpenApi; + +namespace AspNetCore.FluentValidation.OpenApi.Tests; + +class StringContainer +{ + public required string Value { get; set; } +} + +class NullableStringContainer +{ + public string? Value { get; set; } +} + +class CollectionContainer +{ + public IEnumerable Value { get; set; } = []; +} + +class NullableIntegerContainer +{ + public int? Value { get; set; } +} + +class IntegerContainer +{ + public int Value { get; set; } +} + +class BooleanContainer +{ + public bool Value { get; set; } +} + +class NullableBooleanContainer +{ + public bool? Value { get; set; } +} + +class DecimalContainer +{ + public decimal Value { get; set; } +} + +class NullableDecimalContainer +{ + public decimal? Value { get; set; } +} + +[Experimental(Constants.ExperimentalId)] +public class PropertyRuleHandlerTests : RuleTestBase +{ + [Theory] + [MemberData(nameof(GetNotNullValidators))] + public Task Should_generate_required_property(OpenApiResult result) => VerifyJson(result.JsonFactory()).UseParameters(result.Path); + + [Theory] + [MemberData(nameof(GetNotEmptyValidators))] + public Task Should_generate_required_property_and_min_length(OpenApiResult result) => VerifyJson(result.JsonFactory()).UseParameters(result.Path); + + [Theory] + [MemberData(nameof(GetLengthValidators))] + public Task Should_generate_length_properties(OpenApiResult result) => VerifyJson(result.JsonFactory()).UseParameters(result.Path); + + [Theory] + [MemberData(nameof(RegularExpressionValidators))] + public Task Should_generate_regex_properties(OpenApiResult result) => VerifyJson(result.JsonFactory()).UseParameters(result.Path); + + [Theory] + [MemberData(nameof(EmailAddressValidators))] + public Task Should_generate_email_properties(OpenApiResult result) => VerifyJson(result.JsonFactory()).UseParameters(result.Path); + + [Theory] + [MemberData(nameof(ComparisonValidators))] + public Task Should_generate_comparison_properties(OpenApiResult result) => VerifyJson(result.JsonFactory()).UseParameters(result.Path); + + [Theory] + [MemberData(nameof(BetweenValidators))] + public Task Should_generate_between_properties(OpenApiResult result) => VerifyJson(result.JsonFactory()).UseParameters(result.Path); + + public static IEnumerable GetNotNullValidators() + { + yield return [GetOpenApiDocument("/notnull/boolean", x => x.RuleFor(y => y.Value).NotNull())]; + yield return [GetOpenApiDocument("/notnull/collection", x => x.RuleFor(y => y.Value).NotNull())]; + yield return [GetOpenApiDocument("/notnull/decimal", x => x.RuleFor(y => y.Value).NotNull())]; + yield return [GetOpenApiDocument("/notnull/integer", x => x.RuleFor(y => y.Value).NotNull())]; + yield return [GetOpenApiDocument("/notnull/string", x => x.RuleFor(y => y.Value).NotNull())]; + yield return [GetOpenApiDocument("/notnull/nullable/boolean", x => x.RuleFor(y => y.Value).NotNull())]; + yield return [GetOpenApiDocument("/notnull/nullable/decimal", x => x.RuleFor(y => y.Value).NotNull())]; + yield return [GetOpenApiDocument("/notnull/nullable/integer", x => x.RuleFor(y => y.Value).NotNull())]; + yield return [GetOpenApiDocument("/notnull/nullable/string", x => x.RuleFor(y => y.Value).NotNull())]; + } + + public static IEnumerable GetNotEmptyValidators() + { + yield return [GetOpenApiDocument("/notempty/boolean", x => x.RuleFor(y => y.Value).NotEmpty())]; + yield return [GetOpenApiDocument("/notempty/collection", x => x.RuleFor(y => y.Value).NotEmpty())]; + yield return [GetOpenApiDocument("/notempty/decimal", x => x.RuleFor(y => y.Value).NotEmpty())]; + yield return [GetOpenApiDocument("/notempty/integer", x => x.RuleFor(y => y.Value).NotEmpty())]; + yield return [GetOpenApiDocument("/notempty/string", x => x.RuleFor(y => y.Value).NotEmpty())]; + yield return [GetOpenApiDocument("/notempty/nullable/boolean", x => x.RuleFor(y => y.Value).NotEmpty())]; + yield return [GetOpenApiDocument("/notempty/nullable/decimal", x => x.RuleFor(y => y.Value).NotEmpty())]; + yield return [GetOpenApiDocument("/notempty/nullable/integer", x => x.RuleFor(y => y.Value).NotEmpty())]; + yield return [GetOpenApiDocument("/notempty/nullable/string", x => x.RuleFor(y => y.Value).NotEmpty())]; + } + + public static IEnumerable GetLengthValidators() + { + yield return [GetOpenApiDocument("/min-length-5/string", x => x.RuleFor(y => y.Value).MinimumLength(5))]; + yield return [GetOpenApiDocument("/max-length-100/string", x => x.RuleFor(y => y.Value).MaximumLength(100))]; + yield return [GetOpenApiDocument("/length-5-50/string", x => x.RuleFor(y => y.Value).Length(5, 50))]; + yield return [GetOpenApiDocument("/min-length-5/nullable/string", x => x.RuleFor(y => y.Value).MinimumLength(5))]; + yield return [GetOpenApiDocument("/max-length-100/nullable/string", x => x.RuleFor(y => y.Value).MaximumLength(100))]; + yield return [GetOpenApiDocument("/length-5-50/nullable/string", x => x.RuleFor(y => y.Value).Length(5, 50))]; + yield return [GetOpenApiDocument("/min-length-5/collection", x => x.RuleForEach(y => y.Value).MinimumLength(5))]; + yield return [GetOpenApiDocument("/max-length-100/collection", x => x.RuleForEach(y => y.Value).MaximumLength(100))]; + yield return [GetOpenApiDocument("/length-5-50/collection", x => x.RuleForEach(y => y.Value).Length(5, 50))]; + } + + public static IEnumerable RegularExpressionValidators() + { + yield return [GetOpenApiDocument("/regex/string", x => x.RuleFor(y => y.Value).Matches(new Regex("^[a-zA-Z0-9]*$")))]; + yield return [GetOpenApiDocument("/regex/nullable/string", x => x.RuleFor(y => y.Value).Matches(new Regex("^[a-zA-Z0-9]*$")))]; + yield return [GetOpenApiDocument("/regex/collection", x => x.RuleForEach(y => y.Value).Matches(new Regex("^[a-zA-Z0-9]*$")))]; + } + + public static IEnumerable EmailAddressValidators() + { + yield return [GetOpenApiDocument("/email/string", x => x.RuleFor(y => y.Value).EmailAddress())]; + yield return [GetOpenApiDocument("/email/nullable/string", x => x.RuleFor(y => y.Value).EmailAddress())]; + yield return [GetOpenApiDocument("/email/collection", x => x.RuleForEach(y => y.Value).EmailAddress())]; + } + + public static IEnumerable ComparisonValidators() + { + yield return [GetOpenApiDocument("/comparison/decimal/gt", x => x.RuleFor(y => y.Value).GreaterThan(2.1m))]; + yield return [GetOpenApiDocument("/comparison/decimal/gte", x => x.RuleFor(y => y.Value).GreaterThanOrEqualTo(2.1m))]; + yield return [GetOpenApiDocument("/comparison/decimal/lt", x => x.RuleFor(y => y.Value).LessThan(1.1m))]; + yield return [GetOpenApiDocument("/comparison/decimal/lte", x => x.RuleFor(y => y.Value).LessThanOrEqualTo(1.1m))]; + yield return [GetOpenApiDocument("/comparison/decimal/gtlte", x => x.RuleFor(y => y.Value).GreaterThan(1.1m).LessThanOrEqualTo(2.2m))]; + yield return [GetOpenApiDocument("/comparison/decimal/gtelt", x => x.RuleFor(y => y.Value).GreaterThanOrEqualTo(1.1m).LessThan(2.2m))]; + + yield return [GetOpenApiDocument("/comparison/integer/gt", x => x.RuleFor(y => y.Value).GreaterThan(2))]; + yield return [GetOpenApiDocument("/comparison/integer/gte", x => x.RuleFor(y => y.Value).GreaterThanOrEqualTo(2))]; + yield return [GetOpenApiDocument("/comparison/integer/lt", x => x.RuleFor(y => y.Value).LessThan(1))]; + yield return [GetOpenApiDocument("/comparison/integer/lte", x => x.RuleFor(y => y.Value).LessThanOrEqualTo(1))]; + yield return [GetOpenApiDocument("/comparison/integer/gtlte", x => x.RuleFor(y => y.Value).GreaterThan(2).LessThanOrEqualTo(10))]; + yield return [GetOpenApiDocument("/comparison/integer/gtelt", x => x.RuleFor(y => y.Value).GreaterThanOrEqualTo(2).LessThan(11))]; + + yield return [GetOpenApiDocument("/comparison/nullable/integer/gt", x => x.RuleFor(y => y.Value).GreaterThan(2))]; + yield return [GetOpenApiDocument("/comparison/nullable/integer/gte", x => x.RuleFor(y => y.Value).GreaterThanOrEqualTo(2))]; + yield return [GetOpenApiDocument("/comparison/nullable/integer/lt", x => x.RuleFor(y => y.Value).LessThan(1))]; + yield return [GetOpenApiDocument("/comparison/nullable/integer/lte", x => x.RuleFor(y => y.Value).LessThanOrEqualTo(1))]; + yield return [GetOpenApiDocument("/comparison/nullable/integer/gtlte", x => x.RuleFor(y => y.Value).GreaterThan(2).LessThanOrEqualTo(10))]; + yield return [GetOpenApiDocument("/comparison/nullable/integer/gtelt", x => x.RuleFor(y => y.Value).GreaterThanOrEqualTo(2).LessThan(11))]; + + yield return [GetOpenApiDocument("/comparison/nullable/decimal/gt", x => x.RuleFor(y => y.Value).GreaterThan(2.1m))]; + yield return [GetOpenApiDocument("/comparison/nullable/decimal/gte", x => x.RuleFor(y => y.Value).GreaterThanOrEqualTo(2.1m))]; + yield return [GetOpenApiDocument("/comparison/nullable/decimal/lt", x => x.RuleFor(y => y.Value).LessThan(1.1m))]; + yield return [GetOpenApiDocument("/comparison/nullable/decimal/lte", x => x.RuleFor(y => y.Value).LessThanOrEqualTo(1.1m))]; + yield return [GetOpenApiDocument("/comparison/nullable/decimal/gtlte", x => x.RuleFor(y => y.Value).GreaterThan(1.1m).LessThanOrEqualTo(2.2m))]; + yield return [GetOpenApiDocument("/comparison/nullable/decimal/gtelt", x => x.RuleFor(y => y.Value).GreaterThanOrEqualTo(1.1m).LessThan(2.2m))]; + } + + public static IEnumerable BetweenValidators() + { + yield return [GetOpenApiDocument("/comparison/exclusive/decimal/gtlte", x => x.RuleFor(y => y.Value).ExclusiveBetween(1.1m, 2.2m))]; + yield return [GetOpenApiDocument("/comparison/exclusive/decimal/gtelt", x => x.RuleFor(y => y.Value).ExclusiveBetween(1.1m, 2.2m))]; + yield return [GetOpenApiDocument("/comparison/exclusive/integer/gtlte", x => x.RuleFor(y => y.Value).ExclusiveBetween(2, 10))]; + yield return [GetOpenApiDocument("/comparison/exclusive/integer/gtelt", x => x.RuleFor(y => y.Value).ExclusiveBetween(2, 11))]; + yield return [GetOpenApiDocument("/comparison/exclusive/nullable/integer/gtlte", x => x.RuleFor(y => y.Value).ExclusiveBetween(2, 10))]; + yield return [GetOpenApiDocument("/comparison/exclusive/nullable/integer/gtelt", x => x.RuleFor(y => y.Value).ExclusiveBetween(2, 11))]; + yield return [GetOpenApiDocument("/comparison/exclusive/nullable/decimal/gtlte", x => x.RuleFor(y => y.Value).ExclusiveBetween(1.1m, 2.2m))]; + yield return [GetOpenApiDocument("/comparison/exclusive/nullable/decimal/gtelt", x => x.RuleFor(y => y.Value).ExclusiveBetween(1.1m, 2.2m))]; + + yield return [GetOpenApiDocument("/comparison/inclusive/decimal/gtlte", x => x.RuleFor(y => y.Value).InclusiveBetween(1.1m, 2.2m))]; + yield return [GetOpenApiDocument("/comparison/inclusive/decimal/gtelt", x => x.RuleFor(y => y.Value).InclusiveBetween(1.1m, 2.2m))]; + yield return [GetOpenApiDocument("/comparison/inclusive/integer/gtlte", x => x.RuleFor(y => y.Value).InclusiveBetween(2, 10))]; + yield return [GetOpenApiDocument("/comparison/inclusive/integer/gtelt", x => x.RuleFor(y => y.Value).InclusiveBetween(2, 11))]; + yield return [GetOpenApiDocument("/comparison/inclusive/nullable/integer/gtlte", x => x.RuleFor(y => y.Value).InclusiveBetween(2, 10))]; + yield return [GetOpenApiDocument("/comparison/inclusive/nullable/integer/gtelt", x => x.RuleFor(y => y.Value).InclusiveBetween(2, 11))]; + yield return [GetOpenApiDocument("/comparison/inclusive/nullable/decimal/gtlte", x => x.RuleFor(y => y.Value).InclusiveBetween(1.1m, 2.2m))]; + yield return [GetOpenApiDocument("/comparison/inclusive/nullable/decimal/gtelt", x => x.RuleFor(y => y.Value).InclusiveBetween(1.1m, 2.2m))]; + } + + private static OpenApiResult GetOpenApiDocument(string path, Action> configureValidator) + { + return new( + path, + async () => + { + var builder = WebApplication.CreateSlimBuilder( + new WebApplicationOptions() + { + ApplicationName = typeof(TObject).Name, + } + ); + builder.WebHost.UseTestServer(); + builder.Services.AddOpenApi(); + builder.Services.AddFluentValidationAutoValidation(); + builder.Services.AddFluentValidationOpenApi(); + var validator = new InlineValidator(); + configureValidator(validator); + builder.Services.AddSingleton>(validator); + + #pragma warning disable CA2007 + await using var app = builder.Build(); + + app.MapOpenApi(); + + app.MapPost(path, (TObject container) => container); + + await app.StartAsync().ConfigureAwait(false); + var testServer = (TestServer)app.Services.GetRequiredService(); + var _client = testServer.CreateClient(); + + var response = await _client.GetAsync("/openapi/v1.json"); + response.EnsureSuccessStatusCode(); + var json = JObject.Parse(await response.Content.ReadAsStringAsync()); + return json["components"].ToString(); + #pragma warning restore CA2007 + } + ); + } +} + +public record OpenApiResult(string Path, Func> JsonFactory) +{ + public override string ToString() => Path; +} + +static class ModuleInitializer +{ + [ModuleInitializer] + public static void Initialize() + { + DiffRunner.Disabled = true; + DiffTools.UseOrder(DiffTool.Rider, DiffTool.VisualStudioCode, DiffTool.VisualStudio); +// Verify + } +} + +#pragma warning disable RSGEXP +public abstract class RuleTestBase + #pragma warning restore RSGEXP +{ } diff --git a/test/AspNetCore.Tests/AspNetCore.Tests.csproj b/test/AspNetCore.Tests/AspNetCore.Tests.csproj index 0dcffc9ef..e4cfa1656 100644 --- a/test/AspNetCore.Tests/AspNetCore.Tests.csproj +++ b/test/AspNetCore.Tests/AspNetCore.Tests.csproj @@ -1,9 +1,9 @@  - - net8.0 - - - - - + + net9.0 + + + + + diff --git a/test/AspNetCore.Tests/Restful/RestfulApiMethodBuilderTests.cs b/test/AspNetCore.Tests/Restful/RestfulApiMethodBuilderTests.cs index e53645b8a..a4519548b 100644 --- a/test/AspNetCore.Tests/Restful/RestfulApiMethodBuilderTests.cs +++ b/test/AspNetCore.Tests/Restful/RestfulApiMethodBuilderTests.cs @@ -2,13 +2,12 @@ using MediatR; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.AspNetCore; using Rocket.Surgery.LaunchPad.AspNetCore.Composition; namespace AspNetCore.Tests.Restful; -public class RestfulApiMethodBuilderTests(ITestOutputHelper testOutputHelper) : LoggerTest(testOutputHelper) +public class RestfulApiMethodBuilderTests(ITestOutputHelper testOutputHelper) : LoggerTest(XUnitDefaults.CreateTestContext(testOutputHelper)) { [Fact] public void Should_Have_Method() diff --git a/test/Directory.Build.targets b/test/Directory.Build.targets index 944095f16..2bfb719a2 100644 --- a/test/Directory.Build.targets +++ b/test/Directory.Build.targets @@ -4,7 +4,7 @@ - + @@ -18,14 +18,14 @@ - + - \ No newline at end of file + diff --git a/test/Extensions.Tests/ChangeTrackingTests.cs b/test/Extensions.Tests/ChangeTrackingTests.cs index 1f7de03ce..ed72e056a 100644 --- a/test/Extensions.Tests/ChangeTrackingTests.cs +++ b/test/Extensions.Tests/ChangeTrackingTests.cs @@ -1,17 +1,16 @@ -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.Foundation; namespace Extensions.Tests; -public partial class ChangeTrackingTests(ITestOutputHelper testOutputHelper) : LoggerTest(testOutputHelper) +public partial class ChangeTrackingTests(ITestOutputHelper testOutputHelper) : LoggerTest(XUnitDefaults.CreateTestContext(testOutputHelper)) { [Fact] public void ShouldTrackChanges_For_Classes() { var tracking = new ClassWithTracking(1); - tracking.Name.HasBeenSet().Should().BeFalse(); + tracking.Name.HasValue.Should().BeFalse(); tracking.Name = "Test"; - tracking.Name.HasBeenSet().Should().BeTrue(); + tracking.Name.HasValue.Should().BeTrue(); } [Fact] @@ -22,7 +21,7 @@ public void ShouldResetChanges_For_Classes() Name = "Test", }; tracking.ResetChanges(); - tracking.Name.HasBeenSet().Should().BeFalse(); + tracking.Name.HasValue.Should().BeFalse(); } [Fact] @@ -51,8 +50,8 @@ public void ShouldCreate_For_Classes() }; var tracking = ClassWithTracking.TrackChanges(instance); tracking.Description = "My New description"; - tracking.Name.HasBeenSet().Should().BeFalse(); - tracking.Description.HasBeenSet().Should().BeTrue(); + tracking.Name.HasValue.Should().BeFalse(); + tracking.Description.HasValue.Should().BeTrue(); tracking.ApplyChanges(instance); instance.Name.Should().Be("MyName"); @@ -63,9 +62,9 @@ public void ShouldCreate_For_Classes() public void ShouldTrackChanges_For_Records() { var tracking = new RecordWithTracking(1); - tracking.Name.HasBeenSet().Should().BeFalse(); + tracking.Name.HasValue.Should().BeFalse(); tracking.Name = "Test"; - tracking.Name.HasBeenSet().Should().BeTrue(); + tracking.Name.HasValue.Should().BeTrue(); } [Fact] @@ -76,7 +75,7 @@ public void ShouldResetChanges_For_Records() Name = "Test", }; tracking.ResetChanges(); - tracking.Name.HasBeenSet().Should().BeFalse(); + tracking.Name.HasValue.Should().BeFalse(); } [Fact] @@ -101,8 +100,8 @@ public void ShouldCreate_For_Records() }; var tracking = RecordWithTracking.TrackChanges(instance); tracking.Description = "My New description"; - tracking.Name.HasBeenSet().Should().BeFalse(); - tracking.Description.HasBeenSet().Should().BeTrue(); + tracking.Name.HasValue.Should().BeFalse(); + tracking.Description.HasValue.Should().BeTrue(); instance = tracking.ApplyChanges(instance); instance.Name.Should().Be("MyName"); @@ -127,4 +126,4 @@ public partial record Record(int Id, string? Name) } public partial record RecordWithTracking(int Id) : IPropertyTracking; -} \ No newline at end of file +} diff --git a/test/Extensions.Tests/ConventionFakeTest.cs b/test/Extensions.Tests/ConventionFakeTest.cs index 773999ba5..52c79c609 100644 --- a/test/Extensions.Tests/ConventionFakeTest.cs +++ b/test/Extensions.Tests/ConventionFakeTest.cs @@ -2,18 +2,18 @@ using Microsoft.Extensions.DependencyInjection; using Rocket.Surgery.Conventions; using Rocket.Surgery.Conventions.Testing; -using Rocket.Surgery.Extensions.Testing; namespace Extensions.Tests; -public abstract class ConventionFakeTest(ITestOutputHelper testOutputHelper) : AutoFakeTest(testOutputHelper) +public abstract class ConventionFakeTest(ITestOutputHelper testOutputHelper) : AutoFakeTest(XUnitDefaults.CreateTestContext(testOutputHelper)) { protected async Task Init(Action? action = null) { + var factory = CreateLoggerFactory(); var conventionContextBuilder = ConventionContextBuilder .Create() - .ForTesting(Imports.Instance, LoggerFactory) - .WithLogger(Logger); + .ForTesting(Imports.Instance, factory) + .WithLogger(factory.CreateLogger("Test")); action?.Invoke(conventionContextBuilder); var context = await ConventionContext.FromAsync(conventionContextBuilder); @@ -22,4 +22,4 @@ protected async Task Init(Action? action = null) Populate(await new ServiceCollection().ApplyConventionsAsync(context)); } -} \ No newline at end of file +} diff --git a/test/Extensions.Tests/Extensions.Tests.csproj b/test/Extensions.Tests/Extensions.Tests.csproj index 58a56169b..f81acb15b 100644 --- a/test/Extensions.Tests/Extensions.Tests.csproj +++ b/test/Extensions.Tests/Extensions.Tests.csproj @@ -1,16 +1,16 @@  - net8.0 + net8.0;net9.0 - + - - + + (XUnitDefaults.CreateTestContext(testOutputHelper)) { protected SettingsTask VerifyMethod(MethodResult result, object mapper, params object[] instances) { diff --git a/test/Extensions.Tests/MediatRTests.cs b/test/Extensions.Tests/MediatRTests.cs index 987814902..dc0efbd24 100644 --- a/test/Extensions.Tests/MediatRTests.cs +++ b/test/Extensions.Tests/MediatRTests.cs @@ -4,13 +4,11 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Rocket.Surgery.Conventions; -using Rocket.Surgery.Conventions.Reflection; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.Foundation.Conventions; namespace Extensions.Tests; -public class MediatRTests(ITestOutputHelper outputHelper) : AutoFakeTest(outputHelper) +public class MediatRTests(ITestOutputHelper outputHelper) : AutoFakeTest(XUnitDefaults.CreateTestContext(outputHelper)) { [Fact] public async Task Test1() @@ -76,42 +74,6 @@ public async Task Test2() .MustHaveHappenedOnceExactly(); } - private class TestAssemblyProvider : IAssemblyProvider - { - public IEnumerable GetAssemblies() - { - return new[] - { - typeof(TestAssemblyProvider).GetTypeInfo().Assembly, - typeof(MediatRConvention).GetTypeInfo().Assembly, - }; - } - - public IEnumerable GetAssemblies( - Action action, - int lineNumber = 0, - string filePath = "", - string argumentExpression = "" - ) - { - return new[] - { - typeof(TestAssemblyProvider).GetTypeInfo().Assembly, - typeof(MediatRConvention).GetTypeInfo().Assembly, - }; - } - - public IEnumerable GetTypes( - Func> selector, - int lineNumber = 0, - string filePath = "", - string argumentExpression = "" - ) - { - return Enumerable.Empty(); - } - } - public class Request : IRequest; private class TestHandler : IRequestHandler diff --git a/test/Extensions.Tests/ModuleInitializer.cs b/test/Extensions.Tests/ModuleInitializer.cs index 36a7a529a..102d13bf0 100644 --- a/test/Extensions.Tests/ModuleInitializer.cs +++ b/test/Extensions.Tests/ModuleInitializer.cs @@ -28,4 +28,4 @@ static string GetTypeName(Type type) } ); } -} \ No newline at end of file +} diff --git a/test/Extensions.Tests/NewtonsoftJsonNetTopologySuiteGeoJsonTests.cs b/test/Extensions.Tests/NewtonsoftJsonNetTopologySuiteGeoJsonTests.cs index 2d2bd173b..28b379655 100644 --- a/test/Extensions.Tests/NewtonsoftJsonNetTopologySuiteGeoJsonTests.cs +++ b/test/Extensions.Tests/NewtonsoftJsonNetTopologySuiteGeoJsonTests.cs @@ -1,12 +1,11 @@ using FluentAssertions.Primitives; using NetTopologySuite.Geometries; using Newtonsoft.Json; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.Spatial; namespace Extensions.Tests; -public class NewtonsoftJsonNetTopologySuiteGeoJsonTests(ITestOutputHelper outputHelper) : LoggerTest(outputHelper) +public class NewtonsoftJsonNetTopologySuiteGeoJsonTests(ITestOutputHelper outputHelper) : LoggerTest(XUnitDefaults.CreateTestContext(outputHelper)) { private readonly JsonSerializerSettings _settings = new JsonSerializerSettings().ConfigureGeoJsonForLaunchPad(null); diff --git a/test/Extensions.Tests/NewtonsoftJsonNetTopologySuiteWellKnownTextTests.cs b/test/Extensions.Tests/NewtonsoftJsonNetTopologySuiteWellKnownTextTests.cs index 8a42a267f..2dc3e29ec 100644 --- a/test/Extensions.Tests/NewtonsoftJsonNetTopologySuiteWellKnownTextTests.cs +++ b/test/Extensions.Tests/NewtonsoftJsonNetTopologySuiteWellKnownTextTests.cs @@ -1,12 +1,11 @@ using FluentAssertions.Primitives; using NetTopologySuite.Geometries; using Newtonsoft.Json; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.Spatial; namespace Extensions.Tests; -public class NewtonsoftJsonNetTopologySuiteWellKnownTextTests(ITestOutputHelper outputHelper) : LoggerTest(outputHelper) +public class NewtonsoftJsonNetTopologySuiteWellKnownTextTests(ITestOutputHelper outputHelper) : LoggerTest(XUnitDefaults.CreateTestContext(outputHelper)) { private static T DeserializeObject(string geom, JsonSerializerSettings settings) { diff --git a/test/Extensions.Tests/NewtonsoftJsonNodaTimeTests.cs b/test/Extensions.Tests/NewtonsoftJsonNodaTimeTests.cs index 5673629eb..fc7f20c99 100644 --- a/test/Extensions.Tests/NewtonsoftJsonNodaTimeTests.cs +++ b/test/Extensions.Tests/NewtonsoftJsonNodaTimeTests.cs @@ -1,11 +1,10 @@ using Newtonsoft.Json; using NodaTime; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.Foundation; namespace Extensions.Tests; -public class NewtonsoftJsonNodaTimeTests(ITestOutputHelper outputHelper) : LoggerTest(outputHelper) +public class NewtonsoftJsonNodaTimeTests(ITestOutputHelper outputHelper) : LoggerTest(XUnitDefaults.CreateTestContext(outputHelper)) { private JsonSerializerSettings _settings = new JsonSerializerSettings() .ConfigureNodaTimeForLaunchPad(DateTimeZoneProviders.Tzdb); diff --git a/test/Extensions.Tests/SerilogDestructuringTests.cs b/test/Extensions.Tests/SerilogDestructuringTests.cs index 9e94f0a52..53fbbd994 100644 --- a/test/Extensions.Tests/SerilogDestructuringTests.cs +++ b/test/Extensions.Tests/SerilogDestructuringTests.cs @@ -5,21 +5,21 @@ using Newtonsoft.Json.Linq; using NodaTime; using NodaTime.Testing; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.Foundation; using Rocket.Surgery.LaunchPad.Spatial; using Serilog.Context; +using Serilog.Events; namespace Extensions.Tests; -public class SerilogDestructuringTests : LoggerTest +public class SerilogDestructuringTests : LoggerTest { [Fact] public async Task Should_Destructure_Sjt_Values_JsonElement() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation( + Logger.Information( "This is just a test {@Data}", JsonSerializer.Deserialize(JsonSerializer.Serialize(new { test = true, system = new { data = "1234", }, }), options: null) ); @@ -32,7 +32,7 @@ public async Task Should_Destructure_Sjt_Values_JsonDocument() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation( + Logger.Information( "This is just a test {@Data}", JsonSerializer.Deserialize(JsonSerializer.Serialize(new { test = true, system = new { data = "1234", }, }), options: null) ); @@ -45,7 +45,7 @@ public async Task Should_Destructure_NewtonsoftJson_JObject() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", JObject.FromObject(new { test = true, system = new { data = "1234", }, })); + Logger.Information("This is just a test {@Data}", JObject.FromObject(new { test = true, system = new { data = "1234", }, })); await Verify(logs.Select(z => z.RenderMessage())); } @@ -55,7 +55,7 @@ public async Task Should_Destructure_NewtonsoftJson_JArray() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", JArray.FromObject(new object[] { 1, "2", 3d, })); + Logger.Information("This is just a test {@Data}", JArray.FromObject(new object[] { 1, "2", 3d, })); await Verify(logs.Select(z => z.RenderMessage())); } @@ -69,7 +69,7 @@ public async Task Should_Destructure_NewtonsoftJson_JValue() }; using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", new JValue(faker.Random.Guid())); + Logger.Information("This is just a test {@Data}", new JValue(faker.Random.Guid())); await Verify(logs.Select(z => z.RenderMessage())); } @@ -85,7 +85,7 @@ public async Task Should_Destructure_NetTopologySuite_AttributesTable() ); using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", value); + Logger.Information("This is just a test {@Data}", value); await Verify(logs.Select(z => z.RenderMessage())); } @@ -93,7 +93,7 @@ public async Task Should_Destructure_NetTopologySuite_AttributesTable() public async Task Should_Destructure_NodaTime_Instant() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", _clock.GetCurrentInstant()); + Logger.Information("This is just a test {@Data}", _clock.GetCurrentInstant()); await Verify(logs.Select(z => z.RenderMessage())); } @@ -101,7 +101,7 @@ public async Task Should_Destructure_NodaTime_Instant() public async Task Should_Destructure_NodaTime_LocalDateTime() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", LocalDateTime.FromDateTime(_clock.GetCurrentInstant().ToDateTimeUtc())); + Logger.Information("This is just a test {@Data}", LocalDateTime.FromDateTime(_clock.GetCurrentInstant().ToDateTimeUtc())); await Verify(logs.Select(z => z.RenderMessage())); } @@ -109,7 +109,7 @@ public async Task Should_Destructure_NodaTime_LocalDateTime() public async Task Should_Destructure_NodaTime_LocalDate() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", LocalDate.FromDateTime(_clock.GetCurrentInstant().ToDateTimeUtc())); + Logger.Information("This is just a test {@Data}", LocalDate.FromDateTime(_clock.GetCurrentInstant().ToDateTimeUtc())); await Verify(logs.Select(z => z.RenderMessage())); } @@ -117,7 +117,7 @@ public async Task Should_Destructure_NodaTime_LocalDate() public async Task Should_Destructure_NodaTime_LocalTime() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", LocalTime.FromHoursSinceMidnight(4)); + Logger.Information("This is just a test {@Data}", LocalTime.FromHoursSinceMidnight(4)); await Verify(logs.Select(z => z.RenderMessage())); } @@ -125,7 +125,7 @@ public async Task Should_Destructure_NodaTime_LocalTime() public async Task Should_Destructure_NodaTime_OffsetDateTime() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", OffsetDateTime.FromDateTimeOffset(_clock.GetCurrentInstant().ToDateTimeOffset())); + Logger.Information("This is just a test {@Data}", OffsetDateTime.FromDateTimeOffset(_clock.GetCurrentInstant().ToDateTimeOffset())); await Verify(logs.Select(z => z.RenderMessage())); } @@ -133,7 +133,7 @@ public async Task Should_Destructure_NodaTime_OffsetDateTime() public async Task Should_Destructure_NodaTime_OffsetDate() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", OffsetDateTime.FromDateTimeOffset(_clock.GetCurrentInstant().ToDateTimeOffset()).ToOffsetDate()); + Logger.Information("This is just a test {@Data}", OffsetDateTime.FromDateTimeOffset(_clock.GetCurrentInstant().ToDateTimeOffset()).ToOffsetDate()); await Verify(logs.Select(z => z.RenderMessage())); } @@ -141,7 +141,7 @@ public async Task Should_Destructure_NodaTime_OffsetDate() public async Task Should_Destructure_NodaTime_OffsetTime() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", OffsetDateTime.FromDateTimeOffset(_clock.GetCurrentInstant().ToDateTimeOffset()).ToOffsetTime()); + Logger.Information("This is just a test {@Data}", OffsetDateTime.FromDateTimeOffset(_clock.GetCurrentInstant().ToDateTimeOffset()).ToOffsetTime()); await Verify(logs.Select(z => z.RenderMessage())); } @@ -149,7 +149,7 @@ public async Task Should_Destructure_NodaTime_OffsetTime() public async Task Should_Destructure_NodaTime_ZonedDateTime() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", ZonedDateTime.FromDateTimeOffset(_clock.GetCurrentInstant().ToDateTimeOffset())); + Logger.Information("This is just a test {@Data}", ZonedDateTime.FromDateTimeOffset(_clock.GetCurrentInstant().ToDateTimeOffset())); await Verify(logs.Select(z => z.RenderMessage())); } @@ -157,7 +157,7 @@ public async Task Should_Destructure_NodaTime_ZonedDateTime() public async Task Should_Destructure_NodaTime_DateTimeZone() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", DateTimeZoneProviders.Tzdb["America/New_York"]); + Logger.Information("This is just a test {@Data}", DateTimeZoneProviders.Tzdb["America/New_York"]); await Verify(logs.Select(z => z.RenderMessage())); } @@ -165,7 +165,7 @@ public async Task Should_Destructure_NodaTime_DateTimeZone() public async Task Should_Destructure_NodaTime_Duration() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", Duration.FromDays(1)); + Logger.Information("This is just a test {@Data}", Duration.FromDays(1)); await Verify(logs.Select(z => z.RenderMessage())); } @@ -173,7 +173,7 @@ public async Task Should_Destructure_NodaTime_Duration() public async Task Should_Destructure_NodaTime_Period() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", Period.FromDays(1)); + Logger.Information("This is just a test {@Data}", Period.FromDays(1)); await Verify(logs.Select(z => z.RenderMessage())); } @@ -181,19 +181,22 @@ public async Task Should_Destructure_NodaTime_Period() public async Task Should_Destructure_NodaTime_Interval() { using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", new Interval(_clock.GetCurrentInstant(), _clock.GetCurrentInstant())); + Logger.Information("This is just a test {@Data}", new Interval(_clock.GetCurrentInstant(), _clock.GetCurrentInstant())); await Verify(logs.Select(z => z.RenderMessage())); } public SerilogDestructuringTests(ITestOutputHelper outputHelper) : base( - outputHelper, - LogLevel.Information, - configureLogger: configuration => configuration - .Destructure.NewtonsoftJsonTypes() - .Destructure.SystemTextJsonTypes() - .Destructure.NetTopologySuiteTypes() - .Destructure.NodaTimeTypes(DateTimeZoneProviders.Tzdb) + XUnitTestContext.Create( + outputHelper, + LogEventLevel.Information, + configureLogger: (_, configuration) => configuration + .Destructure.NewtonsoftJsonTypes() + .Destructure.SystemTextJsonTypes() + .Destructure.NetTopologySuiteTypes() + .Destructure.NodaTimeTypes() + ) ) + { LogContext.PushProperty("SourceContext", nameof(SerilogDestructuringTests)); _clock = new( @@ -241,7 +244,7 @@ public async Task Should_Destructure_NetTopologySuite_FeatureCollection(OgcGeome } using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", fc); + Logger.Information("This is just a test {@Data}", fc); await Verify(logs.Select(z => z.RenderMessage())).UseParameters(type, num, threeD); } @@ -263,7 +266,7 @@ public async Task Should_Destructure_NetTopologySuite_Geometry(OgcGeometryType t var geometry = new FeatureFactory().CreateRandomGeometry(type, threeD); using var _ = CaptureLogs(out var logs); - Logger.LogInformation("This is just a test {@Data}", geometry); + Logger.Information("This is just a test {@Data}", geometry); await Verify(logs.Select(z => z.RenderMessage())).UseParameters(type, threeD); } -} \ No newline at end of file +} diff --git a/test/Extensions.Tests/StringInValidatorTests.cs b/test/Extensions.Tests/StringInValidatorTests.cs index 6bda48b1f..6a56741e2 100644 --- a/test/Extensions.Tests/StringInValidatorTests.cs +++ b/test/Extensions.Tests/StringInValidatorTests.cs @@ -95,4 +95,4 @@ public Validator() } } } -} \ No newline at end of file +} diff --git a/test/Extensions.Tests/SystemTextJsonNetTopologySuiteGeoJsonTests.cs b/test/Extensions.Tests/SystemTextJsonNetTopologySuiteGeoJsonTests.cs index a01dda36f..decf8237c 100644 --- a/test/Extensions.Tests/SystemTextJsonNetTopologySuiteGeoJsonTests.cs +++ b/test/Extensions.Tests/SystemTextJsonNetTopologySuiteGeoJsonTests.cs @@ -1,12 +1,11 @@ using System.Text.Json; using FluentAssertions.Primitives; using NetTopologySuite.Geometries; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.Spatial; namespace Extensions.Tests; -public class SystemTextJsonNetTopologySuiteGeoJsonTests(ITestOutputHelper outputHelper) : LoggerTest(outputHelper) +public class SystemTextJsonNetTopologySuiteGeoJsonTests(ITestOutputHelper outputHelper) : LoggerTest(XUnitDefaults.CreateTestContext(outputHelper)) { private readonly JsonSerializerOptions _settings = new JsonSerializerOptions().ConfigureGeoJsonForLaunchPad(null); diff --git a/test/Extensions.Tests/SystemTextJsonNetTopologySuiteWellKnownTextTests.cs b/test/Extensions.Tests/SystemTextJsonNetTopologySuiteWellKnownTextTests.cs index 13aba10e9..ae64ed43a 100644 --- a/test/Extensions.Tests/SystemTextJsonNetTopologySuiteWellKnownTextTests.cs +++ b/test/Extensions.Tests/SystemTextJsonNetTopologySuiteWellKnownTextTests.cs @@ -1,12 +1,11 @@ using System.Text.Json; using FluentAssertions.Primitives; using NetTopologySuite.Geometries; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.Spatial; namespace Extensions.Tests; -public class SystemTextJsonNetTopologySuiteWellKnownTextTests(ITestOutputHelper outputHelper) : LoggerTest(outputHelper) +public class SystemTextJsonNetTopologySuiteWellKnownTextTests(ITestOutputHelper outputHelper) : LoggerTest(XUnitDefaults.CreateTestContext(outputHelper)) { private static T DeserializeObject(string geom, JsonSerializerOptions settings) { diff --git a/test/Extensions.Tests/SystemTextJsonNodaTimeTests.cs b/test/Extensions.Tests/SystemTextJsonNodaTimeTests.cs index c3483b8e2..9a7770f5d 100644 --- a/test/Extensions.Tests/SystemTextJsonNodaTimeTests.cs +++ b/test/Extensions.Tests/SystemTextJsonNodaTimeTests.cs @@ -1,11 +1,10 @@ using System.Text.Json; using NodaTime; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.Foundation; namespace Extensions.Tests; -public class SystemTextJsonNodaTimeTests(ITestOutputHelper outputHelper) : LoggerTest(outputHelper) +public class SystemTextJsonNodaTimeTests(ITestOutputHelper outputHelper) : LoggerTest(XUnitDefaults.CreateTestContext(outputHelper)) { private JsonSerializerOptions _settings = new JsonSerializerOptions().ConfigureNodaTimeForLaunchPad(DateTimeZoneProviders.Tzdb); diff --git a/test/Extensions.Tests/Validation/HealthCheckOptionsValidationTests.cs b/test/Extensions.Tests/Validation/HealthCheckOptionsValidationTests.cs index 6f8a53b49..b19682bcf 100644 --- a/test/Extensions.Tests/Validation/HealthCheckOptionsValidationTests.cs +++ b/test/Extensions.Tests/Validation/HealthCheckOptionsValidationTests.cs @@ -4,13 +4,12 @@ using Microsoft.Extensions.Options; using Rocket.Surgery.Conventions; using Rocket.Surgery.Conventions.Testing; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.Foundation; using Rocket.Surgery.LaunchPad.Foundation.Validation; namespace Extensions.Tests.Validation; -public class HealthCheckOptionsValidationTests(ITestOutputHelper outputHelper) : AutoFakeTest(outputHelper), IAsyncLifetime +public class HealthCheckOptionsValidationTests(ITestOutputHelper outputHelper) : AutoFakeTest(XUnitDefaults.CreateTestContext(outputHelper)), IAsyncLifetime { [Fact] public async Task Should_Validate_Options_And_Throw() @@ -142,16 +141,17 @@ public Validator() public async Task InitializeAsync() { + var loggerFactory = CreateLoggerFactory(); var conventionContextBuilder = ConventionContextBuilder .Create() - .ForTesting(Imports.Instance, LoggerFactory) + .ForTesting(Imports.Instance, loggerFactory) .Set( new FoundationOptions { RegisterValidationOptionsAsHealthChecks = true, } ) - .WithLogger(Logger); + .WithLogger(loggerFactory.CreateLogger("Test")); var context = await ConventionContext.FromAsync(conventionContextBuilder); Populate(await new ServiceCollection().ApplyConventionsAsync(context)); @@ -161,4 +161,4 @@ public Task DisposeAsync() { return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/test/Extensions.Tests/Validation/OptionsValidationTests.cs b/test/Extensions.Tests/Validation/OptionsValidationTests.cs index e89b329d3..1736fa5df 100644 --- a/test/Extensions.Tests/Validation/OptionsValidationTests.cs +++ b/test/Extensions.Tests/Validation/OptionsValidationTests.cs @@ -4,12 +4,11 @@ using Microsoft.Extensions.Options; using Rocket.Surgery.Conventions; using Rocket.Surgery.Conventions.Testing; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.Foundation; namespace Extensions.Tests.Validation; -public class OptionsValidationTests(ITestOutputHelper outputHelper) : AutoFakeTest(outputHelper), IAsyncLifetime +public class OptionsValidationTests(ITestOutputHelper outputHelper) : AutoFakeTest(XUnitDefaults.CreateTestContext(outputHelper)), IAsyncLifetime { [Fact] public async Task Should_Validate_Options_And_Throw() @@ -88,11 +87,12 @@ public Validator() public async Task InitializeAsync() { + var loggerFactory = CreateLoggerFactory(); var conventionContextBuilder = ConventionContextBuilder .Create() - .ForTesting(Imports.Instance, LoggerFactory) + .ForTesting(Imports.Instance, loggerFactory) .Set(new FoundationOptions { RegisterValidationOptionsAsHealthChecks = false, }) - .WithLogger(Logger); + .WithLogger(loggerFactory.CreateLogger("Test")); var context = await ConventionContext.FromAsync(conventionContextBuilder); Populate(await new ServiceCollection().ApplyConventionsAsync(context)); @@ -102,4 +102,4 @@ public Task DisposeAsync() { return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/test/Extensions.Tests/Validation/ValidatorFactoryTests.cs b/test/Extensions.Tests/Validation/ValidatorFactoryTests.cs index b8b1eef1d..7f7145dc5 100644 --- a/test/Extensions.Tests/Validation/ValidatorFactoryTests.cs +++ b/test/Extensions.Tests/Validation/ValidatorFactoryTests.cs @@ -1,10 +1,9 @@ using FluentValidation; -using Microsoft.Extensions.Logging; -using Rocket.Surgery.Extensions.Testing; +using Serilog.Events; namespace Extensions.Tests.Validation; -public class ValidatorFactoryTests(ITestOutputHelper outputHelper) : AutoFakeTest(outputHelper, LogLevel.Information) +public class ValidatorFactoryTests(ITestOutputHelper outputHelper) : AutoFakeTest(XUnitTestContext.Create(outputHelper, LogEventLevel.Information)) { // [Fact] // public void Should_Aggregate_Validators() @@ -25,7 +24,7 @@ public interface IThing public string? Thing { get; set; } } - private class AModel : IThing + public class AModel : IThing { [UsedImplicitly] public string? Id { get; set; } [UsedImplicitly] public string? Other { get; set; } diff --git a/test/Metadata.Tests/GitVersionTests.cs b/test/Metadata.Tests/GitVersionTests.cs index 09b3724c6..f5c251c5a 100644 --- a/test/Metadata.Tests/GitVersionTests.cs +++ b/test/Metadata.Tests/GitVersionTests.cs @@ -1,10 +1,9 @@ using System.Reflection; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.Metadata; namespace Metadata.Tests; -public class GitVersionTests(ITestOutputHelper outputHelper) : AutoFakeTest(outputHelper) +public class GitVersionTests(ITestOutputHelper outputHelper) : AutoFakeTest(XUnitDefaults.CreateTestContext(outputHelper)) { [Fact(Skip = "Disabled for CI")] public void ReturnsInformationForVersionedAssembly() diff --git a/test/Metadata.Tests/Metadata.Tests.csproj b/test/Metadata.Tests/Metadata.Tests.csproj index 7da054fed..7b3f1e288 100644 --- a/test/Metadata.Tests/Metadata.Tests.csproj +++ b/test/Metadata.Tests/Metadata.Tests.csproj @@ -1,6 +1,6 @@  - net8.0 + net8.0;net9.0 diff --git a/test/Sample.BlazorServer.Tests/Helpers/WebAppFixtureTest.cs b/test/Sample.BlazorServer.Tests/Helpers/WebAppFixtureTest.cs index 0fe835a37..2c82e7dbd 100644 --- a/test/Sample.BlazorServer.Tests/Helpers/WebAppFixtureTest.cs +++ b/test/Sample.BlazorServer.Tests/Helpers/WebAppFixtureTest.cs @@ -1,59 +1,27 @@ using Alba; using Microsoft.Extensions.Logging; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.AspNetCore.Testing; using Serilog; using Serilog.Events; namespace Sample.BlazorServer.Tests.Helpers; -public abstract class WebAppFixtureTest : LoggerTest, IClassFixture, IAsyncLifetime +public abstract class WebAppFixtureTest + (ITestOutputHelper outputHelper, TAppFixture rocketSurgeryWebAppFixture) + : LoggerTest(XUnitTestContext.Create(outputHelper)), IClassFixture, IAsyncLifetime where TAppFixture : class, ILaunchPadWebAppFixture { - private readonly ILaunchPadWebAppFixture _rocketSurgeryWebAppFixture; - protected IAlbaHost AlbaHost => _rocketSurgeryWebAppFixture.AlbaHost; + protected IAlbaHost AlbaHost => rocketSurgeryWebAppFixture.AlbaHost; /// /// The Service Provider /// protected IServiceProvider ServiceProvider => AlbaHost.Services; - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - LogLevel minLevel, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, minLevel, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - LogEventLevel minLevel, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, minLevel, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - public virtual Task InitializeAsync() { - _rocketSurgeryWebAppFixture.SetLoggerFactory(LoggerFactory); - return _rocketSurgeryWebAppFixture.ResetAsync(); + rocketSurgeryWebAppFixture.SetLoggerFactory(CreateLoggerFactory()); + return rocketSurgeryWebAppFixture.ResetAsync(); } public virtual Task DisposeAsync() diff --git a/test/Sample.BlazorServer.Tests/ModuleInitializer.cs b/test/Sample.BlazorServer.Tests/ModuleInitializer.cs new file mode 100644 index 000000000..fda6c1b1b --- /dev/null +++ b/test/Sample.BlazorServer.Tests/ModuleInitializer.cs @@ -0,0 +1,30 @@ +using System.Runtime.CompilerServices; +using DiffEngine; +using Path = System.IO.Path; + +namespace Sample.BlazorServer.Tests; + +public static class ModuleInitializer +{ + [ModuleInitializer] + public static void Init() + { + DiffRunner.Disabled = true; + VerifierSettings.DontScrubDateTimes(); + VerifierSettings.DisableRequireUniquePrefix(); + DerivePathInfo( + (sourceFile, _, type, method) => + { + static string GetTypeName(Type type) + { + return type.IsNested ? $"{type.ReflectedType!.Name}.{type.Name}" : type.Name; + } + + var typeName = GetTypeName(type); + + var path = Path.Combine(Path.GetDirectoryName(sourceFile)!, "snapshots"); + return new(path, typeName, method.Name); + } + ); + } +} diff --git a/test/Sample.BlazorServer.Tests/Sample.BlazorServer.Tests.csproj b/test/Sample.BlazorServer.Tests/Sample.BlazorServer.Tests.csproj index 9f704ccc9..8a8abf670 100644 --- a/test/Sample.BlazorServer.Tests/Sample.BlazorServer.Tests.csproj +++ b/test/Sample.BlazorServer.Tests/Sample.BlazorServer.Tests.csproj @@ -1,13 +1,13 @@  - net8.0 + net9.0 - - + + diff --git a/test/Sample.BlazorWasm.Tests/HandleTestHostBase.cs b/test/Sample.BlazorWasm.Tests/HandleTestHostBase.cs index 99c536216..9914bdb31 100644 --- a/test/Sample.BlazorWasm.Tests/HandleTestHostBase.cs +++ b/test/Sample.BlazorWasm.Tests/HandleTestHostBase.cs @@ -3,26 +3,23 @@ using Microsoft.Extensions.Logging; using Rocket.Surgery.Conventions; using Rocket.Surgery.Conventions.Testing; -using Rocket.Surgery.Extensions.Testing; +using Serilog.Events; namespace Sample.BlazorWasm.Tests; -public abstract class HandleTestHostBase(ITestOutputHelper outputHelper, LogLevel logLevel = LogLevel.Information) : AutoFakeTest( - outputHelper, - logLevel, - "[{Timestamp:HH:mm:ss} {Level:w4}] {Message} <{SourceContext}>{NewLine}{Exception}" -), IAsyncLifetime +public abstract class HandleTestHostBase(ITestOutputHelper outputHelper, LogEventLevel logLevel = LogEventLevel.Information) : AutoFakeTest(XUnitTestContext.Create(outputHelper, logLevel)), IAsyncLifetime { private IConventionContext _hostBuilder = null!; public async Task InitializeAsync() { + var loggerFactory = CreateLoggerFactory(); _hostBuilder = await ConventionContext.FromAsync( ConventionContextBuilder .Create() - .ForTesting(Imports.Instance, LoggerFactory) - .WithLogger(Logger) + .ForTesting(Imports.Instance, loggerFactory) + .WithLogger(loggerFactory.CreateLogger("Test")) ); ExcludeSourceContext(nameof(WebAssemblyHostBuilder)); ExcludeSourceContext(nameof(WebAssemblyHost)); diff --git a/test/Sample.BlazorWasm.Tests/Sample.BlazorWasm.Tests.csproj b/test/Sample.BlazorWasm.Tests/Sample.BlazorWasm.Tests.csproj index ff064c9f5..a91f497c2 100644 --- a/test/Sample.BlazorWasm.Tests/Sample.BlazorWasm.Tests.csproj +++ b/test/Sample.BlazorWasm.Tests/Sample.BlazorWasm.Tests.csproj @@ -1,6 +1,6 @@  - net8.0-browser + net9.0-browser diff --git a/test/Sample.Classic.Restful.Tests/ClassicFoundationTests.OpenApiDocument.verified.txt b/test/Sample.Classic.Restful.Tests/ClassicFoundationTests.OpenApiDocument.verified.txt new file mode 100644 index 000000000..560834311 --- /dev/null +++ b/test/Sample.Classic.Restful.Tests/ClassicFoundationTests.OpenApiDocument.verified.txt @@ -0,0 +1,1906 @@ +{ + openapi: 3.0.1, + info: { + title: Sample.Classic.Restful | v1, + version: 1.0.0 + }, + paths: { + /LaunchRecord: { + get: { + tags: [ + LaunchRecord + ], + operationId: ListLaunchRecords, + parameters: [ + { + name: rocketType, + in: query, + schema: { + type: string + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/LaunchRecordModel + } + } + }, + text/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/LaunchRecordModel + } + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + post: { + tags: [ + LaunchRecord + ], + operationId: CreateLaunchRecord, + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 201: { + description: Created, + headers: { + location: { + description: The location of the entity that was created, + schema: { + type: string + } + } + }, + content: { + application/json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordResponse + } + }, + text/json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordResponse + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /LaunchRecord/{id}: { + get: { + tags: [ + LaunchRecord + ], + operationId: GetLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/LaunchRecordModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/LaunchRecordModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + put: { + tags: [ + LaunchRecord + ], + operationId: EditLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 204: { + description: No Content + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + patch: { + tags: [ + LaunchRecord + ], + operationId: PatchLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordPatchRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordPatchRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordPatchRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 204: { + description: No Content + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + delete: { + tags: [ + LaunchRecord + ], + operationId: DeleteLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 204: { + description: No Content + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /Rocket: { + get: { + tags: [ + Rocket + ], + operationId: ListRockets, + parameters: [ + { + name: rocketType, + in: query, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/RocketModel + } + } + }, + text/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/RocketModel + } + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + post: { + tags: [ + Rocket + ], + operationId: CreateRocket, + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/CreateRocketRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/CreateRocketRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/CreateRocketRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 201: { + description: Created, + headers: { + location: { + description: The location of the entity that was created, + schema: { + type: string + } + } + }, + content: { + application/json: { + schema: { + $ref: #/components/schemas/CreateRocketResponse + } + }, + text/json: { + schema: { + $ref: #/components/schemas/CreateRocketResponse + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /Rocket/{id}: { + get: { + tags: [ + Rocket + ], + operationId: GetRocket, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + put: { + tags: [ + Rocket + ], + operationId: EditRocket, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/EditRocketRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/EditRocketRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/EditRocketRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + patch: { + tags: [ + Rocket + ], + operationId: PatchRocket, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/EditRocketPatchRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/EditRocketPatchRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/EditRocketPatchRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + delete: { + tags: [ + Rocket + ], + operationId: RemoveRocket, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 204: { + description: No Content + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /Rocket/{id}/launch-records: { + get: { + tags: [ + Rocket + ], + operationId: GetRocketLaunchRecords, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/LaunchRecordModel + } + } + }, + text/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/LaunchRecordModel + } + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /Rocket/{id}/launch-records/{launchRecordId}: { + get: { + tags: [ + Rocket + ], + operationId: GetRocketLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + }, + { + name: launchRecordId, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/LaunchRecordModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/LaunchRecordModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /WeatherForecast: { + get: { + tags: [ + WeatherForecast + ], + operationId: Get, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/WeatherForecast + } + } + }, + text/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/WeatherForecast + } + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + } + }, + components: { + schemas: { + AssignedOfdouble: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + type: number, + format: double + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfInstant: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + $ref: #/components/schemas/RocketId2 + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfNullableOfInstant: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + $ref: #/components/schemas/RocketId2 + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfRocketId: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + $ref: #/components/schemas/RocketId2 + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfRocketType: { + maxLength: 30, + minLength: 10, + required: [ + value + ], + type: object, + properties: { + value: { + $ref: #/components/schemas/RocketType + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfstring: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + type: string, + nullable: true + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfstring2: { + maxLength: 30, + minLength: 10, + required: [ + value + ], + type: object, + properties: { + value: { + type: string, + nullable: true + }, + hasValue: { + type: boolean + } + } + }, + CreateLaunchRecordRequest: { + required: [ + rocketId, + partner, + payload, + payloadWeightKg, + actualLaunchDate, + scheduledLaunchDate + ], + type: object, + properties: { + rocketId: { + $ref: #/components/schemas/Instant2 + }, + partner: { + minimum: 0, + minLength: 1, + type: string, + nullable: true + }, + payload: { + minimum: 0, + minLength: 1, + type: string, + nullable: true + }, + payloadWeightKg: { + minimum: 0, + type: number, + format: double + }, + actualLaunchDate: { + $ref: #/components/schemas/Instant2 + }, + scheduledLaunchDate: { + $ref: #/components/schemas/Instant2 + } + } + }, + CreateLaunchRecordResponse: { + type: object, + properties: { + id: { + $ref: #/components/schemas/LaunchRecordId + } + } + }, + CreateRocketRequest: { + required: [ + serialNumber, + type + ], + type: object, + properties: { + serialNumber: { + maxLength: 30, + minLength: 10, + type: string + }, + type: { + $ref: #/components/schemas/RocketType2 + } + } + }, + CreateRocketResponse: { + type: object, + properties: { + id: { + $ref: #/components/schemas/RocketId2 + } + } + }, + EditLaunchRecordPatchRequest: { + required: [ + id, + partner, + payload, + payloadWeightKg, + actualLaunchDate, + scheduledLaunchDate, + rocketId + ], + type: object, + properties: { + partner: { + $ref: #/components/schemas/AssignedOfstring + }, + payload: { + $ref: #/components/schemas/AssignedOfstring + }, + payloadWeightKg: { + $ref: #/components/schemas/AssignedOfdouble + }, + actualLaunchDate: { + $ref: #/components/schemas/AssignedOfNullableOfInstant + }, + scheduledLaunchDate: { + $ref: #/components/schemas/AssignedOfInstant + }, + rocketId: { + $ref: #/components/schemas/AssignedOfRocketId + } + } + }, + EditLaunchRecordRequest: { + required: [ + partner, + payload, + payloadWeightKg, + actualLaunchDate, + scheduledLaunchDate, + rocketId + ], + type: object, + properties: { + partner: { + minimum: 0, + minLength: 1, + type: string + }, + payload: { + minimum: 0, + minLength: 1, + type: string + }, + payloadWeightKg: { + minimum: 0, + type: number, + format: double + }, + actualLaunchDate: { + $ref: #/components/schemas/RocketId + }, + scheduledLaunchDate: { + $ref: #/components/schemas/RocketId + }, + rocketId: { + $ref: #/components/schemas/RocketId + } + } + }, + EditRocketPatchRequest: { + required: [ + serialNumber, + type + ], + type: object, + properties: { + serialNumber: { + $ref: #/components/schemas/AssignedOfstring2 + }, + type: { + $ref: #/components/schemas/AssignedOfRocketType + } + } + }, + EditRocketRequest: { + required: [ + serialNumber, + type + ], + type: object, + properties: { + serialNumber: { + maxLength: 30, + minLength: 10, + type: string + }, + type: { + $ref: #/components/schemas/RocketType2 + } + } + }, + Instant2: { + minimum: 0 + }, + LaunchRecordModel: { + type: object, + properties: { + id: { + $ref: #/components/schemas/Instant + }, + partner: { + type: string + }, + payload: { + type: string + }, + payloadWeightKg: { + type: integer, + format: int64 + }, + actualLaunchDate: { + $ref: #/components/schemas/Instant + }, + scheduledLaunchDate: { + $ref: #/components/schemas/Instant + }, + rocketSerialNumber: { + type: string + }, + rocketType: { + $ref: #/components/schemas/RocketType + } + } + }, + ProblemDetails: { + required: [ + type, + title, + status, + detail, + instance + ], + type: object, + properties: { + type: { + type: string, + nullable: true + }, + title: { + type: string, + nullable: true + }, + status: { + type: integer, + format: int32, + nullable: true + }, + detail: { + type: string, + nullable: true + }, + instance: { + type: string, + nullable: true + } + } + }, + RocketId: { + minimum: 0 + }, + RocketModel: { + type: object, + properties: { + id: { + $ref: #/components/schemas/RocketId2 + }, + sn: { + type: string + }, + type: { + $ref: #/components/schemas/RocketType + } + } + }, + RocketType: { + enum: [ + falcon9, + falconHeavy, + atlasV + ] + }, + RocketType2: { + maxLength: 30, + minLength: 10, + enum: [ + falcon9, + falconHeavy, + atlasV + ] + }, + WeatherForecast: { + type: object, + properties: { + date: { + type: string, + format: date-time + }, + temperatureC: { + type: integer, + format: int32 + }, + temperatureF: { + type: integer, + format: int32 + }, + summary: { + type: string, + nullable: true + } + } + } + } + }, + tags: [ + { + name: LaunchRecord + }, + { + name: Rocket + }, + { + name: WeatherForecast + } + ] +} \ No newline at end of file diff --git a/test/Sample.Classic.Restful.Tests/ClassicFoundationTests.cs b/test/Sample.Classic.Restful.Tests/ClassicFoundationTests.cs index 7abeb4443..a7e49cb98 100644 --- a/test/Sample.Classic.Restful.Tests/ClassicFoundationTests.cs +++ b/test/Sample.Classic.Restful.Tests/ClassicFoundationTests.cs @@ -1,9 +1,5 @@ using System.Net; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using Sample.Classic.Restful.Tests.Helpers; -using Swashbuckle.AspNetCore.Swagger; -using Swashbuckle.AspNetCore.SwaggerGen; namespace Sample.Classic.Restful.Tests; @@ -18,18 +14,10 @@ public async Task Starts() } [Fact] - public void OpenApiDocument() + public async Task OpenApiDocument() { - var docs = ServiceProvider - .GetRequiredService>() - .Value.SwaggerDocs.Keys; - foreach (var document in docs) - { - ServiceProvider - .GetRequiredService() - .GetSwagger(document) - .Should() - .NotBeNull(); - } + var response = await AlbaHost.Server.CreateClient().GetAsync("/openapi/v1.json"); + var document = await response.Content.ReadAsStringAsync(); + await Verify(document, extension: "json"); } -} \ No newline at end of file +} diff --git a/test/Sample.Classic.Restful.Tests/Helpers/WebAppFixtureTest.cs b/test/Sample.Classic.Restful.Tests/Helpers/WebAppFixtureTest.cs index c1ea3df12..a9e083fb2 100644 --- a/test/Sample.Classic.Restful.Tests/Helpers/WebAppFixtureTest.cs +++ b/test/Sample.Classic.Restful.Tests/Helpers/WebAppFixtureTest.cs @@ -1,16 +1,17 @@ using Alba; using Microsoft.Extensions.Logging; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.AspNetCore.Testing; using Serilog; using Serilog.Events; namespace Sample.Classic.Restful.Tests.Helpers; -public abstract class WebAppFixtureTest : LoggerTest, IClassFixture, IAsyncLifetime +public abstract class WebAppFixtureTest + (ITestOutputHelper outputHelper, TAppFixture rocketSurgeryWebAppFixture) + : LoggerTest(XUnitTestContext.Create(outputHelper)), IClassFixture, IAsyncLifetime where TAppFixture : class, ILaunchPadWebAppFixture { - private readonly ILaunchPadWebAppFixture _rocketSurgeryWebAppFixture; + private readonly ILaunchPadWebAppFixture _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; protected IAlbaHost AlbaHost => _rocketSurgeryWebAppFixture.AlbaHost; /// @@ -18,41 +19,9 @@ public abstract class WebAppFixtureTest : LoggerTest, IClassFixture /// protected IServiceProvider ServiceProvider => AlbaHost.Services; - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - LogLevel minLevel, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, minLevel, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - LogEventLevel minLevel, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, minLevel, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - public virtual Task InitializeAsync() { - _rocketSurgeryWebAppFixture.SetLoggerFactory(LoggerFactory); + _rocketSurgeryWebAppFixture.SetLoggerFactory(CreateLoggerFactory()); return _rocketSurgeryWebAppFixture.ResetAsync(); } @@ -60,4 +29,4 @@ public virtual Task DisposeAsync() { return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/test/Sample.Classic.Restful.Tests/ModuleInitializer.cs b/test/Sample.Classic.Restful.Tests/ModuleInitializer.cs new file mode 100644 index 000000000..431eb0939 --- /dev/null +++ b/test/Sample.Classic.Restful.Tests/ModuleInitializer.cs @@ -0,0 +1,31 @@ +using System.Runtime.CompilerServices; +using DiffEngine; +using Path = System.IO.Path; + +namespace Sample.Restful.Tests; + +internal static class ModuleInitializer +{ + [ModuleInitializer] + public static void Init() + { + DiffRunner.Disabled = true; + VerifierSettings.DontScrubDateTimes(); + VerifierSettings.DisableRequireUniquePrefix(); + DerivePathInfo( + (sourceFile, _, type, method) => + { + static string GetTypeName(Type type) + { + return type.IsNested ? $"{type.ReflectedType!.Name}.{type.Name}" : type.Name; + } + + var typeName = GetTypeName(type); + + var path = Path.Combine(Path.GetDirectoryName(sourceFile)!, "snapshots"); + return new(path, typeName, method.Name); + } + ); + } + +} diff --git a/test/Sample.Classic.Restful.Tests/Sample.Classic.Restful.Tests.csproj b/test/Sample.Classic.Restful.Tests/Sample.Classic.Restful.Tests.csproj index afe297663..2c714d071 100644 --- a/test/Sample.Classic.Restful.Tests/Sample.Classic.Restful.Tests.csproj +++ b/test/Sample.Classic.Restful.Tests/Sample.Classic.Restful.Tests.csproj @@ -1,14 +1,14 @@  - net8.0 + net9.0 - - - + + + diff --git a/test/Sample.Classic.Restful.Tests/snapshots/ClassicFoundationTests.OpenApiDocument.verified.json b/test/Sample.Classic.Restful.Tests/snapshots/ClassicFoundationTests.OpenApiDocument.verified.json new file mode 100644 index 000000000..10b5184a6 --- /dev/null +++ b/test/Sample.Classic.Restful.Tests/snapshots/ClassicFoundationTests.OpenApiDocument.verified.json @@ -0,0 +1,1849 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Sample.Classic.Restful | v1", + "version": "1.0.0" + }, + "paths": { + "/LaunchRecord": { + "get": { + "tags": [ + "LaunchRecord" + ], + "operationId": "ListLaunchRecords", + "parameters": [ + { + "name": "rocketType", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "post": { + "tags": [ + "LaunchRecord" + ], + "operationId": "CreateLaunchRecord", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLaunchRecordRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateLaunchRecordRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateLaunchRecordRequest" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "201": { + "description": "Created", + "headers": { + "location": { + "description": "The location of the entity that was created", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLaunchRecordResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateLaunchRecordResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + }, + "/LaunchRecord/{id}": { + "get": { + "tags": [ + "LaunchRecord" + ], + "operationId": "GetLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "LaunchRecord" + ], + "operationId": "EditLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordRequest" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "patch": { + "tags": [ + "LaunchRecord" + ], + "operationId": "PatchLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordPatchRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordPatchRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordPatchRequest" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "delete": { + "tags": [ + "LaunchRecord" + ], + "operationId": "DeleteLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + }, + "/Rocket": { + "get": { + "tags": [ + "Rocket" + ], + "operationId": "ListRockets", + "parameters": [ + { + "name": "rocketType", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RocketModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RocketModel" + } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "post": { + "tags": [ + "Rocket" + ], + "operationId": "CreateRocket", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRocketRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateRocketRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateRocketRequest" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "201": { + "description": "Created", + "headers": { + "location": { + "description": "The location of the entity that was created", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRocketResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateRocketResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + }, + "/Rocket/{id}": { + "get": { + "tags": [ + "Rocket" + ], + "operationId": "GetRocket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "Rocket" + ], + "operationId": "EditRocket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditRocketRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EditRocketRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/EditRocketRequest" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Rocket" + ], + "operationId": "PatchRocket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditRocketPatchRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EditRocketPatchRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/EditRocketPatchRequest" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Rocket" + ], + "operationId": "RemoveRocket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + }, + "/Rocket/{id}/launch-records": { + "get": { + "tags": [ + "Rocket" + ], + "operationId": "GetRocketLaunchRecords", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + }, + "/Rocket/{id}/launch-records/{launchRecordId}": { + "get": { + "tags": [ + "Rocket" + ], + "operationId": "GetRocketLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "launchRecordId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + }, + "/WeatherForecast": { + "get": { + "tags": [ + "WeatherForecast" + ], + "operationId": "Get", + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WeatherForecast" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WeatherForecast" + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AssignedOfdouble": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "type": "number", + "format": "double" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfInstant": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/RocketId" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfNullableOfInstant": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/RocketId" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfRocketId": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/RocketId" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfRocketType": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/RocketType" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfstring": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "type": "string", + "nullable": true + }, + "hasValue": { + "type": "boolean" + } + } + }, + "CreateLaunchRecordRequest": { + "required": [ + "partner", + "rocketId", + "payload", + "scheduledLaunchDate" + ], + "type": "object", + "properties": { + "rocketId": { + "$ref": "#/components/schemas/Instant" + }, + "partner": { + "minLength": 1, + "type": "string", + "nullable": true + }, + "payload": { + "minLength": 1, + "type": "string", + "nullable": true + }, + "payloadWeightKg": { + "minimum": 0, + "type": "number", + "format": "double" + }, + "actualLaunchDate": { + "$ref": "#/components/schemas/Instant" + }, + "scheduledLaunchDate": { + "$ref": "#/components/schemas/Instant" + } + } + }, + "CreateLaunchRecordResponse": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/LaunchRecordId" + } + } + }, + "CreateRocketRequest": { + "required": [ + "serialNumber" + ], + "type": "object", + "properties": { + "serialNumber": { + "maxLength": 30, + "minLength": 10, + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/RocketType" + } + } + }, + "CreateRocketResponse": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/RocketId" + } + } + }, + "EditLaunchRecordPatchRequest": { + "required": [ + "id" + ], + "type": "object", + "properties": { + "partner": { + "$ref": "#/components/schemas/AssignedOfstring" + }, + "payload": { + "$ref": "#/components/schemas/AssignedOfstring" + }, + "payloadWeightKg": { + "$ref": "#/components/schemas/AssignedOfdouble" + }, + "actualLaunchDate": { + "$ref": "#/components/schemas/AssignedOfNullableOfInstant" + }, + "scheduledLaunchDate": { + "$ref": "#/components/schemas/AssignedOfInstant" + }, + "rocketId": { + "$ref": "#/components/schemas/AssignedOfRocketId" + } + } + }, + "EditLaunchRecordRequest": { + "required": [ + "partner", + "rocketId", + "payload", + "scheduledLaunchDate" + ], + "type": "object", + "properties": { + "partner": { + "minLength": 1, + "type": "string" + }, + "payload": { + "minLength": 1, + "type": "string" + }, + "payloadWeightKg": { + "minimum": 0, + "type": "number", + "format": "double" + }, + "actualLaunchDate": { + "$ref": "#/components/schemas/RocketId" + }, + "scheduledLaunchDate": { + "$ref": "#/components/schemas/RocketId" + }, + "rocketId": { + "$ref": "#/components/schemas/RocketId" + } + } + }, + "EditRocketPatchRequest": { + "type": "object", + "properties": { + "serialNumber": { + "$ref": "#/components/schemas/AssignedOfstring" + }, + "type": { + "$ref": "#/components/schemas/AssignedOfRocketType" + } + } + }, + "EditRocketRequest": { + "required": [ + "type", + "serialNumber" + ], + "type": "object", + "properties": { + "serialNumber": { + "maxLength": 30, + "minLength": 10, + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/RocketType" + } + } + }, + "FluentValidationProblemDetails": { }, + "Instant": { }, + "LaunchRecordId": { }, + "LaunchRecordModel": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/Instant" + }, + "partner": { + "type": "string" + }, + "payload": { + "type": "string" + }, + "payloadWeightKg": { + "type": "integer", + "format": "int64" + }, + "actualLaunchDate": { + "$ref": "#/components/schemas/Instant" + }, + "scheduledLaunchDate": { + "$ref": "#/components/schemas/Instant" + }, + "rocketSerialNumber": { + "type": "string" + }, + "rocketType": { + "$ref": "#/components/schemas/RocketType" + } + } + }, + "ProblemDetails": { + "required": [ + "type", + "title" + ], + "type": "object", + "properties": { + "type": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "status": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "detail": { + "type": "string", + "nullable": true + }, + "instance": { + "type": "string", + "nullable": true + } + } + }, + "RocketId": { }, + "RocketModel": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/RocketId" + }, + "sn": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/RocketType" + } + } + }, + "RocketType": { + "enum": [ + "falcon9", + "falconHeavy", + "atlasV" + ] + }, + "WeatherForecast": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date-time" + }, + "temperatureC": { + "type": "integer", + "format": "int32" + }, + "temperatureF": { + "type": "integer", + "format": "int32" + }, + "summary": { + "type": "string", + "nullable": true + } + } + } + } + }, + "tags": [ + { + "name": "LaunchRecord" + }, + { + "name": "Rocket" + }, + { + "name": "WeatherForecast" + } + ] +} \ No newline at end of file diff --git a/test/Sample.Command.Tests/Sample.Command.Tests.csproj b/test/Sample.Command.Tests/Sample.Command.Tests.csproj index 77972d30a..c51bd4470 100644 --- a/test/Sample.Command.Tests/Sample.Command.Tests.csproj +++ b/test/Sample.Command.Tests/Sample.Command.Tests.csproj @@ -1,11 +1,11 @@  - - net8.0 - - - - - - - + + net8.0;net9.0 + + + + + + + diff --git a/test/Sample.Core.Tests/HandleTestHostBase.cs b/test/Sample.Core.Tests/HandleTestHostBase.cs index bfd398abe..2efadf523 100644 --- a/test/Sample.Core.Tests/HandleTestHostBase.cs +++ b/test/Sample.Core.Tests/HandleTestHostBase.cs @@ -1,31 +1,25 @@ -using Microsoft.Data.Sqlite; +using DryIoc; +using DryIoc.Microsoft.DependencyInjection; +using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Rocket.Surgery.Conventions; using Rocket.Surgery.Conventions.Testing; using Rocket.Surgery.DependencyInjection; -using Rocket.Surgery.Extensions.Testing; using Sample.Core.Domain; +using Serilog.Events; namespace Sample.Core.Tests; -public abstract class HandleTestHostBase : AutoFakeTest, IAsyncLifetime +public abstract class HandleTestHostBase : AutoFakeTest, IAsyncLifetime { - private readonly ConventionContextBuilder _context; + private ConventionContextBuilder? _context; private SqliteConnection? _connection; - protected HandleTestHostBase(ITestOutputHelper outputHelper, LogLevel logLevel = LogLevel.Information) : base( - outputHelper, - logLevel, - "[{Timestamp:HH:mm:ss} {Level:w4}] {Message} <{SourceContext}>{NewLine}{Exception}" + protected HandleTestHostBase(ITestOutputHelper outputHelper, LogEventLevel logLevel = LogEventLevel.Information) : base( + XUnitTestContext.Create(outputHelper, logLevel) ) { - _context = - ConventionContextBuilder - .Create() - .ForTesting(Imports.Instance, LoggerFactory) - .WithLogger(LoggerFactory.CreateLogger(nameof(AutoFakeTest))); ExcludeSourceContext(nameof(AutoFakeTest)); } @@ -33,29 +27,28 @@ public async Task InitializeAsync() { _connection = new("DataSource=:memory:"); await _connection.OpenAsync(); - - _context - .ConfigureServices( - (_, services) => - { - services.AddDbContextPool( - z => z - .EnableDetailedErrors() - .EnableSensitiveDataLogging() - .UseSqlite( - _connection + var factory = CreateLoggerFactory(); + _context = ConventionContextBuilder + .Create() + .ForTesting(Imports.Instance, factory) + .WithLogger(factory.CreateLogger(GetType().Name)); + + var services = await new ServiceCollection() + .AddDbContextPool( + z => z + .EnableDetailedErrors() + .EnableSensitiveDataLogging() + .UseSqlite(_connection) ) - ); - } - ); - - Populate(await new ServiceCollection().ApplyConventionsAsync(await ConventionContext.FromAsync(_context))); - - await ServiceProvider.WithScoped().Invoke(context => context.Database.EnsureCreatedAsync()); + .ApplyConventionsAsync(await ConventionContext.FromAsync(_context)); + Populate(services); + await Container.WithScoped().Invoke(context => context.Database.EnsureCreatedAsync()); } public async Task DisposeAsync() { await _connection!.DisposeAsync(); } -} \ No newline at end of file + + protected override IContainer BuildContainer(IContainer container) => container.WithDependencyInjectionAdapter().Container; +} diff --git a/test/Sample.Core.Tests/LaunchRecords/CreateLaunchRecordTests.cs b/test/Sample.Core.Tests/LaunchRecords/CreateLaunchRecordTests.cs index b9e978766..9886fbcfd 100644 --- a/test/Sample.Core.Tests/LaunchRecords/CreateLaunchRecordTests.cs +++ b/test/Sample.Core.Tests/LaunchRecords/CreateLaunchRecordTests.cs @@ -1,13 +1,13 @@ using MediatR; -using Microsoft.Extensions.Logging; using NodaTime; using Rocket.Surgery.DependencyInjection; using Sample.Core.Domain; using Sample.Core.Operations.LaunchRecords; +using Serilog.Events; namespace Sample.Core.Tests.LaunchRecords; -public class CreateLaunchRecordTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogLevel.Trace) +public class CreateLaunchRecordTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogEventLevel.Verbose) { [Fact] public async Task Should_Create_A_LaunchRecord() diff --git a/test/Sample.Core.Tests/LaunchRecords/GetLaunchRecordTests.cs b/test/Sample.Core.Tests/LaunchRecords/GetLaunchRecordTests.cs index 8fb2ed0f0..5ee5fbd3b 100644 --- a/test/Sample.Core.Tests/LaunchRecords/GetLaunchRecordTests.cs +++ b/test/Sample.Core.Tests/LaunchRecords/GetLaunchRecordTests.cs @@ -1,15 +1,15 @@ using MediatR; -using Microsoft.Extensions.Logging; using NodaTime; using Rocket.Surgery.DependencyInjection; using Rocket.Surgery.LaunchPad.Foundation; using Sample.Core.Domain; using Sample.Core.Models; using Sample.Core.Operations.LaunchRecords; +using Serilog.Events; namespace Sample.Core.Tests.LaunchRecords; -public class GetLaunchRecordTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogLevel.Trace) +public class GetLaunchRecordTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogEventLevel.Verbose) { [Fact] public async Task Should_Get_A_LaunchRecord() @@ -42,7 +42,7 @@ public async Task Should_Get_A_LaunchRecord() ); var response = await ServiceProvider.WithScoped().Invoke( - mediator => mediator.Send(new GetLaunchRecord.Request { Id = record.Id }) + mediator => mediator.Send(new GetLaunchRecord.Request( record.Id)) ); response.Partner.Should().Be("partner"); @@ -56,7 +56,7 @@ public async Task Should_Get_A_LaunchRecord() public async Task Should_Not_Get_A_Missing_Launch_Record() { Func action = () => ServiceProvider.WithScoped().Invoke( - mediator => mediator.Send(new GetLaunchRecord.Request { Id = LaunchRecordId.New() }) + mediator => mediator.Send(new GetLaunchRecord.Request (LaunchRecordId.New())) ); await action.Should().ThrowAsync(); diff --git a/test/Sample.Core.Tests/LaunchRecords/ListLaunchRecordsTests.cs b/test/Sample.Core.Tests/LaunchRecords/ListLaunchRecordsTests.cs index 17e4410cd..68c0fe4a8 100644 --- a/test/Sample.Core.Tests/LaunchRecords/ListLaunchRecordsTests.cs +++ b/test/Sample.Core.Tests/LaunchRecords/ListLaunchRecordsTests.cs @@ -1,12 +1,12 @@ using MediatR; -using Microsoft.Extensions.Logging; using Rocket.Surgery.DependencyInjection; using Sample.Core.Domain; using Sample.Core.Operations.LaunchRecords; +using Serilog.Events; namespace Sample.Core.Tests.LaunchRecords; -public class ListLaunchRecordsTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogLevel.Trace) +public class ListLaunchRecordsTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogEventLevel.Verbose) { [Fact] public async Task Should_List_LaunchRecords() diff --git a/test/Sample.Core.Tests/LaunchRecords/RemoveLaunchRecordsTests.cs b/test/Sample.Core.Tests/LaunchRecords/RemoveLaunchRecordsTests.cs index 1dc6ce2ec..d5cee44b6 100644 --- a/test/Sample.Core.Tests/LaunchRecords/RemoveLaunchRecordsTests.cs +++ b/test/Sample.Core.Tests/LaunchRecords/RemoveLaunchRecordsTests.cs @@ -1,14 +1,14 @@ using MediatR; -using Microsoft.Extensions.Logging; using Rocket.Surgery.DependencyInjection; using Rocket.Surgery.LaunchPad.Foundation; using Sample.Core.Domain; using Sample.Core.Models; using Sample.Core.Operations.LaunchRecords; +using Serilog.Events; namespace Sample.Core.Tests.LaunchRecords; -public class RemoveLaunchRecordsTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogLevel.Trace) +public class RemoveLaunchRecordsTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogEventLevel.Verbose) { [Fact] public async Task Should_Remove_LaunchRecord() @@ -29,7 +29,7 @@ public async Task Should_Remove_LaunchRecord() ); await ServiceProvider.WithScoped().Invoke( - mediator => mediator.Send(new DeleteLaunchRecord.Request { Id = id }) + mediator => mediator.Send(new DeleteLaunchRecord.Request(id)) ); ServiceProvider.WithScoped().Invoke(c => c.LaunchRecords.Should().BeEmpty()); @@ -56,7 +56,7 @@ await ServiceProvider.WithScoped() ); Func action = () => ServiceProvider.WithScoped().Invoke( - mediator => mediator.Send(new DeleteLaunchRecord.Request { Id = id }) + mediator => mediator.Send(new DeleteLaunchRecord.Request(id)) ); await action.Should().ThrowAsync(); } diff --git a/test/Sample.Core.Tests/LaunchRecords/UpdateLaunchRecordTests.cs b/test/Sample.Core.Tests/LaunchRecords/UpdateLaunchRecordTests.cs index 39de39c07..21f52e628 100644 --- a/test/Sample.Core.Tests/LaunchRecords/UpdateLaunchRecordTests.cs +++ b/test/Sample.Core.Tests/LaunchRecords/UpdateLaunchRecordTests.cs @@ -1,15 +1,15 @@ using MediatR; -using Microsoft.Extensions.Logging; using NodaTime; using NodaTime.Extensions; using Rocket.Surgery.DependencyInjection; using Sample.Core.Domain; using Sample.Core.Models; using Sample.Core.Operations.LaunchRecords; +using Serilog.Events; namespace Sample.Core.Tests.LaunchRecords; -public class UpdateLaunchRecordTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogLevel.Trace) +public class UpdateLaunchRecordTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogEventLevel.Verbose) { [Fact] public async Task Should_Update_A_LaunchRecord() diff --git a/test/Sample.Core.Tests/Rockets/CreateRocketTests.cs b/test/Sample.Core.Tests/Rockets/CreateRocketTests.cs index 3015f6a13..1ff7e5403 100644 --- a/test/Sample.Core.Tests/Rockets/CreateRocketTests.cs +++ b/test/Sample.Core.Tests/Rockets/CreateRocketTests.cs @@ -1,15 +1,15 @@ using MediatR; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Rocket.Surgery.DependencyInjection; using Rocket.Surgery.LaunchPad.Foundation; using Sample.Core.Domain; using Sample.Core.Operations.Rockets; +using Serilog.Events; using ValidationException = FluentValidation.ValidationException; namespace Sample.Core.Tests.Rockets; -public class CreateRocketTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogLevel.Trace) +public class CreateRocketTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogEventLevel.Verbose) { [Fact] public async Task Should_Create_A_Rocket() diff --git a/test/Sample.Core.Tests/Rockets/GetRocketTests.cs b/test/Sample.Core.Tests/Rockets/GetRocketTests.cs index 0674c2ff7..5df6b6ec4 100644 --- a/test/Sample.Core.Tests/Rockets/GetRocketTests.cs +++ b/test/Sample.Core.Tests/Rockets/GetRocketTests.cs @@ -1,14 +1,14 @@ using MediatR; -using Microsoft.Extensions.Logging; using Rocket.Surgery.DependencyInjection; using Rocket.Surgery.LaunchPad.Foundation; using Sample.Core.Domain; using Sample.Core.Models; using Sample.Core.Operations.Rockets; +using Serilog.Events; namespace Sample.Core.Tests.Rockets; -public class GetRocketTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogLevel.Trace) +public class GetRocketTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogEventLevel.Verbose) { [Fact] public async Task Should_Get_A_Rocket() @@ -30,7 +30,7 @@ public async Task Should_Get_A_Rocket() ); var response = await ServiceProvider.WithScoped().Invoke( - mediator => mediator.Send(new GetRocket.Request { Id = rocket }) + mediator => mediator.Send(new GetRocket.Request(rocket)) ); response.Type.Should().Be(RocketType.Falcon9); @@ -41,7 +41,7 @@ public async Task Should_Get_A_Rocket() public async Task Should_Not_Get_A_Missing_Rocket() { Func action = () => ServiceProvider.WithScoped().Invoke( - mediator => mediator.Send(new GetRocket.Request { Id = RocketId.New() }) + mediator => mediator.Send(new GetRocket.Request(RocketId.New())) ); await action.Should().ThrowAsync(); diff --git a/test/Sample.Core.Tests/Rockets/ListRocketsTests.cs b/test/Sample.Core.Tests/Rockets/ListRocketsTests.cs index 77d6e9ebd..957214696 100644 --- a/test/Sample.Core.Tests/Rockets/ListRocketsTests.cs +++ b/test/Sample.Core.Tests/Rockets/ListRocketsTests.cs @@ -1,12 +1,12 @@ using MediatR; -using Microsoft.Extensions.Logging; using Rocket.Surgery.DependencyInjection; using Sample.Core.Domain; using Sample.Core.Operations.Rockets; +using Serilog.Events; namespace Sample.Core.Tests.Rockets; -public class ListRocketsTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogLevel.Trace) +public class ListRocketsTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogEventLevel.Verbose) { [Fact] public async Task Should_List_Rockets() diff --git a/test/Sample.Core.Tests/Rockets/RemoveRocketsTests.cs b/test/Sample.Core.Tests/Rockets/RemoveRocketsTests.cs index 549be9711..5c649855e 100644 --- a/test/Sample.Core.Tests/Rockets/RemoveRocketsTests.cs +++ b/test/Sample.Core.Tests/Rockets/RemoveRocketsTests.cs @@ -1,12 +1,12 @@ using MediatR; -using Microsoft.Extensions.Logging; using Rocket.Surgery.DependencyInjection; using Sample.Core.Domain; using Sample.Core.Operations.Rockets; +using Serilog.Events; namespace Sample.Core.Tests.Rockets; -public class RemoveRocketsTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogLevel.Trace) +public class RemoveRocketsTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogEventLevel.Verbose) { [Fact] public async Task Should_Remove_Rocket() @@ -25,7 +25,7 @@ public async Task Should_Remove_Rocket() ); await ServiceProvider.WithScoped().Invoke( - mediator => mediator.Send(new DeleteRocket.Request { Id = id }) + mediator => mediator.Send(new DeleteRocket.Request(id)) ); ServiceProvider.WithScoped().Invoke(c => c.Rockets.Should().BeEmpty()); diff --git a/test/Sample.Core.Tests/Rockets/UpdateRocketTests.cs b/test/Sample.Core.Tests/Rockets/UpdateRocketTests.cs index 80f0e7719..c12ee47dc 100644 --- a/test/Sample.Core.Tests/Rockets/UpdateRocketTests.cs +++ b/test/Sample.Core.Tests/Rockets/UpdateRocketTests.cs @@ -1,15 +1,15 @@ using MediatR; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Rocket.Surgery.DependencyInjection; using Sample.Core.Domain; using Sample.Core.Models; using Sample.Core.Operations.Rockets; +using Serilog.Events; using ValidationException = FluentValidation.ValidationException; namespace Sample.Core.Tests.Rockets; -public class UpdateRocketTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogLevel.Trace) +public class UpdateRocketTests(ITestOutputHelper outputHelper) : HandleTestHostBase(outputHelper, LogEventLevel.Verbose) { [Fact] public async Task Should_Update_A_Rocket() diff --git a/test/Sample.Core.Tests/Sample.Core.Tests.csproj b/test/Sample.Core.Tests/Sample.Core.Tests.csproj index 71806a311..a0211cc23 100644 --- a/test/Sample.Core.Tests/Sample.Core.Tests.csproj +++ b/test/Sample.Core.Tests/Sample.Core.Tests.csproj @@ -1,6 +1,6 @@  - net8.0 + net8.0;net9.0 diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/CustomizationTests.cs b/test/Sample.Graphql.Tests/FairyBread.Tests/CustomizationTests.cs deleted file mode 100644 index 3ea4c3279..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/CustomizationTests.cs +++ /dev/null @@ -1,121 +0,0 @@ -using FluentValidation; -using FluentValidation.Results; -using HotChocolate; -using HotChocolate.Execution; -using HotChocolate.Resolvers; -using HotChocolate.Types; -using Microsoft.Extensions.DependencyInjection; -using Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; -using IOperationResult = HotChocolate.Execution.IOperationResult; - -namespace FairyBread.Tests; - -public class CustomizationTests -{ - [Fact] - public async Task CustomValidationResultHandler_Works() - { - // Arrange - var executor = await GetRequestExecutorAsync(services => { services.AddSingleton(); }); - - // Act - var result = await executor.ExecuteAsync(Query) as IOperationResult; - - // Assert - Assert.NotNull(result); - Assert.NotNull(result!.Errors); - Assert.NotEmpty(result.Errors); - Assert.True(result.Errors!.All(e => e.Message == "lol")); - } - - [Fact] - public async Task CustomValidatorProvider_Works() - { - // Arrange - var executor = await GetRequestExecutorAsync(services => { services.AddSingleton(); }); - - // Act - var result = await executor.ExecuteAsync(Query); - - // Assert - await Verify(result); - } - - private const string Query = @"query { read(foo: { someInteger: 1, someString: ""hello"" }) }"; - - private static async Task GetRequestExecutorAsync( - Action preBuildProviderAction - ) - { - var services = new ServiceCollection(); - services.AddValidatorsFromAssemblyContaining(); - preBuildProviderAction?.Invoke(services); - - return await services - .AddGraphQL() - .AddQueryType() - .AddMutationType() - .AddFairyBread() - .BuildRequestExecutorAsync(); - } - - public class CustomValidationErrorsHandler : DefaultValidationErrorsHandler - { - protected override IErrorBuilder CreateErrorBuilder(IMiddlewareContext context, string argName, IValidator val, ValidationFailure failure) - { - return base - .CreateErrorBuilder(context, argName, val, failure) - .SetMessage("lol"); - } - } - - public class CustomValidatorProvider : DefaultValidatorProvider - { - public CustomValidatorProvider(IServiceProvider serviceProvider, IFairyBreadOptions options) - : base(null!) { } - - public override IEnumerable GetValidators(IMiddlewareContext context, IInputField argument) - { - return argument.RuntimeType == typeof(FooInputDto) - ? new ResolvedValidator[] { new(new CustomValidator()), } - : base.GetValidators(context, argument); - } - } - #pragma warning disable CA1822 // Mark members as static - public class QueryType - { - public string Read(FooInputDto foo) - { - return $"{foo};"; - } - } - - public class MutationType - { - public string Write(FooInputDto foo) - { - return $"{foo};"; - } - } - - public class FooInputDto - { - public int SomeInteger { get; set; } - - public string SomeString { get; set; } = ""; - - public override string ToString() - { - return $"SomeInteger: {SomeInteger}, " + $"SomeString: {SomeString}"; - } - } - - public class CustomValidator : AbstractValidator - { - public CustomValidator() - { - RuleFor(x => x.SomeInteger) - .GreaterThanOrEqualTo(999); - } - } -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/GeneralTests.cs b/test/Sample.Graphql.Tests/FairyBread.Tests/GeneralTests.cs deleted file mode 100644 index 453cedc88..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/GeneralTests.cs +++ /dev/null @@ -1,415 +0,0 @@ -using FluentValidation; -using HotChocolate.Execution; -using HotChocolate.Types; -using Microsoft.Extensions.DependencyInjection; -using Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -namespace FairyBread.Tests; - -public class GeneralTests -{ - [Fact] - public async Task Multi_TopLevelFields_And_MultiRuns_Works() - { - // Arrange - var executor = await GetRequestExecutorAsync(); - - var query = @" - query { - read(foo: { someInteger: -1, someString: ""hello"" }) - read(foo: { someInteger: -1, someString: ""hello"" }) - }"; - - // Act - var result1 = await executor.ExecuteAsync(query); - var result2 = await executor.ExecuteAsync(query); - var result3 = await executor.ExecuteAsync(query); - - - // Assert - await Verify(new { result1, result2, result3, }); - } - - [Fact] - public async Task Ignores_Null_Argument_Value() - { - // Arrange - var caseData = (CaseData)Cases().First()[0]; - var executor = await GetRequestExecutorAsync(); - - var query = "query { read(foo: " + caseData.FooInput + ") }"; - - // Act - var result = await executor.ExecuteAsync(query); - - // Assert - var verifySettings = new VerifySettings(); - await Verify(result, verifySettings); - } - - [Fact] - public async Task Doesnt_Call_Field_Resolver_If_Invalid() - { - // Arrange - var executor = await GetRequestExecutorAsync(); - - var query = @"query { someResolver(foo: { someInteger: -1, someString: ""hello"" }) }"; - - // Act - var result = await executor.ExecuteAsync(query); - - // Assert - Assert.False(Query.WasFieldResolverCalled); - await Verify(result); - } - - [Fact] - public async Task Should_Respect_ShouldValidateArgument_Option() - { - // Arrange - var executor = await GetRequestExecutorAsync( - options => - { - options.ShouldValidateArgument = (o, t, a) - => a.Parameter is { } p && p.ParameterType != typeof(FooInputDto); - } - ); - - var query = @"mutation { - write( - foo: { someInteger: -1, someString: ""hello"" }, - bar: { emailAddress: ""ben@lol.com"" }) }"; - - // Act - var result = await executor.ExecuteAsync(query); - - // Assert - await Verify(result); - } - - static GeneralTests() - { - VerifierSettings.NameForParameter(_ => _.CaseId); - } - - private static async Task GetRequestExecutorAsync( - Action? configureOptions = null, - Action? configureServices = null, - bool registerValidators = true - ) - { - var services = new ServiceCollection(); - configureServices?.Invoke(services); - - if (registerValidators) - { - services.AddValidator(); - services.AddValidator(); - services.AddValidator>(); - services.AddValidator(); - services.AddValidator(); - services.AddValidator(); - } - - var builder = services - .AddGraphQL() - .AddQueryType() - .AddMutationType() - .AddFairyBread(options => { configureOptions?.Invoke(options); }); - - return await builder - .BuildRequestExecutorAsync(); - } - - [Theory] - [MemberData(nameof(Cases))] - public async Task Query_Works(CaseData caseData) - { - // Arrange - var executor = await GetRequestExecutorAsync(); - - var query = "query { read(foo: " + caseData.FooInput + ", bar: " + caseData.BarInput + ") }"; - - // Act - var result = await executor.ExecuteAsync(query); - - // Assert - var verifySettings = new VerifySettings(); - verifySettings.UseParameters(caseData); - await Verify(result, verifySettings); - } - - [Theory] - [MemberData(nameof(Cases))] - public async Task Mutation_Works(CaseData caseData) - { - // Arrange - var executor = await GetRequestExecutorAsync(); - - var query = "mutation { write(foo: " + caseData.FooInput + ", bar: " + caseData.BarInput + ") }"; - - // Act - var result = await executor.ExecuteAsync(query); - - // Assert - var verifySettings = new VerifySettings(); - verifySettings.UseParameters(caseData); - await Verify(result, verifySettings); - } - - // TODO: Unit tests for: - // - cancellation - // - does adding validators after fairybread still work ok? - - public static IEnumerable Cases() - { - var caseId = 1; - yield return new object[] - { - // Happy days - new CaseData(caseId++, @"{ someInteger: 1, someString: ""hello"" }", @"{ emailAddress: ""ben@lol.com"" }"), - }; - yield return new object[] - { - // Sync error - new CaseData(caseId++, @"{ someInteger: -1, someString: ""hello"" }", @"{ emailAddress: ""ben@lol.com"" }"), - }; - yield return new object[] - { - // Async error - new CaseData(caseId++, @"{ someInteger: 1, someString: ""hello"" }", @"{ emailAddress: ""-1"" }"), - }; - yield return new object[] - { - // Multiple sync errors and async error - new CaseData(caseId++, @"{ someInteger: -1, someString: ""-1"" }", @"{ emailAddress: ""-1"" }"), - }; - } - - public class CaseData - { - public CaseData(int caseId, string fooInput, string barInput) - { - CaseId = caseId.ToString(); - FooInput = fooInput; - BarInput = barInput; - } - - public string CaseId { get; set; } - public string FooInput { get; set; } - public string BarInput { get; set; } - } - - [Theory] - [MemberData(nameof(CollectionCases))] - public async Task Query_Array_Works(CollectionCaseData caseData) - { - // Arrange - var executor = await GetRequestExecutorAsync(); - - var query = "query { readWithArrayArg(foos: " + caseData.FoosInput + ") }"; - - // Act - var result = await executor.ExecuteAsync(query); - - // Assert - var verifySettings = new VerifySettings(); - verifySettings.UseParameters(caseData.CaseId); - await Verify(result, verifySettings); - } - - [Theory] - [MemberData(nameof(CollectionCases))] - public async Task Query_List_Works(CollectionCaseData caseData) - { - // Arrange - var executor = await GetRequestExecutorAsync(); - - var query = "query { readWithListArg(foos: " + caseData.FoosInput + ") }"; - - // Act - var result = await executor.ExecuteAsync(query); - - // Assert - var verifySettings = new VerifySettings(); - verifySettings.UseParameters(caseData.CaseId); - await Verify(result, verifySettings); - } - - public static IEnumerable CollectionCases() - { - var caseId = 1; - yield return new object[] - { - // Happy days, implied array - new CollectionCaseData(caseId++, @"{ someInteger: 1, someString: ""hello"" }"), - }; - yield return new object[] - { - // Happy days, explicit array - new CollectionCaseData(caseId++, @"[{ someInteger: 1, someString: ""hello"" }]"), - }; - yield return new object[] - { - // Happy days, multiple items - new CollectionCaseData(caseId++, @"[{ someInteger: 1, someString: ""hello"" }, { someInteger: 1, someString: ""hello"" }]"), - }; - yield return new object[] - { - // Error, implied array - new CollectionCaseData(caseId++, @"{ someInteger: -1, someString: ""hello"" }"), - }; - yield return new object[] - { - // Error, explicit array - new CollectionCaseData(caseId++, @"[{ someInteger: -1, someString: ""hello"" }]"), - }; - yield return new object[] - { - // Error, multiple items - new CollectionCaseData(caseId++, @"[{ someInteger: -1, someString: ""hello"" }, { someInteger: -1, someString: ""hello"" }]"), - }; - } - - public class CollectionCaseData - { - public CollectionCaseData(int caseId, string foosInput) - { - CaseId = caseId.ToString(); - FoosInput = foosInput; - } - - public string CaseId { get; set; } - public string FoosInput { get; set; } - } - #pragma warning disable CA1822 // Mark members as static - public class Query - { - public static bool WasFieldResolverCalled { get; private set; } - - public string Read(FooInputDto foo, BarInputDto? bar) - { - return $"{foo}; {bar}"; - } - - public string ReadWithArrayArg(FooInputDto[] foos) - { - return string.Join(", ", foos.Select(f => f.ToString())); - } - - public string ReadWithListArg(List foos) - { - return string.Join(", ", foos.Select(f => f.ToString())); - } - - public string SomeResolver(FooInputDto foo, BarInputDto? bar) - { - WasFieldResolverCalled = true; - return $"{foo}; {bar}"; - } - - public string IntResolver(int count) - { - return count.ToString(); - } - - public string NullableIntResolver(int? count) - { - return count?.ToString() ?? "null"; - } - } - - public class Mutation - { - public string Write(FooInputDto foo, BarInputDto bar) - { - return $"{foo}; {bar}"; - } - } - - public class MyInput : InputObjectType - { - protected override void Configure(IInputObjectTypeDescriptor descriptor) - { - descriptor.BindFieldsImplicitly(); - } - } - - public class FooInputDto - { - public int SomeInteger { get; set; } - - public string SomeString { get; set; } = ""; - - public override string ToString() - { - return $"SomeInteger: {SomeInteger}, " + $"SomeString: {SomeString}"; - } - } - - public class FooInputDtoValidator : AbstractValidator - { - public FooInputDtoValidator() - { - RuleFor(x => x.SomeInteger).Equal(1); - RuleFor(x => x.SomeString).Equal("hello"); - } - } - - public class ArrayOfFooInputDtoValidator : AbstractValidator - { - public ArrayOfFooInputDtoValidator() - { - RuleForEach(x => x).SetValidator(new FooInputDtoValidator()); - } - } - - public class ListOfFooInputDtoValidator : AbstractValidator> - { - public ListOfFooInputDtoValidator() - { - RuleForEach(x => x).SetValidator(new FooInputDtoValidator()); - } - } - - public class BarInputDto - { - public string EmailAddress { get; set; } = ""; - - public override string ToString() - { - return $"EmailAddress: {EmailAddress}"; - } - } - - public abstract class BarInputDtoValidatorBase : AbstractValidator - { - public BarInputDtoValidatorBase() - { - RuleFor(x => x.EmailAddress).NotNull(); - } - } - - public class BarInputDtoValidator : BarInputDtoValidatorBase { } - - public class BarInputDtoAsyncValidator : AbstractValidator - { - public BarInputDtoAsyncValidator() - { - RuleFor(x => x.EmailAddress) - // TODO: Cancellation unit test - .MustAsync((val, _) => Task.FromResult(val == "ben@lol.com")); - } - } - - public class NullableIntValidator : AbstractValidator - { - public NullableIntValidator() - { - RuleFor(x => x) - //.Null() - .GreaterThan(0) - .When(x => x is { }); - } - } -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/IServiceCollectionExtensions.cs b/test/Sample.Graphql.Tests/FairyBread.Tests/IServiceCollectionExtensions.cs deleted file mode 100644 index 0785c4016..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using FluentValidation; -using Microsoft.Extensions.DependencyInjection; - -namespace FairyBread.Tests; - -public static class IServiceCollectionExtensions -{ - public static IServiceCollection AddValidator(this IServiceCollection services) - where TValidator : AbstractValidator - { - // Add the validator as its interface and its self as per - // how FV does it and so that FairyBread can search it for the registry (via interface) - // but instantiate it directly from the provider - // https://github.com/FluentValidation/FluentValidation/blob/0e45f4efbab956d84f425b0b7d207ada516720bd/src/FluentValidation.DependencyInjectionExtensions/ServiceCollectionExtensions.cs#L90 - return services - .AddTransient, TValidator>() - .AddTransient(); - } -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/InjectorTests.cs b/test/Sample.Graphql.Tests/FairyBread.Tests/InjectorTests.cs deleted file mode 100644 index 359945d4f..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/InjectorTests.cs +++ /dev/null @@ -1,478 +0,0 @@ -using FluentValidation; -using HotChocolate.Data; -using HotChocolate.Execution; -using HotChocolate.Types; -using Microsoft.Extensions.DependencyInjection; -using Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -namespace FairyBread.Tests; - -public class InjectorTests -{ - private static async Task GetRequestExecutorAsync( - Action? configureOptions = null, - Action? configureServices = null, - bool registerValidators = true - ) - { - var services = new ServiceCollection(); - configureServices?.Invoke(services); - - if (registerValidators) - { - services.AddValidator(); - services.AddValidator(); - services.AddValidator(); - services.AddValidator(); - services.AddValidator(); - services.AddValidator(); - services.AddValidator>(); - services.AddValidator>>(); - services.AddValidator(); - services.AddValidator(); - } - - var builder = services - .AddGraphQL() - .AddQueryType() - .AddType() - .AddSorting() - .AddFiltering() - .AddFairyBread(options => { configureOptions?.Invoke(options); }); - - return await builder - .BuildRequestExecutorAsync(); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task Works(bool registerValidators) - { - // Arrange - var executor = await GetRequestExecutorAsync( - registerValidators: registerValidators, - configureOptions: options => - { - if (!registerValidators) - { - options.ThrowIfNoValidatorsFound = false; - } - } - ); - - var query = "query { " - + "noArgs " - + "scalarArgsA(a: 0, b: false) " - + "scalarArgsB(a: 0, b: false) " - + "scalarArgsC(a: 0, b: false) " - + "scalarArgsD(a: 0, b: false) " - + "nullableScalarArgsA(a: 0, b: false) " - + "nullableScalarArgsB(a: 0, b: false) " - + "nullableScalarArgsC(a: 0, b: false) " - + "nullableScalarArgsD(a: 0, b: false) " - + "objectArgA(input: { a: 0, b: false }) " - + "objectArgB(input: { a: 0, b: false }) " - + "objectArgC(input: { a: 0, b: false }) " - + "objectArgD(input: { a: 0, b: false }) " - + "arrayArgA(items: [0, 0]) " - + "listArgA(items: [0, 0]) " - + "listArgB(items: [0, 0]) " - + "listArgC(items: [0, 0]) " - + "listArgD(items: [0, 0]) " - + "listOfListArgC(items: [[0, 0], [0, 0]]) " - + "filterSortAndPagingArgs(first: 10) { nodes { a } }" - + "}"; - - // Act - var result = await executor.ExecuteAsync(query); - - // Assert - var verifySettings = new VerifySettings(); - verifySettings.UseParameters(registerValidators); - await Verify(result, verifySettings); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task Should_Respect_ExplicitValidationAttributes(bool valid) - { - // Arrange - var executor = await GetRequestExecutorAsync(); - - var args = valid - ? @"fooInt: 1, - barInt: 1, - lolInt: 1, - fooInput: { a: 1, b: true }, - barInput: { a: 1, b: true }, - lolInput: { a: 1, b: true }, - dblInput: { a: 1, b: true }" - : @"fooInt: -1, - barInt: -1, - lolInt: -1, - fooInput: { a: 0, b: false }, - barInput: { a: 0, b: false }, - lolInput: { a: 0, b: false }, - dblInput: { a: 0, b: false }"; - - var query = @" - query { - readWithExplicitValidation(" - + args - + @") - }"; - - // Act - var result = await executor.ExecuteAsync(query); - - // Assert - await Verify(result).UseParameters(valid); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task Should_Respect_ExplicitValidationFluent(bool valid) - { - // Arrange - var executor = await GetRequestExecutorAsync(); - - var args = valid - ? @"fooInt: 1, - barInt: 1, - lolInt: 1, - fooInput: { a: 1, b: true }, - barInput: { a: 1, b: true }, - lolInput: { a: 1, b: true }, - dblInput: { a: 1, b: true }" - : @"fooInt: -1, - barInt: -1, - lolInt: -1, - fooInput: { a: 0, b: false }, - barInput: { a: 0, b: false }, - lolInput: { a: 0, b: false }, - dblInput: { a: 0, b: false }"; - - var query = @" - query { - readWithExplicitValidationFluent(" - + args - + @") - }"; - - // Act - var result = await executor.ExecuteAsync(query); - - // Assert - await Verify(result).UseParameters(valid); - } - #pragma warning disable CA1822 // Mark members as static - public class QueryI - { - public string NoArgs => "foo"; - - public string ScalarArgsA(int a, bool b) - { - return $"{a} | {b}"; - } - - public string NullableScalarArgsA(int? a, bool? b) - { - return $"{a} | {b}"; - } - - public string ObjectArgA(TestInput input) - { - return input.ToString(); - } - - public string ArrayArgA(int?[] items) - { - return string.Join(", ", items); - } - - public string ListArgA(List items) - { - return string.Join(", ", items); - } - - [UsePaging] - [UseFiltering] - [UseSorting] - public IEnumerable GetFilterSortAndPagingArgs() - { - return new FooI[] { new(), }; - } - - public string ReadWithExplicitValidation( - // Should validate explicitly - [Validate(typeof(PositiveIntValidator))] - int fooInt, - // Shouldn't validate implicitly - [Validate(typeof(PositiveIntValidator))] [DontValidateImplicitly] - int barInt, - // Shouldn't validate - [Validate(typeof(PositiveIntValidator))] [DontValidate] - int lolInt, - // Should validate explicitly - [Validate(typeof(TestInputExplicitValidator))] - TestInput fooInput, - // Shouldn't validate implicitly - [Validate(typeof(TestInputExplicitValidator))] [DontValidateImplicitly] - TestInput barInput, - // Shouldn't validate - [Validate(typeof(TestInputExplicitValidator))] [DontValidate] - TestInput lolInput, - // Shouldn't add an implicitly added validator again - [Validate(typeof(TestInputValidator))] - TestInput dblInput - ) - { - return $"{fooInt} {barInt} {lolInt} {fooInput} {barInput} {lolInput}"; - } - } - - public class QueryIType - : ObjectType - { - protected override void Configure(IObjectTypeDescriptor descriptor) - { - descriptor - .Field("scalarArgsB") - .Argument("a", arg => arg.Type>()) - .Argument("b", arg => arg.Type>()) - .Type() - .ResolveWith(x => x.ScalarArgsBResolver(default, default)); - - descriptor - .Field("scalarArgsC") - .Argument("a", arg => arg.Type>()) - .Argument("b", arg => arg.Type>()) - .Type() - .Resolve(ctx => "hello"); - - descriptor - .Field("scalarArgsD") - .Argument("a", arg => arg.Type(typeof(int))) - .Argument("b", arg => arg.Type(typeof(bool))) - .Type() - .Resolve(ctx => "hello"); - - descriptor - .Field("nullableScalarArgsB") - .Argument("a", arg => arg.Type()) - .Argument("b", arg => arg.Type()) - .Type() - .ResolveWith(x => x.NullableScalarArgsBResolver(default, default)); - - descriptor - .Field("nullableScalarArgsC") - .Argument("a", arg => arg.Type()) - .Argument("b", arg => arg.Type()) - .Type() - .Resolve(ctx => "hello"); - - descriptor - .Field("nullableScalarArgsD") - .Argument("a", arg => arg.Type(typeof(int?))) - .Argument("b", arg => arg.Type(typeof(bool?))) - .Type() - .Resolve(ctx => "hello"); - - descriptor - .Field("objectArgB") - .Argument("input", arg => arg.Type()) - .Type() - .ResolveWith(x => x.ObjArgResolver(default!)); - - descriptor - .Field("objectArgC") - .Argument("input", arg => arg.Type()) - .Type() - .Resolve(ctx => "hello"); - - descriptor - .Field("objectArgD") - .Argument("input", arg => arg.Type(typeof(TestInput))) - .Type() - .Resolve(ctx => "hello"); - - descriptor - .Field("listArgB") - .Argument("items", arg => arg.Type>>()) - .Type() - .ResolveWith(x => x.ListArgResolver(default!)); - - descriptor - .Field("listArgC") - .Argument("items", arg => arg.Type>>()) - .Type() - .Resolve(ctx => "hello"); - - descriptor - .Field("listArgD") - .Argument("items", arg => arg.Type(typeof(List))) - .Type() - .Resolve(ctx => "hello"); - - descriptor - .Field("listOfListArgC") - .Argument("items", arg => arg.Type>>>>()) - .Type() - .Resolve(ctx => "hello"); - - descriptor - .Field("readWithExplicitValidationFluent") - // Should validate explicitly - .Argument("fooInt", arg => arg.Type().ValidateWith()) - // Shouldn't validate implicitly - .Argument("barInt", arg => arg.Type().ValidateWith().DontValidateImplicitly()) - // Shouldn't validate - .Argument("lolInt", arg => arg.Type().ValidateWith().DontValidate()) - // Should validate explicitly - .Argument("fooInput", arg => arg.Type().ValidateWith()) - // Shouldn't validate implicitly - .Argument("barInput", arg => arg.Type().ValidateWith().DontValidateImplicitly()) - // Shouldn't validate - .Argument("lolInput", arg => arg.Type().ValidateWith().DontValidate()) - // Shouldn't add an implicitly added validator again - .Argument("dblInput", arg => arg.Type().ValidateWith()) - .ResolveWith( - q => q.ReadWithExplicitValidation( - default, - default, - default, - default!, - default!, - default!, - default! - ) - ); - } - - public string ScalarArgsBResolver(int a, bool b) - { - return $"{a} | {b}"; - } - - public string NullableScalarArgsBResolver(int? a, bool? b) - { - return $"{a?.ToString() ?? "null"} | {b?.ToString() ?? "null"}"; - } - - public string ObjArgResolver(TestInput input) - { - return input.ToString(); - } - - public string ListArgResolver(List items) - { - return string.Join(",", items); - } - } - - public class FooI - { - public string A { get; set; } = "A"; - } - - public class TestInput - { - public int A { get; set; } - - public bool B { get; set; } - - public override string ToString() - { - return $"{A} | {B}"; - } - } - - public class TestInputType : InputObjectType { } - - public class IntValidator : AbstractValidator - { - public IntValidator() - { - RuleFor(x => x).NotEmpty(); - } - } - - public class NullableIntValidator : AbstractValidator - { - public NullableIntValidator() - { - RuleFor(x => x).NotEmpty().GreaterThan(0); - } - } - - public class BoolValidator : AbstractValidator - { - public BoolValidator() - { - RuleFor(x => x).NotEmpty(); - } - } - - public class NullableBoolValidator : AbstractValidator - { - public NullableBoolValidator() - { - RuleFor(x => x).NotEmpty().Equal(true); - } - } - - public class TestInputValidator : AbstractValidator - { - public TestInputValidator() - { - RuleFor(x => x.A).NotEmpty(); - RuleFor(x => x.B).NotEmpty(); - } - } - - public class TestInputExplicitValidator : AbstractValidator, IExplicitUsageOnlyValidator - { - public TestInputExplicitValidator() - { - RuleFor(x => x.A).NotNull().GreaterThan(0).WithMessage("Explicit validator error msg."); - } - } - - public class ArrayOfNullableIntValidator : AbstractValidator - { - public ArrayOfNullableIntValidator() - { - RuleForEach(x => x).NotEmpty().GreaterThan(0); - } - } - - public class ListOfNullableIntValidator : AbstractValidator> - { - public ListOfNullableIntValidator() - { - RuleForEach(x => x).NotEmpty().GreaterThan(0); - } - } - - public class ListOfListOfNullableIntValidator : AbstractValidator>> - { - public ListOfListOfNullableIntValidator( - ListOfNullableIntValidator innerValidator - ) - { - RuleForEach(x => x) - .SetValidator(innerValidator); - } - } - - public class PositiveIntValidator : AbstractValidator, IExplicitUsageOnlyValidator - { - public PositiveIntValidator() - { - RuleFor(x => x).NotNull().GreaterThan(0); - } - } -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/RequiresOwnScopeValidatorTests.cs b/test/Sample.Graphql.Tests/FairyBread.Tests/RequiresOwnScopeValidatorTests.cs deleted file mode 100644 index 5db89a79a..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/RequiresOwnScopeValidatorTests.cs +++ /dev/null @@ -1,191 +0,0 @@ -using FakeItEasy; -using FluentValidation; -using HotChocolate.Execution; -using HotChocolate.Resolvers; -using HotChocolate.Types; -using Microsoft.Extensions.DependencyInjection; -using Rocket.Surgery.LaunchPad.HotChocolate.FairyBread; - -namespace FairyBread.Tests; - -public class RequiresOwnScopeValidatorTests -{ - [Fact] - public async Task OwnScopes_Work() - { - // Arrange - var executor = await GetRequestExecutorAsync(services => { services.AddScoped(); }); - - // Act - var result = await executor.ExecuteAsync(Query); - - // Assert - await Verify(result); - } - - [Fact] - public async Task OwnScopes_Are_Disposed() - { - // Arrange - var scopeMock = A.Fake(); - A.CallTo(() => scopeMock.Dispose()).DoesNothing(); - - var executor = await GetRequestExecutorAsync( - services => - { - services.AddScoped( - sp => - new ScopeMockingValidatorProvider(sp.GetRequiredService(), scopeMock) - ); - } - ); - - // Act - var result = await executor.ExecuteAsync(Query); - - // Assert - A.CallTo(() => scopeMock.Dispose()).MustHaveHappenedOnceExactly(); - await Verify(result); - } - - private const string Query = @"query { read(foo: { someInteger: 1, someString: ""hello"" }) }"; - - private static async Task GetRequestExecutorAsync(Action preBuildProviderAction) - { - var services = new ServiceCollection(); - - services.AddValidator(); - services.AddValidator(); - services.AddValidator(); - services.AddValidator(); - - preBuildProviderAction?.Invoke(services); - - return await services - .AddGraphQL() - .AddQueryType() - .AddMutationType() - .AddFairyBread() - .BuildRequestExecutorAsync(); - } - - public class AssertingScopageValidatorProvider : DefaultValidatorProvider - { - public AssertingScopageValidatorProvider(IValidatorRegistry validatorRegistry) - : base(validatorRegistry) { } - - public override IEnumerable GetValidators(IMiddlewareContext context, IInputField argument) - { - var validators = base.GetValidators(context, argument).ToList(); - - var standardValidator = validators.Single(v => v.Validator is StandardValidator); - var anotherStandardValidator = validators.Single(v => v.Validator is AnotherStandardValidator); - Assert.Null(standardValidator.Scope); - Assert.Null(anotherStandardValidator.Scope); - Assert.Equal(standardValidator.Scope, anotherStandardValidator.Scope); - - var ownScopeValidator = validators.Single(v => v.Validator is RequiresOwnScopeValidator); - Assert.NotNull(ownScopeValidator.Scope); - Assert.NotEqual(standardValidator.Scope, ownScopeValidator.Scope); - - var anotherOwnScopeValidator = validators.Single(v => v.Validator is AnotherRequiresOwnScopeValidator); - Assert.NotNull(anotherOwnScopeValidator.Scope); - Assert.NotEqual(standardValidator.Scope, anotherOwnScopeValidator.Scope); - - Assert.NotEqual(ownScopeValidator.Scope, anotherOwnScopeValidator.Scope); - - return validators; - } - } - - public class ScopeMockingValidatorProvider : DefaultValidatorProvider - { - private readonly IServiceScope _mockScope; - - public ScopeMockingValidatorProvider( - IValidatorRegistry validatorRegistry, - IServiceScope mockScope - ) - : base(validatorRegistry) - { - _mockScope = mockScope; - } - - public override IEnumerable GetValidators( - IMiddlewareContext context, - IInputField argument - ) - { - yield return new( - new RequiresOwnScopeValidator(), - _mockScope - ); - } - } - #pragma warning disable CA1822 // Mark members as static - public class QueryType - { - public string Read(FooInputDto foo) - { - return $"{foo};"; - } - } - - public class MutationType - { - public string Write(FooInputDto foo) - { - return $"{foo};"; - } - } - - public class FooInputDto - { - public int SomeInteger { get; set; } - - public string SomeString { get; set; } = ""; - - public override string ToString() - { - return $"SomeInteger: {SomeInteger}, " + $"SomeString: {SomeString}"; - } - } - - public class StandardValidator : AbstractValidator - { - public StandardValidator() - { - RuleFor(x => x.SomeInteger) - .GreaterThanOrEqualTo(50); - } - } - - public class AnotherStandardValidator : AbstractValidator - { - public AnotherStandardValidator() - { - RuleFor(x => x.SomeInteger) - .GreaterThanOrEqualTo(100); - } - } - - public class RequiresOwnScopeValidator - : AbstractValidator, IRequiresOwnScopeValidator - { - public RequiresOwnScopeValidator() - { - RuleFor(x => x.SomeInteger) - .GreaterThanOrEqualTo(999); - } - } - - public class AnotherRequiresOwnScopeValidator - : AbstractValidator, IRequiresOwnScopeValidator - { - public AnotherRequiresOwnScopeValidator() - { - RuleFor(x => x.SomeInteger) - .GreaterThanOrEqualTo(9999); - } - } -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/CustomizationTests.CustomValidatorProvider_Works.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/CustomizationTests.CustomValidatorProvider_Works.verified.txt deleted file mode 100644 index 67e4d05d3..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/CustomizationTests.CustomValidatorProvider_Works.verified.txt +++ /dev/null @@ -1,40 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be greater than or equal to '999'., - Code: FairyBread_ValidationError, - Path: { - Name: read, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foo, - attemptedValue: 1, - code: FairyBread_ValidationError, - errorCode: GreaterThanOrEqualValidator, - errorMessage: 'Some Integer' must be greater than or equal to '999'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 999, - PropertyName: Some Integer, - PropertyPath: SomeInteger, - PropertyValue: 1 - }, - propertyName: SomeInteger, - severity: Error, - validatorName: CustomValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Doesnt_Call_Field_Resolver_If_Invalid.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Doesnt_Call_Field_Resolver_If_Invalid.verified.txt deleted file mode 100644 index 5dc3e14fd..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Doesnt_Call_Field_Resolver_If_Invalid.verified.txt +++ /dev/null @@ -1,40 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: someResolver, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foo, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: SomeInteger, - PropertyValue: -1 - }, - propertyName: SomeInteger, - severity: Error, - validatorName: FooInputDtoValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Ignores_Null_Argument_Value.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Ignores_Null_Argument_Value.verified.txt deleted file mode 100644 index 9ab76f96f..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Ignores_Null_Argument_Value.verified.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - Data: { - read: SomeInteger: 1, SomeString: hello; - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Multi_TopLevelFields_And_MultiRuns_Works.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Multi_TopLevelFields_And_MultiRuns_Works.verified.txt deleted file mode 100644 index a2f07342e..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Multi_TopLevelFields_And_MultiRuns_Works.verified.txt +++ /dev/null @@ -1,122 +0,0 @@ -{ - result1: { - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: read, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: foo, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: SomeInteger, - PropertyValue: -1 - }, - propertyName: SomeInteger, - severity: Error, - validatorName: FooInputDtoValidator - } - } - ], - IsDataSet: true - }, - result2: { - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: read, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: foo, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: SomeInteger, - PropertyValue: -1 - }, - propertyName: SomeInteger, - severity: Error, - validatorName: FooInputDtoValidator - } - } - ], - IsDataSet: true - }, - result3: { - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: read, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: foo, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: SomeInteger, - PropertyValue: -1 - }, - propertyName: SomeInteger, - severity: Error, - validatorName: FooInputDtoValidator - } - } - ], - IsDataSet: true - } -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Mutation_Works_caseData=1.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Mutation_Works_caseData=1.verified.txt deleted file mode 100644 index f171f43cc..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Mutation_Works_caseData=1.verified.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - Data: { - write: SomeInteger: 1, SomeString: hello; EmailAddress: ben@lol.com - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Mutation_Works_caseData=2.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Mutation_Works_caseData=2.verified.txt deleted file mode 100644 index 208807cc8..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Mutation_Works_caseData=2.verified.txt +++ /dev/null @@ -1,40 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: write, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 12 - } - ], - Extensions: { - argumentName: foo, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: SomeInteger, - PropertyValue: -1 - }, - propertyName: SomeInteger, - severity: Error, - validatorName: FooInputDtoValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Mutation_Works_caseData=3.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Mutation_Works_caseData=3.verified.txt deleted file mode 100644 index 03d15a0e5..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Mutation_Works_caseData=3.verified.txt +++ /dev/null @@ -1,38 +0,0 @@ -{ - Errors: [ - { - Message: The specified condition was not met for 'Email Address'., - Code: FairyBread_ValidationError, - Path: { - Name: write, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 12 - } - ], - Extensions: { - argumentName: bar, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: AsyncPredicateValidator, - errorMessage: The specified condition was not met for 'Email Address'., - formattedMessagePlaceholderValues: { - PropertyName: Email Address, - PropertyPath: EmailAddress, - PropertyValue: -1 - }, - propertyName: EmailAddress, - severity: Error, - validatorName: BarInputDtoAsyncValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Mutation_Works_caseData=4.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Mutation_Works_caseData=4.verified.txt deleted file mode 100644 index 9245b176e..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Mutation_Works_caseData=4.verified.txt +++ /dev/null @@ -1,108 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: write, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 12 - } - ], - Extensions: { - argumentName: foo, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: SomeInteger, - PropertyValue: -1 - }, - propertyName: SomeInteger, - severity: Error, - validatorName: FooInputDtoValidator - } - }, - { - Message: 'Some String' must be equal to 'hello'., - Code: FairyBread_ValidationError, - Path: { - Name: write, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 12 - } - ], - Extensions: { - argumentName: foo, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some String' must be equal to 'hello'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: hello, - PropertyName: Some String, - PropertyPath: SomeString, - PropertyValue: -1 - }, - propertyName: SomeString, - severity: Error, - validatorName: FooInputDtoValidator - } - }, - { - Message: The specified condition was not met for 'Email Address'., - Code: FairyBread_ValidationError, - Path: { - Name: write, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 12 - } - ], - Extensions: { - argumentName: bar, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: AsyncPredicateValidator, - errorMessage: The specified condition was not met for 'Email Address'., - formattedMessagePlaceholderValues: { - PropertyName: Email Address, - PropertyPath: EmailAddress, - PropertyValue: -1 - }, - propertyName: EmailAddress, - severity: Error, - validatorName: BarInputDtoAsyncValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=1.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=1.verified.txt deleted file mode 100644 index 393d07ad8..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=1.verified.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - Data: { - readWithArrayArg: SomeInteger: 1, SomeString: hello - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=2.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=2.verified.txt deleted file mode 100644 index 393d07ad8..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=2.verified.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - Data: { - readWithArrayArg: SomeInteger: 1, SomeString: hello - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=3.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=3.verified.txt deleted file mode 100644 index 8466fed99..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=3.verified.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - Data: { - readWithArrayArg: SomeInteger: 1, SomeString: hello, SomeInteger: 1, SomeString: hello - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=4.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=4.verified.txt deleted file mode 100644 index f4636cee5..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=4.verified.txt +++ /dev/null @@ -1,41 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithArrayArg, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foos, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: x[0].SomeInteger, - PropertyValue: -1 - }, - propertyName: x[0].SomeInteger, - severity: Error, - validatorName: ArrayOfFooInputDtoValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=5.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=5.verified.txt deleted file mode 100644 index f4636cee5..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=5.verified.txt +++ /dev/null @@ -1,41 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithArrayArg, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foos, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: x[0].SomeInteger, - PropertyValue: -1 - }, - propertyName: x[0].SomeInteger, - severity: Error, - validatorName: ArrayOfFooInputDtoValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=6.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=6.verified.txt deleted file mode 100644 index 630414d76..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Array_Works_caseData=6.verified.txt +++ /dev/null @@ -1,77 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithArrayArg, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foos, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: x[0].SomeInteger, - PropertyValue: -1 - }, - propertyName: x[0].SomeInteger, - severity: Error, - validatorName: ArrayOfFooInputDtoValidator - } - }, - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithArrayArg, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foos, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - CollectionIndex: 1, - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: x[1].SomeInteger, - PropertyValue: -1 - }, - propertyName: x[1].SomeInteger, - severity: Error, - validatorName: ArrayOfFooInputDtoValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=1.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=1.verified.txt deleted file mode 100644 index dc9e55a74..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=1.verified.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - Data: { - readWithListArg: SomeInteger: 1, SomeString: hello - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=2.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=2.verified.txt deleted file mode 100644 index dc9e55a74..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=2.verified.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - Data: { - readWithListArg: SomeInteger: 1, SomeString: hello - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=3.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=3.verified.txt deleted file mode 100644 index 2e84894d6..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=3.verified.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - Data: { - readWithListArg: SomeInteger: 1, SomeString: hello, SomeInteger: 1, SomeString: hello - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=4.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=4.verified.txt deleted file mode 100644 index d80f9d1a9..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=4.verified.txt +++ /dev/null @@ -1,41 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithListArg, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foos, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: x[0].SomeInteger, - PropertyValue: -1 - }, - propertyName: x[0].SomeInteger, - severity: Error, - validatorName: ListOfFooInputDtoValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=5.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=5.verified.txt deleted file mode 100644 index d80f9d1a9..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=5.verified.txt +++ /dev/null @@ -1,41 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithListArg, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foos, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: x[0].SomeInteger, - PropertyValue: -1 - }, - propertyName: x[0].SomeInteger, - severity: Error, - validatorName: ListOfFooInputDtoValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=6.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=6.verified.txt deleted file mode 100644 index 289394c9b..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_List_Works_caseData=6.verified.txt +++ /dev/null @@ -1,77 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithListArg, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foos, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: x[0].SomeInteger, - PropertyValue: -1 - }, - propertyName: x[0].SomeInteger, - severity: Error, - validatorName: ListOfFooInputDtoValidator - } - }, - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithListArg, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foos, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - CollectionIndex: 1, - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: x[1].SomeInteger, - PropertyValue: -1 - }, - propertyName: x[1].SomeInteger, - severity: Error, - validatorName: ListOfFooInputDtoValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Works_caseData=1.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Works_caseData=1.verified.txt deleted file mode 100644 index 484071268..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Works_caseData=1.verified.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - Data: { - read: SomeInteger: 1, SomeString: hello; EmailAddress: ben@lol.com - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Works_caseData=2.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Works_caseData=2.verified.txt deleted file mode 100644 index c688074e5..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Works_caseData=2.verified.txt +++ /dev/null @@ -1,40 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: read, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foo, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: SomeInteger, - PropertyValue: -1 - }, - propertyName: SomeInteger, - severity: Error, - validatorName: FooInputDtoValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Works_caseData=3.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Works_caseData=3.verified.txt deleted file mode 100644 index c98f6686c..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Works_caseData=3.verified.txt +++ /dev/null @@ -1,38 +0,0 @@ -{ - Errors: [ - { - Message: The specified condition was not met for 'Email Address'., - Code: FairyBread_ValidationError, - Path: { - Name: read, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: bar, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: AsyncPredicateValidator, - errorMessage: The specified condition was not met for 'Email Address'., - formattedMessagePlaceholderValues: { - PropertyName: Email Address, - PropertyPath: EmailAddress, - PropertyValue: -1 - }, - propertyName: EmailAddress, - severity: Error, - validatorName: BarInputDtoAsyncValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Works_caseData=4.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Works_caseData=4.verified.txt deleted file mode 100644 index fcf1eb210..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Query_Works_caseData=4.verified.txt +++ /dev/null @@ -1,108 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be equal to '1'., - Code: FairyBread_ValidationError, - Path: { - Name: read, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foo, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some Integer' must be equal to '1'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 1, - PropertyName: Some Integer, - PropertyPath: SomeInteger, - PropertyValue: -1 - }, - propertyName: SomeInteger, - severity: Error, - validatorName: FooInputDtoValidator - } - }, - { - Message: 'Some String' must be equal to 'hello'., - Code: FairyBread_ValidationError, - Path: { - Name: read, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foo, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: 'Some String' must be equal to 'hello'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: hello, - PropertyName: Some String, - PropertyPath: SomeString, - PropertyValue: -1 - }, - propertyName: SomeString, - severity: Error, - validatorName: FooInputDtoValidator - } - }, - { - Message: The specified condition was not met for 'Email Address'., - Code: FairyBread_ValidationError, - Path: { - Name: read, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: bar, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: AsyncPredicateValidator, - errorMessage: The specified condition was not met for 'Email Address'., - formattedMessagePlaceholderValues: { - PropertyName: Email Address, - PropertyPath: EmailAddress, - PropertyValue: -1 - }, - propertyName: EmailAddress, - severity: Error, - validatorName: BarInputDtoAsyncValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Should_Respect_ShouldValidateArgument_Option.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Should_Respect_ShouldValidateArgument_Option.verified.txt deleted file mode 100644 index 84d6eb7c3..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/GeneralTests.Should_Respect_ShouldValidateArgument_Option.verified.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - Data: { - write: SomeInteger: -1, SomeString: hello; EmailAddress: ben@lol.com - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Should_Respect_ExplicitValidationAttributes_valid=False.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Should_Respect_ExplicitValidationAttributes_valid=False.verified.txt deleted file mode 100644 index a262aaa67..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Should_Respect_ExplicitValidationAttributes_valid=False.verified.txt +++ /dev/null @@ -1,313 +0,0 @@ -{ - Errors: [ - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidation, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: fooInt, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: -1 - }, - severity: Error, - validatorName: PositiveIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidation, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: fooInt, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: -1 - }, - severity: Error, - validatorName: PositiveIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidation, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: barInt, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: -1 - }, - severity: Error, - validatorName: PositiveIntValidator - } - }, - { - Message: Explicit validator error msg., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidation, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: fooInput, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: Explicit validator error msg., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputExplicitValidator - } - }, - { - Message: Explicit validator error msg., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidation, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: fooInput, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: Explicit validator error msg., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputExplicitValidator - } - }, - { - Message: Explicit validator error msg., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidation, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: barInput, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: Explicit validator error msg., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputExplicitValidator - } - }, - { - Message: Explicit validator error msg., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidation, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: dblInput, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: Explicit validator error msg., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputExplicitValidator - } - }, - { - Message: 'A' must not be empty., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidation, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: dblInput, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: NotEmptyValidator, - errorMessage: 'A' must not be empty., - formattedMessagePlaceholderValues: { - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputValidator - } - }, - { - Message: 'B' must not be empty., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidation, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: dblInput, - attemptedValue: false, - code: FairyBread_ValidationError, - errorCode: NotEmptyValidator, - errorMessage: 'B' must not be empty., - formattedMessagePlaceholderValues: { - PropertyName: B, - PropertyPath: B, - PropertyValue: false - }, - propertyName: B, - severity: Error, - validatorName: TestInputValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Should_Respect_ExplicitValidationAttributes_valid=True.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Should_Respect_ExplicitValidationAttributes_valid=True.verified.txt deleted file mode 100644 index e094a7aba..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Should_Respect_ExplicitValidationAttributes_valid=True.verified.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - Data: { - readWithExplicitValidation: 1 1 1 1 | True 1 | True 1 | True - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Should_Respect_ExplicitValidationFluent_valid=False.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Should_Respect_ExplicitValidationFluent_valid=False.verified.txt deleted file mode 100644 index c108b144d..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Should_Respect_ExplicitValidationFluent_valid=False.verified.txt +++ /dev/null @@ -1,313 +0,0 @@ -{ - Errors: [ - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidationFluent, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: fooInt, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: -1 - }, - severity: Error, - validatorName: NullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidationFluent, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: fooInt, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: -1 - }, - severity: Error, - validatorName: PositiveIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidationFluent, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: barInt, - attemptedValue: -1, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: -1 - }, - severity: Error, - validatorName: PositiveIntValidator - } - }, - { - Message: Explicit validator error msg., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidationFluent, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: fooInput, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: Explicit validator error msg., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputExplicitValidator - } - }, - { - Message: Explicit validator error msg., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidationFluent, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: fooInput, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: Explicit validator error msg., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputExplicitValidator - } - }, - { - Message: Explicit validator error msg., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidationFluent, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: barInput, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: Explicit validator error msg., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputExplicitValidator - } - }, - { - Message: Explicit validator error msg., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidationFluent, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: dblInput, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: Explicit validator error msg., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputExplicitValidator - } - }, - { - Message: 'A' must not be empty., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidationFluent, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: dblInput, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: NotEmptyValidator, - errorMessage: 'A' must not be empty., - formattedMessagePlaceholderValues: { - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputValidator - } - }, - { - Message: 'B' must not be empty., - Code: FairyBread_ValidationError, - Path: { - Name: readWithExplicitValidationFluent, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 3, - Column: 21 - } - ], - Extensions: { - argumentName: dblInput, - attemptedValue: false, - code: FairyBread_ValidationError, - errorCode: NotEmptyValidator, - errorMessage: 'B' must not be empty., - formattedMessagePlaceholderValues: { - PropertyName: B, - PropertyPath: B, - PropertyValue: false - }, - propertyName: B, - severity: Error, - validatorName: TestInputValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Should_Respect_ExplicitValidationFluent_valid=True.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Should_Respect_ExplicitValidationFluent_valid=True.verified.txt deleted file mode 100644 index 3af78f0fd..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Should_Respect_ExplicitValidationFluent_valid=True.verified.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - Data: { - readWithExplicitValidationFluent: 1 1 1 1 | True 1 | True 1 | True - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Works_registerValidators=False.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Works_registerValidators=False.verified.txt deleted file mode 100644 index 2ca968f69..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Works_registerValidators=False.verified.txt +++ /dev/null @@ -1,31 +0,0 @@ -{ - Data: { - arrayArgA: 0, 0, - filterSortAndPagingArgs: { - nodes: [ - { - a: A - } - ] - }, - listArgA: 0, 0, - listArgB: 0,0, - listArgC: hello, - listArgD: hello, - listOfListArgC: hello, - noArgs: foo, - nullableScalarArgsA: 0 | False, - nullableScalarArgsB: 0 | False, - nullableScalarArgsC: hello, - nullableScalarArgsD: hello, - objectArgA: 0 | False, - objectArgB: 0 | False, - objectArgC: hello, - objectArgD: hello, - scalarArgsA: 0 | False, - scalarArgsB: 0 | False, - scalarArgsC: hello, - scalarArgsD: hello - }, - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Works_registerValidators=True.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Works_registerValidators=True.verified.txt deleted file mode 100644 index 9629db8c6..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/InjectorTests.Works_registerValidators=True.verified.txt +++ /dev/null @@ -1,1185 +0,0 @@ -{ - Errors: [ - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: scalarArgsA, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 16 - } - ], - Extensions: { - argumentName: a, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: 0 - }, - severity: Error, - validatorName: PositiveIntValidator - } - }, - { - Message: '' must not be empty., - Code: FairyBread_ValidationError, - Path: { - Name: scalarArgsA, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 16 - } - ], - Extensions: { - argumentName: b, - attemptedValue: false, - code: FairyBread_ValidationError, - errorCode: NotEmptyValidator, - errorMessage: '' must not be empty., - formattedMessagePlaceholderValues: { - PropertyName: null, - PropertyPath: , - PropertyValue: false - }, - severity: Error, - validatorName: BoolValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: scalarArgsB, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 44 - } - ], - Extensions: { - argumentName: a, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: 0 - }, - severity: Error, - validatorName: PositiveIntValidator - } - }, - { - Message: '' must not be empty., - Code: FairyBread_ValidationError, - Path: { - Name: scalarArgsB, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 44 - } - ], - Extensions: { - argumentName: b, - attemptedValue: false, - code: FairyBread_ValidationError, - errorCode: NotEmptyValidator, - errorMessage: '' must not be empty., - formattedMessagePlaceholderValues: { - PropertyName: null, - PropertyPath: , - PropertyValue: false - }, - severity: Error, - validatorName: BoolValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: scalarArgsC, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 72 - } - ], - Extensions: { - argumentName: a, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: 0 - }, - severity: Error, - validatorName: PositiveIntValidator - } - }, - { - Message: '' must not be empty., - Code: FairyBread_ValidationError, - Path: { - Name: scalarArgsC, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 72 - } - ], - Extensions: { - argumentName: b, - attemptedValue: false, - code: FairyBread_ValidationError, - errorCode: NotEmptyValidator, - errorMessage: '' must not be empty., - formattedMessagePlaceholderValues: { - PropertyName: null, - PropertyPath: , - PropertyValue: false - }, - severity: Error, - validatorName: BoolValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: scalarArgsD, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 100 - } - ], - Extensions: { - argumentName: a, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: 0 - }, - severity: Error, - validatorName: PositiveIntValidator - } - }, - { - Message: '' must not be empty., - Code: FairyBread_ValidationError, - Path: { - Name: scalarArgsD, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 100 - } - ], - Extensions: { - argumentName: b, - attemptedValue: false, - code: FairyBread_ValidationError, - errorCode: NotEmptyValidator, - errorMessage: '' must not be empty., - formattedMessagePlaceholderValues: { - PropertyName: null, - PropertyPath: , - PropertyValue: false - }, - severity: Error, - validatorName: BoolValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: nullableScalarArgsA, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 128 - } - ], - Extensions: { - argumentName: a, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: 0 - }, - severity: Error, - validatorName: NullableIntValidator - } - }, - { - Message: '' must be equal to 'True'., - Code: FairyBread_ValidationError, - Path: { - Name: nullableScalarArgsA, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 128 - } - ], - Extensions: { - argumentName: b, - attemptedValue: false, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: '' must be equal to 'True'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: true, - PropertyName: null, - PropertyPath: , - PropertyValue: false - }, - severity: Error, - validatorName: NullableBoolValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: nullableScalarArgsB, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 164 - } - ], - Extensions: { - argumentName: a, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: 0 - }, - severity: Error, - validatorName: NullableIntValidator - } - }, - { - Message: '' must be equal to 'True'., - Code: FairyBread_ValidationError, - Path: { - Name: nullableScalarArgsB, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 164 - } - ], - Extensions: { - argumentName: b, - attemptedValue: false, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: '' must be equal to 'True'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: true, - PropertyName: null, - PropertyPath: , - PropertyValue: false - }, - severity: Error, - validatorName: NullableBoolValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: nullableScalarArgsC, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 200 - } - ], - Extensions: { - argumentName: a, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: 0 - }, - severity: Error, - validatorName: NullableIntValidator - } - }, - { - Message: '' must be equal to 'True'., - Code: FairyBread_ValidationError, - Path: { - Name: nullableScalarArgsC, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 200 - } - ], - Extensions: { - argumentName: b, - attemptedValue: false, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: '' must be equal to 'True'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: true, - PropertyName: null, - PropertyPath: , - PropertyValue: false - }, - severity: Error, - validatorName: NullableBoolValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: nullableScalarArgsD, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 236 - } - ], - Extensions: { - argumentName: a, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: , - PropertyValue: 0 - }, - severity: Error, - validatorName: NullableIntValidator - } - }, - { - Message: '' must be equal to 'True'., - Code: FairyBread_ValidationError, - Path: { - Name: nullableScalarArgsD, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 236 - } - ], - Extensions: { - argumentName: b, - attemptedValue: false, - code: FairyBread_ValidationError, - errorCode: EqualValidator, - errorMessage: '' must be equal to 'True'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: true, - PropertyName: null, - PropertyPath: , - PropertyValue: false - }, - severity: Error, - validatorName: NullableBoolValidator - } - }, - { - Message: Explicit validator error msg., - Code: FairyBread_ValidationError, - Path: { - Name: objectArgA, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 272 - } - ], - Extensions: { - argumentName: input, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: Explicit validator error msg., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputExplicitValidator - } - }, - { - Message: Explicit validator error msg., - Code: FairyBread_ValidationError, - Path: { - Name: objectArgB, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 310 - } - ], - Extensions: { - argumentName: input, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: Explicit validator error msg., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputExplicitValidator - } - }, - { - Message: Explicit validator error msg., - Code: FairyBread_ValidationError, - Path: { - Name: objectArgC, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 348 - } - ], - Extensions: { - argumentName: input, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: Explicit validator error msg., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputExplicitValidator - } - }, - { - Message: Explicit validator error msg., - Code: FairyBread_ValidationError, - Path: { - Name: objectArgD, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 386 - } - ], - Extensions: { - argumentName: input, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: Explicit validator error msg., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: A, - PropertyPath: A, - PropertyValue: 0 - }, - propertyName: A, - severity: Error, - validatorName: TestInputExplicitValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: arrayArgA, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 424 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[0], - PropertyValue: 0 - }, - propertyName: x[0], - severity: Error, - validatorName: ArrayOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: arrayArgA, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 424 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 1, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[1], - PropertyValue: 0 - }, - propertyName: x[1], - severity: Error, - validatorName: ArrayOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: listArgA, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 449 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[0], - PropertyValue: 0 - }, - propertyName: x[0], - severity: Error, - validatorName: ListOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: listArgA, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 449 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 1, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[1], - PropertyValue: 0 - }, - propertyName: x[1], - severity: Error, - validatorName: ListOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: listArgB, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 473 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 1, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[1], - PropertyValue: 0 - }, - propertyName: x[1], - severity: Error, - validatorName: ListOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: listArgB, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 473 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[0], - PropertyValue: 0 - }, - propertyName: x[0], - severity: Error, - validatorName: ListOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: listArgC, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 497 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[0], - PropertyValue: 0 - }, - propertyName: x[0], - severity: Error, - validatorName: ListOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: listArgC, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 497 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 1, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[1], - PropertyValue: 0 - }, - propertyName: x[1], - severity: Error, - validatorName: ListOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: listArgD, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 521 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[0], - PropertyValue: 0 - }, - propertyName: x[0], - severity: Error, - validatorName: ListOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: listArgD, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 521 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 1, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[1], - PropertyValue: 0 - }, - propertyName: x[1], - severity: Error, - validatorName: ListOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: listOfListArgC, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 545 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[1][0], - PropertyValue: 0 - }, - propertyName: x[1][0], - severity: Error, - validatorName: ListOfListOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: listOfListArgC, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 545 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 0, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[0][0], - PropertyValue: 0 - }, - propertyName: x[0][0], - severity: Error, - validatorName: ListOfListOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: listOfListArgC, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 545 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 1, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[0][1], - PropertyValue: 0 - }, - propertyName: x[0][1], - severity: Error, - validatorName: ListOfListOfNullableIntValidator - } - }, - { - Message: '' must be greater than '0'., - Code: FairyBread_ValidationError, - Path: { - Name: listOfListArgC, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 545 - } - ], - Extensions: { - argumentName: items, - attemptedValue: 0, - code: FairyBread_ValidationError, - errorCode: GreaterThanValidator, - errorMessage: '' must be greater than '0'., - formattedMessagePlaceholderValues: { - CollectionIndex: 1, - ComparisonProperty: , - ComparisonValue: 0, - PropertyName: null, - PropertyPath: x[1][1], - PropertyValue: 0 - }, - propertyName: x[1][1], - severity: Error, - validatorName: ListOfListOfNullableIntValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/RequiresOwnScopeValidatorTests.OwnScopes_Are_Disposed.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/RequiresOwnScopeValidatorTests.OwnScopes_Are_Disposed.verified.txt deleted file mode 100644 index 5e9cff70f..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/RequiresOwnScopeValidatorTests.OwnScopes_Are_Disposed.verified.txt +++ /dev/null @@ -1,40 +0,0 @@ -{ - Errors: [ - { - Message: 'Some Integer' must be greater than or equal to '999'., - Code: FairyBread_ValidationError, - Path: { - Name: read, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Extensions: { - argumentName: foo, - attemptedValue: 1, - code: FairyBread_ValidationError, - errorCode: GreaterThanOrEqualValidator, - errorMessage: 'Some Integer' must be greater than or equal to '999'., - formattedMessagePlaceholderValues: { - ComparisonProperty: , - ComparisonValue: 999, - PropertyName: Some Integer, - PropertyPath: SomeInteger, - PropertyValue: 1 - }, - propertyName: SomeInteger, - severity: Error, - validatorName: RequiresOwnScopeValidator - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/RequiresOwnScopeValidatorTests.OwnScopes_Work.verified.txt b/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/RequiresOwnScopeValidatorTests.OwnScopes_Work.verified.txt deleted file mode 100644 index c952d1586..000000000 --- a/test/Sample.Graphql.Tests/FairyBread.Tests/snapshots/RequiresOwnScopeValidatorTests.OwnScopes_Work.verified.txt +++ /dev/null @@ -1,35 +0,0 @@ -{ - Errors: [ - { - Message: Unexpected Execution Error, - Path: { - Name: read, - Parent: { - IsRoot: true - }, - Length: 1, - IsRoot: false - }, - Locations: [ - { - Line: 1, - Column: 9 - } - ], - Exception: { - $type: InvalidOperationException, - Type: InvalidOperationException, - Message: Sequence contains no matching element, - StackTrace: -at System.Linq.ThrowHelper.ThrowNoMatchException() -at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source, Func`2 predicate) -at FairyBread.Tests.RequiresOwnScopeValidatorTests.AssertingScopageValidatorProvider.GetValidators(IMiddlewareContext context, IInputField argument) -at Rocket.Surgery.LaunchPad.HotChocolate.FairyBread.ValidationMiddleware.InvokeAsync(IMiddlewareContext context) -at HotChocolate.Utilities.MiddlewareCompiler`1.ExpressionHelper.AwaitTaskHelper(Task task) -at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken) -at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken) - } - } - ], - IsDataSet: true -} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/FoundationTests.cs b/test/Sample.Graphql.Tests/FoundationTests.cs index c6050bc40..30260dd52 100644 --- a/test/Sample.Graphql.Tests/FoundationTests.cs +++ b/test/Sample.Graphql.Tests/FoundationTests.cs @@ -1,20 +1,19 @@ using System.Net; using Alba; using HotChocolate.Execution; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.AspNetCore.Testing; using Sample.Core.Domain; using Sample.Graphql.Tests.Helpers; namespace Sample.Graphql.Tests; -public class FoundationTests(ITestOutputHelper testOutputHelper) : LoggerTest(testOutputHelper) +public class FoundationTests(ITestOutputHelper testOutputHelper) : LoggerTest(XUnitDefaults.CreateTestContext(testOutputHelper)) { [Fact] public async Task Starts() { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); @@ -26,11 +25,11 @@ public async Task Starts() public async Task GraphqlSchema() { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); var exeuctor = await host.Services.GetRequestExecutorAsync(); await Verify(exeuctor.Schema.Print(), "graphql"); } -} \ No newline at end of file +} diff --git a/test/Sample.Graphql.Tests/Helpers/GraphQlWebAppFixtureTest.cs b/test/Sample.Graphql.Tests/Helpers/GraphQlWebAppFixtureTest.cs index e4fcac34c..99428c3d7 100644 --- a/test/Sample.Graphql.Tests/Helpers/GraphQlWebAppFixtureTest.cs +++ b/test/Sample.Graphql.Tests/Helpers/GraphQlWebAppFixtureTest.cs @@ -1,59 +1,24 @@ using Alba; using Microsoft.Extensions.Logging; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.AspNetCore.Testing; using Serilog; using Serilog.Events; namespace Sample.Graphql.Tests.Helpers; - -public class GraphQlWebAppFixtureTest : LoggerTest, IClassFixture, IAsyncLifetime +public abstract class GraphQlWebAppFixtureTest(ITestOutputHelper outputHelper, TAppFixture rocketSurgeryWebAppFixture, LogEventLevel logEventLevel = LogEventLevel.Verbose) : LoggerTest(XUnitTestContext.Create(outputHelper, logEventLevel)), IClassFixture, IAsyncLifetime where TAppFixture : class, ILaunchPadWebAppFixture { - private readonly ILaunchPadWebAppFixture _rocketSurgeryWebAppFixture; - protected IAlbaHost Host => _rocketSurgeryWebAppFixture.AlbaHost; + protected IAlbaHost Host => rocketSurgeryWebAppFixture.AlbaHost; /// /// The Service Provider /// protected IServiceProvider ServiceProvider => Host.Services; - protected GraphQlWebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - - protected GraphQlWebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - LogLevel minLevel, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, minLevel, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - - protected GraphQlWebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - LogEventLevel minLevel, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, minLevel, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - public virtual Task InitializeAsync() { - _rocketSurgeryWebAppFixture.SetLoggerFactory(LoggerFactory); - return _rocketSurgeryWebAppFixture.ResetAsync(); + rocketSurgeryWebAppFixture.SetLoggerFactory(CreateLoggerFactory()); + return rocketSurgeryWebAppFixture.ResetAsync(); } public virtual Task DisposeAsync() diff --git a/test/Sample.Graphql.Tests/Helpers/SqliteExtension.cs b/test/Sample.Graphql.Tests/Helpers/SqliteExtension.cs index de6d90af4..3d54e22d5 100644 --- a/test/Sample.Graphql.Tests/Helpers/SqliteExtension.cs +++ b/test/Sample.Graphql.Tests/Helpers/SqliteExtension.cs @@ -41,9 +41,11 @@ public ValueTask DisposeAsync() return _connection.DisposeAsync(); } - public Task Start(IAlbaHost host) + public async Task Start(IAlbaHost host) { - return Task.CompletedTask; + await _connection.CloseAsync(); + await _connection.OpenAsync(); + await host.Services.WithScoped().Invoke(c => c.Database.EnsureCreatedAsync()); } public IHostBuilder Configure(IHostBuilder builder) @@ -65,4 +67,4 @@ public IHostBuilder Configure(IHostBuilder builder) return builder; } -} \ No newline at end of file +} diff --git a/test/Sample.Graphql.Tests/ModuleInitializer.cs b/test/Sample.Graphql.Tests/ModuleInitializer.cs index 578d94a38..0b0f38321 100644 --- a/test/Sample.Graphql.Tests/ModuleInitializer.cs +++ b/test/Sample.Graphql.Tests/ModuleInitializer.cs @@ -15,6 +15,8 @@ public static void Init() VerifierSettings.DontScrubDateTimes(); // VerifierSettings.AddExtraSettings(settings => settings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)); VerifierSettings.AddExtraSettings(settings => settings.Converters.Add(new GeometryConverter())); + VerifierSettings.IgnoreMember(z => z.DataInfo); + VerifierSettings.IgnoreMember(z => z.DataFactory); VerifierSettings.DisableRequireUniquePrefix(); DerivePathInfo( diff --git a/test/Sample.Graphql.Tests/Rockets/UpdateRocketTests.cs b/test/Sample.Graphql.Tests/Rockets/UpdateRocketTests.cs index afc8d71f6..169f39575 100644 --- a/test/Sample.Graphql.Tests/Rockets/UpdateRocketTests.cs +++ b/test/Sample.Graphql.Tests/Rockets/UpdateRocketTests.cs @@ -39,7 +39,7 @@ public async Task Should_Update_A_Rocket() SerialNumber = string.Join("", rocket.SerialNumber.Reverse()), } ); - u.IsSuccessResult().Should().Be(true); + await Verify(u); } [Fact] @@ -73,8 +73,7 @@ public async Task Should_Patch_A_Rocket_SerialNumber() ); u.EnsureNoErrors(); - u.Data!.PatchRocket.Type.Should().Be(RocketType.AtlasV); - u.Data!.PatchRocket.SerialNumber.Should().Be("123456789012345"); + await Verify(u); } [Fact] @@ -107,9 +106,7 @@ public async Task Should_Fail_To_Patch_A_Null_Rocket_SerialNumber() SerialNumber = null, } ); - - u.IsErrorResult().Should().BeTrue(); - u.Errors[0].Message.Should().Be("'Serial Number' must not be empty."); + await Verify(u); } [Fact] @@ -142,8 +139,7 @@ public async Task Should_Patch_A_Rocket_Type() } ); - u.Data!.PatchRocket.Type.Should().Be(RocketType.FalconHeavy); - u.Data!.PatchRocket.SerialNumber.Should().Be("12345678901234"); + await Verify(u); } [Theory] diff --git a/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Fail_To_Patch_A_Null_Rocket_SerialNumber.verified.txt b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Fail_To_Patch_A_Null_Rocket_SerialNumber.verified.txt new file mode 100644 index 000000000..9fa163710 --- /dev/null +++ b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Fail_To_Patch_A_Null_Rocket_SerialNumber.verified.txt @@ -0,0 +1,32 @@ +{ + Errors: [ + { + Message: 'Serial Number Value' must not be empty., + Code: FairyBread_ValidationError, + Path: [ + patchRocket + ], + Locations: [ + { + Line: 1, + Column: 60 + } + ], + Extensions: { + argumentName: request, + attemptedValue: null, + code: FairyBread_ValidationError, + errorCode: NotNullValidator, + errorMessage: 'Serial Number Value' must not be empty., + formattedMessagePlaceholderValues: { + PropertyName: Serial Number Value, + PropertyPath: SerialNumber.Value, + PropertyValue: null + }, + propertyName: SerialNumber.Value, + severity: Error, + validatorName: Validator + } + } + ] +} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Patch_A_Rocket_SerialNumber.verified.txt b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Patch_A_Rocket_SerialNumber.verified.txt new file mode 100644 index 000000000..00adfe917 --- /dev/null +++ b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Patch_A_Rocket_SerialNumber.verified.txt @@ -0,0 +1,9 @@ +{ + Data: { + PatchRocket: { + Id: Guid_1, + Type: AtlasV, + SerialNumber: 123456789012345 + } + } +} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Patch_A_Rocket_Type.verified.txt b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Patch_A_Rocket_Type.verified.txt new file mode 100644 index 000000000..7e7fa7291 --- /dev/null +++ b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Patch_A_Rocket_Type.verified.txt @@ -0,0 +1,9 @@ +{ + Data: { + PatchRocket: { + Id: Guid_1, + Type: FalconHeavy, + SerialNumber: 12345678901234 + } + } +} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Update_A_Rocket.verified.txt b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Update_A_Rocket.verified.txt new file mode 100644 index 000000000..c067fba18 --- /dev/null +++ b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Update_A_Rocket.verified.txt @@ -0,0 +1,9 @@ +{ + Data: { + EditRocket: { + Id: Guid_1, + Type: FalconHeavy, + SerialNumber: 43210987654321 + } + } +} \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_2890bee44143284c.verified.txt b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_2890bee44143284c.verified.txt index e21d71e2e..63b28ab00 100644 --- a/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_2890bee44143284c.verified.txt +++ b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_2890bee44143284c.verified.txt @@ -1,5 +1,4 @@ { - DataFactory: {}, Errors: [ { Message: The length of 'Serial Number' must be at least 10 characters. You entered 2 characters., diff --git a/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_8fdf0365a768fbaa.verified.txt b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_8fdf0365a768fbaa.verified.txt index 4c69461d2..71ba551e6 100644 --- a/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_8fdf0365a768fbaa.verified.txt +++ b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_8fdf0365a768fbaa.verified.txt @@ -1,5 +1,4 @@ { - DataFactory: {}, Errors: [ { Message: The required input field `serialNumber` is missing., diff --git a/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_ab9a2096466e244b.verified.txt b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_ab9a2096466e244b.verified.txt index 21dc74cd1..9c89f537d 100644 --- a/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_ab9a2096466e244b.verified.txt +++ b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_ab9a2096466e244b.verified.txt @@ -1,5 +1,4 @@ { - DataFactory: {}, Errors: [ { Message: The length of 'Serial Number' must be 30 characters or fewer. You entered 725 characters., diff --git a/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_ad5a5f4a4dbd0e5c.verified.txt b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_ad5a5f4a4dbd0e5c.verified.txt index f4c338862..0d168112f 100644 --- a/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_ad5a5f4a4dbd0e5c.verified.txt +++ b/test/Sample.Graphql.Tests/Rockets/snapshots/UpdateRocketTests.Should_Validate_Required_Fields_ad5a5f4a4dbd0e5c.verified.txt @@ -1,5 +1,4 @@ { - DataFactory: {}, Errors: [ { Message: The required input field `type` is missing., diff --git a/test/Sample.Graphql.Tests/Sample.Graphql.Tests.csproj b/test/Sample.Graphql.Tests/Sample.Graphql.Tests.csproj index 12c7e21f4..4dc581e34 100644 --- a/test/Sample.Graphql.Tests/Sample.Graphql.Tests.csproj +++ b/test/Sample.Graphql.Tests/Sample.Graphql.Tests.csproj @@ -1,11 +1,11 @@  - net8.0 + net9.0 true - true - enable - enable + true + enable + enable @@ -14,10 +14,10 @@ - - - - + + + + diff --git a/test/Sample.Graphql.Tests/StrawberryShakeSerializerTests.cs b/test/Sample.Graphql.Tests/StrawberryShakeSerializerTests.cs index 7dfdeedf8..f54c981cb 100644 --- a/test/Sample.Graphql.Tests/StrawberryShakeSerializerTests.cs +++ b/test/Sample.Graphql.Tests/StrawberryShakeSerializerTests.cs @@ -2,20 +2,19 @@ using Microsoft.Extensions.DependencyInjection; using NetTopologySuite.IO; using NodaTime; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.AspNetCore.Testing; using Sample.Core.Domain; using Sample.Graphql.Tests.Helpers; namespace Sample.Graphql.Tests; -public class StrawberryShakeSerializerTests(ITestOutputHelper testOutputHelper) : LoggerTest(testOutputHelper) +public class StrawberryShakeSerializerTests(ITestOutputHelper testOutputHelper) : LoggerTest(XUnitDefaults.CreateTestContext(testOutputHelper)) { [Fact] public async Task Should_Roundtrip_Instant() { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); @@ -39,11 +38,12 @@ public async Task Should_Roundtrip_Instant() public async Task Should_Roundtrip_LocalDate() { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); var clock = host.Services.GetRequiredService(); + var timeProvider = host.Services.GetRequiredService(); var client = host.Services.GetRequiredService(); @@ -63,7 +63,7 @@ public async Task Should_Roundtrip_LocalDate() public async Task Should_Roundtrip_LocalTime() { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); @@ -87,7 +87,7 @@ public async Task Should_Roundtrip_LocalTime() public async Task Should_Roundtrip_LocalDateTime() { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); @@ -111,7 +111,7 @@ public async Task Should_Roundtrip_LocalDateTime() public async Task Should_Roundtrip_OffsetDateTime() { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); @@ -135,7 +135,7 @@ public async Task Should_Roundtrip_OffsetDateTime() public async Task Should_Roundtrip_OffsetTime() { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); @@ -159,7 +159,7 @@ public async Task Should_Roundtrip_OffsetTime() // [Fact] // public async Task Should_Roundtrip_ZonedDateTime() // { -// await using var host = await AlbaHost.For(new LaunchPadExtension(LoggerFactory), new GraphQlExtension(), new SqliteExtension()); +// await using var host = await AlbaHost.For(new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension()); // var clock = host.Services.GetRequiredService(); // var client = host.Services.GetRequiredService(); // @@ -180,7 +180,7 @@ public async Task Should_Roundtrip_OffsetTime() public async Task Should_Roundtrip_Period() { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); @@ -203,7 +203,7 @@ public async Task Should_Roundtrip_Period() public async Task Should_Roundtrip_Duration() { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); @@ -226,7 +226,7 @@ public async Task Should_Roundtrip_Duration() public async Task Should_Roundtrip_Offset() { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); @@ -249,7 +249,7 @@ public async Task Should_Roundtrip_Offset() public async Task Should_Roundtrip_IsoDayOfWeek() { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); @@ -276,7 +276,7 @@ public async Task Should_Roundtrip_IsoDayOfWeek() public async Task Should_Roundtrip_Geometry(string wkt) { await using var host = await AlbaHost.For( - new LaunchPadExtension(LoggerFactory), + new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() ); @@ -299,7 +299,7 @@ public async Task Should_Roundtrip_Geometry(string wkt) // public async Task Should_Roundtrip_Point() // { // await using var host = await AlbaHost.For( -// new LaunchPadExtension(LoggerFactory), new GraphQlExtension(), new SqliteExtension() +// new LaunchPadExtension(CreateLoggerFactory()), new GraphQlExtension(), new SqliteExtension() // ); // // var client = host.Services.GetRequiredService(); @@ -325,4 +325,4 @@ public async Task Should_Roundtrip_Geometry(string wkt) multiLineString: "MULTILINESTRING((19 20, 21 22), (23 24, 25 26))" multiPolygon: "MULTIPOLYGON(((27 28, 29 30, 31 32, 27 28)), ((33 34, 35 36, 37 38, 33 34)))" */ -} \ No newline at end of file +} diff --git a/test/Sample.Graphql.Tests/snapshots/FoundationTests.GraphqlSchema.verified.graphql b/test/Sample.Graphql.Tests/snapshots/FoundationTests.GraphqlSchema.verified.graphql index cd08ad901..d32a5d3ea 100644 --- a/test/Sample.Graphql.Tests/snapshots/FoundationTests.GraphqlSchema.verified.graphql +++ b/test/Sample.Graphql.Tests/snapshots/FoundationTests.GraphqlSchema.verified.graphql @@ -308,13 +308,13 @@ input DateTimeOperationFilterInput { "The request to delete a launch record" input DeleteLaunchRecordRequest { - "The launch record to delete" + "The id of the record to delete" id: UUID! } "The request to remove a rocket from the system" input DeleteRocketRequest { - "The rocket id" + "The id of the rocket to remove" id: UUID! } diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Duration.verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Duration.verified.txt index 16d5859c5..ee9408319 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Duration.verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Duration.verified.txt @@ -3,13 +3,5 @@ NodaTimeTest: { Duration: 0:01:00:00 } - }, - DataInfo: { - NodaTimeTest: { - __typename: NodaTimeOutputs, - Duration: 0:01:00:00 - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_LINESTRING(5 6, 7 8).verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_LINESTRING(5 6, 7 8).verified.txt index b31db2436..fb64dc6a2 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_LINESTRING(5 6, 7 8).verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_LINESTRING(5 6, 7 8).verified.txt @@ -3,13 +3,5 @@ GeometryTest: { Geometry: LINESTRING (5 6, 7 8) } - }, - DataInfo: { - GeometryTest: { - __typename: GeometryOutputs, - Geometry: LINESTRING (5 6, 7 8) - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_MULTILINESTRING((19 20, 21 22), (23 24, 25 26)).verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_MULTILINESTRING((19 20, 21 22), (23 24, 25 26)).verified.txt index 57f9dcbe2..cfbe3722e 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_MULTILINESTRING((19 20, 21 22), (23 24, 25 26)).verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_MULTILINESTRING((19 20, 21 22), (23 24, 25 26)).verified.txt @@ -3,13 +3,5 @@ GeometryTest: { Geometry: MULTILINESTRING ((19 20, 21 22), (23 24, 25 26)) } - }, - DataInfo: { - GeometryTest: { - __typename: GeometryOutputs, - Geometry: MULTILINESTRING ((19 20, 21 22), (23 24, 25 26)) - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_MULTIPOINT((15 16), (17 18)).verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_MULTIPOINT((15 16), (17 18)).verified.txt index b12271e6a..a1044a062 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_MULTIPOINT((15 16), (17 18)).verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_MULTIPOINT((15 16), (17 18)).verified.txt @@ -3,13 +3,5 @@ GeometryTest: { Geometry: MULTIPOINT ((15 16), (17 18)) } - }, - DataInfo: { - GeometryTest: { - __typename: GeometryOutputs, - Geometry: MULTIPOINT ((15 16), (17 18)) - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_MULTIPOLYGON(((27 28, 29 30, 31 32, 27 28)), ((33 34, 35 36, 37 38, 33 34))).verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_MULTIPOLYGON(((27 28, 29 30, 31 32, 27 28)), ((33 34, 35 36, 37 38, 33 34))).verified.txt index 8647ad0e1..44a047473 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_MULTIPOLYGON(((27 28, 29 30, 31 32, 27 28)), ((33 34, 35 36, 37 38, 33 34))).verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_MULTIPOLYGON(((27 28, 29 30, 31 32, 27 28)), ((33 34, 35 36, 37 38, 33 34))).verified.txt @@ -3,13 +3,5 @@ GeometryTest: { Geometry: MULTIPOLYGON (((27 28, 31 32, 29 30, 27 28)), ((33 34, 37 38, 35 36, 33 34))) } - }, - DataInfo: { - GeometryTest: { - __typename: GeometryOutputs, - Geometry: MULTIPOLYGON (((27 28, 31 32, 29 30, 27 28)), ((33 34, 37 38, 35 36, 33 34))) - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_POINT(3 4).verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_POINT(3 4).verified.txt index 85605db86..7e1de067e 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_POINT(3 4).verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_POINT(3 4).verified.txt @@ -3,13 +3,5 @@ GeometryTest: { Geometry: POINT (3 4) } - }, - DataInfo: { - GeometryTest: { - __typename: GeometryOutputs, - Geometry: POINT (3 4) - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_POLYGON((9 10, 11 12, 13 14, 9 10)).verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_POLYGON((9 10, 11 12, 13 14, 9 10)).verified.txt index b0479c94d..00a27354e 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_POLYGON((9 10, 11 12, 13 14, 9 10)).verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Geometry_POLYGON((9 10, 11 12, 13 14, 9 10)).verified.txt @@ -3,13 +3,5 @@ GeometryTest: { Geometry: POLYGON ((9 10, 13 14, 11 12, 9 10)) } - }, - DataInfo: { - GeometryTest: { - __typename: GeometryOutputs, - Geometry: POLYGON ((9 10, 13 14, 11 12, 9 10)) - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Instant.verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Instant.verified.txt index 57a4adddd..7bea3ddb6 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Instant.verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Instant.verified.txt @@ -3,13 +3,5 @@ NodaTimeTest: { Instant: 2020-01-01T00:00:00Z } - }, - DataInfo: { - NodaTimeTest: { - __typename: NodaTimeOutputs, - Instant: 2020-01-01T00:00:00Z - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_IsoDayOfWeek.verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_IsoDayOfWeek.verified.txt index 685f56e04..513dfa0e5 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_IsoDayOfWeek.verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_IsoDayOfWeek.verified.txt @@ -3,13 +3,5 @@ NodaTimeTest: { IsoDayOfWeek: Wednesday } - }, - DataInfo: { - NodaTimeTest: { - __typename: NodaTimeOutputs, - IsoDayOfWeek: Wednesday - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_LocalDate.verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_LocalDate.verified.txt index cf7a1a0b7..c8b837b3e 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_LocalDate.verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_LocalDate.verified.txt @@ -3,13 +3,5 @@ NodaTimeTest: { LocalDate: 2020-01-01 } - }, - DataInfo: { - NodaTimeTest: { - __typename: NodaTimeOutputs, - LocalDate: 2020-01-01 - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_LocalDateTime.verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_LocalDateTime.verified.txt index fbc3e7e19..025058389 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_LocalDateTime.verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_LocalDateTime.verified.txt @@ -3,13 +3,5 @@ NodaTimeTest: { LocalDateTime: 2020-01-01T00:00:00 } - }, - DataInfo: { - NodaTimeTest: { - __typename: NodaTimeOutputs, - LocalDateTime: 2020-01-01T00:00:00 - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_LocalTime.verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_LocalTime.verified.txt index 5567a9763..7755a5d1f 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_LocalTime.verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_LocalTime.verified.txt @@ -3,13 +3,5 @@ NodaTimeTest: { LocalTime: 00:00:00 } - }, - DataInfo: { - NodaTimeTest: { - __typename: NodaTimeOutputs, - LocalTime: 00:00:00 - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Offset.verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Offset.verified.txt index ac7a580cd..c07e08dca 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Offset.verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Offset.verified.txt @@ -3,13 +3,5 @@ NodaTimeTest: { Offset: +02 } - }, - DataInfo: { - NodaTimeTest: { - __typename: NodaTimeOutputs, - Offset: +02 - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_OffsetDateTime.verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_OffsetDateTime.verified.txt index 50f33c0d5..48e4e265b 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_OffsetDateTime.verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_OffsetDateTime.verified.txt @@ -3,13 +3,5 @@ NodaTimeTest: { OffsetDateTime: 2020-01-01T00:00:00Z } - }, - DataInfo: { - NodaTimeTest: { - __typename: NodaTimeOutputs, - OffsetDateTime: 2020-01-01T00:00:00Z - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_OffsetTime.verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_OffsetTime.verified.txt index 6a3722b66..e198bebe9 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_OffsetTime.verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_OffsetTime.verified.txt @@ -3,13 +3,5 @@ NodaTimeTest: { OffsetTime: 00:00:00+01 } - }, - DataInfo: { - NodaTimeTest: { - __typename: NodaTimeOutputs, - OffsetTime: 00:00:00+01 - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Period.verified.txt b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Period.verified.txt index f8d1b7106..fb3038201 100644 --- a/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Period.verified.txt +++ b/test/Sample.Graphql.Tests/snapshots/StrawberryShakeSerializerTests.Should_Roundtrip_Period.verified.txt @@ -3,13 +3,5 @@ NodaTimeTest: { Period: PT1H } - }, - DataInfo: { - NodaTimeTest: { - __typename: NodaTimeOutputs, - Period: PT1H - }, - Version: 1 - }, - DataFactory: {} + } } \ No newline at end of file diff --git a/test/Sample.Grpc.Tests/Helpers/WebAppFixtureTest.cs b/test/Sample.Grpc.Tests/Helpers/WebAppFixtureTest.cs index 2c48882d6..9d49e0f9d 100644 --- a/test/Sample.Grpc.Tests/Helpers/WebAppFixtureTest.cs +++ b/test/Sample.Grpc.Tests/Helpers/WebAppFixtureTest.cs @@ -1,59 +1,26 @@ using Alba; using Microsoft.Extensions.Logging; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.AspNetCore.Testing; using Serilog; using Serilog.Events; namespace Sample.Grpc.Tests.Helpers; -public abstract class WebAppFixtureTest : LoggerTest, IClassFixture, IAsyncLifetime +public abstract class WebAppFixtureTest(ITestOutputHelper outputHelper, + TAppFixture rocketSurgeryWebAppFixture) : LoggerTest(XUnitTestContext.Create(outputHelper)), IClassFixture, IAsyncLifetime where TAppFixture : class, ILaunchPadWebAppFixture { - private readonly ILaunchPadWebAppFixture _rocketSurgeryWebAppFixture; - public IAlbaHost AlbaHost => _rocketSurgeryWebAppFixture.AlbaHost; + public IAlbaHost AlbaHost => rocketSurgeryWebAppFixture.AlbaHost; /// /// The Service Provider /// protected IServiceProvider ServiceProvider => AlbaHost.Services; - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - LogLevel minLevel, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, minLevel, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - LogEventLevel minLevel, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, minLevel, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - public virtual Task InitializeAsync() { - _rocketSurgeryWebAppFixture.SetLoggerFactory(LoggerFactory); - return _rocketSurgeryWebAppFixture.ResetAsync(); + rocketSurgeryWebAppFixture.SetLoggerFactory(CreateLoggerFactory()); + return rocketSurgeryWebAppFixture.ResetAsync(); } public virtual Task DisposeAsync() diff --git a/test/Sample.Grpc.Tests/Sample.Grpc.Tests.csproj b/test/Sample.Grpc.Tests/Sample.Grpc.Tests.csproj index 00e479cc2..d90f4612c 100644 --- a/test/Sample.Grpc.Tests/Sample.Grpc.Tests.csproj +++ b/test/Sample.Grpc.Tests/Sample.Grpc.Tests.csproj @@ -1,13 +1,13 @@  - net8.0 + net9.0 - - + + diff --git a/test/Sample.Minimal.Tests/ApiDescriptionData.cs b/test/Sample.Minimal.Tests/ApiDescriptionData.cs new file mode 100644 index 000000000..2d08fa849 --- /dev/null +++ b/test/Sample.Minimal.Tests/ApiDescriptionData.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.Extensions.DependencyInjection; +using Rocket.Surgery.LaunchPad.AspNetCore.Testing; + +namespace Sample.Minimal.Tests; + +internal sealed class ApiDescriptionData : TheoryData + where T : class, ILaunchPadWebAppFixture, IAsyncLifetime, new() +{ + public ApiDescriptionData() + { + var host = new T(); + host.InitializeAsync().GetAwaiter().GetResult(); + var provider = host.AlbaHost.Services.GetRequiredService(); + foreach (var item in provider.ApiDescriptionGroups.Items.SelectMany(z => z.Items)) + { + Add(new ApiDescriptionData(item)); + } + } +} + +public class ApiDescriptionData(ApiDescription description) +{ + public ApiDescription Description { get; } = description; + + public override string ToString() + { + return $"[{Description.HttpMethod}] {Description.RelativePath}"; + } +} diff --git a/test/Sample.Minimal.Tests/FoundationTests.OpenApiDocument.verified.txt b/test/Sample.Minimal.Tests/FoundationTests.OpenApiDocument.verified.txt new file mode 100644 index 000000000..a7177e9bb --- /dev/null +++ b/test/Sample.Minimal.Tests/FoundationTests.OpenApiDocument.verified.txt @@ -0,0 +1,1791 @@ +{ + openapi: 3.0.1, + info: { + title: Sample.Restful | v1, + version: 1.0.0 + }, + paths: { + /LaunchRecord: { + get: { + tags: [ + LaunchRecord + ], + operationId: ListLaunchRecords, + parameters: [ + { + name: rocketType, + in: query, + schema: { + type: string + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/LaunchRecordModel + } + } + }, + text/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/LaunchRecordModel + } + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + post: { + tags: [ + LaunchRecord + ], + operationId: CreateLaunchRecord, + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 201: { + description: Created, + headers: { + location: { + description: The location of the entity that was created, + schema: { + type: string + } + } + }, + content: { + application/json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordResponse + } + }, + text/json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordResponse + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /LaunchRecord/{id}: { + get: { + tags: [ + LaunchRecord + ], + operationId: GetLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/LaunchRecordModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/LaunchRecordModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + put: { + tags: [ + LaunchRecord + ], + operationId: EditLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 204: { + description: No Content + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + patch: { + tags: [ + LaunchRecord + ], + operationId: PatchLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordPatchRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordPatchRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordPatchRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 204: { + description: No Content + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + delete: { + tags: [ + LaunchRecord + ], + operationId: DeleteLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 204: { + description: No Content + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /Rocket: { + get: { + tags: [ + Rocket + ], + operationId: ListRockets, + parameters: [ + { + name: rocketType, + in: query, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/RocketModel + } + } + }, + text/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/RocketModel + } + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + post: { + tags: [ + Rocket + ], + operationId: CreateRocket, + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/CreateRocketRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/CreateRocketRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/CreateRocketRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 201: { + description: Created, + headers: { + location: { + description: The location of the entity that was created, + schema: { + type: string + } + } + }, + content: { + application/json: { + schema: { + $ref: #/components/schemas/CreateRocketResponse + } + }, + text/json: { + schema: { + $ref: #/components/schemas/CreateRocketResponse + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /Rocket/{id}: { + get: { + tags: [ + Rocket + ], + operationId: GetRocket, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + put: { + tags: [ + Rocket + ], + operationId: EditRocket, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/EditRocketRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/EditRocketRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/EditRocketRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + patch: { + tags: [ + Rocket + ], + operationId: PatchRocket, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/EditRocketPatchRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/EditRocketPatchRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/EditRocketPatchRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + delete: { + tags: [ + Rocket + ], + operationId: RemoveRocket, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 204: { + description: No Content + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /Rocket/{id}/launch-records: { + get: { + tags: [ + Rocket + ], + operationId: GetRocketLaunchRecords, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/LaunchRecordModel + } + } + }, + text/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/LaunchRecordModel + } + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /Rocket/{id}/launch-records/{launchRecordId}: { + get: { + tags: [ + Rocket + ], + operationId: GetRocketLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + }, + { + name: launchRecordId, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/LaunchRecordModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/LaunchRecordModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + } + }, + components: { + schemas: { + AssignedOfdouble: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + type: number, + format: double + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfInstant: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + $ref: #/components/schemas/RocketId2 + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfNullableOfInstant: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + $ref: #/components/schemas/RocketId2 + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfRocketId: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + $ref: #/components/schemas/RocketId2 + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfRocketType: { + maxLength: 30, + minLength: 10, + required: [ + value + ], + type: object, + properties: { + value: { + $ref: #/components/schemas/RocketType + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfstring: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + type: string, + nullable: true + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfstring2: { + maxLength: 30, + minLength: 10, + required: [ + value + ], + type: object, + properties: { + value: { + type: string, + nullable: true + }, + hasValue: { + type: boolean + } + } + }, + CreateLaunchRecordRequest: { + required: [ + rocketId, + partner, + payload, + payloadWeightKg, + actualLaunchDate, + scheduledLaunchDate + ], + type: object, + properties: { + rocketId: { + $ref: #/components/schemas/Instant2 + }, + partner: { + minimum: 0, + minLength: 1, + type: string, + nullable: true + }, + payload: { + minimum: 0, + minLength: 1, + type: string, + nullable: true + }, + payloadWeightKg: { + minimum: 0, + type: number, + format: double + }, + actualLaunchDate: { + $ref: #/components/schemas/Instant2 + }, + scheduledLaunchDate: { + $ref: #/components/schemas/Instant2 + } + } + }, + CreateLaunchRecordResponse: { + type: object, + properties: { + id: { + $ref: #/components/schemas/LaunchRecordId + } + } + }, + CreateRocketRequest: { + required: [ + serialNumber, + type + ], + type: object, + properties: { + serialNumber: { + maxLength: 30, + minLength: 10, + type: string + }, + type: { + $ref: #/components/schemas/RocketType2 + } + } + }, + CreateRocketResponse: { + type: object, + properties: { + id: { + $ref: #/components/schemas/RocketId2 + } + } + }, + EditLaunchRecordPatchRequest: { + required: [ + id, + partner, + payload, + payloadWeightKg, + actualLaunchDate, + scheduledLaunchDate, + rocketId + ], + type: object, + properties: { + partner: { + $ref: #/components/schemas/AssignedOfstring + }, + payload: { + $ref: #/components/schemas/AssignedOfstring + }, + payloadWeightKg: { + $ref: #/components/schemas/AssignedOfdouble + }, + actualLaunchDate: { + $ref: #/components/schemas/AssignedOfNullableOfInstant + }, + scheduledLaunchDate: { + $ref: #/components/schemas/AssignedOfInstant + }, + rocketId: { + $ref: #/components/schemas/AssignedOfRocketId + } + } + }, + EditLaunchRecordRequest: { + required: [ + partner, + payload, + payloadWeightKg, + actualLaunchDate, + scheduledLaunchDate, + rocketId + ], + type: object, + properties: { + partner: { + minimum: 0, + minLength: 1, + type: string + }, + payload: { + minimum: 0, + minLength: 1, + type: string + }, + payloadWeightKg: { + minimum: 0, + type: number, + format: double + }, + actualLaunchDate: { + $ref: #/components/schemas/RocketId + }, + scheduledLaunchDate: { + $ref: #/components/schemas/RocketId + }, + rocketId: { + $ref: #/components/schemas/RocketId + } + } + }, + EditRocketPatchRequest: { + required: [ + serialNumber, + type + ], + type: object, + properties: { + serialNumber: { + $ref: #/components/schemas/AssignedOfstring2 + }, + type: { + $ref: #/components/schemas/AssignedOfRocketType + } + } + }, + EditRocketRequest: { + required: [ + serialNumber, + type + ], + type: object, + properties: { + serialNumber: { + maxLength: 30, + minLength: 10, + type: string + }, + type: { + $ref: #/components/schemas/RocketType2 + } + } + }, + Instant2: { + minimum: 0 + }, + LaunchRecordModel: { + type: object, + properties: { + id: { + $ref: #/components/schemas/Instant + }, + partner: { + type: string + }, + payload: { + type: string + }, + payloadWeightKg: { + type: integer, + format: int64 + }, + actualLaunchDate: { + $ref: #/components/schemas/Instant + }, + scheduledLaunchDate: { + $ref: #/components/schemas/Instant + }, + rocketSerialNumber: { + type: string + }, + rocketType: { + $ref: #/components/schemas/RocketType + } + } + }, + ProblemDetails: { + required: [ + type, + title, + status, + detail, + instance + ], + type: object, + properties: { + type: { + type: string, + nullable: true + }, + title: { + type: string, + nullable: true + }, + status: { + type: integer, + format: int32, + nullable: true + }, + detail: { + type: string, + nullable: true + }, + instance: { + type: string, + nullable: true + } + } + }, + RocketId: { + minimum: 0 + }, + RocketModel: { + type: object, + properties: { + id: { + $ref: #/components/schemas/RocketId2 + }, + sn: { + type: string + }, + type: { + $ref: #/components/schemas/RocketType + } + } + }, + RocketType: { + enum: [ + falcon9, + falconHeavy, + atlasV + ] + }, + RocketType2: { + maxLength: 30, + minLength: 10, + enum: [ + falcon9, + falconHeavy, + atlasV + ] + } + } + }, + tags: [ + { + name: LaunchRecord + }, + { + name: Rocket + } + ] +} \ No newline at end of file diff --git a/test/Sample.Minimal.Tests/FoundationTests.cs b/test/Sample.Minimal.Tests/FoundationTests.cs new file mode 100644 index 000000000..0d4c58807 --- /dev/null +++ b/test/Sample.Minimal.Tests/FoundationTests.cs @@ -0,0 +1,24 @@ +using System.Net; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Sample.Minimal.Tests.Helpers; + +namespace Sample.Restful.Tests; + +public class FoundationTests(ITestOutputHelper testOutputHelper, TestWebAppFixture factory) : WebAppFixtureTest(testOutputHelper, factory) +{ + [Fact] + public async Task Starts() + { + var response = await AlbaHost.Server.CreateClient().GetAsync("/"); + response.StatusCode.Should().Be(HttpStatusCode.NotFound); + } + + [Fact] + public async Task OpenApiDocument() + { + var response = await AlbaHost.Server.CreateClient().GetAsync("/openapi/v1.json"); + var document = await response.Content.ReadAsStringAsync(); + await Verify(document, extension: "json"); + } +} diff --git a/test/Sample.Minimal.Tests/Helpers/SqliteExtension.cs b/test/Sample.Minimal.Tests/Helpers/SqliteExtension.cs new file mode 100644 index 000000000..1137fc617 --- /dev/null +++ b/test/Sample.Minimal.Tests/Helpers/SqliteExtension.cs @@ -0,0 +1,68 @@ +using Alba; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Rocket.Surgery.DependencyInjection; +using Rocket.Surgery.LaunchPad.AspNetCore.Testing; + +namespace Sample.Minimal.Tests.Helpers; + +public sealed class SqliteExtension : IResettableAlbaExtension where TDbContext : DbContext +{ + private readonly SqliteConnection _connection; + + public SqliteExtension() + { + _connection = new("DataSource=:memory:"); + } + + public void Reset(IServiceProvider serviceProvider) + { + _connection.Close(); + _connection.Open(); + serviceProvider.WithScoped().Invoke(c => c.Database.EnsureCreated()); + } + + public async Task ResetAsync(IServiceProvider serviceProvider) + { + await _connection.CloseAsync(); + await _connection.OpenAsync(); + await serviceProvider.WithScoped().Invoke(c => c.Database.EnsureCreatedAsync()); + } + + public void Dispose() + { + _connection.Dispose(); + } + + public ValueTask DisposeAsync() + { + return _connection.DisposeAsync(); + } + + public Task Start(IAlbaHost host) + { + return Task.CompletedTask; + } + + public IHostBuilder Configure(IHostBuilder builder) + { + builder + .ConfigureServices( + (_, services) => + { + services.AddDbContextPool( + z => z + .EnableDetailedErrors() + .EnableSensitiveDataLogging() + .EnableThreadSafetyChecks() + .EnableServiceProviderCaching(false) + .UseSqlite(_connection) + ); + } + ); + + return builder; + } +} \ No newline at end of file diff --git a/test/Sample.Minimal.Tests/Helpers/TestWebAppFixture.cs b/test/Sample.Minimal.Tests/Helpers/TestWebAppFixture.cs new file mode 100644 index 000000000..641d13e7f --- /dev/null +++ b/test/Sample.Minimal.Tests/Helpers/TestWebAppFixture.cs @@ -0,0 +1,6 @@ +using Rocket.Surgery.LaunchPad.AspNetCore.Testing; +using Sample.Core.Domain; + +namespace Sample.Minimal.Tests.Helpers; + +public class TestWebAppFixture() : LaunchPadWebAppFixture(new SqliteExtension()), IAsyncLifetime; diff --git a/test/Sample.Minimal.Tests/Helpers/WebAppFixtureTest.cs b/test/Sample.Minimal.Tests/Helpers/WebAppFixtureTest.cs new file mode 100644 index 000000000..9b4756edd --- /dev/null +++ b/test/Sample.Minimal.Tests/Helpers/WebAppFixtureTest.cs @@ -0,0 +1,26 @@ +using Alba; +using Rocket.Surgery.LaunchPad.AspNetCore.Testing; + +namespace Sample.Minimal.Tests.Helpers; + +public abstract class WebAppFixtureTest(ITestOutputHelper outputHelper, TestWebAppFixture rocketSurgeryWebAppFixture) : LoggerTest(XUnitTestContext.Create(outputHelper)), IClassFixture, IAsyncLifetime + where TAppFixture : class, ILaunchPadWebAppFixture +{ + protected IAlbaHost AlbaHost => rocketSurgeryWebAppFixture.AlbaHost; + + /// + /// The Service Provider + /// + protected IServiceProvider ServiceProvider => AlbaHost.Services; + + public virtual Task InitializeAsync() + { + rocketSurgeryWebAppFixture.SetLoggerFactory(CreateLoggerFactory()); + return rocketSurgeryWebAppFixture.ResetAsync(); + } + + public virtual Task DisposeAsync() + { + return Task.CompletedTask; + } +} diff --git a/test/Sample.Minimal.Tests/LaunchRecords/CreateLaunchRecordTests.cs b/test/Sample.Minimal.Tests/LaunchRecords/CreateLaunchRecordTests.cs new file mode 100644 index 000000000..d8c895953 --- /dev/null +++ b/test/Sample.Minimal.Tests/LaunchRecords/CreateLaunchRecordTests.cs @@ -0,0 +1,53 @@ +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Rocket.Surgery.DependencyInjection; +using Sample.Core.Domain; +using Sample.Restful.Client; +using RocketType = Sample.Core.Domain.RocketType; + +namespace Sample.Restful.Tests.LaunchRecords; + +public class CreateLaunchRecordTests : HandleWebHostBase +{ + [Fact] + public async Task Should_Create_A_LaunchRecord() + { + var client = new LaunchRecordClient(Factory.CreateClient()); + var clock = ServiceProvider.GetRequiredService(); + var rocket = await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var rocket = new ReadyRocket + { + Type = RocketType.Falcon9, + SerialNumber = "12345678901234" + }; + z.Add(rocket); + + await z.SaveChangesAsync(); + return rocket; + } + ); + + + var response = await client.CreateLaunchRecordAsync( + new CreateLaunchRecordRequest + { + Partner = "partner", + Payload = "geo-fence-ftl", + RocketId = rocket.Id.Value, + ScheduledLaunchDate = clock.GetCurrentInstant().ToDateTimeOffset(), + PayloadWeightKg = 100, + } + ); + + response.Result.Id.Should().NotBeEmpty(); + } + + public CreateLaunchRecordTests(ITestOutputHelper outputHelper, TestWebHost host) : base(outputHelper, host) + { + } + + private static readonly Faker Faker = new(); +} diff --git a/test/Sample.Minimal.Tests/LaunchRecords/GetLaunchRecordTests.cs b/test/Sample.Minimal.Tests/LaunchRecords/GetLaunchRecordTests.cs new file mode 100644 index 000000000..a89f480f1 --- /dev/null +++ b/test/Sample.Minimal.Tests/LaunchRecords/GetLaunchRecordTests.cs @@ -0,0 +1,70 @@ +using NodaTime; +using Rocket.Surgery.DependencyInjection; +using Sample.Core.Domain; +using Sample.Core.Models; +using Sample.Restful.Client; +using HttpRocketType = Sample.Restful.Client.RocketType; +using RocketType = Sample.Core.Domain.RocketType; + +namespace Sample.Restful.Tests.LaunchRecords; + +public class GetLaunchRecordTests : HandleWebHostBase +{ + [Fact] + public async Task Should_Get_A_LaunchRecord() + { + var client = new LaunchRecordClient(Factory.CreateClient()); + var record = await ServiceProvider.WithScoped() + .Invoke( + async (context, clock) => + { + var rocket = new ReadyRocket + { + Id = RocketId.New(), + Type = RocketType.Falcon9, + SerialNumber = "12345678901234" + }; + + var record = new LaunchRecord + { + Partner = "partner", + Payload = "geo-fence-ftl", + RocketId = rocket.Id, + ScheduledLaunchDate = clock.GetCurrentInstant().ToDateTimeOffset(), + PayloadWeightKg = 100, + }; + context.Add(rocket); + context.Add(record); + + await context.SaveChangesAsync(); + return record; + } + ); + + var response = ( await client.GetLaunchRecordAsync(record.Id.Value) ).Result; + + response.Partner.Should().Be("partner"); + response.Payload.Should().Be("geo-fence-ftl"); + response.RocketType.Should().Be(HttpRocketType.Falcon9); + response.RocketSerialNumber.Should().Be("12345678901234"); + response.ScheduledLaunchDate.Should().Be(record.ScheduledLaunchDate); + } + + [Fact] + public async Task Should_Not_Get_A_Missing_Launch_Record() + { + var client = new LaunchRecordClient(Factory.CreateClient()); + + Func action = () => client.GetLaunchRecordAsync(Guid.NewGuid()); + await action.Should().ThrowAsync>() + .Where( + z => z.StatusCode == 404 && z.Result.Status == 404 && z.Result.Title == "Not Found" + ); + } + + public GetLaunchRecordTests(ITestOutputHelper outputHelper, TestWebHost host) : base(outputHelper, host) + { + } + + private static readonly Faker Faker = new(); +} diff --git a/test/Sample.Minimal.Tests/LaunchRecords/ListLaunchRecordsTests.cs b/test/Sample.Minimal.Tests/LaunchRecords/ListLaunchRecordsTests.cs new file mode 100644 index 000000000..d17a59fa6 --- /dev/null +++ b/test/Sample.Minimal.Tests/LaunchRecords/ListLaunchRecordsTests.cs @@ -0,0 +1,61 @@ +#if NET6_0_OR_GREATER +using Rocket.Surgery.DependencyInjection; +using Sample.Core; +using Sample.Core.Domain; +using Sample.Restful.Client; +using RocketType = Sample.Restful.Client.RocketType; + +namespace Sample.Restful.Tests.LaunchRecords; + +public class ListLaunchRecordsTests : HandleWebHostBase +{ + [Fact] + public async Task Should_List_LaunchRecords() + { + var client = new LaunchRecordClient(Factory.CreateClient()); + await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var faker = new RocketFaker(); + var rockets = faker.Generate(3); + var records = new LaunchRecordFaker(rockets).Generate(10); + z.AddRange(rockets); + z.AddRange(records); + await z.SaveChangesAsync(); + } + ); + + var response = await client.ListLaunchRecordsAsync(); + + response.Result.Should().HaveCount(10); + } + + [Fact] + public async Task Should_List_Specific_Kinds_Of_LaunchRecords() + { + var client = new LaunchRecordClient(Factory.CreateClient()); + await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var faker = new RocketFaker(); + var rockets = faker.UseSeed(100).Generate(3); + var records = new LaunchRecordFaker(rockets).UseSeed(100).Generate(10); + z.AddRange(rockets); + z.AddRange(records); + await z.SaveChangesAsync(); + } + ); + + var response = await client.ListLaunchRecordsAsync(RocketType.FalconHeavy); + response.Result.Should().HaveCount(3); + } + + public ListLaunchRecordsTests(ITestOutputHelper outputHelper, TestWebHost host) : base(outputHelper, host) + { + } + + private static readonly Faker Faker = new(); +} +#endif diff --git a/test/Sample.Minimal.Tests/LaunchRecords/RemoveLaunchRecordsTests.cs b/test/Sample.Minimal.Tests/LaunchRecords/RemoveLaunchRecordsTests.cs new file mode 100644 index 000000000..9e4e06216 --- /dev/null +++ b/test/Sample.Minimal.Tests/LaunchRecords/RemoveLaunchRecordsTests.cs @@ -0,0 +1,67 @@ +using Rocket.Surgery.DependencyInjection; +using Sample.Core; +using Sample.Core.Domain; +using Sample.Core.Models; +using Sample.Restful.Client; + +namespace Sample.Restful.Tests.LaunchRecords; + +public class RemoveLaunchRecordsTests : HandleWebHostBase +{ + [Fact] + public async Task Should_Remove_LaunchRecord() + { + var client = new LaunchRecordClient(Factory.CreateClient()); + var id = await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var faker = new RocketFaker(); + var rocket = faker.Generate(); + var record = new LaunchRecordFaker(new[] { rocket }.ToList()).Generate(); + z.Add(rocket); + z.Add(record); + + await z.SaveChangesAsync(); + return record.Id; + } + ); + + await client.DeleteLaunchRecordAsync(id.Value); + + ServiceProvider.WithScoped().Invoke(c => c.LaunchRecords.Should().BeEmpty()); + } + + [Fact] + public async Task Should_Not_Be_Authorized_On_Given_Record() + { + var id = new LaunchRecordId(new Guid("bad361de-a6d5-425a-9cf6-f9b2dd236be6")); + var client = new LaunchRecordClient(Factory.CreateClient()); + await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var faker = new RocketFaker(); + var rocket = faker.Generate(); + var record = new LaunchRecordFaker(new[] { rocket }.ToList()).Generate(); + z.Add(rocket); + record.Id = id; + z.Add(record); + + await z.SaveChangesAsync(); + return record.Id; + } + ); + + + Func action = () => client.DeleteLaunchRecordAsync(id.Value); + await action.Should().ThrowAsync>() + .Where(z => z.StatusCode == 403 && z.Result.Status == 403 && z.Result.Title == "Forbidden"); + } + + public RemoveLaunchRecordsTests(ITestOutputHelper outputHelper, TestWebHost host) : base(outputHelper, host) + { + } + + private static readonly Faker Faker = new(); +} diff --git a/test/Sample.Minimal.Tests/LaunchRecords/UpdateLaunchRecordTests.cs b/test/Sample.Minimal.Tests/LaunchRecords/UpdateLaunchRecordTests.cs new file mode 100644 index 000000000..aca5d64c7 --- /dev/null +++ b/test/Sample.Minimal.Tests/LaunchRecords/UpdateLaunchRecordTests.cs @@ -0,0 +1,70 @@ +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using NodaTime.Extensions; +using Rocket.Surgery.DependencyInjection; +using Sample.Core.Domain; +using Sample.Core.Models; +using Sample.Restful.Client; +using RocketType = Sample.Core.Domain.RocketType; + +namespace Sample.Restful.Tests.LaunchRecords; + +public class UpdateLaunchRecordTests : HandleWebHostBase +{ + [Fact] + public async Task Should_Update_A_LaunchRecord() + { + var client = new LaunchRecordClient(Factory.CreateClient()); + var clock = ServiceProvider.GetRequiredService(); + var record = await ServiceProvider.WithScoped() + .Invoke( + async (context, clk) => + { + var rocket = new ReadyRocket + { + Id = RocketId.New(), + Type = RocketType.Falcon9, + SerialNumber = "12345678901234" + }; + + var record = new LaunchRecord + { + Partner = "partner", + Payload = "geo-fence-ftl", + RocketId = rocket.Id, + ScheduledLaunchDate = clk.GetCurrentInstant().ToDateTimeOffset(), + PayloadWeightKg = 100, + }; + context.Add(rocket); + context.Add(record); + + await context.SaveChangesAsync(); + return record; + } + ); + + var launchDate = record.ScheduledLaunchDate.AddSeconds(1); + await client.EditLaunchRecordAsync( + record.Id.Value, + new EditLaunchRecordRequest + { + Partner = "partner", + Payload = "geo-fence-ftl", + RocketId = record.RocketId.Value, + ScheduledLaunchDate = launchDate, + PayloadWeightKg = 200, + } + ); + + var response = await client.GetLaunchRecordAsync(record.Id.Value); + + response.Result.ScheduledLaunchDate.Should().Be(launchDate); + response.Result.PayloadWeightKg.Should().Be(200); + } + + public UpdateLaunchRecordTests(ITestOutputHelper outputHelper, TestWebHost host) : base(outputHelper, host) + { + } + + private static readonly Faker Faker = new(); +} diff --git a/test/Sample.Minimal.Tests/ModuleInitializer.cs b/test/Sample.Minimal.Tests/ModuleInitializer.cs new file mode 100644 index 000000000..26014a9aa --- /dev/null +++ b/test/Sample.Minimal.Tests/ModuleInitializer.cs @@ -0,0 +1,31 @@ +using System.Runtime.CompilerServices; +using DiffEngine; +using Path = System.IO.Path; + +namespace Sample.Minimal.Tests; + +internal static class ModuleInitializer +{ + [ModuleInitializer] + public static void Init() + { + DiffRunner.Disabled = true; + VerifierSettings.DontScrubDateTimes(); + VerifierSettings.DisableRequireUniquePrefix(); + DerivePathInfo( + (sourceFile, _, type, method) => + { + static string GetTypeName(Type type) + { + return type.IsNested ? $"{type.ReflectedType!.Name}.{type.Name}" : type.Name; + } + + var typeName = GetTypeName(type); + + var path = Path.Combine(Path.GetDirectoryName(sourceFile)!, "snapshots"); + return new(path, typeName, method.Name); + } + ); + } + +} diff --git a/test/Sample.Minimal.Tests/Rockets/CreateRocketTests.cs b/test/Sample.Minimal.Tests/Rockets/CreateRocketTests.cs new file mode 100644 index 000000000..d64f77894 --- /dev/null +++ b/test/Sample.Minimal.Tests/Rockets/CreateRocketTests.cs @@ -0,0 +1,50 @@ +using Sample.Restful.Client; + +namespace Sample.Restful.Tests.Rockets; + +public class CreateRocketTests : HandleWebHostBase +{ + [Fact] + public async Task Should_Create_A_Rocket() + { + var client = new RocketClient(Factory.CreateClient()); + + var response = await client.CreateRocketAsync( + new CreateRocketRequest + { + Type = RocketType.Falcon9, + SerialNumber = "12345678901234" + } + ); + + response.Result.Id.Should().NotBeEmpty(); + } + + [Fact] + public async Task Should_Throw_If_Rocket_Exists() + { + var client = new RocketClient(Factory.CreateClient()); + await client.CreateRocketAsync( + new CreateRocketRequest + { + Type = RocketType.Falcon9, + SerialNumber = "12345678901234" + } + ); + + Func action = () => client.CreateRocketAsync( + new CreateRocketRequest + { + Type = RocketType.Falcon9, + SerialNumber = "12345678901234" + } + ); + var r = ( await action.Should().ThrowAsync>() ) + .And.Result; + r.Title.Should().Be("Rocket Creation Failed"); + } + + public CreateRocketTests(ITestOutputHelper testOutputHelper, TestWebHost host) : base(testOutputHelper, host) + { + } +} diff --git a/test/Sample.Minimal.Tests/Rockets/GetRocketTests.cs b/test/Sample.Minimal.Tests/Rockets/GetRocketTests.cs new file mode 100644 index 000000000..21291f626 --- /dev/null +++ b/test/Sample.Minimal.Tests/Rockets/GetRocketTests.cs @@ -0,0 +1,54 @@ +using Rocket.Surgery.DependencyInjection; +using Sample.Core.Domain; +using Sample.Restful.Client; +using HttpRocketType = Sample.Restful.Client.RocketType; +using RocketType = Sample.Core.Domain.RocketType; + +namespace Sample.Restful.Tests.Rockets; + +public class GetRocketTests : HandleWebHostBase +{ + [Fact] + public async Task Should_Get_A_Rocket() + { + var client = new RocketClient(Factory.CreateClient()); + var rocket = await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var rocket = new ReadyRocket + { + Type = RocketType.Falcon9, + SerialNumber = "12345678901234" + }; + z.Add(rocket); + + await z.SaveChangesAsync(); + return rocket.Id; + } + ); + + var response = await client.GetRocketAsync(rocket.Value); + + response.Result.Type.Should().Be(HttpRocketType.Falcon9); + response.Result.Sn.Should().Be("12345678901234"); + } + + [Fact] + public async Task Should_Not_Get_A_Missing_Rocket() + { + var client = new RocketClient(Factory.CreateClient()); + + Func action = () => client.GetRocketAsync(Guid.NewGuid()); + await action.Should().ThrowAsync>() + .Where( + z => z.StatusCode == 404 && z.Result.Status == 404 && z.Result.Title == "Not Found" + ); + } + + public GetRocketTests(ITestOutputHelper outputHelper, TestWebHost host) : base(outputHelper, host) + { + } + + private static readonly Faker Faker = new(); +} diff --git a/test/Sample.Minimal.Tests/Rockets/ListRocketsTests.cs b/test/Sample.Minimal.Tests/Rockets/ListRocketsTests.cs new file mode 100644 index 000000000..5ab646912 --- /dev/null +++ b/test/Sample.Minimal.Tests/Rockets/ListRocketsTests.cs @@ -0,0 +1,58 @@ +#if NET6_0_OR_GREATER +using Rocket.Surgery.DependencyInjection; +using Sample.Core; +using Sample.Core.Domain; +using Sample.Restful.Client; +using RocketType = Sample.Restful.Client.RocketType; + +namespace Sample.Restful.Tests.Rockets; + +public class ListRocketsTests : HandleWebHostBase +{ + [Fact] + public async Task Should_List_Rockets() + { + var client = new RocketClient(Factory.CreateClient()); + await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var faker = new RocketFaker(); + z.AddRange(faker.Generate(10)); + + await z.SaveChangesAsync(); + } + ); + + var response = await client.ListRocketsAsync(); + + response.Result.Should().HaveCount(10); + } + + [Fact] + public async Task Should_List_Specific_Kinds_Of_Rockets() + { + var client = new RocketClient(Factory.CreateClient()); + await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var faker = new RocketFaker(); + z.AddRange(faker.UseSeed(100).Generate(10)); + + await z.SaveChangesAsync(); + } + ); + + var response = await client.ListRocketsAsync(RocketType.AtlasV); + + response.Result.Should().HaveCount(5); + } + + public ListRocketsTests(ITestOutputHelper outputHelper, TestWebHost host) : base(outputHelper, host) + { + } + + private static readonly Faker Faker = new(); +} +#endif diff --git a/test/Sample.Minimal.Tests/Rockets/RemoveRocketsTests.cs b/test/Sample.Minimal.Tests/Rockets/RemoveRocketsTests.cs new file mode 100644 index 000000000..21661d047 --- /dev/null +++ b/test/Sample.Minimal.Tests/Rockets/RemoveRocketsTests.cs @@ -0,0 +1,37 @@ +using Rocket.Surgery.DependencyInjection; +using Sample.Core; +using Sample.Core.Domain; +using Sample.Restful.Client; + +namespace Sample.Restful.Tests.Rockets; + +public class RemoveRocketsTests : HandleWebHostBase +{ + [Fact] + public async Task Should_Remove_Rocket() + { + var client = new RocketClient(Factory.CreateClient()); + var id = await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var faker = new RocketFaker(); + var rocket = faker.Generate(); + z.Add(rocket); + + await z.SaveChangesAsync().ConfigureAwait(false); + return rocket.Id; + } + ); + + await client.RemoveRocketAsync(id.Value); + + ServiceProvider.WithScoped().Invoke(c => c.Rockets.Should().BeEmpty()); + } + + public RemoveRocketsTests(ITestOutputHelper outputHelper, TestWebHost host) : base(outputHelper, host) + { + } + + private static readonly Faker Faker = new(); +} diff --git a/test/Sample.Minimal.Tests/Rockets/UpdateRocketTests.cs b/test/Sample.Minimal.Tests/Rockets/UpdateRocketTests.cs new file mode 100644 index 000000000..2a6fe58ad --- /dev/null +++ b/test/Sample.Minimal.Tests/Rockets/UpdateRocketTests.cs @@ -0,0 +1,191 @@ +using Rocket.Surgery.DependencyInjection; +using Sample.Core.Domain; +using Sample.Restful.Client; +using RocketType = Sample.Core.Domain.RocketType; +using ClientRocketType = Sample.Restful.Client.RocketType; + +namespace Sample.Restful.Tests.Rockets; + +public class UpdateRocketTests : HandleWebHostBase +{ + [Fact] + public async Task Should_Update_A_Rocket() + { + var client = new RocketClient(Factory.CreateClient()); + + var rocket = await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var rocket = new ReadyRocket + { + Type = RocketType.Falcon9, + SerialNumber = "12345678901234" + }; + z.Add(rocket); + + await z.SaveChangesAsync(); + return rocket; + } + ); + + var u = await client.EditRocketAsync( + rocket.Id.Value, + new EditRocketRequest + { + Type = ClientRocketType.FalconHeavy, + SerialNumber = string.Join("", rocket.SerialNumber.Reverse()) + } + ); + ( u.StatusCode is >= 200 and < 300 ).Should().BeTrue(); + + var response = await client.GetRocketAsync(rocket.Id.Value); + + response.Result.Type.Should().Be(ClientRocketType.FalconHeavy); + response.Result.Sn.Should().Be("43210987654321"); + } + + [Fact] + public async Task Should_Patch_A_Rocket_SerialNumber() + { + var client = new RocketClient(Factory.CreateClient()); + + var rocket = await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var rocket = new ReadyRocket + { + Type = RocketType.AtlasV, + SerialNumber = "12345678901234" + }; + z.Add(rocket); + + await z.SaveChangesAsync(); + return rocket; + } + ); + + var u = await client.PatchRocketAsync( + rocket.Id.Value, + new EditRocketPatchRequest + { + SerialNumber = new() { Value = "123456789012345" } + } + ); + ( u.StatusCode is >= 200 and < 300 ).Should().BeTrue(); + + var response = await client.GetRocketAsync(rocket.Id.Value); + + response.Result.Type.Should().Be(ClientRocketType.AtlasV); + response.Result.Sn.Should().Be("123456789012345"); + } + + [Fact] + public async Task Should_Fail_To_Patch_A_Null_Rocket_SerialNumber() + { + var client = new RocketClient(Factory.CreateClient()); + + var rocket = await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var rocket = new ReadyRocket + { + Type = RocketType.AtlasV, + SerialNumber = "12345678901234" + }; + z.Add(rocket); + + await z.SaveChangesAsync(); + return rocket; + } + ); + + Func>> action = () => client.PatchRocketAsync( + rocket.Id.Value, + new EditRocketPatchRequest + { + SerialNumber = new() { Value = null } + } + ); + var result = ( await action.Should().ThrowAsync>() ).And.Result; + result.Errors.Should().HaveCount(1); + result.Errors["SerialNumber"][0].ErrorMessage.Should().Be("'Serial Number' must not be empty."); + } + + [Fact] + public async Task Should_Patch_A_Rocket_Type() + { + var client = new RocketClient(Factory.CreateClient()); + + var rocket = await ServiceProvider.WithScoped() + .Invoke( + async z => + { + var rocket = new ReadyRocket + { + Type = RocketType.AtlasV, + SerialNumber = "12345678901234" + }; + z.Add(rocket); + + await z.SaveChangesAsync(); + return rocket; + } + ); + + var u = await client.PatchRocketAsync( + rocket.Id.Value, + new EditRocketPatchRequest + { + Type = new() { Value = ClientRocketType.FalconHeavy } + } + ); + ( u.StatusCode is >= 200 and < 300 ).Should().BeTrue(); + + var response = await client.GetRocketAsync(rocket.Id.Value); + response.Result.Type.Should().Be(ClientRocketType.FalconHeavy); + response.Result.Sn.Should().Be("12345678901234"); + } + + public UpdateRocketTests(ITestOutputHelper outputHelper, TestWebHost host) : base(outputHelper, host) + { + } + + private static readonly Faker Faker = new(); + + [Theory] + [ClassData(typeof(ShouldValidateUsersRequiredFieldData))] + public async Task Should_Validate_Required_Fields(EditRocketRequest request, string propertyName) + { + var client = new RocketClient(Factory.CreateClient()); + Func a = () => client.EditRocketAsync(Guid.NewGuid(), request); + ( await a.Should().ThrowAsync>() ) + .And + .Result.Errors.Values + .SelectMany(x => x) + .Select(z => z.PropertyName) + .Should() + .Contain(propertyName); + } + + private class ShouldValidateUsersRequiredFieldData : TheoryData + { + public ShouldValidateUsersRequiredFieldData() + { + Add( + new EditRocketRequest(), + nameof(EditRocketRequest.SerialNumber) + ); + Add( + new EditRocketRequest { SerialNumber = Faker.Random.String2(0, 9) }, + nameof(EditRocketRequest.SerialNumber) + ); + Add( + new EditRocketRequest { SerialNumber = Faker.Random.String2(600, 800) }, + nameof(EditRocketRequest.SerialNumber) + ); + } + } +} diff --git a/test/Sample.Minimal.Tests/Sample.Minimal.Tests.csproj b/test/Sample.Minimal.Tests/Sample.Minimal.Tests.csproj new file mode 100644 index 000000000..9bffead80 --- /dev/null +++ b/test/Sample.Minimal.Tests/Sample.Minimal.Tests.csproj @@ -0,0 +1,18 @@ + + + net9.0 + + + + + + + + + + + + + + + diff --git a/test/Sample.Minimal.Tests/SqliteConnectionService.cs b/test/Sample.Minimal.Tests/SqliteConnectionService.cs new file mode 100644 index 000000000..91197716d --- /dev/null +++ b/test/Sample.Minimal.Tests/SqliteConnectionService.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.Hosting; +using Rocket.Surgery.DependencyInjection; +using Sample.Core.Domain; + +namespace Sample.Minimal.Tests; + +internal sealed class SqliteConnectionService(IServiceProvider serviceProvider) : IHostedService +{ + public async Task StartAsync(CancellationToken cancellationToken) + { + await serviceProvider.WithScoped() + .Invoke(z => z.Database.EnsureCreatedAsync(cancellationToken)) + .ConfigureAwait(false); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} diff --git a/test/Sample.Minimal.Tests/snapshots/FoundationTests.OpenApiDocument.verified.json b/test/Sample.Minimal.Tests/snapshots/FoundationTests.OpenApiDocument.verified.json new file mode 100644 index 000000000..016173293 --- /dev/null +++ b/test/Sample.Minimal.Tests/snapshots/FoundationTests.OpenApiDocument.verified.json @@ -0,0 +1,775 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Sample.Minimal | v1", + "version": "1.0.0" + }, + "paths": { + "/launch-records": { + "get": { + "tags": [ + "LaunchRecordEndpoints" + ], + "operationId": "ListLaunchRecords", + "parameters": [ + { + "name": "rocketType", + "in": "query", + "schema": { + "$ref": "#/components/schemas/NullableOfRocketType" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + } + } + } + } + }, + "post": { + "tags": [ + "LaunchRecordEndpoints" + ], + "operationId": "CreateLaunchRecord", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLaunchRecordRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "headers": { + "location": { + "description": "The location of the entity that was created", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLaunchRecordResponse" + } + } + } + }, + "400": { + "description": "Bad Request" + } + } + } + }, + "/launch-records/{id}": { + "get": { + "tags": [ + "LaunchRecordEndpoints" + ], + "operationId": "GetLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + } + }, + "404": { + "description": "Not Found" + } + } + }, + "put": { + "tags": [ + "LaunchRecordEndpoints" + ], + "operationId": "EditLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/LaunchRecordId2" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "400": { + "description": "Bad Request" + } + } + }, + "patch": { + "tags": [ + "LaunchRecordEndpoints" + ], + "operationId": "PatchLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/LaunchRecordId2" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordPatchRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "400": { + "description": "Bad Request" + } + } + }, + "delete": { + "tags": [ + "LaunchRecordEndpoints" + ], + "operationId": "DeleteLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/LaunchRecordId2" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/rockets": { + "get": { + "tags": [ + "RocketEndpoints" + ], + "parameters": [ + { + "name": "rocketType", + "in": "query", + "schema": { + "$ref": "#/components/schemas/NullableOfRocketType" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RocketModel" + } + } + } + } + } + } + }, + "post": { + "tags": [ + "RocketEndpoints" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRocketRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "headers": { + "location": { + "description": "The location of the entity that was created", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRocketResponse" + } + } + } + }, + "400": { + "description": "Bad Request" + } + } + } + }, + "/rockets/{id}": { + "get": { + "tags": [ + "RocketEndpoints" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/RocketId2" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + } + } + }, + "404": { + "description": "Not Found" + } + } + }, + "put": { + "tags": [ + "RocketEndpoints" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/RocketId2" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditRocketRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "400": { + "description": "Bad Request" + } + } + }, + "patch": { + "tags": [ + "RocketEndpoints" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/RocketId2" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditRocketPatchRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "400": { + "description": "Bad Request" + } + } + }, + "delete": { + "tags": [ + "RocketEndpoints" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/RocketId2" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found" + } + } + } + } + }, + "components": { + "schemas": { + "AssignedOfdouble": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "type": "number", + "format": "double" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfInstant": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/RocketId" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfNullableOfInstant": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/RocketId" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfRocketId": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/RocketId" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfRocketType": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/RocketType" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfstring": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "type": "string", + "nullable": true + }, + "hasValue": { + "type": "boolean" + } + } + }, + "CreateLaunchRecordRequest": { + "required": [ + "partner", + "rocketId", + "payload", + "scheduledLaunchDate" + ], + "type": "object", + "properties": { + "rocketId": { + "$ref": "#/components/schemas/Instant" + }, + "partner": { + "minLength": 1, + "type": "string", + "nullable": true + }, + "payload": { + "minLength": 1, + "type": "string", + "nullable": true + }, + "payloadWeightKg": { + "minimum": 0, + "type": "number", + "format": "double" + }, + "actualLaunchDate": { + "$ref": "#/components/schemas/Instant" + }, + "scheduledLaunchDate": { + "$ref": "#/components/schemas/Instant" + } + } + }, + "CreateLaunchRecordResponse": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/LaunchRecordId" + } + } + }, + "CreateRocketRequest": { + "required": [ + "serialNumber" + ], + "type": "object", + "properties": { + "serialNumber": { + "maxLength": 30, + "minLength": 10, + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/RocketType" + } + } + }, + "CreateRocketResponse": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/RocketId" + } + } + }, + "EditLaunchRecordPatchRequest": { + "required": [ + "id" + ], + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/RocketId" + }, + "partner": { + "$ref": "#/components/schemas/AssignedOfstring" + }, + "payload": { + "$ref": "#/components/schemas/AssignedOfstring" + }, + "payloadWeightKg": { + "$ref": "#/components/schemas/AssignedOfdouble" + }, + "actualLaunchDate": { + "$ref": "#/components/schemas/AssignedOfNullableOfInstant" + }, + "scheduledLaunchDate": { + "$ref": "#/components/schemas/AssignedOfInstant" + }, + "rocketId": { + "$ref": "#/components/schemas/AssignedOfRocketId" + } + } + }, + "EditLaunchRecordRequest": { + "required": [ + "id", + "partner", + "rocketId", + "payload", + "scheduledLaunchDate" + ], + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/RocketId" + }, + "partner": { + "minLength": 1, + "type": "string" + }, + "payload": { + "minLength": 1, + "type": "string" + }, + "payloadWeightKg": { + "minimum": 0, + "type": "number", + "format": "double" + }, + "actualLaunchDate": { + "$ref": "#/components/schemas/RocketId" + }, + "scheduledLaunchDate": { + "$ref": "#/components/schemas/RocketId" + }, + "rocketId": { + "$ref": "#/components/schemas/RocketId" + } + } + }, + "EditRocketPatchRequest": { + "required": [ + "id" + ], + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/RocketId" + }, + "serialNumber": { + "$ref": "#/components/schemas/AssignedOfstring" + }, + "type": { + "$ref": "#/components/schemas/AssignedOfRocketType" + } + } + }, + "EditRocketRequest": { + "required": [ + "id", + "type", + "serialNumber" + ], + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/RocketId" + }, + "serialNumber": { + "maxLength": 30, + "minLength": 10, + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/RocketType" + } + } + }, + "Instant": { }, + "LaunchRecordId": { }, + "LaunchRecordId2": { + "type": "string", + "format": "uuid" + }, + "LaunchRecordModel": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/Instant" + }, + "partner": { + "type": "string" + }, + "payload": { + "type": "string" + }, + "payloadWeightKg": { + "type": "integer", + "format": "int64" + }, + "actualLaunchDate": { + "$ref": "#/components/schemas/Instant" + }, + "scheduledLaunchDate": { + "$ref": "#/components/schemas/Instant" + }, + "rocketSerialNumber": { + "type": "string" + }, + "rocketType": { + "$ref": "#/components/schemas/RocketType" + } + } + }, + "NullableOfRocketType": { + "enum": [ + "falcon9", + "falconHeavy", + "atlasV", + null + ] + }, + "RocketId": { }, + "RocketId2": { + "type": "string", + "format": "uuid" + }, + "RocketModel": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/RocketId" + }, + "sn": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/RocketType" + } + } + }, + "RocketType": { + "enum": [ + "falcon9", + "falconHeavy", + "atlasV" + ] + } + } + }, + "tags": [ + { + "name": "LaunchRecordEndpoints" + }, + { + "name": "RocketEndpoints" + } + ] +} \ No newline at end of file diff --git a/test/Sample.Pages.Tests/Helpers/WebAppFixtureTest.cs b/test/Sample.Pages.Tests/Helpers/WebAppFixtureTest.cs index 0b797d1fb..9ce2cf840 100644 --- a/test/Sample.Pages.Tests/Helpers/WebAppFixtureTest.cs +++ b/test/Sample.Pages.Tests/Helpers/WebAppFixtureTest.cs @@ -1,59 +1,27 @@ using Alba; using Microsoft.Extensions.Logging; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.AspNetCore.Testing; using Serilog; using Serilog.Events; namespace Sample.Pages.Tests.Helpers; -public abstract class WebAppFixtureTest : LoggerTest, IClassFixture, IAsyncLifetime +public abstract class WebAppFixtureTest( + ITestOutputHelper outputHelper, + TAppFixture rocketSurgeryWebAppFixture) : LoggerTest(XUnitTestContext.Create(outputHelper)), IClassFixture, IAsyncLifetime where TAppFixture : class, ILaunchPadWebAppFixture { - private readonly ILaunchPadWebAppFixture _rocketSurgeryWebAppFixture; - protected IAlbaHost AlbaHost => _rocketSurgeryWebAppFixture.AlbaHost; + protected IAlbaHost AlbaHost => rocketSurgeryWebAppFixture.AlbaHost; /// /// The Service Provider /// protected IServiceProvider ServiceProvider => AlbaHost.Services; - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - LogLevel minLevel, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, minLevel, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - LogEventLevel minLevel, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, minLevel, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - public virtual Task InitializeAsync() { - _rocketSurgeryWebAppFixture.SetLoggerFactory(LoggerFactory); - return _rocketSurgeryWebAppFixture.ResetAsync(); + rocketSurgeryWebAppFixture.SetLoggerFactory(CreateLoggerFactory()); + return rocketSurgeryWebAppFixture.ResetAsync(); } public virtual Task DisposeAsync() diff --git a/test/Sample.Pages.Tests/Sample.Pages.Tests.csproj b/test/Sample.Pages.Tests/Sample.Pages.Tests.csproj index e41c7997d..bec0706fc 100644 --- a/test/Sample.Pages.Tests/Sample.Pages.Tests.csproj +++ b/test/Sample.Pages.Tests/Sample.Pages.Tests.csproj @@ -1,13 +1,13 @@  - net8.0 + net9.0 - - + + diff --git a/test/Sample.Restful.Tests/FoundationTests.OpenApiDocument.verified.txt b/test/Sample.Restful.Tests/FoundationTests.OpenApiDocument.verified.txt new file mode 100644 index 000000000..a7177e9bb --- /dev/null +++ b/test/Sample.Restful.Tests/FoundationTests.OpenApiDocument.verified.txt @@ -0,0 +1,1791 @@ +{ + openapi: 3.0.1, + info: { + title: Sample.Restful | v1, + version: 1.0.0 + }, + paths: { + /LaunchRecord: { + get: { + tags: [ + LaunchRecord + ], + operationId: ListLaunchRecords, + parameters: [ + { + name: rocketType, + in: query, + schema: { + type: string + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/LaunchRecordModel + } + } + }, + text/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/LaunchRecordModel + } + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + post: { + tags: [ + LaunchRecord + ], + operationId: CreateLaunchRecord, + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 201: { + description: Created, + headers: { + location: { + description: The location of the entity that was created, + schema: { + type: string + } + } + }, + content: { + application/json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordResponse + } + }, + text/json: { + schema: { + $ref: #/components/schemas/CreateLaunchRecordResponse + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /LaunchRecord/{id}: { + get: { + tags: [ + LaunchRecord + ], + operationId: GetLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/LaunchRecordModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/LaunchRecordModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + put: { + tags: [ + LaunchRecord + ], + operationId: EditLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 204: { + description: No Content + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + patch: { + tags: [ + LaunchRecord + ], + operationId: PatchLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordPatchRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordPatchRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/EditLaunchRecordPatchRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 204: { + description: No Content + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + delete: { + tags: [ + LaunchRecord + ], + operationId: DeleteLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 204: { + description: No Content + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /Rocket: { + get: { + tags: [ + Rocket + ], + operationId: ListRockets, + parameters: [ + { + name: rocketType, + in: query, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/RocketModel + } + } + }, + text/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/RocketModel + } + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + post: { + tags: [ + Rocket + ], + operationId: CreateRocket, + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/CreateRocketRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/CreateRocketRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/CreateRocketRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 201: { + description: Created, + headers: { + location: { + description: The location of the entity that was created, + schema: { + type: string + } + } + }, + content: { + application/json: { + schema: { + $ref: #/components/schemas/CreateRocketResponse + } + }, + text/json: { + schema: { + $ref: #/components/schemas/CreateRocketResponse + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /Rocket/{id}: { + get: { + tags: [ + Rocket + ], + operationId: GetRocket, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + put: { + tags: [ + Rocket + ], + operationId: EditRocket, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/EditRocketRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/EditRocketRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/EditRocketRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + patch: { + tags: [ + Rocket + ], + operationId: PatchRocket, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + requestBody: { + content: { + application/json: { + schema: { + $ref: #/components/schemas/EditRocketPatchRequest + } + }, + text/json: { + schema: { + $ref: #/components/schemas/EditRocketPatchRequest + } + }, + application/*+json: { + schema: { + $ref: #/components/schemas/EditRocketPatchRequest + } + } + }, + required: true + }, + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/RocketModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + }, + delete: { + tags: [ + Rocket + ], + operationId: RemoveRocket, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 204: { + description: No Content + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /Rocket/{id}/launch-records: { + get: { + tags: [ + Rocket + ], + operationId: GetRocketLaunchRecords, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/LaunchRecordModel + } + } + }, + text/json: { + schema: { + type: array, + items: { + $ref: #/components/schemas/LaunchRecordModel + } + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + }, + /Rocket/{id}/launch-records/{launchRecordId}: { + get: { + tags: [ + Rocket + ], + operationId: GetRocketLaunchRecord, + parameters: [ + { + name: id, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + }, + { + name: launchRecordId, + in: path, + required: true, + schema: { + type: string, + format: uuid + } + } + ], + responses: { + default: { + description: , + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 200: { + description: OK, + content: { + application/json: { + schema: { + $ref: #/components/schemas/LaunchRecordModel + } + }, + text/json: { + schema: { + $ref: #/components/schemas/LaunchRecordModel + } + } + } + }, + 404: { + description: Not Found, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 400: { + description: Bad Request, + content: { + application/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/ProblemDetails + } + } + } + }, + 422: { + description: Unprocessable Entity, + content: { + application/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + }, + text/json: { + schema: { + $ref: #/components/schemas/FluentValidationProblemDetails + } + } + } + } + } + } + } + }, + components: { + schemas: { + AssignedOfdouble: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + type: number, + format: double + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfInstant: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + $ref: #/components/schemas/RocketId2 + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfNullableOfInstant: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + $ref: #/components/schemas/RocketId2 + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfRocketId: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + $ref: #/components/schemas/RocketId2 + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfRocketType: { + maxLength: 30, + minLength: 10, + required: [ + value + ], + type: object, + properties: { + value: { + $ref: #/components/schemas/RocketType + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfstring: { + minimum: 0, + required: [ + value + ], + type: object, + properties: { + value: { + type: string, + nullable: true + }, + hasValue: { + type: boolean + } + } + }, + AssignedOfstring2: { + maxLength: 30, + minLength: 10, + required: [ + value + ], + type: object, + properties: { + value: { + type: string, + nullable: true + }, + hasValue: { + type: boolean + } + } + }, + CreateLaunchRecordRequest: { + required: [ + rocketId, + partner, + payload, + payloadWeightKg, + actualLaunchDate, + scheduledLaunchDate + ], + type: object, + properties: { + rocketId: { + $ref: #/components/schemas/Instant2 + }, + partner: { + minimum: 0, + minLength: 1, + type: string, + nullable: true + }, + payload: { + minimum: 0, + minLength: 1, + type: string, + nullable: true + }, + payloadWeightKg: { + minimum: 0, + type: number, + format: double + }, + actualLaunchDate: { + $ref: #/components/schemas/Instant2 + }, + scheduledLaunchDate: { + $ref: #/components/schemas/Instant2 + } + } + }, + CreateLaunchRecordResponse: { + type: object, + properties: { + id: { + $ref: #/components/schemas/LaunchRecordId + } + } + }, + CreateRocketRequest: { + required: [ + serialNumber, + type + ], + type: object, + properties: { + serialNumber: { + maxLength: 30, + minLength: 10, + type: string + }, + type: { + $ref: #/components/schemas/RocketType2 + } + } + }, + CreateRocketResponse: { + type: object, + properties: { + id: { + $ref: #/components/schemas/RocketId2 + } + } + }, + EditLaunchRecordPatchRequest: { + required: [ + id, + partner, + payload, + payloadWeightKg, + actualLaunchDate, + scheduledLaunchDate, + rocketId + ], + type: object, + properties: { + partner: { + $ref: #/components/schemas/AssignedOfstring + }, + payload: { + $ref: #/components/schemas/AssignedOfstring + }, + payloadWeightKg: { + $ref: #/components/schemas/AssignedOfdouble + }, + actualLaunchDate: { + $ref: #/components/schemas/AssignedOfNullableOfInstant + }, + scheduledLaunchDate: { + $ref: #/components/schemas/AssignedOfInstant + }, + rocketId: { + $ref: #/components/schemas/AssignedOfRocketId + } + } + }, + EditLaunchRecordRequest: { + required: [ + partner, + payload, + payloadWeightKg, + actualLaunchDate, + scheduledLaunchDate, + rocketId + ], + type: object, + properties: { + partner: { + minimum: 0, + minLength: 1, + type: string + }, + payload: { + minimum: 0, + minLength: 1, + type: string + }, + payloadWeightKg: { + minimum: 0, + type: number, + format: double + }, + actualLaunchDate: { + $ref: #/components/schemas/RocketId + }, + scheduledLaunchDate: { + $ref: #/components/schemas/RocketId + }, + rocketId: { + $ref: #/components/schemas/RocketId + } + } + }, + EditRocketPatchRequest: { + required: [ + serialNumber, + type + ], + type: object, + properties: { + serialNumber: { + $ref: #/components/schemas/AssignedOfstring2 + }, + type: { + $ref: #/components/schemas/AssignedOfRocketType + } + } + }, + EditRocketRequest: { + required: [ + serialNumber, + type + ], + type: object, + properties: { + serialNumber: { + maxLength: 30, + minLength: 10, + type: string + }, + type: { + $ref: #/components/schemas/RocketType2 + } + } + }, + Instant2: { + minimum: 0 + }, + LaunchRecordModel: { + type: object, + properties: { + id: { + $ref: #/components/schemas/Instant + }, + partner: { + type: string + }, + payload: { + type: string + }, + payloadWeightKg: { + type: integer, + format: int64 + }, + actualLaunchDate: { + $ref: #/components/schemas/Instant + }, + scheduledLaunchDate: { + $ref: #/components/schemas/Instant + }, + rocketSerialNumber: { + type: string + }, + rocketType: { + $ref: #/components/schemas/RocketType + } + } + }, + ProblemDetails: { + required: [ + type, + title, + status, + detail, + instance + ], + type: object, + properties: { + type: { + type: string, + nullable: true + }, + title: { + type: string, + nullable: true + }, + status: { + type: integer, + format: int32, + nullable: true + }, + detail: { + type: string, + nullable: true + }, + instance: { + type: string, + nullable: true + } + } + }, + RocketId: { + minimum: 0 + }, + RocketModel: { + type: object, + properties: { + id: { + $ref: #/components/schemas/RocketId2 + }, + sn: { + type: string + }, + type: { + $ref: #/components/schemas/RocketType + } + } + }, + RocketType: { + enum: [ + falcon9, + falconHeavy, + atlasV + ] + }, + RocketType2: { + maxLength: 30, + minLength: 10, + enum: [ + falcon9, + falconHeavy, + atlasV + ] + } + } + }, + tags: [ + { + name: LaunchRecord + }, + { + name: Rocket + } + ] +} \ No newline at end of file diff --git a/test/Sample.Restful.Tests/FoundationTests.cs b/test/Sample.Restful.Tests/FoundationTests.cs index a5fc8e6c6..125b0a9b1 100644 --- a/test/Sample.Restful.Tests/FoundationTests.cs +++ b/test/Sample.Restful.Tests/FoundationTests.cs @@ -2,8 +2,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Sample.Restful.Tests.Helpers; -using Swashbuckle.AspNetCore.Swagger; -using Swashbuckle.AspNetCore.SwaggerGen; namespace Sample.Restful.Tests; @@ -16,27 +14,11 @@ public async Task Starts() response.StatusCode.Should().Be(HttpStatusCode.NotFound); } - [Theory] - [ClassData(typeof(OpenApiDocuments))] - public void OpenApiDocument(string document) - { - AlbaHost - .Services.GetRequiredService() - .GetSwagger(document) - .Should() - .NotBeNull(); - } - - private sealed class OpenApiDocuments : TheoryData + [Fact] + public async Task OpenApiDocument() { - public OpenApiDocuments() - { - using var host = new TestWebAppFixture(); - host.InitializeAsync().GetAwaiter().GetResult(); - foreach (var item in host.AlbaHost.Services.GetRequiredService>().Value.SwaggerDocs.Keys) - { - Add(item); - } - } + var response = await AlbaHost.Server.CreateClient().GetAsync("/openapi/v1.json"); + var document = await response.Content.ReadAsStringAsync(); + await Verify(document, extension: "json"); } -} \ No newline at end of file +} diff --git a/test/Sample.Restful.Tests/Helpers/WebAppFixtureTest.cs b/test/Sample.Restful.Tests/Helpers/WebAppFixtureTest.cs index 49763ae4c..58b759c1b 100644 --- a/test/Sample.Restful.Tests/Helpers/WebAppFixtureTest.cs +++ b/test/Sample.Restful.Tests/Helpers/WebAppFixtureTest.cs @@ -1,63 +1,29 @@ using Alba; using Microsoft.Extensions.Logging; -using Rocket.Surgery.Extensions.Testing; using Rocket.Surgery.LaunchPad.AspNetCore.Testing; using Serilog; using Serilog.Events; namespace Sample.Restful.Tests.Helpers; -public abstract class WebAppFixtureTest : LoggerTest, IClassFixture, IAsyncLifetime +public abstract class WebAppFixtureTest(ITestOutputHelper outputHelper, TestWebAppFixture rocketSurgeryWebAppFixture) : LoggerTest(XUnitTestContext.Create(outputHelper)), IClassFixture, IAsyncLifetime where TAppFixture : class, ILaunchPadWebAppFixture { - private readonly ILaunchPadWebAppFixture _rocketSurgeryWebAppFixture; - protected IAlbaHost AlbaHost => _rocketSurgeryWebAppFixture.AlbaHost; + protected IAlbaHost AlbaHost => rocketSurgeryWebAppFixture.AlbaHost; /// /// The Service Provider /// protected IServiceProvider ServiceProvider => AlbaHost.Services; - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - LogLevel minLevel, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, minLevel, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - - protected WebAppFixtureTest( - ITestOutputHelper outputHelper, - TAppFixture rocketSurgeryWebAppFixture, - LogEventLevel minLevel, - string? logFormat = null, - Action? configureLogger = null - ) : base(outputHelper, minLevel, logFormat, configureLogger) - { - _rocketSurgeryWebAppFixture = rocketSurgeryWebAppFixture; - } - public virtual Task InitializeAsync() { - _rocketSurgeryWebAppFixture.SetLoggerFactory(LoggerFactory); - return _rocketSurgeryWebAppFixture.ResetAsync(); + rocketSurgeryWebAppFixture.SetLoggerFactory(CreateLoggerFactory()); + return rocketSurgeryWebAppFixture.ResetAsync(); } public virtual Task DisposeAsync() { return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/test/Sample.Restful.Tests/ModuleInitializer.cs b/test/Sample.Restful.Tests/ModuleInitializer.cs new file mode 100644 index 000000000..431eb0939 --- /dev/null +++ b/test/Sample.Restful.Tests/ModuleInitializer.cs @@ -0,0 +1,31 @@ +using System.Runtime.CompilerServices; +using DiffEngine; +using Path = System.IO.Path; + +namespace Sample.Restful.Tests; + +internal static class ModuleInitializer +{ + [ModuleInitializer] + public static void Init() + { + DiffRunner.Disabled = true; + VerifierSettings.DontScrubDateTimes(); + VerifierSettings.DisableRequireUniquePrefix(); + DerivePathInfo( + (sourceFile, _, type, method) => + { + static string GetTypeName(Type type) + { + return type.IsNested ? $"{type.ReflectedType!.Name}.{type.Name}" : type.Name; + } + + var typeName = GetTypeName(type); + + var path = Path.Combine(Path.GetDirectoryName(sourceFile)!, "snapshots"); + return new(path, typeName, method.Name); + } + ); + } + +} diff --git a/test/Sample.Restful.Tests/Sample.Restful.Tests.csproj b/test/Sample.Restful.Tests/Sample.Restful.Tests.csproj index 687aa52eb..f82d35b6f 100644 --- a/test/Sample.Restful.Tests/Sample.Restful.Tests.csproj +++ b/test/Sample.Restful.Tests/Sample.Restful.Tests.csproj @@ -1,18 +1,18 @@  - - net8.0 - - - - - - - - - - - - - - + + net9.0 + + + + + + + + + + + + + + diff --git a/test/Sample.Restful.Tests/snapshots/FoundationTests.OpenApiDocument.verified.json b/test/Sample.Restful.Tests/snapshots/FoundationTests.OpenApiDocument.verified.json new file mode 100644 index 000000000..956fa5a31 --- /dev/null +++ b/test/Sample.Restful.Tests/snapshots/FoundationTests.OpenApiDocument.verified.json @@ -0,0 +1,1734 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Sample.Restful | v1", + "version": "1.0.0" + }, + "paths": { + "/LaunchRecord": { + "get": { + "tags": [ + "LaunchRecord" + ], + "operationId": "ListLaunchRecords", + "parameters": [ + { + "name": "rocketType", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "post": { + "tags": [ + "LaunchRecord" + ], + "operationId": "CreateLaunchRecord", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLaunchRecordRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateLaunchRecordRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateLaunchRecordRequest" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "201": { + "description": "Created", + "headers": { + "location": { + "description": "The location of the entity that was created", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLaunchRecordResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateLaunchRecordResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + }, + "/LaunchRecord/{id}": { + "get": { + "tags": [ + "LaunchRecord" + ], + "operationId": "GetLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "LaunchRecord" + ], + "operationId": "EditLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordRequest" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "patch": { + "tags": [ + "LaunchRecord" + ], + "operationId": "PatchLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordPatchRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordPatchRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/EditLaunchRecordPatchRequest" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "delete": { + "tags": [ + "LaunchRecord" + ], + "operationId": "DeleteLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + }, + "/Rocket": { + "get": { + "tags": [ + "Rocket" + ], + "operationId": "ListRockets", + "parameters": [ + { + "name": "rocketType", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RocketModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RocketModel" + } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "post": { + "tags": [ + "Rocket" + ], + "operationId": "CreateRocket", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRocketRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateRocketRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateRocketRequest" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "201": { + "description": "Created", + "headers": { + "location": { + "description": "The location of the entity that was created", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRocketResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateRocketResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + }, + "/Rocket/{id}": { + "get": { + "tags": [ + "Rocket" + ], + "operationId": "GetRocket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "Rocket" + ], + "operationId": "EditRocket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditRocketRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EditRocketRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/EditRocketRequest" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Rocket" + ], + "operationId": "PatchRocket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditRocketPatchRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EditRocketPatchRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/EditRocketPatchRequest" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RocketModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Rocket" + ], + "operationId": "RemoveRocket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + }, + "/Rocket/{id}/launch-records": { + "get": { + "tags": [ + "Rocket" + ], + "operationId": "GetRocketLaunchRecords", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + }, + "/Rocket/{id}/launch-records/{launchRecordId}": { + "get": { + "tags": [ + "Rocket" + ], + "operationId": "GetRocketLaunchRecord", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "launchRecordId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/LaunchRecordModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/FluentValidationProblemDetails" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AssignedOfdouble": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "type": "number", + "format": "double" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfInstant": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/RocketId" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfNullableOfInstant": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/RocketId" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfRocketId": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/RocketId" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfRocketType": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/RocketType" + }, + "hasValue": { + "type": "boolean" + } + } + }, + "AssignedOfstring": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "type": "string", + "nullable": true + }, + "hasValue": { + "type": "boolean" + } + } + }, + "CreateLaunchRecordRequest": { + "required": [ + "partner", + "rocketId", + "payload", + "scheduledLaunchDate" + ], + "type": "object", + "properties": { + "rocketId": { + "$ref": "#/components/schemas/Instant" + }, + "partner": { + "minLength": 1, + "type": "string", + "nullable": true + }, + "payload": { + "minLength": 1, + "type": "string", + "nullable": true + }, + "payloadWeightKg": { + "minimum": 0, + "type": "number", + "format": "double" + }, + "actualLaunchDate": { + "$ref": "#/components/schemas/Instant" + }, + "scheduledLaunchDate": { + "$ref": "#/components/schemas/Instant" + } + } + }, + "CreateLaunchRecordResponse": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/LaunchRecordId" + } + } + }, + "CreateRocketRequest": { + "required": [ + "serialNumber" + ], + "type": "object", + "properties": { + "serialNumber": { + "maxLength": 30, + "minLength": 10, + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/RocketType" + } + } + }, + "CreateRocketResponse": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/RocketId" + } + } + }, + "EditLaunchRecordPatchRequest": { + "required": [ + "id" + ], + "type": "object", + "properties": { + "partner": { + "$ref": "#/components/schemas/AssignedOfstring" + }, + "payload": { + "$ref": "#/components/schemas/AssignedOfstring" + }, + "payloadWeightKg": { + "$ref": "#/components/schemas/AssignedOfdouble" + }, + "actualLaunchDate": { + "$ref": "#/components/schemas/AssignedOfNullableOfInstant" + }, + "scheduledLaunchDate": { + "$ref": "#/components/schemas/AssignedOfInstant" + }, + "rocketId": { + "$ref": "#/components/schemas/AssignedOfRocketId" + } + } + }, + "EditLaunchRecordRequest": { + "required": [ + "partner", + "rocketId", + "payload", + "scheduledLaunchDate" + ], + "type": "object", + "properties": { + "partner": { + "minLength": 1, + "type": "string" + }, + "payload": { + "minLength": 1, + "type": "string" + }, + "payloadWeightKg": { + "minimum": 0, + "type": "number", + "format": "double" + }, + "actualLaunchDate": { + "$ref": "#/components/schemas/RocketId" + }, + "scheduledLaunchDate": { + "$ref": "#/components/schemas/RocketId" + }, + "rocketId": { + "$ref": "#/components/schemas/RocketId" + } + } + }, + "EditRocketPatchRequest": { + "type": "object", + "properties": { + "serialNumber": { + "$ref": "#/components/schemas/AssignedOfstring" + }, + "type": { + "$ref": "#/components/schemas/AssignedOfRocketType" + } + } + }, + "EditRocketRequest": { + "required": [ + "type", + "serialNumber" + ], + "type": "object", + "properties": { + "serialNumber": { + "maxLength": 30, + "minLength": 10, + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/RocketType" + } + } + }, + "FluentValidationProblemDetails": { }, + "Instant": { }, + "LaunchRecordId": { }, + "LaunchRecordModel": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/Instant" + }, + "partner": { + "type": "string" + }, + "payload": { + "type": "string" + }, + "payloadWeightKg": { + "type": "integer", + "format": "int64" + }, + "actualLaunchDate": { + "$ref": "#/components/schemas/Instant" + }, + "scheduledLaunchDate": { + "$ref": "#/components/schemas/Instant" + }, + "rocketSerialNumber": { + "type": "string" + }, + "rocketType": { + "$ref": "#/components/schemas/RocketType" + } + } + }, + "ProblemDetails": { + "required": [ + "type", + "title" + ], + "type": "object", + "properties": { + "type": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "status": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "detail": { + "type": "string", + "nullable": true + }, + "instance": { + "type": "string", + "nullable": true + } + } + }, + "RocketId": { }, + "RocketModel": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/RocketId" + }, + "sn": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/RocketType" + } + } + }, + "RocketType": { + "enum": [ + "falcon9", + "falconHeavy", + "atlasV" + ] + } + } + }, + "tags": [ + { + "name": "LaunchRecord" + }, + { + "name": "Rocket" + } + ] +} \ No newline at end of file diff --git a/test/Sample.Worker.Tests/Sample.Worker.Tests.csproj b/test/Sample.Worker.Tests/Sample.Worker.Tests.csproj index 39a936dc7..e45127db0 100644 --- a/test/Sample.Worker.Tests/Sample.Worker.Tests.csproj +++ b/test/Sample.Worker.Tests/Sample.Worker.Tests.csproj @@ -1,11 +1,11 @@  - - net8.0 - - - - - - - + + net8.0;net9.0 + + + + + + +