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); } }