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;
///
/// Tests du MobileApiClient : aucun fallback d'URL, échec propre sans URL,
/// et utilisation de l'URL d'inscription une fois configurée.
///
public class MobileApiClientNoUrlTests
{
private sealed class RecordingHandler : HttpMessageHandler
{
public int Calls { get; private set; }
public string? LastUri { get; private set; }
protected override Task 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")
});
}
}
///
/// 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.
///
[Fact]
public async Task SansUrl_LeveExceptionDedieeSansAppelHttp()
{
var handler = new RecordingHandler();
var client = new MobileApiClient(new HttpClient(handler));
MobileEnrollmentRequiredException ex =
await Assert.ThrowsAsync(() => client.GetBootstrapAsync());
Assert.Equal(0, handler.Calls); // aucun appel HTTP, aucun fallback localhost.
Assert.Equal(MobileEnrollmentRequiredException.DefaultMessage, ex.Message);
Assert.IsAssignableFrom(ex);
}
///
/// 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.
///
[Fact]
public async Task SansUrl_AppelMetierAgenda_LeveExceptionDedieeSansAppelHttp()
{
var handler = new RecordingHandler();
var client = new MobileApiClient(new HttpClient(handler));
await Assert.ThrowsAsync(() =>
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);
}
}