280 lines
11 KiB
C#
280 lines
11 KiB
C#
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;
|
||
}
|
||
}
|