Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,456 @@
|
||||
using Omika.Core.Compatibility;
|
||||
using Omika.Core.Domain.Entities.Clients;
|
||||
using Omika.Core.Domain.ValueObjects;
|
||||
using Omika.Data.Mapping.Common;
|
||||
using OmikaModels = Omika.Data.HfSql.Models.Omika;
|
||||
using OmikaMappers = Omika.Data.Mapping.Omika;
|
||||
|
||||
namespace Omika.Tests.Mapping;
|
||||
|
||||
/// <summary>
|
||||
/// Tests unitaires du mapper Omika → canonique des sites (table Lieu).
|
||||
/// Aucune base réelle, aucun HFSQL : uniquement des objets en mémoire.
|
||||
/// </summary>
|
||||
public class OmikaSiteMapperTests
|
||||
{
|
||||
// ---- 1. Site Omika minimal --------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_Minimal_MapsWithoutExceptionAndKeepsNoFabricatedValue()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow { LIPKID = 1 };
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.Equal(DatabaseSchemaVersion.Omika, 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(DatabaseSchemaVersion.Omika, result.Value.SourceReference!.Source);
|
||||
Assert.Equal(1, result.Value.SourceReference!.NumericId);
|
||||
Assert.Null(result.Value.SourceReference!.EntityGuid);
|
||||
Assert.Empty(result.Warnings);
|
||||
}
|
||||
|
||||
// ---- 2. Site Omika complet --------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_Full_MapsAllCanonicalProperties()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow
|
||||
{
|
||||
LIPKID = 42,
|
||||
GUID = "550e8400-e29b-41d4-a716-446655440000",
|
||||
LICodeLieu = "S-001",
|
||||
LICleType = "IMM",
|
||||
LIIDType = 2,
|
||||
LINote = "Site principal",
|
||||
Archive = true,
|
||||
LIIDImport = "IMP-O-42"
|
||||
};
|
||||
|
||||
var referenceRows = new Dictionary<string, OmikaModels.ParametreMetierRow>
|
||||
{
|
||||
["IMM"] = new() { PAPKID = 2, PACLE = "IMM", PALibelle = "Immeuble", PACategorie = "TYPE" }
|
||||
};
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row, referenceRows);
|
||||
|
||||
Site site = result.Value;
|
||||
Assert.Equal("S-001", site.Code);
|
||||
Assert.Equal("Immeuble", site.Type!.Label);
|
||||
Assert.Equal("IMM", site.Type!.Key);
|
||||
Assert.Equal(2, site.Type!.LegacyId);
|
||||
Assert.Equal("Site principal", site.InformationsComplementaires);
|
||||
Assert.True(site.EstArchive);
|
||||
|
||||
Assert.Equal(42, site.SourceReference!.NumericId);
|
||||
Assert.Equal("IMP-O-42", site.SourceReference!.ImportId);
|
||||
Assert.Equal(DatabaseSchemaVersion.Omika, site.SourceReference!.Source);
|
||||
Assert.NotNull(site.SourceReference!.EntityGuid);
|
||||
}
|
||||
|
||||
// ---- 3. GUID valide avec tirets ----------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_ValidGuidWithDashes_IsKeptInEntityGuid()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow
|
||||
{
|
||||
LIPKID = 7,
|
||||
GUID = "550e8400-e29b-41d4-a716-446655440000",
|
||||
LICodeLieu = "S-007"
|
||||
};
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.NotNull(result.Value.SourceReference!.EntityGuid);
|
||||
Assert.Equal(
|
||||
"550e8400-e29b-41d4-a716-446655440000",
|
||||
result.Value.SourceReference!.EntityGuid!.Value.ToString("D"));
|
||||
Assert.Equal("550e8400-e29b-41d4-a716-446655440000", result.Value.SourceReference!.PhysicalGuid);
|
||||
Assert.Empty(result.Warnings);
|
||||
}
|
||||
|
||||
// ---- 4. GUID valide sans tirets ----------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_ValidGuidWithoutDashes_IsParsedByTryParse()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow
|
||||
{
|
||||
LIPKID = 8,
|
||||
GUID = "550e8400e29b41d4a716446655440000"
|
||||
};
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.NotNull(result.Value.SourceReference!.EntityGuid);
|
||||
Assert.Equal(
|
||||
"550e8400-e29b-41d4-a716-446655440000",
|
||||
result.Value.SourceReference!.EntityGuid!.Value.ToString("D"));
|
||||
Assert.Empty(result.Warnings);
|
||||
}
|
||||
|
||||
// ---- 5. GUID invalide ---------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_InvalidGuid_ProducesNullGuidAndWarningWithoutException()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow
|
||||
{
|
||||
LIPKID = 9,
|
||||
GUID = "not-a-guid",
|
||||
LICodeLieu = "S-009"
|
||||
};
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.Null(result.Value.SourceReference!.EntityGuid);
|
||||
Assert.Equal("not-a-guid", result.Value.SourceReference!.PhysicalGuid);
|
||||
Assert.Contains(result.Warnings, w => w.StartsWith(MappingWarningCodes.InvalidPhysicalGuid, StringComparison.Ordinal));
|
||||
Assert.Equal("S-009", result.Value.Code);
|
||||
}
|
||||
|
||||
// ---- 6. Absence de GUID fabriqué ----------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_WithoutGuid_ProducesNoFabricatedGuid()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow { LIPKID = 11 };
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.Null(result.Value.SourceReference!.EntityGuid);
|
||||
Assert.Null(result.Value.SourceReference!.PhysicalGuid);
|
||||
Assert.Empty(result.Warnings);
|
||||
}
|
||||
|
||||
// ---- 7. Conservation de l'identifiant numérique historique ---------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_NumericIdentifier_IsPreserved()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow { LIPKID = 1234 };
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.NotNull(result.Value.SourceReference);
|
||||
Assert.Equal(DatabaseSchemaVersion.Omika, result.Value.SourceReference!.Source);
|
||||
Assert.Equal(1234, result.Value.SourceReference!.NumericId);
|
||||
}
|
||||
|
||||
// ---- 8. Conservation de l'identifiant d'import ---------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_ImportId_IsPreserved()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow { LIPKID = 5678, LIIDImport = "IMP-O" };
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.Equal("IMP-O", result.Value.SourceReference!.ImportId);
|
||||
Assert.Equal(5678, result.Value.SourceReference!.NumericId);
|
||||
}
|
||||
|
||||
// ---- 9. Priorité de la clé métier sur l'ancien identifiant ---------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_BusinessKeyIsPreferredAndLegacyIdIsKeptAsCompatibility()
|
||||
{
|
||||
var referenceRows = new Dictionary<string, OmikaModels.ParametreMetierRow>
|
||||
{
|
||||
["IMM"] = new() { PAPKID = 50, PACLE = "IMM", PALibelle = "Immeuble", PACategorie = "TYPE" }
|
||||
};
|
||||
|
||||
var row = new OmikaModels.LieuRow
|
||||
{
|
||||
LIPKID = 13,
|
||||
LICleType = "IMM",
|
||||
LIIDType = 99 // zombie : ancien identifiant
|
||||
};
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row, referenceRows);
|
||||
|
||||
Assert.Equal("IMM", result.Value.Type!.Key);
|
||||
Assert.Equal("Immeuble", result.Value.Type!.Label);
|
||||
Assert.Equal(99, result.Value.Type!.LegacyId);
|
||||
Assert.Empty(result.Warnings);
|
||||
}
|
||||
|
||||
// ---- 10. Repli sur un champ zombie avec avertissement ---------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_MissingBusinessKey_ExposesZombieLegacyIdWithWarning()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow
|
||||
{
|
||||
LIPKID = 14,
|
||||
LICleType = null, // rubrique active absente
|
||||
LIIDType = 77 // ancien identifiant (zombie)
|
||||
};
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.NotNull(result.Value.Type);
|
||||
Assert.Equal(77, result.Value.Type!.LegacyId);
|
||||
Assert.Null(result.Value.Type!.Key);
|
||||
Assert.Contains(result.Warnings, w => w.StartsWith(MappingWarningCodes.ZombieFieldUsedAsFallback, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
// ---- 11. Référentiel non résolu -------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_UnresolvedBusinessKey_KeepsKeyAndLegacyIdAndWarns()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow
|
||||
{
|
||||
LIPKID = 15,
|
||||
LICleType = "IMM",
|
||||
LIIDType = 77
|
||||
};
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.NotNull(result.Value.Type);
|
||||
Assert.Equal("IMM", result.Value.Type!.Key);
|
||||
Assert.Equal(77, result.Value.Type!.LegacyId);
|
||||
Assert.Equal(string.Empty, result.Value.Type!.Label);
|
||||
Assert.Contains(result.Warnings, w => w.Contains(MappingWarningCodes.UnresolvedReference, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_ZeroType_ProducesNoReference()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow { LIPKID = 16, LIIDType = 0 };
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.Null(result.Value.Type);
|
||||
Assert.Empty(result.Warnings);
|
||||
}
|
||||
|
||||
// ---- 12. Adresse déjà chargée en mémoire ---------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_Adresse_IsMappedFromInMemoryDictionary()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow { LIPKID = 17, LIIDAdresse = 20 };
|
||||
|
||||
var adresses = new Dictionary<long, OmikaModels.AdresseRow>
|
||||
{
|
||||
[20] = new() { ADPKID = 20, ADAdresse = "1 place de la Comédie", ADVille = "Lyon", ADCodePostal = "69001" }
|
||||
};
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.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 SiteOmika_NoAdresse_WhenNothingProvided()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow { LIPKID = 18, LIIDAdresse = 0 };
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.Null(result.Value.Adresse);
|
||||
Assert.Empty(result.Warnings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_UnresolvedAdresse_WarnsAndProducesNull()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow { LIPKID = 19, LIIDAdresse = 99 };
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.Null(result.Value.Adresse);
|
||||
Assert.Contains(result.Warnings, w => w.Contains(MappingWarningCodes.UnresolvedReference, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
// ---- 13. Relations différées ----------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_Relations_AreLeftForLaterAssembly()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow { LIPKID = 20, LIIDClientPrincipal = 30 };
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.Null(result.Value.ClientId);
|
||||
Assert.Empty(result.Value.ContactIds);
|
||||
Assert.Contains(result.IgnoredFields, f => f == "LI_IDClientPrincipal");
|
||||
}
|
||||
|
||||
// ---- 14. Chaînes vides sans exception -------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_EmptyOrNullStrings_NeverThrow()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow
|
||||
{
|
||||
LICodeLieu = "",
|
||||
LINote = " ",
|
||||
LIIDImport = "",
|
||||
LICodeAcces = " ",
|
||||
LIRaisonBlocage = "",
|
||||
LIRepertoireDocs = " ",
|
||||
GUID = null
|
||||
};
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.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 SiteOmika_DefaultRow_NeverThrows()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow();
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.Map(row);
|
||||
|
||||
Assert.NotNull(result.Value);
|
||||
Assert.Null(result.Value.SourceReference!.NumericId);
|
||||
Assert.False(result.Value.EstArchive);
|
||||
}
|
||||
|
||||
// ---- 15. Champs sans équivalent dans IgnoredFields --------------------------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_FieldsWithoutCanonicalEquivalent_AreReportedAsIgnored()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow
|
||||
{
|
||||
LIPKID = 21,
|
||||
LICodeAcces = "Code 1234",
|
||||
LIBadge = true,
|
||||
LIInterphone = true,
|
||||
LIBlocage = true,
|
||||
LIRaisonBlocage = "Travaux",
|
||||
LIRepertoireDocs = "docs/site",
|
||||
LIIDClientPrincipal = 30,
|
||||
DateArchivage = new DateTime(2024, 6, 1),
|
||||
ModifDate = new DateTime(2024, 1, 1),
|
||||
CreationDate = new DateTime(2023, 1, 1),
|
||||
CreationUser = 5,
|
||||
ModifUser = 6
|
||||
};
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.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");
|
||||
Assert.Contains(result.IgnoredFields, f => f == "LI_Blocage");
|
||||
Assert.Contains(result.IgnoredFields, f => f == "LI_RaisonBlocage");
|
||||
Assert.Contains(result.IgnoredFields, f => f == "LI_RepertoireDocs");
|
||||
Assert.Contains(result.IgnoredFields, f => f == "LI_IDClientPrincipal");
|
||||
Assert.Contains(result.IgnoredFields, f => f == "DateArchivage");
|
||||
Assert.Equal(new DateTime(2024, 1, 1), result.Value.DateModification);
|
||||
Assert.Contains(result.IgnoredFields, f => f == "CreationDate");
|
||||
Assert.Contains(result.IgnoredFields, f => f == "CreationUser");
|
||||
Assert.Contains(result.IgnoredFields, f => f == "ModifUser");
|
||||
}
|
||||
|
||||
// ---- 16. Absence de noms physiques HFSQL dans le domaine canonique ----------------
|
||||
|
||||
[Fact]
|
||||
public void SiteOmika_CanonicalDomain_DoesNotExposeHfSqlNames()
|
||||
{
|
||||
var row = new OmikaModels.LieuRow
|
||||
{
|
||||
LIPKID = 40,
|
||||
GUID = "550e8400-e29b-41d4-a716-446655440000",
|
||||
LICodeLieu = "S-040",
|
||||
LICleType = "IMM",
|
||||
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<string, OmikaModels.ParametreMetierRow>
|
||||
{
|
||||
["IMM"] = new() { PAPKID = 2, PACLE = "IMM", PALibelle = "Immeuble", PACategorie = "TYPE" }
|
||||
};
|
||||
|
||||
var adresses = new Dictionary<long, OmikaModels.AdresseRow>
|
||||
{
|
||||
[20] = new() { ADPKID = 20, ADAdresse = "1 rue de la Paix", ADVille = "Lyon", ADCodePostal = "69001" }
|
||||
};
|
||||
|
||||
CanonicalMappingResult<Site> result = OmikaMappers.OmikaSiteMapper.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user