Files
Omika/tests/Omika.Diagnostics.Tests/DiagnosticDeviceContextUiTests.cs
2026-10-23 18:46:50 +02:00

280 lines
11 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.AspNetCore.Mvc;
using Omika.Diagnostics.Models;
using Omika.Diagnostics.Queries;
using Omika.Diagnostics.Tests.Fakes;
using Omika.Diagnostics.Web.Pages;
namespace Omika.Diagnostics.Tests;
/// <summary>
/// T17-D — Contexte Client / Licence / Appareil / Application : formatage d'affichage
/// (NULL historique → « — »), détail diagnostic complet, filtres Licence/Appareil,
/// pagination, et non-régression sécurité (aucun identifiant brut exposé).
/// </summary>
public class DiagnosticDeviceContextUiTests
{
// ------------------------------------------------ formatage -------------------------
[Fact]
public void Value_NullOrEmpty_RendersEmDash()
{
Assert.Equal("—", UiDeviceContext.Value(null));
Assert.Equal("—", UiDeviceContext.Value(""));
Assert.Equal("—", UiDeviceContext.Value(" "));
Assert.Equal("Android", UiDeviceContext.Value(" Android "));
}
[Theory]
[InlineData(1080, 2400, "1080 × 2400 px")]
[InlineData(null, 2400, "—")]
[InlineData(1080, null, "—")]
[InlineData(0, 2400, "—")]
[InlineData(1080, 0, "—")]
public void Resolution_RequiresBothDimensions(int? w, int? h, string expected)
=> Assert.Equal(expected, UiDeviceContext.Resolution(w, h, "px"));
[Fact]
public void Scale_FormatsWithoutFloatingNoise()
{
Assert.Equal("2.75×", UiDeviceContext.Scale(2.75));
Assert.Equal("3×", UiDeviceContext.Scale(3.0));
Assert.Equal("—", UiDeviceContext.Scale(null));
Assert.Equal("—", UiDeviceContext.Scale(0));
}
[Fact]
public void DeviceIdentity_CombinesManufacturerAndModel()
{
Assert.Equal("Xiaomi · 23113RKC6G", UiDeviceContext.DeviceIdentity("Xiaomi", "23113RKC6G"));
Assert.Equal("Apple", UiDeviceContext.DeviceIdentity("Apple", null));
Assert.Equal("—", UiDeviceContext.DeviceIdentity(null, null));
}
[Fact]
public void PlatformOs_CombinesOsAndVersion()
{
Assert.Equal("Android 16", UiDeviceContext.PlatformOs("Android", "16"));
Assert.Equal("iOS 18.6", UiDeviceContext.PlatformOs("iOS", "18.6"));
Assert.Equal("—", UiDeviceContext.PlatformOs(null, "16"));
}
[Fact]
public void VersionBuild_ShowsBuildWhenPresent()
{
Assert.Equal("1.4.1 (10401)", UiDeviceContext.VersionBuild("1.4.1", "10401"));
Assert.Equal("1.4.1", UiDeviceContext.VersionBuild("1.4.1", null));
Assert.Equal("—", UiDeviceContext.VersionBuild(null, "10401"));
}
[Fact]
public void Short_TruncatesAndKeepsFullViaTitle()
{
Assert.Equal("—", UiDeviceContext.Short(null));
Assert.Equal("f71919f4f9974d70…", UiDeviceContext.Short("f71919f4f9974d70967014fbbefabf9d", 16));
Assert.Equal("abc", UiDeviceContext.Short("abc", 16)); // court → intact
}
// ------------------------------------------------ détail complet (§41) -------------
[Fact]
public async Task Detail_LoadsFullDeviceAndLicenseContext()
{
var repo = new FakeDiagnosticEventRepository();
Guid traceId = Guid.NewGuid();
Guid operationId = Guid.NewGuid();
Guid sessionId = Guid.NewGuid();
DiagnosticEventRecord record = DeviceRecord(42, traceId: traceId, operationId: operationId, sessionId: sessionId);
await repo.InsertBatchAsync(new[] { record });
var model = new DiagnosticsDetailModel(repo);
IActionResult result = await model.OnGetAsync(42);
Assert.IsType<Microsoft.AspNetCore.Mvc.RazorPages.PageResult>(result);
Assert.NotNull(model.Event);
Assert.Equal("CLIENT-042", model.Event!.ClientCode);
Assert.Equal("LIC-000842", model.Event.LicenseNumber);
Assert.Equal("hash-test", model.Event.DeviceKey);
Assert.Equal(1, model.Event.DeviceKeyVersion);
Assert.Equal("Android", model.Event.Platform);
Assert.Equal("Android", model.Event.OperatingSystem);
Assert.Equal("16", model.Event.OperatingSystemVersion);
Assert.Equal("Xiaomi", model.Event.DeviceManufacturer);
Assert.Equal("23113RKC6G", model.Event.DeviceModel);
Assert.Equal("1.4.1", model.Event.AppVersion);
Assert.Equal("10401", model.Event.AppBuild);
Assert.Equal(1080, model.Event.ScreenWidth);
Assert.Equal(2400, model.Event.ScreenHeight);
Assert.Equal(393, model.Event.LogicalWidth);
Assert.Equal(873, model.Event.LogicalHeight);
Assert.Equal(2.75, model.Event.DisplayScale);
Assert.Equal(traceId, model.Event.TraceId);
Assert.Equal(operationId, model.Event.OperationId);
Assert.Equal(sessionId, model.Event.SessionId);
}
// ------------------------------------------------ anciennes données NULL (§42) ------
[Fact]
public void OldData_WithAllNulls_FormatsWithoutBreaking()
{
DiagnosticEventRecord legacy = DiagnosticTestData.Event(Guid.NewGuid());
legacy.ClientCode = null;
legacy.LicenseNumber = null;
legacy.DeviceKey = null;
legacy.DeviceManufacturer = null;
legacy.DeviceModel = null;
legacy.OperatingSystem = null;
legacy.ScreenWidth = null;
legacy.ScreenHeight = null;
legacy.LogicalWidth = null;
legacy.LogicalHeight = null;
legacy.DisplayScale = null;
Assert.Equal("Non renseigné", UiDeploymentContext.Value(legacy.ClientCode));
Assert.Equal("—", UiDeviceContext.Value(legacy.LicenseNumber));
Assert.Equal("—", UiDeviceContext.Resolution(legacy.ScreenWidth, legacy.ScreenHeight, "px"));
Assert.Equal("—", UiDeviceContext.Resolution(legacy.LogicalWidth, legacy.LogicalHeight));
Assert.Equal("—", UiDeviceContext.Scale(legacy.DisplayScale));
Assert.Null(UiDeviceContext.CompactDeviceOrNull(legacy));
}
// ------------------------------------------------ filtre licence (§43) -------------
[Fact]
public async Task LicenseFilter_ReturnsOnlyMatchingEvents()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
LicenseRecord(1, "LIC-A"),
LicenseRecord(2, "LIC-A"),
LicenseRecord(3, "LIC-B")
});
var model = new DiagnosticsModel(repo) { LicenseNumber = "LIC-A" };
await model.OnGetAsync();
Assert.Equal(2, model.Items.Count);
Assert.All(model.Items, e => Assert.Equal("LIC-A", e.LicenseNumber));
}
[Fact]
public void Route_PreservesLicenseAndDeviceFilters()
{
var model = new DiagnosticsModel(new FakeDiagnosticEventRepository())
{
LicenseNumber = "LIC-A",
DeviceKey = "hash-test"
};
Dictionary<string, string> route = model.Route(2);
Assert.Equal("2", route["pageNumber"]);
Assert.Equal("LIC-A", route["licenseNumber"]);
Assert.Equal("hash-test", route["deviceKey"]);
}
// ------------------------------------------------ filtre appareil (§44) ------------
[Fact]
public async Task DeviceKeyFilter_ReturnsOnlyMatchingDevice()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
DeviceKeyRecord(1, "hash-test"),
DeviceKeyRecord(2, "hash-test"),
DeviceKeyRecord(3, "hash-other")
});
var model = new DiagnosticsModel(repo) { DeviceKey = "hash-test" };
await model.OnGetAsync();
Assert.Equal(2, model.Items.Count);
Assert.All(model.Items, e => Assert.Equal("hash-test", e.DeviceKey));
Assert.DoesNotContain(model.Items, e => e.DeviceKey == "hash-other");
}
// ------------------------------------------------ filtres analytiques T17-E -------
[Fact]
public async Task ManufacturerAndOsFilters_ReturnOnlyMatchingEvents()
{
var repo = new FakeDiagnosticEventRepository();
DiagnosticEventRecord samsung = DiagnosticTestData.Event(Guid.NewGuid());
samsung.Id = 1;
samsung.DeviceManufacturer = "Samsung";
samsung.OperatingSystem = "Android";
DiagnosticEventRecord apple = DiagnosticTestData.Event(Guid.NewGuid());
apple.Id = 2;
apple.DeviceManufacturer = "Apple";
apple.OperatingSystem = "iOS";
await repo.InsertBatchAsync(new[] { samsung, apple });
var model = new DiagnosticsModel(repo) { Manufacturer = "Samsung", OperatingSystem = "Android" };
await model.OnGetAsync();
DiagnosticEventRecord record = Assert.Single(model.Items);
Assert.Equal("Samsung", record.DeviceManufacturer);
}
// ------------------------------------------------ sécurité UI (§45) ----------------
[Fact]
public void DiagnosticEventRecord_ExposesNoRawIdentityOrSecrets()
{
string[] names = typeof(DiagnosticEventRecord).GetProperties().Select(p => p.Name).ToArray();
Assert.DoesNotContain("DeviceId", names);
Assert.DoesNotContain("UserId", names);
Assert.DoesNotContain("CollaborateurId", names);
Assert.DoesNotContain("Token", names);
Assert.DoesNotContain("HmacKey", names);
Assert.DoesNotContain("LI_PKID", names);
Assert.DoesNotContain("LI_CleAPI", names);
Assert.DoesNotContain("CL_CleAPIInstall", names);
// DeviceKey (pseudonymisé) est autorisé.
Assert.Contains("DeviceKey", names);
}
// ------------------------------------------------ fabriques ------------------------
private static DiagnosticEventRecord DeviceRecord(
long id,
Guid? traceId = null,
Guid? operationId = null,
Guid? sessionId = null)
{
DiagnosticEventRecord record = DiagnosticTestData.Event(Guid.NewGuid());
record.Id = id;
record.ClientCode = "CLIENT-042";
record.LicenseNumber = "LIC-000842";
record.DeviceKey = "hash-test";
record.DeviceKeyVersion = 1;
record.Platform = "Android";
record.OperatingSystem = "Android";
record.OperatingSystemVersion = "16";
record.DeviceManufacturer = "Xiaomi";
record.DeviceModel = "23113RKC6G";
record.AppVersion = "1.4.1";
record.AppBuild = "10401";
record.ScreenWidth = 1080;
record.ScreenHeight = 2400;
record.LogicalWidth = 393;
record.LogicalHeight = 873;
record.DisplayScale = 2.75;
record.TraceId = traceId;
record.OperationId = operationId;
record.SessionId = sessionId;
return record;
}
private static DiagnosticEventRecord LicenseRecord(long id, string licenseNumber)
{
DiagnosticEventRecord record = DiagnosticTestData.Event(Guid.NewGuid());
record.Id = id;
record.LicenseNumber = licenseNumber;
return record;
}
private static DiagnosticEventRecord DeviceKeyRecord(long id, string deviceKey)
{
DiagnosticEventRecord record = DiagnosticTestData.Event(Guid.NewGuid());
record.Id = id;
record.DeviceKey = deviceKey;
return record;
}
}