using Omika.Core.Compatibility; using Omika.Core.Domain.Entities.Clients; using Omika.Core.Domain.ValueObjects; using Omika.Data.Mapping.Common; using BatikModels = Omika.Data.HfSql.Models.Batik; using BatikMappers = Omika.Data.Mapping.Batik; namespace Omika.Tests.Mapping; /// /// Tests unitaires du mapper Batik → canonique des sites (table Lieu). /// Aucune base réelle, aucun HFSQL : uniquement des objets en mémoire. /// public class BatikSiteMapperTests { // ---- 1. Site Batik minimal ------------------------------------------------- [Fact] public void SiteBatik_Minimal_MapsWithoutExceptionAndKeepsNoFabricatedValue() { var row = new BatikModels.LieuRow { LIPKID = 1 }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row); Assert.Equal(DatabaseSchemaVersion.Batik, result.SourceVersion); Assert.NotNull(result.Value); Assert.Null(result.Value.Code); Assert.Null(result.Value.Type); Assert.Null(result.Value.Adresse); Assert.Null(result.Value.InformationsComplementaires); Assert.False(result.Value.EstArchive); Assert.NotNull(result.Value.SourceReference); Assert.Equal(1, result.Value.SourceReference!.NumericId); Assert.Equal(DatabaseSchemaVersion.Batik, result.Value.SourceReference!.Source); Assert.Null(result.Value.SourceReference!.EntityGuid); Assert.Empty(result.Warnings); } // ---- 2. Site Batik complet ------------------------------------------------- [Fact] public void SiteBatik_Full_MapsAllCanonicalProperties() { var row = new BatikModels.LieuRow { LIPKID = 42, LICodeLieu = "S-001", LIIDType = 2, LINote = "Site principal", Archive = true, LIIDImport = "IMP-B-42" }; var referenceRows = new Dictionary { [2] = new() { PAPKID = 2, PACode = "T", PAValeur = "1", PALibelle = "Immeuble" } }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row, referenceRows); Site site = result.Value; Assert.Equal("S-001", site.Code); Assert.Equal("Immeuble", site.Type!.Label); Assert.Equal(2, site.Type!.LegacyId); Assert.Equal("T", site.Type!.Code); Assert.Equal("Site principal", site.InformationsComplementaires); Assert.True(site.EstArchive); Assert.Equal(42, site.SourceReference!.NumericId); Assert.Equal("IMP-B-42", site.SourceReference!.ImportId); Assert.Equal(DatabaseSchemaVersion.Batik, site.SourceReference!.Source); } // ---- 3. Conservation de l'identifiant source ---------------------------------- [Fact] public void SiteBatik_SourceIdentifier_IsPreservedWithoutFabricatedGuid() { var row = new BatikModels.LieuRow { LIPKID = 1234, LIIDImport = "IMP-B" }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row); Assert.NotNull(result.Value.SourceReference); Assert.Equal(DatabaseSchemaVersion.Batik, result.Value.SourceReference!.Source); Assert.Equal(1234, result.Value.SourceReference!.NumericId); Assert.Equal("IMP-B", result.Value.SourceReference!.ImportId); Assert.Equal("1234", result.Value.SourceReference!.PublicIdentifier); Assert.Null(result.Value.SourceReference!.EntityGuid); Assert.Null(result.Value.SourceReference!.PhysicalGuid); } // ---- 4. Type de site ---------------------------------------------------------- [Fact] public void SiteBatik_Type_IsResolvedFromLegacyId() { var row = new BatikModels.LieuRow { LIPKID = 5, LIIDType = 7 }; var referenceRows = new Dictionary { [7] = new() { PAPKID = 7, PACode = "T", PAValeur = "2", PALibelle = "Maison Indiv." } }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row, referenceRows); Assert.NotNull(result.Value.Type); Assert.Equal("Maison Indiv.", result.Value.Type!.Label); Assert.Equal(7, result.Value.Type!.LegacyId); Assert.Empty(result.Warnings); } [Fact] public void SiteBatik_UnresolvedType_KeepsLegacyIdAndWarns() { var row = new BatikModels.LieuRow { LIPKID = 6, LIIDType = 77 }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row); Assert.NotNull(result.Value.Type); Assert.Equal(77, result.Value.Type!.LegacyId); Assert.Equal(string.Empty, result.Value.Type!.Label); Assert.Contains(result.Warnings, w => w.Contains("LI_IDType", StringComparison.Ordinal) && w.Contains(MappingWarningCodes.UnresolvedReference, StringComparison.Ordinal)); } [Fact] public void SiteBatik_ZeroType_ProducesNoReference() { var row = new BatikModels.LieuRow { LIPKID = 8, LIIDType = 0 }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row); Assert.Null(result.Value.Type); Assert.Empty(result.Warnings); } // ---- 5. Informations d'accès -------------------------------------------- [Fact] public void SiteBatik_AccessInformation_CodeAccesBadgeInterphone_Mapped() { var row = new BatikModels.LieuRow { LIPKID = 9, LICodeAcces = "Code 1234", LIBadge = true, LIInterphone = true }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row); Assert.Equal("Code 1234", result.Value.CodeAcces); Assert.True(result.Value.Badge); Assert.True(result.Value.Interphone); Assert.DoesNotContain(result.IgnoredFields, f => f == "LI_CodeAcces"); Assert.DoesNotContain(result.IgnoredFields, f => f == "LI_Badge"); Assert.DoesNotContain(result.IgnoredFields, f => f == "LI_Interphone"); } // ---- 6. Blocage sans équivalent métier ---------------------------------------- [Fact] public void SiteBatik_Blocage_IsReportedAsIgnored() { var row = new BatikModels.LieuRow { LIPKID = 10, LIBlocage = true, LIRaisonBlocage = "Travaux en cours" }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row); Assert.Contains(result.IgnoredFields, f => f == "LI_Blocage"); Assert.Contains(result.IgnoredFields, f => f == "LI_RaisonBlocage"); } // ---- 7. Adresse déjà chargée en mémoire --------------------------------------- [Fact] public void SiteBatik_Adresse_IsMappedFromInMemoryDictionary() { var row = new BatikModels.LieuRow { LIPKID = 11, LIIDAdresse = 20 }; var adresses = new Dictionary { [20] = new() { ADPKID = 20, ADEtiquette = "Siège", ADLigne1 = "1 place de la Comédie", ADVille = "Lyon", ADCodePostal = "69001" } }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row, adresses: adresses); Assert.NotNull(result.Value.Adresse); Assert.Equal("1 place de la Comédie", result.Value.Adresse!.Line1); Assert.Equal("69001", result.Value.Adresse!.PostalCode); Assert.Equal("Lyon", result.Value.Adresse!.City); } [Fact] public void SiteBatik_NoAdresse_WhenNothingProvided() { var row = new BatikModels.LieuRow { LIPKID = 12, LIIDAdresse = 0 }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row); Assert.Null(result.Value.Adresse); Assert.Empty(result.Warnings); } [Fact] public void SiteBatik_UnresolvedAdresse_WarnsAndProducesNull() { var row = new BatikModels.LieuRow { LIPKID = 13, LIIDAdresse = 99 }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row); Assert.Null(result.Value.Adresse); Assert.Contains(result.Warnings, w => w.Contains(MappingWarningCodes.UnresolvedReference, StringComparison.Ordinal)); } // ---- 8. Relations différées ---------------------------------------------------- [Fact] public void SiteBatik_Relations_AreLeftForLaterAssembly() { var row = new BatikModels.LieuRow { LIPKID = 14, LIIDClientPrincipal = 30 }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row); Assert.Null(result.Value.ClientId); Assert.Empty(result.Value.ContactIds); Assert.Contains(result.IgnoredFields, f => f == "LI_IDClientPrincipal"); } // ---- 9. Aucune exception sur les chaînes vides --------------------------------- [Fact] public void SiteBatik_EmptyOrNullStrings_NeverThrow() { var row = new BatikModels.LieuRow { LICodeLieu = "", LINote = " ", LIIDImport = "", LICodeAcces = " ", LIRaisonBlocage = "", LIRepertoireDocs = " " }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row); Assert.NotNull(result.Value); Assert.Null(result.Value.Code); Assert.Null(result.Value.InformationsComplementaires); Assert.Null(result.Value.SourceReference!.ImportId); Assert.Empty(result.Warnings); } [Fact] public void SiteBatik_DefaultRow_NeverThrows() { var row = new BatikModels.LieuRow(); CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row); Assert.NotNull(result.Value); Assert.Null(result.Value.SourceReference!.NumericId); Assert.False(result.Value.EstArchive); } // ---- 10. Champs ignorés et warnings attendus ----------------------------------- [Fact] public void SiteBatik_FieldsWithoutCanonicalEquivalent_AreReportedAsIgnored() { var row = new BatikModels.LieuRow { LIPKID = 15, LIRepertoireDocs = "docs/site", DateArchivage = new DateTime(2024, 6, 1), ModifDate = new DateTime(2024, 1, 1), CreationDate = new DateTime(2023, 1, 1) }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row); Assert.Contains(result.IgnoredFields, f => f == "LI_RepertoireDocs"); Assert.Contains(result.IgnoredFields, f => f == "DateArchivage"); Assert.Equal(new DateTime(2024, 1, 1), result.Value.DateModification); Assert.Contains(result.IgnoredFields, f => f == "CreationDate"); } // ---- 11. Absence de noms physiques HFSQL dans le domaine canonique ------------- [Fact] public void SiteBatik_CanonicalDomain_DoesNotExposeHfSqlNames() { var row = new BatikModels.LieuRow { LIPKID = 40, LICodeLieu = "S-040", LIIDType = 2, LINote = "note", Archive = false, LIIDImport = "IMP-40", LIIDAdresse = 20, LIIDClientPrincipal = 30, LICodeAcces = "code", LIBadge = true, LIInterphone = true, LIBlocage = true, LIRaisonBlocage = "raison", LIRepertoireDocs = "docs" }; var referenceRows = new Dictionary { [2] = new() { PAPKID = 2, PACode = "T", PAValeur = "1", PALibelle = "Immeuble" } }; var adresses = new Dictionary { [20] = new() { ADPKID = 20, ADLigne1 = "1 rue de la Paix", ADVille = "Lyon", ADCodePostal = "69001" } }; CanonicalMappingResult result = BatikMappers.BatikSiteMapper.Map(row, referenceRows, adresses); Site site = result.Value; string[] forbidden = [ "LI_", "AD_", "PA_", "PKID", "HfSql", "HFSQL", "GUID" ]; AssertHasNoPhysicalNames(site, forbidden); if (site.Adresse is not null) { AssertHasNoPhysicalNames(site.Adresse, forbidden); } if (site.SourceReference is not null) { AssertHasNoPhysicalNames(site.SourceReference, forbidden); } } // ---- Helper --------------------------------------------------------------------- private static void AssertHasNoPhysicalNames(object value, string[] forbidden) { foreach (var property in value.GetType().GetProperties()) { Assert.DoesNotContain(forbidden, f => property.Name.Contains(f, StringComparison.Ordinal)); } } }