Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using Omika.Core.Observability.Outbox;
|
||||
|
||||
namespace Omika.Tests.Observability.Outbox;
|
||||
|
||||
/// <summary>Tests du client HTTP d'upload : classification des statuts, timeout, anti-boucle.</summary>
|
||||
public class DiagnosticUploadClientTests
|
||||
{
|
||||
private static DiagnosticBatchDto NewBatch() =>
|
||||
new()
|
||||
{
|
||||
Events =
|
||||
[
|
||||
new DiagnosticEventDto { EventId = Guid.NewGuid(), EventCode = "OMK-MOB-APP-001" }
|
||||
]
|
||||
};
|
||||
|
||||
private static DiagnosticUploadClient CreateClient(
|
||||
StubHandler handler,
|
||||
DiagnosticUploadOptions? options = null,
|
||||
string? token = "token-1") =>
|
||||
new(
|
||||
new HttpClient(handler),
|
||||
"http://localhost:5000",
|
||||
() => token,
|
||||
options ?? new DiagnosticUploadOptions { Timeout = TimeSpan.FromSeconds(5) });
|
||||
|
||||
[Fact]
|
||||
public async Task NoToken_ReturnsUnauthorized_WithoutHttp()
|
||||
{
|
||||
var handler = new StubHandler { StatusCode = HttpStatusCode.OK };
|
||||
DiagnosticUploadClient client = CreateClient(handler, token: null);
|
||||
|
||||
DiagnosticUploadResult result = await client.UploadAsync(NewBatch());
|
||||
|
||||
Assert.Equal(DiagnosticUploadOutcome.Unauthorized, result.Outcome);
|
||||
Assert.Equal(0, handler.Calls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Success_MapsToSuccess_WithAcceptedCount()
|
||||
{
|
||||
var handler = new StubHandler
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
ResponseBody = """{"batchId":"00000000-0000-0000-0000-000000000001","acceptedCount":3}"""
|
||||
};
|
||||
DiagnosticUploadClient client = CreateClient(handler);
|
||||
|
||||
DiagnosticUploadResult result = await client.UploadAsync(NewBatch());
|
||||
|
||||
Assert.Equal(DiagnosticUploadOutcome.Success, result.Outcome);
|
||||
Assert.Equal(3, result.AcceptedCount);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(HttpStatusCode.Unauthorized, DiagnosticUploadOutcome.Unauthorized)]
|
||||
[InlineData(HttpStatusCode.Forbidden, DiagnosticUploadOutcome.Unauthorized)]
|
||||
[InlineData(HttpStatusCode.BadRequest, DiagnosticUploadOutcome.Rejected)]
|
||||
[InlineData(HttpStatusCode.RequestEntityTooLarge, DiagnosticUploadOutcome.TooLarge)]
|
||||
[InlineData(HttpStatusCode.InternalServerError, DiagnosticUploadOutcome.Transient)]
|
||||
[InlineData(HttpStatusCode.ServiceUnavailable, DiagnosticUploadOutcome.Transient)]
|
||||
[InlineData(HttpStatusCode.GatewayTimeout, DiagnosticUploadOutcome.Transient)]
|
||||
public async Task StatusCodes_MapToExpectedOutcome(HttpStatusCode status, DiagnosticUploadOutcome expected)
|
||||
{
|
||||
var handler = new StubHandler { StatusCode = status };
|
||||
DiagnosticUploadClient client = CreateClient(handler);
|
||||
|
||||
DiagnosticUploadResult result = await client.UploadAsync(NewBatch());
|
||||
|
||||
Assert.Equal(expected, result.Outcome);
|
||||
Assert.Equal((int)status, result.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RateLimited_HonorsRetryAfter()
|
||||
{
|
||||
var handler = new StubHandler
|
||||
{
|
||||
StatusCode = HttpStatusCode.TooManyRequests,
|
||||
RetryAfterSeconds = 42
|
||||
};
|
||||
DiagnosticUploadClient client = CreateClient(handler);
|
||||
|
||||
DiagnosticUploadResult result = await client.UploadAsync(NewBatch());
|
||||
|
||||
Assert.Equal(DiagnosticUploadOutcome.RateLimited, result.Outcome);
|
||||
Assert.Equal(TimeSpan.FromSeconds(42), result.RetryAfter);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Timeout_MapsToTransient()
|
||||
{
|
||||
var handler = new StubHandler { DelayMs = 2000, StatusCode = HttpStatusCode.OK };
|
||||
DiagnosticUploadClient client = CreateClient(handler, new DiagnosticUploadOptions { Timeout = TimeSpan.FromMilliseconds(10) });
|
||||
|
||||
DiagnosticUploadResult result = await client.UploadAsync(NewBatch());
|
||||
|
||||
Assert.Equal(DiagnosticUploadOutcome.Transient, result.Outcome);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Upload_SendsBearerToken()
|
||||
{
|
||||
var handler = new StubHandler { StatusCode = HttpStatusCode.OK };
|
||||
DiagnosticUploadClient client = CreateClient(handler, token: "secret-token");
|
||||
|
||||
await client.UploadAsync(NewBatch());
|
||||
|
||||
Assert.Equal("Bearer secret-token", handler.LastAuthorization);
|
||||
}
|
||||
|
||||
private sealed class StubHandler : HttpMessageHandler
|
||||
{
|
||||
public HttpStatusCode StatusCode { get; set; } = HttpStatusCode.OK;
|
||||
|
||||
public int DelayMs { get; set; }
|
||||
|
||||
public string ResponseBody { get; set; } = "{}";
|
||||
|
||||
public int? RetryAfterSeconds { get; set; }
|
||||
|
||||
public int Calls { get; private set; }
|
||||
|
||||
public string? LastAuthorization { get; private set; }
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(
|
||||
HttpRequestMessage request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Calls++;
|
||||
LastAuthorization = request.Headers.Authorization?.ToString();
|
||||
if (DelayMs > 0)
|
||||
{
|
||||
await Task.Delay(DelayMs, cancellationToken);
|
||||
}
|
||||
|
||||
var response = new HttpResponseMessage(StatusCode);
|
||||
if (RetryAfterSeconds is { } seconds)
|
||||
{
|
||||
response.Headers.RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromSeconds(seconds));
|
||||
}
|
||||
|
||||
response.Content = new StringContent(ResponseBody, Encoding.UTF8, "application/json");
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user