diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..75811a2 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,29 @@ +name: CI + +on: + pull_request: + branches: [ "main" ] + push: + branches: [ "main" ] + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 10.0.x + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore --configuration Release + + - name: Run tests + run: dotnet test --no-build --configuration Release --verbosity normal diff --git a/.gitignore b/.gitignore index 3fc3b2a..92e6d18 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,8 @@ [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ -[Rr]eleases/ +Releases/ +!releases/ x64/ x86/ [Ww][Ii][Nn]32/ @@ -33,6 +34,7 @@ ScaffoldingReadMe.txt # NuGet Packages *.nupkg +!releases/*.nupkg # NuGet Symbol Packages *.snupkg diff --git a/Directory.Build.props b/Directory.Build.props index ab5b304..48358df 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,9 +5,9 @@ true enable enable - net8.0;net9.0 - 13 + net10.0 + 14 - + \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index 7943cd7..a47b9c1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,7 +4,18 @@ true - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + \ No newline at end of file diff --git a/RestExceptions.slnx b/RestExceptions.slnx index 755a2b4..65ffb2b 100644 --- a/RestExceptions.slnx +++ b/RestExceptions.slnx @@ -1,6 +1,6 @@  - + @@ -13,7 +13,13 @@ + + + - + + + + \ No newline at end of file diff --git a/examples/RestExceptions.Demo/Program.cs b/examples/RestExceptions.Demo/Program.cs index 8a1f784..744f036 100644 --- a/examples/RestExceptions.Demo/Program.cs +++ b/examples/RestExceptions.Demo/Program.cs @@ -65,3 +65,6 @@ app.UseHttpsRedirection(); app.Run(); + +// Needed so WebApplicationFactory in tests can find the entry point +public partial class Program; diff --git a/releases/RestExceptions.10.0.0.nupkg b/releases/RestExceptions.10.0.0.nupkg new file mode 100644 index 0000000..72d7298 Binary files /dev/null and b/releases/RestExceptions.10.0.0.nupkg differ diff --git a/releases/RestExceptions.9.0.11.nupkg b/releases/RestExceptions.9.0.11.nupkg new file mode 100644 index 0000000..cbeaa49 Binary files /dev/null and b/releases/RestExceptions.9.0.11.nupkg differ diff --git a/releases/RestExceptions.9.0.7.1.nupkg b/releases/RestExceptions.9.0.7.1.nupkg new file mode 100644 index 0000000..4c64f51 Binary files /dev/null and b/releases/RestExceptions.9.0.7.1.nupkg differ diff --git a/releases/RestExceptions.9.0.7.2.nupkg b/releases/RestExceptions.9.0.7.2.nupkg new file mode 100644 index 0000000..3890654 Binary files /dev/null and b/releases/RestExceptions.9.0.7.2.nupkg differ diff --git a/releases/RestExceptions.9.0.7.3.nupkg b/releases/RestExceptions.9.0.7.3.nupkg new file mode 100644 index 0000000..75275d8 Binary files /dev/null and b/releases/RestExceptions.9.0.7.3.nupkg differ diff --git a/releases/RestExceptions.9.0.7.4.nupkg b/releases/RestExceptions.9.0.7.4.nupkg new file mode 100644 index 0000000..22af6fd Binary files /dev/null and b/releases/RestExceptions.9.0.7.4.nupkg differ diff --git a/releases/RestExceptions.9.0.7.nupkg b/releases/RestExceptions.9.0.7.nupkg new file mode 100644 index 0000000..f823fb4 Binary files /dev/null and b/releases/RestExceptions.9.0.7.nupkg differ diff --git a/releases/RestExceptions.9.0.8.nupkg b/releases/RestExceptions.9.0.8.nupkg new file mode 100644 index 0000000..11b43a6 Binary files /dev/null and b/releases/RestExceptions.9.0.8.nupkg differ diff --git a/releases/RestExceptions.9.0.9.nupkg b/releases/RestExceptions.9.0.9.nupkg new file mode 100644 index 0000000..bf1bcf8 Binary files /dev/null and b/releases/RestExceptions.9.0.9.nupkg differ diff --git a/src/RestExceptions/Exceptions/4xx/BadRequestRestException.cs b/src/RestExceptions/Exceptions/4xx/BadRequestRestException.cs index da83709..cbb22c0 100644 --- a/src/RestExceptions/Exceptions/4xx/BadRequestRestException.cs +++ b/src/RestExceptions/Exceptions/4xx/BadRequestRestException.cs @@ -10,6 +10,6 @@ public class BadRequestRestException( { public static string DefaultMessage => "Malformed request syntax."; - public override string Title => "Bad request"; + public override string Title => "Bad Request"; public override HttpStatusCode StatusCode => HttpStatusCode.BadRequest; } diff --git a/src/RestExceptions/RestExceptions.csproj b/src/RestExceptions/RestExceptions.csproj index da43fcc..d2a636a 100644 --- a/src/RestExceptions/RestExceptions.csproj +++ b/src/RestExceptions/RestExceptions.csproj @@ -2,7 +2,7 @@ RestExceptions RestExceptions - 9.0.11 + 10.0.0 Stratis-Dermanoutsos Stratis-OSS @@ -16,17 +16,12 @@ Extensible Web API middleware that maps all exceptions to standardized RFC7807-compliant HTTP responses. open-source http oss rest errors csharp dotnet exceptions problem-details rfc7807 README.md - * Updated dependencies to latest versions. + * Upgraded to .NET 10 and C# 14. true - - - - - diff --git a/tests/RestExceptions.IntegrationTests/ApiFactory.cs b/tests/RestExceptions.IntegrationTests/ApiFactory.cs new file mode 100644 index 0000000..883ad41 --- /dev/null +++ b/tests/RestExceptions.IntegrationTests/ApiFactory.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc.Testing; + +namespace RestExceptions.IntegrationTests; + +public class ApiFactory : WebApplicationFactory +{ + public HttpClient CreateHttpsClient() + { + return CreateClient(new WebApplicationFactoryClientOptions + { + BaseAddress = new Uri("https://localhost") + }); + } +} diff --git a/tests/RestExceptions.IntegrationTests/RestExceptions.IntegrationTests.csproj b/tests/RestExceptions.IntegrationTests/RestExceptions.IntegrationTests.csproj new file mode 100644 index 0000000..49c63f5 --- /dev/null +++ b/tests/RestExceptions.IntegrationTests/RestExceptions.IntegrationTests.csproj @@ -0,0 +1,25 @@ + + + + false + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/RestExceptions.IntegrationTests/Services/Abstractions/ApiTests.cs b/tests/RestExceptions.IntegrationTests/Services/Abstractions/ApiTests.cs new file mode 100644 index 0000000..fc740a0 --- /dev/null +++ b/tests/RestExceptions.IntegrationTests/Services/Abstractions/ApiTests.cs @@ -0,0 +1,18 @@ +using System.Net.Http.Json; +using Microsoft.AspNetCore.Mvc; + +// ReSharper disable once CheckNamespace +namespace RestExceptions.IntegrationTests.Services; + +public abstract class ApiTests(ApiFactory factory) +{ + protected readonly HttpClient Client = factory.CreateHttpsClient(); + + protected async Task GetProblemDetailsAsync(string path, int statusCode) + { + var resp = await Client.GetAsync(Path.Join(path, statusCode.ToString())); + + var result = await resp.Content.ReadFromJsonAsync(); + return result; + } +} diff --git a/tests/RestExceptions.IntegrationTests/Services/RestExceptionsTests.cs b/tests/RestExceptions.IntegrationTests/Services/RestExceptionsTests.cs new file mode 100644 index 0000000..138e40f --- /dev/null +++ b/tests/RestExceptions.IntegrationTests/Services/RestExceptionsTests.cs @@ -0,0 +1,56 @@ +using Shouldly; + +namespace RestExceptions.IntegrationTests.Services; + +public class RestExceptionsTests(ApiFactory factory) : ApiTests(factory), IClassFixture +{ + [Theory] + [InlineData(0, "Internal Server Error")] + [InlineData(400, "Bad Request")] + [InlineData(401, "Unauthorized")] + [InlineData(402, "Payment Required")] + [InlineData(403, "Forbidden")] + [InlineData(404, "Not Found")] + [InlineData(405, "Method Not Allowed")] + [InlineData(406, "Not Acceptable")] + [InlineData(407, "Proxy Authentication Required")] + [InlineData(408, "Request Timeout")] + [InlineData(409, "Conflict")] + [InlineData(410, "Gone")] + [InlineData(411, "Length Required")] + [InlineData(412, "Precondition Failed")] + [InlineData(413, "Content Too Large")] + [InlineData(414, "URI Too Long")] + [InlineData(415, "Unsupported Media Type")] + [InlineData(416, "Range Not Satisfiable")] + [InlineData(417, "Expectation Failed")] + [InlineData(421, "Misdirected Request")] + [InlineData(422, "Unprocessable Content")] + [InlineData(423, "Locked")] + [InlineData(424, "Failed Dependency")] + [InlineData(426, "Upgrade Required")] + [InlineData(428, "Precondition Required")] + [InlineData(429, "Too Many Requests")] + [InlineData(431, "Request Header Fields Too Large")] + [InlineData(451, "Unavailable For Legal Reasons")] + [InlineData(500, "Internal Server Error")] + [InlineData(501, "Not Implemented")] + [InlineData(502, "Bad Gateway")] + [InlineData(503, "Service Unavailable")] + [InlineData(504, "Gateway Timeout")] + [InlineData(505, "HTTP Version Not Supported")] + [InlineData(506, "Variant Also Negotiates")] + [InlineData(507, "Insufficient Storage")] + [InlineData(508, "Loop Detected")] + [InlineData(510, "Not Extended")] + [InlineData(511, "Network Authentication Required")] + public async Task Status_Code_To_Http_Error_Title(int statusCode, string expected) + { + // Act + var problemDetails = await GetProblemDetailsAsync("error", statusCode); + var result = problemDetails?.Title ?? string.Empty; + + // Assert + result.ShouldBe(expected); + } +}