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.");
}
}
}
}