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