Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user