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

91 lines
3.4 KiB
C#

using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using Omika.App.Local.Abstractions;
using Omika.App.Local.Services;
using Omika.Core.Contracts.Mobile.License;
namespace Omika.Tests.Mobile.Local;
/// <summary>
/// Tests du MobileApiClient : aucun fallback d'URL, échec propre sans URL,
/// et utilisation de l'URL d'inscription une fois configurée.
/// </summary>
public class MobileApiClientNoUrlTests
{
private sealed class RecordingHandler : HttpMessageHandler
{
public int Calls { get; private set; }
public string? LastUri { get; private set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Calls++;
LastUri = request.RequestUri?.ToString();
string json = JsonSerializer.Serialize(new MobileLicenseSessionResponse
{
License = new LicenseResponse { IsValid = true }
}, new JsonSerializerOptions(JsonSerializerDefaults.Web));
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(json, Encoding.UTF8, "application/json")
});
}
}
/// <summary>
/// Aucune URL d'API : l'échec est immédiat, typé, et AUCUN octet ne part (aucun
/// fallback localhost). Le type dédié permet de classer cet état en Warning/P2 —
/// et non en incident P1 — tout en restant une InvalidOperationException pour les
/// traitements existants qui le considèrent comme non terminal.
/// </summary>
[Fact]
public async Task SansUrl_LeveExceptionDedieeSansAppelHttp()
{
var handler = new RecordingHandler();
var client = new MobileApiClient(new HttpClient(handler));
MobileEnrollmentRequiredException ex =
await Assert.ThrowsAsync<MobileEnrollmentRequiredException>(() => client.GetBootstrapAsync());
Assert.Equal(0, handler.Calls); // aucun appel HTTP, aucun fallback localhost.
Assert.Equal(MobileEnrollmentRequiredException.DefaultMessage, ex.Message);
Assert.IsAssignableFrom<InvalidOperationException>(ex);
}
/// <summary>
/// Ticket de production du 09/10 (AgendaViewModel) : c'est le repli API de
/// l'agenda qui a révélé l'absence d'URL. Le garde-fou doit s'appliquer à tout
/// appel métier, sans jamais émettre de requête.
/// </summary>
[Fact]
public async Task SansUrl_AppelMetierAgenda_LeveExceptionDedieeSansAppelHttp()
{
var handler = new RecordingHandler();
var client = new MobileApiClient(new HttpClient(handler));
await Assert.ThrowsAsync<MobileEnrollmentRequiredException>(() =>
client.GetRendezVousByCollaborateurAsync(
Guid.NewGuid(), DateTime.Today, DateTime.Today.AddDays(7)));
Assert.Equal(0, handler.Calls);
}
[Fact]
public async Task UrlConfiguree_UtiliseUrlEnregistree()
{
var handler = new RecordingHandler();
var client = new MobileApiClient(new HttpClient(handler))
{
BaseUrl = "https://api.example.com"
};
client.AccessToken = "token";
await client.GetBootstrapAsync();
Assert.Equal(1, handler.Calls);
Assert.StartsWith("https://api.example.com/api/mobile/bootstrap", handler.LastUri);
}
}