using Omika.Diagnostics.Models; using Omika.Diagnostics.Tests.Fakes; using Omika.Diagnostics.Web.Pages; namespace Omika.Diagnostics.Tests; /// /// T17-E — PageModel /Parc : vue générale, répartitions, liste appareils, filtres, /// pagination, drill-down et réattribution de licence. Testés sans HttpContext /// (constructeur repository uniquement, comme les autres PageModels). /// public class DiagnosticParcModelTests { private static DiagnosticEventRecord Ev( long id, string deviceKey, DateTimeOffset ts, string? client = null, string? license = null, string? platform = "Android", string? os = "Android", string? osVersion = "15", string? manufacturer = null, string? model = null, string? appVersion = null) { DiagnosticEventRecord e = DiagnosticTestData.Event(Guid.NewGuid(), timestamp: ts); e.Id = id; e.DeviceKey = deviceKey; e.ClientCode = client; e.LicenseNumber = license; e.Platform = platform; e.OperatingSystem = os; e.OperatingSystemVersion = osVersion; e.DeviceManufacturer = manufacturer; e.DeviceModel = model; e.AppVersion = appVersion; return e; } // ------------------------------- vue générale + répartitions ---- [Fact] public async Task ParcModel_LoadsOverviewAndDistributions() { var repo = new FakeDiagnosticEventRepository(); DateTimeOffset now = DateTimeOffset.UtcNow; await repo.InsertBatchAsync(new[] { Ev(1, "d1", now.AddMinutes(-60), platform: "Android", osVersion: "16", manufacturer: "Xiaomi"), Ev(2, "d2", now.AddMinutes(-50), platform: "iOS", os: "iOS", osVersion: "18.5", manufacturer: "Apple"), Ev(3, "d3", now.AddMinutes(-40), platform: "Android", osVersion: "15", manufacturer: "Samsung") }); var model = new ParcModel(repo); await model.OnGetAsync(); Assert.True(model.PostgreSqlAvailable); Assert.True(model.HasData); Assert.Equal(3, model.ActiveDevices); Assert.Equal(2, model.AndroidDevices); Assert.Equal(1, model.IosDevices); Assert.Equal(0, model.DesktopDevices); Assert.Equal(3, model.Devices.Count); Assert.NotEmpty(model.ManufacturerDistribution); Assert.NotEmpty(model.OsVersionDistribution); } // ------------------------------- réattribution de licence ---- [Fact] public async Task ParcModel_LicenseReassigned_ShowsTwoDevices() { var repo = new FakeDiagnosticEventRepository(); DateTimeOffset now = DateTimeOffset.UtcNow; await repo.InsertBatchAsync(new[] { Ev(1, "dev-a", now.AddDays(-20), license: "LIC-001", manufacturer: "Samsung", model: "SM-DEMO"), Ev(2, "dev-b", now.AddDays(-1), license: "LIC-001", manufacturer: "Apple", model: "iPhoneDemo", os: "iOS", osVersion: "18") }); var model = new ParcModel(repo); await model.OnGetAsync(); Assert.Equal(2, model.ActiveDevices); Assert.Equal(2, model.Devices.Count); Assert.Contains(model.Devices, d => d.DeviceKey == "dev-a"); Assert.Contains(model.Devices, d => d.DeviceKey == "dev-b"); // « Appareils observés par licence » : LIC-001 → 2 appareils distincts, jamais 1. Omika.Diagnostics.Queries.DeviceDistribution lic = Assert.Single(model.LicenseDistribution); Assert.Equal("LIC-001", lic.Key); Assert.Equal(2, lic.DeviceCount); } // ------------------------------- filtre licence ---- [Fact] public async Task ParcModel_LicenseFilter_ReturnsOnlyMatchingDevices() { var repo = new FakeDiagnosticEventRepository(); DateTimeOffset now = DateTimeOffset.UtcNow; await repo.InsertBatchAsync(new[] { Ev(1, "dev-a", now.AddMinutes(-30), license: "LIC-A"), Ev(2, "dev-b", now.AddMinutes(-20), license: "LIC-A"), Ev(3, "dev-c", now.AddMinutes(-10), license: "LIC-B") }); var model = new ParcModel(repo) { LicenseNumber = "LIC-A" }; await model.OnGetAsync(); Assert.Equal(2, model.ActiveDevices); Assert.All(model.Devices, d => Assert.Equal("LIC-A", d.LicenseNumber)); } // ------------------------------- filtre client + plateforme ---- [Fact] public async Task ParcModel_ClientAndPlatformFilters_Combine() { var repo = new FakeDiagnosticEventRepository(); DateTimeOffset now = DateTimeOffset.UtcNow; await repo.InsertBatchAsync(new[] { Ev(1, "d1", now.AddMinutes(-60), client: "CLIENT-A", platform: "Android"), Ev(2, "d2", now.AddMinutes(-50), client: "CLIENT-A", platform: "iOS", os: "iOS"), Ev(3, "d3", now.AddMinutes(-40), client: "CLIENT-B", platform: "Android") }); var model = new ParcModel(repo) { ClientCode = "CLIENT-A", Platform = "Android" }; await model.OnGetAsync(); Assert.Equal(1, model.ActiveDevices); Omika.Diagnostics.Queries.DeviceFleetRecord device = Assert.Single(model.Devices); Assert.Equal("d1", device.DeviceKey); } // ------------------------------- pagination / Route ---- [Fact] public void ParcModel_Route_PreservesFilters() { var model = new ParcModel(new FakeDiagnosticEventRepository()) { ClientCode = "CLIENT-A", LicenseNumber = "LIC-A", Platform = "Android", Manufacturer = "Samsung" }; Dictionary route = model.Route(2); Assert.Equal("2", route["pageNumber"]); Assert.Equal("CLIENT-A", route["clientCode"]); Assert.Equal("LIC-A", route["licenseNumber"]); Assert.Equal("Android", route["platform"]); Assert.Equal("Samsung", route["manufacturer"]); } // ------------------------------- zéro appareil ---- [Fact] public async Task ParcModel_NoDevices_HasDataFalse() { var repo = new FakeDiagnosticEventRepository(); var model = new ParcModel(repo); await model.OnGetAsync(); Assert.False(model.HasData); Assert.Equal(0, model.ActiveDevices); Assert.Empty(model.Devices); } // ------------------------------- pourcentage formaté ---- [Fact] public async Task ParcModel_FormatPercentage_NoNaN() { var repo = new FakeDiagnosticEventRepository(); DateTimeOffset now = DateTimeOffset.UtcNow; await repo.InsertBatchAsync(new[] { Ev(1, "d1", now.AddMinutes(-60), platform: "Android"), Ev(2, "d2", now.AddMinutes(-50), platform: "iOS", os: "iOS") }); var model = new ParcModel(repo); await model.OnGetAsync(); Assert.Equal(2, model.ActiveDevices); Assert.Equal("50 %", model.FormatPercentage(1)); } }