Skip to content

Commit 1aeabe9

Browse files
authored
Add Health Check (#167)
* Avoid using custom middleware for auth * Add health check * Remove unecessary test * Wait for health endpoint before running Docker tests * Use standard test key in HTTP file * Use AddAuthorizationBuilder to register authorization services and construct policies
1 parent 5ede5db commit 1aeabe9

File tree

6 files changed

+65
-15
lines changed

6 files changed

+65
-15
lines changed
Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using AspNetCore.Authentication.ApiKey;
2+
using Microsoft.AspNetCore.Authorization;
23

34
namespace Elzik.Breef.Api.Auth;
45

@@ -12,22 +13,19 @@ public static void AddAuth(this IServiceCollection services)
1213
options.KeyName = "BREEF-API-KEY";
1314
options.Realm = "BreefAPI";
1415
});
15-
services.AddAuthorization();
16+
17+
var authBuilder = services.AddAuthorizationBuilder();
18+
authBuilder.AddPolicy("RequireAuthenticated", p => p.RequireAuthenticatedUser());
19+
20+
services.Configure<AuthorizationOptions>(options =>
21+
{
22+
options.FallbackPolicy = options.GetPolicy("RequireAuthenticated");
23+
});
1624
}
1725

1826
public static void UseAuth(this WebApplication app)
1927
{
2028
app.UseAuthentication();
2129
app.UseAuthorization();
22-
app.Use(async (context, next) =>
23-
{
24-
if (context.User.Identity != null && !context.User.Identity.IsAuthenticated)
25-
{
26-
context.Response.StatusCode = 401;
27-
await context.Response.WriteAsync("Unauthorised");
28-
return;
29-
}
30-
await next();
31-
});
3230
}
3331
}
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
@Elzik.Breef.Api_HostAddress = http://localhost:5079
22

3+
### Breefs
4+
35
Post {{Elzik.Breef.Api_HostAddress}}/breefs
46
Content-Type: application/json
57
BREEF-API-KEY: test-key
68
{
7-
"url":"https://www.reddit.com/r/dotnet/comments/1o0j6or/im_giving_up_on_copilot_i_spend_more_time/"
9+
"url":"https://www.reddit.com/r/selfhosted/comments/1og7kjd/a_note_to_myself_from_the_future_document/"
810
}
11+
12+
### Health
13+
14+
Get {{Elzik.Breef.Api_HostAddress}}/health
15+
Content-Type: application/json

src/Elzik.Breef.Api/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ public static async Task Main(string[] args)
135135
app.UseCors();
136136
app.UseAuth();
137137

138+
app.MapGet("/health", () => Results.Ok(new { status = "Healthy" }))
139+
.AllowAnonymous();
140+
138141
app.AddBreefEndpoints();
139142

140143
await app.RunAsync();

tests/Elzik.Breef.Api.Tests.Functional/BreefTestsBase.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,13 @@ public async Task Unauthorised()
7373

7474
// Assert
7575
response.StatusCode.ShouldBe(HttpStatusCode.Unauthorized);
76+
response.Headers.WwwAuthenticate.ShouldNotBeEmpty();
77+
var challenge = response.Headers.WwwAuthenticate.First();
78+
challenge.Scheme.ShouldBe("ApiKey");
79+
challenge.Parameter.ShouldNotBeNullOrEmpty();
80+
challenge.Parameter.ShouldContain("BREEF-API-KEY");
7681
var responseString = await response.Content.ReadAsStringAsync();
77-
responseString.ShouldNotBeNullOrEmpty();
78-
responseString.ShouldContain("Unauthorised");
82+
responseString.ShouldBeEmpty();
7983
}
8084
}
8185
}

tests/Elzik.Breef.Api.Tests.Functional/BreefTestsDocker.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ public BreefTestsDocker(ITestOutputHelper testOutputHelper)
8080
.WithEnvironment("breef_Wallabag__Password", breefWallabagPassword)
8181
.WithEnvironment("breef_Wallabag__ClientId", breefWallabagClientId)
8282
.WithEnvironment("breef_Wallabag__ClientSecret", breefWallabagClientSecret)
83-
.WithWaitStrategy(Wait.ForUnixContainer().UntilInternalTcpPortIsAvailable(8080))
83+
.WithWaitStrategy(Wait.ForUnixContainer().UntilHttpRequestIsSucceeded(request => request
84+
.ForPort(8080)
85+
.ForPath("/health")
86+
.ForStatusCode(System.Net.HttpStatusCode.OK)))
8487
.WithOutputConsumer(outputConsumer)
8588
.Build();
8689
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Shouldly;
2+
using System.Net;
3+
using System.Net.Http.Json;
4+
using Xunit;
5+
6+
namespace Elzik.Breef.Api.Tests.Functional
7+
{
8+
public class HealthEndpointTests : IClassFixture<Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<Elzik.Breef.Api.Program>>
9+
{
10+
private readonly HttpClient _client;
11+
12+
public HealthEndpointTests(Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<Elzik.Breef.Api.Program> factory)
13+
{
14+
_client = factory.CreateClient();
15+
}
16+
17+
[Fact]
18+
public async Task Health_Called_ReturnsOK()
19+
{
20+
// Act
21+
var response = await _client.GetAsync("/health");
22+
23+
// Assert
24+
response.StatusCode.ShouldBe(HttpStatusCode.OK);
25+
var body = await response.Content.ReadFromJsonAsync<HealthResponse>();
26+
body.ShouldNotBeNull();
27+
body!.Status.ShouldBe("Healthy");
28+
}
29+
30+
private class HealthResponse
31+
{
32+
public string Status { get; set; } = string.Empty;
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)