mirror of
https://github.com/azaion/missions.git
synced 2026-06-21 08:21:08 +00:00
3398ec49a0
ci/woodpecker/push/build-arm Pipeline was successful
- Updated Azaion.Missions.csproj to exclude test sources from service compilation, preventing build failures due to test project dependencies. - Modified docker-compose.test.yml to preload the pg_stat_statements extension for testing and adjusted JWT refresh intervals for better test execution timing. - Enhanced Dockerfile to install wget for health checks and ensure proper initialization of the container. - Introduced a test-only endpoint for JWKS refresh to facilitate end-to-end testing without relying on the default refresh intervals. - Updated DTOs in ApiDtos.cs to reflect camelCase naming conventions for consistency with service responses. - Improved test cases to handle JWKS rotation and refresh scenarios effectively, ensuring robust validation of JWT handling. This commit lays the groundwork for more reliable and efficient testing of the Azaion.Missions project.
87 lines
3.7 KiB
C#
87 lines
3.7 KiB
C#
using System.Diagnostics;
|
|
using System.Net;
|
|
using System.Net.Http.Headers;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using Azaion.Missions.E2E.Fixtures;
|
|
using Azaion.Missions.E2E.Helpers;
|
|
using Xunit;
|
|
|
|
namespace Azaion.Missions.E2E.Tests.Resilience;
|
|
|
|
/// <summary>
|
|
/// NFT-RES-07 — operational counterpart of NFT-SEC-11. Verifies that a JWKS
|
|
/// rotation propagates through the SUT WITHOUT a process restart. The
|
|
/// security-shaped variant lives in <c>Tests/Security/JwksRotationTests.cs</c>;
|
|
/// here the assertion focuses on
|
|
/// <c>docker inspect --format '{{.State.StartedAt}}' missions-sut</c>
|
|
/// returning the SAME ISO-8601 timestamp before and after the rotation flow.
|
|
/// Traces: AC-5.7.
|
|
/// </summary>
|
|
[Collection("JwksRotation")]
|
|
[Trait("Category", "Res")]
|
|
[Trait("db_access", "seed-or-assert-only")]
|
|
public sealed class JwksRotationNoRestartTests : TestBase, IClassFixture<DbResetFixture>
|
|
{
|
|
[SkippableFact(Timeout = 200_000)]
|
|
[Trait("Traces", "AC-5.7")]
|
|
[Trait("max_ms", "180000")]
|
|
public async Task NFT_RES_07_jwks_rotation_propagates_without_missions_restart()
|
|
{
|
|
Skip.IfNot(MissionsContainerHelper.Enabled,
|
|
"Requires docker CLI access (COMPOSE_RESTART_ENABLED=1) to read StartedAt.");
|
|
|
|
// Arrange — capture StartedAt before any rotation activity so the
|
|
// post-flow comparison is anchored to "before this test started".
|
|
DbResetFixture.ResetDatabase(TestEnvironment.DbSideChannel);
|
|
Seeds.Apply(Seeds.OneDefaultVehicle.Sql);
|
|
|
|
var startedAtBefore = MissionsContainerHelper.GetStartedAt("missions-sut");
|
|
|
|
var t1 = await Tokens.MintDefaultAsync();
|
|
var kidV1 = t1.Kid;
|
|
using (var resp = await CallVehiclesAsync(t1.Jwt))
|
|
await HttpAssertions.AssertStatusAsync(resp, HttpStatusCode.OK);
|
|
|
|
// Act 1 — rotate; mint a token with the new kid; assert pre-refresh 401.
|
|
var kidV2 = await RotateMockAsync();
|
|
Assert.NotEqual(kidV1, kidV2);
|
|
|
|
var t2 = await Tokens.MintDefaultAsync();
|
|
Assert.Equal(kidV2, t2.Kid);
|
|
|
|
using (var resp = await CallVehiclesAsync(t2.Jwt))
|
|
await HttpAssertions.AssertStatusAsync(resp, HttpStatusCode.Unauthorized);
|
|
|
|
// Act 2 — force JWKS refresh via the test-only hook (the library's
|
|
// 5-minute floor on AutomaticRefreshInterval forbids the proactive
|
|
// path and our custom IssuerSigningKeyResolver bypasses the JwtBearer
|
|
// signature-failure refresh path; see Helpers/JwksRefreshHelper.cs).
|
|
await JwksRefreshHelper.ForceRefreshAsync(Missions);
|
|
using (var resp = await CallVehiclesAsync(t2.Jwt))
|
|
await HttpAssertions.AssertStatusAsync(resp, HttpStatusCode.OK);
|
|
|
|
// Assert — service did NOT restart.
|
|
var startedAtAfter = MissionsContainerHelper.GetStartedAt("missions-sut");
|
|
Assert.Equal(startedAtBefore, startedAtAfter);
|
|
}
|
|
|
|
private async Task<HttpResponseMessage> CallVehiclesAsync(string jwt)
|
|
{
|
|
var req = new HttpRequestMessage(HttpMethod.Get, "/vehicles");
|
|
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
|
|
return await Missions.SendAsync(req);
|
|
}
|
|
|
|
private static async Task<string> RotateMockAsync()
|
|
{
|
|
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
|
|
var rotateUrl = new Uri(new Uri(TestEnvironment.JwksMockBaseUrl), "/rotate-key");
|
|
using var resp = await http.PostAsync(rotateUrl, content: null);
|
|
resp.EnsureSuccessStatusCode();
|
|
var body = await resp.Content.ReadFromJsonAsync<JsonElement>();
|
|
return body.GetProperty("kid").GetString()
|
|
?? throw new InvalidOperationException("mock /rotate-key returned no kid");
|
|
}
|
|
}
|