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

207 lines
7.9 KiB
C#

using System.Net.Http;
using System.Security.Cryptography;
using Omika.App.Local.Abstractions;
using Omika.App.Local.Models;
using Omika.App.Local.Repositories;
using Omika.App.Local.Services;
using Omika.Core.Contracts.Mobile.License;
using Omika.Core.Observability.Diagnostic;
using Omika.Core.Services.Mobile;
namespace Omika.Tests.Mobile.Local;
/// <summary>
/// Tests du service d'inscription (QR → licence → API → stockage).
/// </summary>
public class MobileEnrollmentServiceTests : LocalTestBase
{
private const string Kid = "test-kid-1";
private sealed class FakeLicenseEnrollmentClient : ILicenseEnrollmentClient
{
public MobileLicenseSessionResponse ActivateResponse { get; set; } = new();
public string? LastUrl { get; private set; }
public LicenseActivateRequest? LastRequest { get; private set; }
public Task<MobileLicenseSessionResponse> ActivateAsync(
string licenseServiceUrl, LicenseActivateRequest request, CancellationToken cancellationToken = default)
{
LastUrl = licenseServiceUrl;
LastRequest = request;
return Task.FromResult(ActivateResponse);
}
public Task<MobileLicenseSessionResponse> ValidateAsync(
string licenseServiceUrl, LicenseValidateRequest request, CancellationToken cancellationToken = default)
=> Task.FromResult(new MobileLicenseSessionResponse());
}
private sealed class FakeDeviceIdProvider : IDeviceIdProvider
{
public Task<string> GetDeviceIdAsync(CancellationToken cancellationToken = default)
=> Task.FromResult("device-1");
}
private static string CreateSignedQr(string code, string numero, string url, out IReadOnlyDictionary<string, string> registry)
{
using var rsa = RSA.Create(2048);
registry = new Dictionary<string, string> { [Kid] = rsa.ToXmlString(false) };
return EnrollmentQrCodec.Sign(
EnrollmentQrCodec.BuildPayloadJson(Kid, code, numero, url),
rsa.ToXmlString(true));
}
private MobileEnrollmentService CreateService(
FakeLicenseEnrollmentClient licenseClient,
IReadOnlyDictionary<string, string> registry,
MobileApiClient apiClient,
MobileEnrollmentStore? store = null)
=> new(
licenseClient,
store ?? new MobileEnrollmentStore(Database),
new FakeDeviceIdProvider(),
new StubSessionService(),
apiClient,
registry);
[Fact]
public async Task LicenceInvalide_OmikaApiUrlNonEnregistree()
{
var licenseClient = new FakeLicenseEnrollmentClient
{
ActivateResponse = new MobileLicenseSessionResponse
{
License = new LicenseResponse { IsValid = false, ErrorCode = "E3002", ErrorMessage = "Licence inactive" }
}
};
var apiClient = new MobileApiClient(new HttpClient());
string qr = CreateSignedQr("BATIK.01", "LIC-001", "https://licence.example.com", out var registry);
var service = CreateService(licenseClient, registry, apiClient);
EnrollmentResult result = await service.EnrollAsync(qr);
Assert.False(result.Success);
Assert.Null(await service.LoadAsync());
Assert.Null(apiClient.BaseUrl);
}
[Fact]
public async Task LicenceValideSansApiUrl_InscriptionRefusee()
{
var licenseClient = new FakeLicenseEnrollmentClient
{
ActivateResponse = new MobileLicenseSessionResponse
{
License = new LicenseResponse { IsValid = true, ApiUrl = null }
}
};
var apiClient = new MobileApiClient(new HttpClient());
string qr = CreateSignedQr("BATIK.01", "LIC-001", "https://licence.example.com", out var registry);
var service = CreateService(licenseClient, registry, apiClient);
EnrollmentResult result = await service.EnrollAsync(qr);
Assert.False(result.Success);
Assert.Null(await service.LoadAsync());
}
[Fact]
public async Task LicenceValideApiUrlHttp_Refusee()
{
var licenseClient = new FakeLicenseEnrollmentClient
{
ActivateResponse = new MobileLicenseSessionResponse
{
License = new LicenseResponse { IsValid = true, ApiUrl = "http://api.example.com" }
}
};
var apiClient = new MobileApiClient(new HttpClient());
string qr = CreateSignedQr("BATIK.01", "LIC-001", "https://licence.example.com", out var registry);
var service = CreateService(licenseClient, registry, apiClient);
EnrollmentResult result = await service.EnrollAsync(qr);
Assert.False(result.Success);
Assert.Null(await service.LoadAsync());
}
[Fact]
public async Task LicenceValideApiUrlHttps_Enregistree()
{
var licenseClient = new FakeLicenseEnrollmentClient
{
ActivateResponse = new MobileLicenseSessionResponse
{
License = new LicenseResponse { IsValid = true, ApiUrl = "https://api.example.com" },
AccessToken = "token-1"
}
};
var apiClient = new MobileApiClient(new HttpClient());
string qr = CreateSignedQr("BATIK.01", "LIC-001", "https://licence.example.com", out var registry);
var service = CreateService(licenseClient, registry, apiClient);
EnrollmentResult result = await service.EnrollAsync(qr);
Assert.True(result.Success);
Assert.Equal("https://api.example.com", result.Configuration!.OmikaApiUrl);
Assert.Equal("https://licence.example.com", result.Configuration.LicenseServiceUrl);
Assert.Equal("https://api.example.com", apiClient.BaseUrl);
MobileEnrollmentConfiguration? persisted = await service.LoadAsync();
Assert.NotNull(persisted);
Assert.Equal("https://api.example.com", persisted!.OmikaApiUrl);
}
[Fact]
public async Task QrInvalide_RefuseSansAppelReseau()
{
var licenseClient = new FakeLicenseEnrollmentClient();
var apiClient = new MobileApiClient(new HttpClient());
string qr = CreateSignedQr("BATIK.01", "LIC-001", "https://licence.example.com", out var registry);
var service = CreateService(licenseClient, registry, apiClient);
EnrollmentResult result = await service.EnrollAsync("qr-corrompu");
Assert.False(result.Success);
Assert.Null(licenseClient.LastUrl); // aucun appel au service de licence
}
[Fact]
public async Task Enroll_AvecDeviceInfo_RemplitChampsActivation()
{
var licenseClient = new FakeLicenseEnrollmentClient
{
ActivateResponse = new MobileLicenseSessionResponse
{
License = new LicenseResponse { IsValid = false } // suffit pour observer la requête
}
};
var apiClient = new MobileApiClient(new HttpClient());
string qr = CreateSignedQr("BATIK.01", "LIC-001", "https://licence.example.com", out var registry);
var deviceInfo = new DeviceInfoSnapshot
{
OperatingSystem = "Android",
OperatingSystemVersion = "16",
AppVersion = "1.0.0",
DeviceManufacturer = "Xiaomi",
DeviceModel = "23113RKC6G"
};
var service = new MobileEnrollmentService(
licenseClient,
new MobileEnrollmentStore(Database),
new FakeDeviceIdProvider(),
new StubSessionService(),
apiClient,
registry,
deviceInfo);
await service.EnrollAsync(qr);
Assert.Equal("Android", licenseClient.LastRequest!.OsType);
Assert.Equal("16", licenseClient.LastRequest.OsVersion);
Assert.Equal("1.0.0", licenseClient.LastRequest.AppVersion);
Assert.Equal("manufacturer=Xiaomi;model=23113RKC6G", licenseClient.LastRequest.DeviceInfo);
Assert.Null(licenseClient.LastRequest.NotificationKey);
}
}