using Manager.DTOs; using ManagerService.Data; using ManagerService.Data.SubSection; using ManagerService.DTOs; using ManagerService.Tests.Infrastructure; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text.Json; using Xunit; namespace ManagerService.Tests.Data { /// /// Le journal d'audit ne voyait aucune section : le filtre exigeait l'égalité exacte /// de type, or est abstraite. Ces tests tiennent l'invariant — /// un sous-type est journalisé, et il l'est sous le nom « Section ». /// public class AuditLogTests { private static SectionArticle NewArticle(string id = "s1") => TestSection.Article(id, "i1", "Label", "c1"); [Fact] public void SectionSubType_IsAudited() { using var db = DbContextFactory.Create(); db.Sections.Add(NewArticle()); db.SaveChanges(); var log = Assert.Single(db.AuditLogs.ToList()); Assert.Equal("Create", log.Action); Assert.Equal("s1", log.EntityId); Assert.Equal("i1", log.InstanceId); } [Fact] public void SectionSubType_IsLoggedUnderBaseTypeName() { using var db = DbContextFactory.Create(); db.Sections.Add(NewArticle()); db.SaveChanges(); // Le filtre « Section » de l'écran d'audit interroge ce nom : s'il portait // « SectionArticle », l'écran ne rendrait toujours aucune ligne. Assert.Equal("Section", db.AuditLogs.Single().EntityType); } [Fact] public void SectionSubType_KeepsConcreteTypeInValues() { using var db = DbContextFactory.Create(); db.Sections.Add(NewArticle()); db.SaveChanges(); // Normaliser EntityType ne perd pas le sous-type : le discriminateur TPH est // une propriété du modèle, donc sérialisée avec les autres. var values = JsonSerializer.Deserialize>( db.AuditLogs.Single().NewValues!); Assert.Equal("Article", values!["Discriminator"].GetString()); } [Fact] public void SectionUpdate_RecordsOnlyChangedFields() { using var db = DbContextFactory.Create(); db.Sections.Add(NewArticle()); db.SaveChanges(); var section = db.Sections.Single(); section.Label = "Renommée"; db.SaveChanges(); var update = db.AuditLogs.Single(a => a.Action == "Update"); var oldValues = JsonSerializer.Deserialize>(update.OldValues!); var newValues = JsonSerializer.Deserialize>(update.NewValues!); Assert.Equal("Label", Assert.Single(newValues!.Keys.Where(k => k != "DateUpdate"))); Assert.Equal("Label", oldValues!["Label"].GetString()); Assert.Equal("Renommée", newValues["Label"].GetString()); } [Fact] public void SectionDelete_IsAudited() { using var db = DbContextFactory.Create(); db.Sections.Add(NewArticle()); db.SaveChanges(); db.Sections.Remove(db.Sections.Single()); db.SaveChanges(); var delete = db.AuditLogs.Single(a => a.Action == "Delete"); Assert.Equal("Section", delete.EntityType); Assert.Equal("s1", delete.EntityId); } [Fact] public void NonAuditedEntity_IsNotLogged() { using var db = DbContextFactory.Create(); db.VisitEvents.Add(new VisitEvent { Id = "v1", InstanceId = "i1", SessionId = "sess", EventType = VisitEventType.SectionView, Timestamp = DateTime.UtcNow }); db.SaveChanges(); Assert.Empty(db.AuditLogs.ToList()); } [Fact] public void OtherAuditedTypes_KeepTheirOwnName() { using var db = DbContextFactory.Create(); db.Users.Add(new User { Id = "u1", InstanceId = "i1", Email = "a@b.c", FirstName = "A", LastName = "B", Password = "x", Token = "t" }); db.SaveChanges(); Assert.Equal("User", db.AuditLogs.Single().EntityType); } /// /// Le trou d'origine venait d'une liste de types que rien n'obligeait à suivre les /// sous-classes. Par réflexion : tout sous-type concret de Section doit être journalisé. /// [Fact] public void EverySectionSubType_ResolvesToSection() { var subTypes = typeof(Section).Assembly.GetTypes() .Where(t => typeof(Section).IsAssignableFrom(t) && !t.IsAbstract) .ToList(); Assert.Equal(13, subTypes.Count); var auditedTypeOf = typeof(MyInfoMateDbContext) .GetMethod("AuditedTypeOf", BindingFlags.NonPublic | BindingFlags.Static)!; foreach (var subType in subTypes) { var instance = System.Runtime.CompilerServices.RuntimeHelpers .GetUninitializedObject(subType); Assert.Equal(typeof(Section), auditedTypeOf.Invoke(null, new[] { instance })); } } } }