132 lines
5.1 KiB
C#
132 lines
5.1 KiB
C#
using System.Text.Json;
|
|
using App.Core.Interfaces.Licensing;
|
|
using App.Core.Services.Licensing;
|
|
|
|
namespace App.Core.Tests.Licensing;
|
|
|
|
/// <summary>
|
|
/// Couvre uniquement le flux "essai" de LicenseService (pas de licence installée) : c'est la
|
|
/// partie testable sans dépendre de la vraie clé publique Ed25519, qui n'est pas encore générée
|
|
/// (voir Ed25519PublicKeyProvider). Le flux "licence installée" (ActivateLicense / licence déjà
|
|
/// présente) sera couvert une fois LicenseGenerator exécuté pour de vrai et la clé embarquée.
|
|
/// </summary>
|
|
public class LicenseServiceTests
|
|
{
|
|
private sealed class FakeHardwareIdProvider(HardwareId hardwareId) : IHardwareIdProvider
|
|
{
|
|
public HardwareId GetCurrent() => hardwareId;
|
|
}
|
|
|
|
private sealed class FakeTrialStateStore : ITrialStateStore
|
|
{
|
|
public TrialState? Stored { get; private set; }
|
|
|
|
public TrialState? Load() => Stored;
|
|
|
|
public void Save(TrialState state) => Stored = state;
|
|
}
|
|
|
|
private sealed class FakeLicenseFileRepository : ILicenseFileRepository
|
|
{
|
|
public LicenseFile? Installed { get; set; }
|
|
|
|
public LicenseFile? LoadInstalled() => Installed;
|
|
|
|
public void Install(LicenseFile license) => Installed = license;
|
|
}
|
|
|
|
private sealed class CorruptedLicenseFileRepository : ILicenseFileRepository
|
|
{
|
|
public LicenseFile? LoadInstalled() => throw new JsonException("fichier .lic corrompu (test)");
|
|
|
|
public void Install(LicenseFile license) => throw new NotSupportedException();
|
|
}
|
|
|
|
private static readonly HardwareId TestHardware = new("MB-TEST", "DISK-TEST");
|
|
|
|
private static LicenseService CreateService(FakeTrialStateStore trialStore, ILicenseFileRepository? licenseRepo = null) =>
|
|
new(new FakeHardwareIdProvider(TestHardware), trialStore, licenseRepo ?? new FakeLicenseFileRepository());
|
|
|
|
[Fact]
|
|
public void CheckLicenseStatus_FirstLaunch_CreatesTrialAndReturnsFullDuration()
|
|
{
|
|
var trialStore = new FakeTrialStateStore();
|
|
var service = CreateService(trialStore);
|
|
var now = new DateTime(2026, 1, 1, 9, 0, 0, DateTimeKind.Utc);
|
|
|
|
var result = service.CheckLicenseStatus(now);
|
|
|
|
Assert.Equal(LicenseStatus.TrialActive, result.Status);
|
|
Assert.Equal(TrialState.TrialDurationDays, result.TrialDaysRemaining);
|
|
Assert.NotNull(trialStore.Stored);
|
|
Assert.Equal(now, trialStore.Stored!.InstallDateUtc);
|
|
Assert.Equal(TestHardware.ComputeHash(), trialStore.Stored.HardwareIdHash);
|
|
}
|
|
|
|
[Fact]
|
|
public void CheckLicenseStatus_SubsequentLaunch_ReturnsRemainingDaysAndUpdatesLastRun()
|
|
{
|
|
var trialStore = new FakeTrialStateStore();
|
|
var service = CreateService(trialStore);
|
|
var installDate = new DateTime(2026, 1, 1, 9, 0, 0, DateTimeKind.Utc);
|
|
service.CheckLicenseStatus(installDate);
|
|
|
|
var result = service.CheckLicenseStatus(installDate.AddDays(10));
|
|
|
|
Assert.Equal(LicenseStatus.TrialActive, result.Status);
|
|
Assert.Equal(20, result.TrialDaysRemaining);
|
|
Assert.Equal(installDate.AddDays(10), trialStore.Stored!.LastRunTimestampUtc);
|
|
Assert.Equal(installDate, trialStore.Stored.InstallDateUtc); // date d'installation jamais modifiée
|
|
}
|
|
|
|
[Fact]
|
|
public void CheckLicenseStatus_After30Days_ReturnsTrialExpired()
|
|
{
|
|
var trialStore = new FakeTrialStateStore();
|
|
var service = CreateService(trialStore);
|
|
var installDate = new DateTime(2026, 1, 1, 9, 0, 0, DateTimeKind.Utc);
|
|
service.CheckLicenseStatus(installDate);
|
|
|
|
var result = service.CheckLicenseStatus(installDate.AddDays(31));
|
|
|
|
Assert.Equal(LicenseStatus.TrialExpired, result.Status);
|
|
Assert.Equal(0, result.TrialDaysRemaining);
|
|
}
|
|
|
|
[Fact]
|
|
public void CheckLicenseStatus_ClockRolledBack_ReturnsClockTamperDetected()
|
|
{
|
|
var trialStore = new FakeTrialStateStore();
|
|
var service = CreateService(trialStore);
|
|
var installDate = new DateTime(2026, 1, 15, 9, 0, 0, DateTimeKind.Utc);
|
|
service.CheckLicenseStatus(installDate);
|
|
|
|
// L'utilisateur recule l'horloge système de 5 jours pour tenter de prolonger l'essai.
|
|
var result = service.CheckLicenseStatus(installDate.AddDays(-5));
|
|
|
|
Assert.Equal(LicenseStatus.ClockTamperDetected, result.Status);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetCurrentHardwareId_DelegatesToHardwareIdProvider()
|
|
{
|
|
var service = CreateService(new FakeTrialStateStore());
|
|
|
|
Assert.Equal(TestHardware, service.GetCurrentHardwareId());
|
|
}
|
|
|
|
[Fact]
|
|
public void CheckLicenseStatus_CorruptedLicenseFile_ReturnsInvalidRatherThanFallingBackToTrial()
|
|
{
|
|
var trialStore = new FakeTrialStateStore();
|
|
var service = CreateService(trialStore, new CorruptedLicenseFileRepository());
|
|
|
|
var result = service.CheckLicenseStatus(DateTime.UtcNow);
|
|
|
|
Assert.Equal(LicenseStatus.InvalidSignature, result.Status);
|
|
// Ne doit surtout pas avoir créé un nouvel essai : corrompre le fichier ne doit jamais
|
|
// redonner 30 jours gratuits.
|
|
Assert.Null(trialStore.Stored);
|
|
}
|
|
}
|