Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c79a61c
Do not leak exception details in production
elzik Nov 1, 2025
86f0986
Fix unauthorised scenario asserts
elzik Nov 1, 2025
a07c208
Fix whitespace issue
elzik Nov 1, 2025
d4309f8
Fix whitespace
elzik Nov 1, 2025
e765be2
Fix whitespace
elzik Nov 1, 2025
4b34d4b
Ensure tests cover exception handler as well as development excetion …
elzik Nov 1, 2025
9571f3f
Merge branch 'improve-api-error-handling' of https://github.com/elzik…
elzik Nov 1, 2025
a0d1d9f
Revert "Ensure tests cover exception handler as well as development e…
elzik Nov 1, 2025
040c833
Use additional class to ensure tests cover exception handler as well …
elzik Nov 1, 2025
12871ac
Use multiple WebApplicationFactories to cover production and deveopme…
elzik Nov 1, 2025
3ef8ba8
Use TheoryData as return type to provide better type safety
elzik Nov 1, 2025
6867e19
Explicit;ly configure exception handler
elzik Nov 1, 2025
dcde89c
Ensure WebApplicationFactory and client get disposed
elzik Nov 1, 2025
22ce3e7
Add http sample file to solution
elzik Nov 1, 2025
e7f7505
Upgrade to .NET 10 (#172)
elzik Nov 22, 2025
c22f15b
Do not leak exception details in production
elzik Nov 1, 2025
65a7f54
Fix unauthorised scenario asserts
elzik Nov 1, 2025
980b709
Fix whitespace
elzik Nov 1, 2025
11c2d94
Fix whitespace
elzik Nov 1, 2025
a717e05
Ensure tests cover exception handler as well as development excetion …
elzik Nov 1, 2025
7a85bde
Fix whitespace issue
elzik Nov 1, 2025
27eaba5
Revert "Ensure tests cover exception handler as well as development e…
elzik Nov 1, 2025
fa9df67
Use additional class to ensure tests cover exception handler as well …
elzik Nov 1, 2025
2f47678
Use multiple WebApplicationFactories to cover production and deveopme…
elzik Nov 1, 2025
829f415
Use TheoryData as return type to provide better type safety
elzik Nov 1, 2025
539d294
Explicit;ly configure exception handler
elzik Nov 1, 2025
9a806d5
Ensure WebApplicationFactory and client get disposed
elzik Nov 1, 2025
5adc8ed
Add http sample file to solution
elzik Nov 1, 2025
52641f1
Implement ProblemDetails for only caller fixable extensions
elzik Nov 23, 2025
bc484ca
Use extension method for exception handling
elzik Nov 23, 2025
3de67a7
Don't end Problem Deatils titles with full-stops
elzik Nov 23, 2025
89c82cf
Refactor deprecated ConfigureWebHost override pattern
elzik Nov 23, 2025
48fe13c
Treat all ICallerFixableException instaces as a Status400BadRequest
elzik Nov 24, 2025
5b08a15
Add funcitonal Problem Details tests
elzik Nov 24, 2025
fd38a49
Remove unnecessary null-conditional operator
elzik Nov 24, 2025
329fe4a
Don't perform seperate dev and prod health tests - there is no value
elzik Nov 24, 2025
9193756
Make namespaces clearer
elzik Nov 24, 2025
64344d8
Remove unused WebApplicationFactories
elzik Nov 24, 2025
6448dac
Remove unecessary launch profile
elzik Nov 24, 2025
f0862a4
Add trace ID to Problem Details and log
elzik Nov 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Elzik.Breef.Api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ RUN dotnet publish "./Elzik.Breef.Api.csproj" -c "$BUILD_CONFIGURATION" -o /app/
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENV ASPNETCORE_ENVIRONMENT=Production
ENTRYPOINT ["dotnet", "Elzik.Breef.Api.dll"]
16 changes: 15 additions & 1 deletion src/Elzik.Breef.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@
options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});

builder.Services.AddProblemDetails();

builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyOrigin()

Check warning on line 54 in src/Elzik.Breef.Api/Program.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Make sure this permissive CORS policy is safe here. (https://rules.sonarsource.com/csharp/RSPEC-5122)

Check warning on line 54 in src/Elzik.Breef.Api/Program.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Make sure this permissive CORS policy is safe here. (https://rules.sonarsource.com/csharp/RSPEC-5122)

Check warning on line 54 in src/Elzik.Breef.Api/Program.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Make sure this permissive CORS policy is safe here. (https://rules.sonarsource.com/csharp/RSPEC-5122)

Check warning on line 54 in src/Elzik.Breef.Api/Program.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Make sure this permissive CORS policy is safe here. (https://rules.sonarsource.com/csharp/RSPEC-5122)
.AllowAnyMethod()
.AllowAnyHeader();
});
Expand Down Expand Up @@ -137,11 +139,23 @@
builder.Services.AddTransient<IBreefGenerator, BreefGenerator>();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(options => { });
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

app.UseStatusCodePages();

app.UseCors();
app.UseAuth();

app.MapGet("/health", () => Results.Ok(new { status = "Healthy" }))
.AllowAnonymous();
.AllowAnonymous();

app.AddBreefEndpoints();

Expand Down
8 changes: 8 additions & 0 deletions src/Elzik.Breef.Api/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5079"
},
"Local (Production)": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Production"
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5079"
},
"Docker": {
"commandName": "Docker",
"environmentVariables": {
Expand Down
10 changes: 9 additions & 1 deletion tests/Elzik.Breef.Api.Tests.Functional/BreefTestsBase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Elzik.Breef.Api.Presentation;
using Elzik.Breef.Infrastructure.Wallabag;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Shouldly;
using System.Net;
Expand Down Expand Up @@ -78,8 +79,15 @@ public async Task Unauthorised()
challenge.Scheme.ShouldBe("ApiKey");
challenge.Parameter.ShouldNotBeNullOrEmpty();
challenge.Parameter.ShouldContain("BREEF-API-KEY");

var responseString = await response.Content.ReadAsStringAsync();
responseString.ShouldBeEmpty();
responseString.ShouldNotBeNullOrEmpty();
var problemDetails = JsonSerializer
.Deserialize<ProblemDetails>(responseString, JsonSerializerOptions);
problemDetails.ShouldNotBeNull();
problemDetails.Status.ShouldBe(401);
problemDetails.Title.ShouldBe("Unauthorized");
problemDetails.Type.ShouldNotBeNullOrWhiteSpace();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;

namespace Elzik.Breef.Api.Tests.Functional;

public class DevelopmentWebApplicationFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Development");
}
}
44 changes: 37 additions & 7 deletions tests/Elzik.Breef.Api.Tests.Functional/HealthTestsNative.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,47 @@
using Microsoft.AspNetCore.Mvc.Testing;
using Shouldly;
using System.Net;
using System.Net.Http.Json;
using Xunit.Abstractions;

namespace Elzik.Breef.Api.Tests.Functional;

public class HealthTestsNative : HealthTestsBase, IClassFixture<WebApplicationFactory<Program>>
public class HealthTestsNative
{
private readonly WebApplicationFactory<Program> _webApplicationFactory;
private readonly HttpClient _client;
private readonly ITestOutputHelper _output;

protected override HttpClient Client => _client;
public HealthTestsNative(ITestOutputHelper output)
{
_output = output;
}

[Theory]
[InlineData("Development")]
[InlineData("Production")]
public async Task Health_Called_ReturnsOK(string environmentName)
{
// Arrange
_output.WriteLine($"Testing health endpoint in {environmentName} environment");

using WebApplicationFactory<Program> factory = environmentName == "Development"
? new DevelopmentWebApplicationFactory()
: new ProductionWebApplicationFactory();

using var client = factory.CreateClient();
const string baseUrl = "http://localhost";

// Act
var response = await client.GetAsync($"{baseUrl}/health");

// Assert
response.StatusCode.ShouldBe(HttpStatusCode.OK);
var body = await response.Content.ReadFromJsonAsync<HealthResponse>();
body.ShouldNotBeNull();
body!.Status.ShouldBe("Healthy");
}

public HealthTestsNative(WebApplicationFactory<Program> webAppFactory)
private class HealthResponse
{
_webApplicationFactory = webAppFactory;
_client = _webApplicationFactory.CreateClient();
public string Status { get; set; } = string.Empty;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;

namespace Elzik.Breef.Api.Tests.Functional;

public class ProductionWebApplicationFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Production");
}
}