using Omika.Core.Compatibility; using Omika.Core.Domain.Entities.Interventions; 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 dossiers (table Dossier). /// Aucune base réelle, aucun HFSQL : uniquement des objets en mémoire. /// public class BatikDossierMapperTests { // ---- 1. Dossier Batik minimal ------------------------------------------------ [Fact] public void DossierBatik_Minimal_MapsWithoutExceptionAndKeepsNoFabricatedValue() { var row = new BatikModels.DossierRow { DOPKID = 1 }; CanonicalMappingResult result = BatikMappers.BatikDossierMapper.Map(row); Assert.Equal(DatabaseSchemaVersion.Batik, result.SourceVersion); Assert.NotNull(result.Value); Assert.Null(result.Value.ReferenceMetier); Assert.Null(result.Value.Titre); Assert.Null(result.Value.Type); Assert.Null(result.Value.Statut); Assert.False(result.Value.EstArchive); Assert.Empty(result.Warnings); } // ---- 2. Conservation de l'identifiant source (cause du count=0 snapshot) ----- [Fact] public void DossierBatik_SourceReference_IsPreservedWithNumericId() { // Cause réelle du micro-lot 19 : le mapper ne remplissait pas SourceReference, // donc LoadDossiersAsync skippait tous les dossiers (NumericId null) → count=0. var row = new BatikModels.DossierRow { DOPKID = 1234 }; CanonicalMappingResult result = BatikMappers.BatikDossierMapper.Map(row); Assert.NotNull(result.Value.SourceReference); Assert.Equal(DatabaseSchemaVersion.Batik, result.Value.SourceReference!.Source); Assert.Equal(1234, result.Value.SourceReference!.NumericId); Assert.Equal("1234", result.Value.SourceReference!.PublicIdentifier); Assert.Null(result.Value.SourceReference!.EntityGuid); Assert.Null(result.Value.SourceReference!.PhysicalGuid); } // ---- 3. Dossier Batik complet ------------------------------------------------ [Fact] public void DossierBatik_Full_MapsAllCanonicalProperties() { var row = new BatikModels.DossierRow { DOPKID = 42, DOIDTypeDossier = 2, DOIDStatut = 3, DOTitre = "Dossier chantier", DONumDossier = "DOS-2024-001", DOIDClient = 7, Archive = false, DODateDebut = new DateOnly(2024, 1, 15) }; var referenceRows = new Dictionary { [2] = new() { PAPKID = 2, PACode = "T", PAValeur = "1", PALibelle = "Chantier" }, [3] = new() { PAPKID = 3, PACode = "S", PAValeur = "1", PALibelle = "Ouvert" } }; CanonicalMappingResult result = BatikMappers.BatikDossierMapper.Map(row, referenceRows); Dossier dossier = result.Value; Assert.Equal("DOS-2024-001", dossier.ReferenceMetier); Assert.Equal("Dossier chantier", dossier.Titre); Assert.Equal("Chantier", dossier.Type!.Label); Assert.Equal(2, dossier.Type!.LegacyId); Assert.Equal("Ouvert", dossier.Statut!.Label); Assert.Equal(3, dossier.Statut!.LegacyId); Assert.Equal(new DateTime(2024, 1, 15), dossier.DateOuverture); Assert.False(dossier.EstArchive); Assert.Equal(42, dossier.SourceReference!.NumericId); Assert.Equal(DatabaseSchemaVersion.Batik, dossier.SourceReference!.Source); // La référence physique du client lié est conservée en interne. Assert.NotNull(dossier.ClientSourceReference); Assert.Equal(7, dossier.ClientSourceReference!.NumericId); } // ---- 4. Aucune exception sur les chaînes vides -------------------------------- [Fact] public void DossierBatik_EmptyOrNullStrings_NeverThrow() { var row = new BatikModels.DossierRow { DOPKID = 5, DOTitre = "", DONumDossier = " " }; CanonicalMappingResult result = BatikMappers.BatikDossierMapper.Map(row); Assert.NotNull(result.Value); Assert.Null(result.Value.Titre); Assert.Null(result.Value.ReferenceMetier); Assert.Empty(result.Warnings); } [Fact] public void DossierBatik_DefaultRow_NeverThrows() { var row = new BatikModels.DossierRow(); CanonicalMappingResult result = BatikMappers.BatikDossierMapper.Map(row); Assert.NotNull(result.Value); // SourceReference est toujours rempli (porte DO_PKID) ; avec une ligne par // défaut DO_PKID = 0, NumericId vaut 0 (jamais null en production car la // clé primaire est auto-générée et strictement positive). Assert.NotNull(result.Value.SourceReference); Assert.Equal(0, result.Value.SourceReference!.NumericId); Assert.False(result.Value.EstArchive); } // ---- 5. Absence de noms physiques HFSQL dans le domaine canonique -------------- [Fact] public void DossierBatik_CanonicalDomain_DoesNotExposeHfSqlNames() { var row = new BatikModels.DossierRow { DOPKID = 40, DOIDTypeDossier = 2, DOIDStatut = 3, DOTitre = "Dossier", DONumDossier = "DOS-40", DOIDClient = 7, Archive = false }; var referenceRows = new Dictionary { [2] = new() { PAPKID = 2, PACode = "T", PAValeur = "1", PALibelle = "Chantier" }, [3] = new() { PAPKID = 3, PACode = "S", PAValeur = "1", PALibelle = "Ouvert" } }; CanonicalMappingResult result = BatikMappers.BatikDossierMapper.Map(row, referenceRows); Dossier dossier = result.Value; string[] forbidden = [ "DO_", "PA_", "PKID", "HfSql", "HFSQL", "GUID" ]; AssertHasNoPhysicalNames(dossier, forbidden); if (dossier.SourceReference is not null) { AssertHasNoPhysicalNames(dossier.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)); } } }