using Manager.Services; using ManagerService.Controllers; using ManagerService.Data; using ManagerService.DTOs; using ManagerService.Tests.Infrastructure; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging.Abstractions; using System.Linq; using Xunit; namespace ManagerService.Tests.Controllers { public class DeviceControllerTests { private static DeviceController BuildController(MyInfoMateDbContext db) { var cfg = FakeMongoConfig.Create(); var controller = new DeviceController( NullLogger.Instance, new DeviceDatabaseService(cfg), new ConfigurationDatabaseService(cfg), db); FakeUser.SetUser(controller, FakeUser.Create("Manager.instanceadmin", "inst-test")); return controller; } private static void SeedPrerequisites(MyInfoMateDbContext db) { db.Configurations.Add(new Configuration { Id = "conf-1", InstanceId = "inst-test", Label = "Conf", Title = new System.Collections.Generic.List() }); db.ApplicationInstances.Add(new ApplicationInstance { Id = "ai-1", InstanceId = "inst-test", AppType = AppType.Tablet }); db.ApplicationInstances.Add(new ApplicationInstance { Id = "ai-vr", InstanceId = "inst-test", AppType = AppType.VR }); db.SaveChanges(); } // ── HEARTBEAT (XR-5) ───────────────────────────────────────────────── private static Device SeedDevice(MyInfoMateDbContext db, string instanceId = "inst-test") { var device = new Device { Id = "dev-1", Identifier = "headset-abc", InstanceId = instanceId, ConfigurationId = "conf-1", AppType = AppType.VR, Connected = false }; db.Devices.Add(device); db.SaveChanges(); return device; } [Fact] public void Heartbeat_WritesBatteryVersionAndLastSeen() { using var db = DbContextFactory.Create(); SeedPrerequisites(db); SeedDevice(db); var result = BuildController(db).Heartbeat("dev-1", new DeviceHeartbeatDTO { batteryLevel = "87", appVersion = "1.4.2" }); Assert.IsType(result); var device = db.Devices.First(); Assert.Equal("87", device.BatteryLevel); Assert.Equal("1.4.2", device.AppVersion); Assert.NotNull(device.LastSeen); // Recevoir le battement est la preuve que l'appareil est en ligne : l'app // n'a pas à le déclarer. Assert.True(device.Connected); } [Fact] public void Heartbeat_CannotRenameOrMoveTheDevice() { using var db = DbContextFactory.Create(); SeedPrerequisites(db); SeedDevice(db); BuildController(db).Heartbeat("dev-1", new DeviceHeartbeatDTO { batteryLevel = "50" }); var device = db.Devices.First(); Assert.Equal("inst-test", device.InstanceId); Assert.Equal("conf-1", device.ConfigurationId); Assert.Equal("headset-abc", device.Identifier); } [Fact] public void Heartbeat_OtherInstance_Returns403() { using var db = DbContextFactory.Create(); SeedPrerequisites(db); SeedDevice(db, instanceId: "autre-instance"); var result = BuildController(db).Heartbeat("dev-1", new DeviceHeartbeatDTO { batteryLevel = "10" }); Assert.Equal(403, result.StatusCode); } [Fact] public void Heartbeat_UnknownDevice_Returns404() { using var db = DbContextFactory.Create(); SeedPrerequisites(db); var result = BuildController(db).Heartbeat("nope", new DeviceHeartbeatDTO()); Assert.IsType(result); } // ── CREATE ─────────────────────────────────────────────────────────── [Fact] public void Create_NewIdentifier_CreatesDevice() { using var db = DbContextFactory.Create(); SeedPrerequisites(db); var result = BuildController(db).Create(new DeviceDetailDTO { identifier = "device-abc", instanceId = "inst-test", configurationId = "conf-1", name = "Tablet 1" }); Assert.IsType(result); Assert.Equal(1, db.Devices.Count()); Assert.Equal("Tablet 1", db.Devices.First().Name); } [Fact] public void Create_ExistingIdentifier_UpdatesDeviceNoDuplicate() { using var db = DbContextFactory.Create(); SeedPrerequisites(db); db.Devices.Add(new Device { Id = "d1", Identifier = "device-abc", InstanceId = "inst-test", ConfigurationId = "conf-1" }); db.SaveChanges(); var result = BuildController(db).Create(new DeviceDetailDTO { identifier = "device-abc", instanceId = "inst-test", configurationId = "conf-1", name = "Updated Name" }); Assert.IsType(result); Assert.Equal(1, db.Devices.Count()); // pas de doublon Assert.Equal("Updated Name", db.Devices.First().Name); } [Fact] public void Create_NewDevice_AutoCreatesAppConfigurationLink() { using var db = DbContextFactory.Create(); SeedPrerequisites(db); BuildController(db).Create(new DeviceDetailDTO { identifier = "device-abc", instanceId = "inst-test", configurationId = "conf-1", name = "Tablet 1" }); Assert.Equal(1, db.AppConfigurationLinks.Count()); } // ── UPDATE ─────────────────────────────────────────────────────────── [Fact] public void Update_ExistingDevice_UpdatesName() { using var db = DbContextFactory.Create(); db.Configurations.Add(new Configuration { Id = "conf-1", InstanceId = "inst-test", Label = "Conf", Title = new System.Collections.Generic.List() }); db.Devices.Add(new Device { Id = "d1", Identifier = "dev-1", InstanceId = "inst-test", Name = "Old Name", ConfigurationId = "conf-1" }); db.SaveChanges(); var result = BuildController(db).Update(new DeviceDetailDTO { id = "d1", name = "New Name", instanceId = "inst-test", identifier = "dev-1" }); Assert.IsType(result); Assert.Equal("New Name", db.Devices.First().Name); } // ── DELETE ─────────────────────────────────────────────────────────── [Fact] public void Delete_ExistingDevice_Returns202() { using var db = DbContextFactory.Create(); db.Configurations.Add(new Configuration { Id = "conf-1", InstanceId = "inst-test", Label = "Conf", Title = new System.Collections.Generic.List() }); db.Devices.Add(new Device { Id = "d1", Identifier = "dev-1", InstanceId = "inst-test", ConfigurationId = "conf-1" }); db.SaveChanges(); var result = BuildController(db).Delete("d1"); var obj = Assert.IsType(result); Assert.Equal(202, obj.StatusCode); Assert.Equal(0, db.Devices.Count()); } [Fact] public void Delete_UnknownId_Returns404() { using var db = DbContextFactory.Create(); var result = BuildController(db).Delete("unknown"); Assert.IsType(result); } // ── CANAL (AppType) ────────────────────────────────────────────────── [Fact] public void Create_VrDevice_LinksToVrApplicationInstance() { using var db = DbContextFactory.Create(); SeedPrerequisites(db); var result = BuildController(db).Create(new DeviceDetailDTO { identifier = "quest-1", instanceId = "inst-test", configurationId = "conf-1", name = "Casque 1", appType = AppType.VR }); Assert.IsType(result); Assert.Equal(AppType.VR, db.Devices.Single().AppType); // Le rattachement se faisait sur AppType.Tablet en dur : un casque atterrissait // dans l'onglet Kiosk. Assert.Equal("ai-vr", db.AppConfigurationLinks.Single().ApplicationInstanceId); } [Fact] public void Create_WithoutAppType_DefaultsToTablet() { using var db = DbContextFactory.Create(); SeedPrerequisites(db); BuildController(db).Create(new DeviceDetailDTO { identifier = "device-abc", instanceId = "inst-test", configurationId = "conf-1", name = "Tablet 1" }); Assert.Equal(AppType.Tablet, db.Devices.Single().AppType); Assert.Equal("ai-1", db.AppConfigurationLinks.Single().ApplicationInstanceId); } [Fact] public void Create_VrDevice_WithoutVrApplicationInstance_Returns404() { using var db = DbContextFactory.Create(); db.Configurations.Add(new Configuration { Id = "conf-1", InstanceId = "inst-test", Label = "Conf", Title = new System.Collections.Generic.List() }); db.ApplicationInstances.Add(new ApplicationInstance { Id = "ai-1", InstanceId = "inst-test", AppType = AppType.Tablet }); db.SaveChanges(); var result = BuildController(db).Create(new DeviceDetailDTO { identifier = "quest-1", instanceId = "inst-test", configurationId = "conf-1", appType = AppType.VR }); Assert.IsType(result); Assert.Equal(0, db.Devices.Count()); } [Fact] public void Get_FiltersByAppType() { using var db = DbContextFactory.Create(); SeedPrerequisites(db); db.Devices.Add(new Device { Id = "d1", Identifier = "tab-1", InstanceId = "inst-test", ConfigurationId = "conf-1", AppType = AppType.Tablet }); db.Devices.Add(new Device { Id = "d2", Identifier = "quest-1", InstanceId = "inst-test", ConfigurationId = "conf-1", AppType = AppType.VR }); db.SaveChanges(); var vr = Assert.IsType(BuildController(db).Get(null, AppType.VR)); Assert.Equal("quest-1", Assert.Single((System.Collections.Generic.IEnumerable)vr.Value).identifier); // Sans filtre : comportement historique, tous les appareils. var all = Assert.IsType(BuildController(db).Get(null)); Assert.Equal(2, ((System.Collections.Generic.IEnumerable)all.Value).Count()); } } }