207 lines
7.9 KiB
C#
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);
|
|
}
|
|
}
|