Files
Omika/tests/Omika.Tests/Mobile/Local/SessionRefreshHandlerTests.cs
T
2026-10-23 18:46:50 +02:00

180 lines
7.0 KiB
C#

using System.Net;
using System.Net.Http.Headers;
using Omika.App.Local.Services;
namespace Omika.Tests.Mobile.Local;
/// <summary>
/// Tests du SessionRefreshHandler : interception du 401, renouvellement unique,
/// retry borné des GET/HEAD et non-rejeu des écritures.
/// </summary>
public class SessionRefreshHandlerTests
{
private sealed class SequenceHandler : HttpMessageHandler
{
private readonly Queue<HttpResponseMessage> _responses;
public List<HttpRequestMessage> Requests { get; } = [];
public SequenceHandler(params HttpStatusCode[] statuses)
=> _responses = new Queue<HttpResponseMessage>(statuses.Select(s => new HttpResponseMessage(s)));
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Requests.Add(request);
return Task.FromResult(_responses.Count > 0
? _responses.Dequeue()
: new HttpResponseMessage(HttpStatusCode.OK));
}
}
private static HttpRequestMessage GetRequest(string token = "T1")
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://test.local/api/clients");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
return request;
}
private static SessionRefreshHandler CreateHandler(
SequenceHandler inner,
Func<string?, CancellationToken, Task<bool>> revalidate,
Func<string?> getAccessToken)
{
var handler = new SessionRefreshHandler(inner);
handler.Configure(revalidate, getAccessToken);
return handler;
}
[Fact]
public async Task Get401_RevalideUneFois_PuisRetryAvecNouveauToken()
{
var inner = new SequenceHandler(HttpStatusCode.Unauthorized, HttpStatusCode.OK);
string currentToken = "T1";
int validateCalls = 0;
SessionRefreshHandler handler = CreateHandler(
inner,
revalidate: (_, _) => { validateCalls++; currentToken = "T2"; return Task.FromResult(true); },
getAccessToken: () => currentToken);
using var client = new HttpClient(handler);
using var request = GetRequest("T1");
using HttpResponseMessage response = await client.SendAsync(request);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(1, validateCalls);
Assert.Equal(2, inner.Requests.Count);
Assert.Equal("Bearer T2", inner.Requests[1].Headers.Authorization!.ToString());
}
[Fact]
public async Task Get401_RetryEncore401_StopSansSecondValidate()
{
var inner = new SequenceHandler(HttpStatusCode.Unauthorized, HttpStatusCode.Unauthorized);
string currentToken = "T1";
int validateCalls = 0;
SessionRefreshHandler handler = CreateHandler(
inner,
revalidate: (_, _) => { validateCalls++; currentToken = "T2"; return Task.FromResult(true); },
getAccessToken: () => currentToken);
using var client = new HttpClient(handler);
using var request = GetRequest("T1");
using HttpResponseMessage response = await client.SendAsync(request);
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
Assert.Equal(1, validateCalls);
Assert.Equal(2, inner.Requests.Count);
}
[Fact]
public async Task Post401_RevalideMaisNeRejouePasEcriture()
{
var inner = new SequenceHandler(HttpStatusCode.Unauthorized);
string currentToken = "T1";
int validateCalls = 0;
SessionRefreshHandler handler = CreateHandler(
inner,
revalidate: (_, _) => { validateCalls++; currentToken = "T2"; return Task.FromResult(true); },
getAccessToken: () => currentToken);
using var client = new HttpClient(handler);
using var request = new HttpRequestMessage(HttpMethod.Post, "http://test.local/api/notes")
{
Content = new StringContent("{\"contenu\":\"x\"}", System.Text.Encoding.UTF8, "application/json")
};
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "T1");
using HttpResponseMessage response = await client.SendAsync(request);
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
Assert.Equal(1, validateCalls); // renouvelé pour les appels futurs…
Assert.Single(inner.Requests); // …mais l'écriture n'est PAS rejouée.
Assert.Equal("T2", currentToken);
}
[Fact]
public async Task RequeteSkipAuthRefresh_NeDeclenchePasRefresh()
{
var inner = new SequenceHandler(HttpStatusCode.Unauthorized);
int validateCalls = 0;
SessionRefreshHandler handler = CreateHandler(
inner,
revalidate: (_, _) => { validateCalls++; return Task.FromResult(true); },
getAccessToken: () => "T1");
using var client = new HttpClient(handler);
using var request = new HttpRequestMessage(HttpMethod.Post, "http://test.local/api/license/validate");
request.Options.Set(SessionRefreshHandler.SkipAuthRefresh, true);
using HttpResponseMessage response = await client.SendAsync(request);
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
Assert.Equal(0, validateCalls);
Assert.Single(inner.Requests);
}
[Fact]
public async Task Get401_TokenDejaRenouvele_NeRevalidePasEtRetry()
{
var inner = new SequenceHandler(HttpStatusCode.Unauthorized, HttpStatusCode.OK);
int validateCalls = 0;
// Le token courant diffère déjà du token échoué : un autre appel a renouvelé.
SessionRefreshHandler handler = CreateHandler(
inner,
revalidate: (_, _) => { validateCalls++; return Task.FromResult(false); },
getAccessToken: () => "T2");
using var client = new HttpClient(handler);
using var request = GetRequest("T1");
using HttpResponseMessage response = await client.SendAsync(request);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(0, validateCalls);
Assert.Equal(2, inner.Requests.Count);
Assert.Equal("Bearer T2", inner.Requests[1].Headers.Authorization!.ToString());
}
[Fact]
public async Task AppelSansAuthorization_401_NeRevalidePas()
{
var inner = new SequenceHandler(HttpStatusCode.Unauthorized);
int validateCalls = 0;
SessionRefreshHandler handler = CreateHandler(
inner,
revalidate: (_, _) => { validateCalls++; return Task.FromResult(true); },
getAccessToken: () => "T1");
using var client = new HttpClient(handler);
using var request = new HttpRequestMessage(HttpMethod.Get, "http://test.local/api/clients");
using HttpResponseMessage response = await client.SendAsync(request);
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
Assert.Equal(0, validateCalls);
Assert.Single(inner.Requests);
}
}