Files
Omika/tests/Omika.Tests/Mapping/OmikaInterventionMapperTests.cs
T
2026-10-23 18:46:50 +02:00

302 lines
12 KiB
C#

using Omika.Core.Compatibility;
using Omika.Core.Domain.Entities.Interventions;
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 interventions.
/// Aucune base réelle, aucun HFSQL : uniquement des objets en mémoire.
///
/// Le mapper ne renseigne pas <c>SourceReference</c> sur l'entité canonique
/// (la référence source est construite en interne mais non affectée) ; ces
/// tests se concentrent donc sur les propriétés canoniques réellement mappées.
/// </summary>
public class OmikaInterventionMapperTests
{
// ---- 1. Intervention Omika minimale -------------------------------------------
[Fact]
public void InterventionOmika_Minimal_MapsWithoutExceptionAndKeepsNoFabricatedValue()
{
var row = new OmikaModels.InterventionRow { INPKID = 1 };
CanonicalMappingResult<Intervention> result = OmikaMappers.OmikaInterventionMapper.Map(row);
Assert.Equal(DatabaseSchemaVersion.Omika, result.SourceVersion);
Assert.NotNull(result.Value);
Assert.Null(result.Value.Titre);
Assert.Null(result.Value.Reference);
Assert.Null(result.Value.Type);
Assert.Null(result.Value.Etat);
Assert.False(result.Value.EstArchive);
Assert.Empty(result.Warnings);
}
// ---- 2. Intervention Omika complète -------------------------------------------
[Fact]
public void InterventionOmika_Full_MapsAllCanonicalProperties()
{
var row = new OmikaModels.InterventionRow
{
INPKID = 42,
INTitre = "Réparation chaudière",
INRefInterne = "INT-042",
INDescription = "Remplacement de la chaudière",
INCleType = "TYP-MAINT",
INCleEtat = "STA-ENCOURS",
INDateIntervention = new DateOnly(2026, 3, 15),
Archive = true,
INIDImport = "IMP-O-42",
GUID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
};
var referenceRows = new Dictionary<string, OmikaModels.ParametreMetierRow>
{
["TYP-MAINT"] = new() { PAPKID = 2, PACLE = "TYP-MAINT", PALibelle = "Maintenance" },
["STA-ENCOURS"] = new() { PAPKID = 3, PACLE = "STA-ENCOURS", PALibelle = "En cours" }
};
CanonicalMappingResult<Intervention> result = OmikaMappers.OmikaInterventionMapper.Map(row, referenceRows);
Intervention intervention = result.Value;
Assert.Equal("Réparation chaudière", intervention.Titre);
Assert.Equal("INT-042", intervention.Reference);
Assert.Equal("Remplacement de la chaudière", intervention.Description);
Assert.Equal("Maintenance", intervention.Type!.Label);
Assert.Equal("TYP-MAINT", intervention.Type!.Key);
Assert.Equal("En cours", intervention.Etat!.Label);
Assert.Equal("STA-ENCOURS", intervention.Etat!.Key);
Assert.Equal(new DateTime(2026, 3, 15), intervention.DatePrevueDebut);
Assert.True(intervention.EstArchive);
}
// ---- 3. GUID ------------------------------------------------------------------
[Fact]
public void InterventionOmika_InvalidGuid_ProducesWarning()
{
var row = new OmikaModels.InterventionRow { INPKID = 42, GUID = "not-a-valid-guid" };
CanonicalMappingResult<Intervention> result = OmikaMappers.OmikaInterventionMapper.Map(row);
Assert.Contains(result.Warnings, w => w.Contains(MappingWarningCodes.InvalidPhysicalGuid, StringComparison.Ordinal));
}
[Fact]
public void InterventionOmika_EmptyGuid_NoWarning()
{
var row = new OmikaModels.InterventionRow { INPKID = 42, GUID = "" };
CanonicalMappingResult<Intervention> result = OmikaMappers.OmikaInterventionMapper.Map(row);
Assert.Empty(result.Warnings);
}
// ---- 4. Type et état (clés métier) --------------------------------------------
[Fact]
public void InterventionOmika_Type_IsResolvedFromCleType()
{
var row = new OmikaModels.InterventionRow { INPKID = 5, INCleType = "TYP-URG" };
var referenceRows = new Dictionary<string, OmikaModels.ParametreMetierRow>
{
["TYP-URG"] = new() { PAPKID = 7, PACLE = "TYP-URG", PALibelle = "Urgence" }
};
CanonicalMappingResult<Intervention> result = OmikaMappers.OmikaInterventionMapper.Map(row, referenceRows);
Assert.NotNull(result.Value.Type);
Assert.Equal("Urgence", result.Value.Type!.Label);
Assert.Equal("TYP-URG", result.Value.Type!.Key);
Assert.Empty(result.Warnings);
}
[Fact]
public void InterventionOmika_UnresolvedType_KeepsKeyAndWarns()
{
var row = new OmikaModels.InterventionRow { INPKID = 6, INCleType = "TYP-INCONNU" };
CanonicalMappingResult<Intervention> result = OmikaMappers.OmikaInterventionMapper.Map(row);
Assert.NotNull(result.Value.Type);
Assert.Equal("TYP-INCONNU", result.Value.Type!.Key);
Assert.Equal(string.Empty, result.Value.Type!.Label);
Assert.Contains(result.Warnings, w => w.Contains("IN_CleType", StringComparison.Ordinal));
}
[Fact]
public void InterventionOmika_NoTypeKey_ProducesNoReference()
{
var row = new OmikaModels.InterventionRow { INPKID = 8 };
CanonicalMappingResult<Intervention> result = OmikaMappers.OmikaInterventionMapper.Map(row);
Assert.Null(result.Value.Type);
Assert.Empty(result.Warnings);
}
[Fact]
public void InterventionOmika_UnresolvedEtat_KeepsKeyAndWarns()
{
var row = new OmikaModels.InterventionRow { INPKID = 9, INCleEtat = "STA-INCONNU" };
CanonicalMappingResult<Intervention> result = OmikaMappers.OmikaInterventionMapper.Map(row);
Assert.NotNull(result.Value.Etat);
Assert.Equal("STA-INCONNU", result.Value.Etat!.Key);
Assert.Contains(result.Warnings, w => w.Contains("IN_CleEtat", StringComparison.Ordinal));
}
// ---- 5. Champs ignorés --------------------------------------------------------
[Fact]
public void InterventionOmika_FieldsWithoutCanonicalEquivalent_AreReportedAsIgnored()
{
var row = new OmikaModels.InterventionRow
{
INPKID = 15,
INIDClient = 1,
INIDLieu = 2,
INIDDossier = 3,
INIDSuiviPar = 4,
INIDDemandeur = 5,
INIDSociete = 6,
INAttestationTVAPresente = true,
INNoteInterne = "note",
INRefExterne = "EXT-1",
INDureePrevue = 2.5f,
INNumDevis = "DEV-1",
INDateDevis = new DateOnly(2026, 1, 1),
INNumFacture = "FAC-1",
INDateFacture = new DateOnly(2026, 2, 1),
INNumIntervention = "N-1",
ModifDate = new DateTime(2024, 1, 1),
CreationDate = new DateTime(2023, 1, 1),
DateArchivage = new DateTime(2024, 6, 1)
};
CanonicalMappingResult<Intervention> result = OmikaMappers.OmikaInterventionMapper.Map(row);
Assert.Contains(result.IgnoredFields, f => f == "IN_IDClient");
Assert.Contains(result.IgnoredFields, f => f == "IN_IDLieu");
Assert.Contains(result.IgnoredFields, f => f == "IN_IDDossier");
Assert.Contains(result.IgnoredFields, f => f == "IN_IDSuiviPar");
Assert.Contains(result.IgnoredFields, f => f == "IN_IDDemandeur");
Assert.Contains(result.IgnoredFields, f => f == "IN_IDSociete");
Assert.Contains(result.IgnoredFields, f => f == "IN_AttestationTVAPresente");
Assert.Contains(result.IgnoredFields, f => f == "IN_NoteInterne");
Assert.Contains(result.IgnoredFields, f => f == "IN_DureePrevue");
Assert.Contains(result.IgnoredFields, f => f == "IN_DateDevis");
Assert.Contains(result.IgnoredFields, f => f == "IN_DateFacture");
Assert.Equal(new DateTime(2024, 1, 1), result.Value.DateModification);
Assert.Contains(result.IgnoredFields, f => f == "CreationDate");
Assert.Contains(result.IgnoredFields, f => f == "DateArchivage");
// Champs désormais dotés d'un équivalent canonique : ils ne sont plus
// ignorés, ils sont mappés dans l'entité Intervention.
Assert.Equal("EXT-1", result.Value.ReferenceExterne);
Assert.Equal("DEV-1", result.Value.NumeroDevis);
Assert.Equal("N-1", result.Value.NumeroIntervention);
Assert.Equal("FAC-1", result.Value.NumeroFacture);
}
// ---- 6. Aucune exception sur les chaînes vides --------------------------------
[Fact]
public void InterventionOmika_EmptyOrNullStrings_NeverThrow()
{
var row = new OmikaModels.InterventionRow
{
INTitre = "",
INRefInterne = " ",
INDescription = "",
INNoteInterne = " ",
INRefExterne = "",
INIDImport = " "
};
CanonicalMappingResult<Intervention> result = OmikaMappers.OmikaInterventionMapper.Map(row);
Assert.NotNull(result.Value);
Assert.Null(result.Value.Titre);
Assert.Null(result.Value.Reference);
Assert.Null(result.Value.Description);
Assert.Empty(result.Warnings);
}
[Fact]
public void InterventionOmika_DefaultRow_NeverThrows()
{
var row = new OmikaModels.InterventionRow();
CanonicalMappingResult<Intervention> result = OmikaMappers.OmikaInterventionMapper.Map(row);
Assert.NotNull(result.Value);
Assert.False(result.Value.EstArchive);
}
// ---- 7. Absence de noms physiques HFSQL dans le domaine canonique -------------
[Fact]
public void InterventionOmika_CanonicalDomain_DoesNotExposeHfSqlNames()
{
var row = new OmikaModels.InterventionRow
{
INPKID = 40,
INTitre = "Titre",
INRefInterne = "INT-040",
INDescription = "desc",
INCleType = "TYP-MAINT",
INCleEtat = "STA-ENCOURS",
INDateIntervention = new DateOnly(2026, 1, 1),
Archive = false,
INIDImport = "IMP-40",
INIDClient = 1,
INIDLieu = 2,
INIDDossier = 3,
INIDSuiviPar = 4,
INIDDemandeur = 5,
INIDSociete = 6,
INAttestationTVAPresente = true,
INNoteInterne = "note",
INRefExterne = "EXT",
INDureePrevue = 1f,
INNumDevis = "DEV",
INNumFacture = "FAC",
INNumIntervention = "N",
GUID = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
};
var referenceRows = new Dictionary<string, OmikaModels.ParametreMetierRow>
{
["TYP-MAINT"] = new() { PAPKID = 2, PACLE = "TYP-MAINT", PALibelle = "Maintenance" },
["STA-ENCOURS"] = new() { PAPKID = 3, PACLE = "STA-ENCOURS", PALibelle = "En cours" }
};
CanonicalMappingResult<Intervention> result = OmikaMappers.OmikaInterventionMapper.Map(row, referenceRows);
Intervention intervention = result.Value;
string[] forbidden =
[
"IN_", "PA_", "PKID", "HfSql", "HFSQL"
];
AssertHasNoPhysicalNames(intervention, 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));
}
}
}