using System.Reflection; using Omika.Core.Contracts.Common; namespace Omika.Tests.Architecture; /// /// Aucun contrat public ne doit exposer de « champ zombie » : les rubriques /// physiques obsolètes (anciens identifiants de civilité ou de statut, clés /// internes, rubriques marquées legacy) sont converties par le mapper et ne /// doivent jamais transiter telles quelles vers l'application. /// public class PublicContractsMustNotContainZombieFieldsTests { private static readonly string[] ZombieMarkers = [ "Zombie", "LegacyField", "IsLegacy", "Ancien", "OldId", "IdCivilite", "IdStatut", "CleStatut", "CleType", "IDType", "PKID", "RDP_GUID", "PL_PKID", "GUIDPhysique", "PhysicalGuid", "NumeroCle", "CleNumero" ]; private static IEnumerable GetContractTypes() => typeof(PagedResult<>).Assembly .GetTypes() .Where(t => t.IsPublic) .Where(t => t.Namespace?.StartsWith("Omika.Core.Contracts", StringComparison.Ordinal) == true); [Fact] public void NoContractProperty_HasZombieMarker() { foreach (Type type in GetContractTypes()) { foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { foreach (string marker in ZombieMarkers) { Assert.False( property.Name.Contains(marker, StringComparison.OrdinalIgnoreCase), $"Le contrat « {type.FullName}.{property.Name} » expose le champ zombie « {marker} »."); } } } } [Fact] public void NoContractProperty_HasPhysicalRowShapedTypes() { // Les DTO ne doivent pas exposer de propriété dont le type est une classe // physique (Row) d'Omika.Data : les contrats sont autonomes. foreach (Type type in GetContractTypes()) { foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { string ns = property.PropertyType.Namespace ?? string.Empty; Assert.False( ns.StartsWith("Omika.Data", StringComparison.Ordinal), $"Le contrat « {type.FullName}.{property.Name} » référence un type d'Omika.Data."); } } } }