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

282 lines
11 KiB
C#

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;
/// <summary>
/// Tests unitaires du mapper Batik → canonique des interventions.
/// Aucune base réelle, aucun HFSQL : uniquement des objets en mémoire.
/// </summary>
public class BatikInterventionMapperTests
{
// ---- 1. Intervention Batik minimale -------------------------------------------
[Fact]
public void InterventionBatik_Minimal_MapsWithoutExceptionAndKeepsNoFabricatedValue()
{
var row = new BatikModels.InterventionRow { INPKID = 1 };
CanonicalMappingResult<Intervention> result = BatikMappers.BatikInterventionMapper.Map(row);
Assert.Equal(DatabaseSchemaVersion.Batik, 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 Batik complète -------------------------------------------
[Fact]
public void InterventionBatik_Full_MapsAllCanonicalProperties()
{
var row = new BatikModels.InterventionRow
{
INPKID = 42,
INTitre = "Réparation chaudière",
INRefInterne = "INT-042",
INDescription = "Remplacement de la chaudière",
INIDTypeIntervention = 2,
INIDEtatIntervention = 3,
INDateIntervention = new DateOnly(2026, 3, 15),
Archive = true,
INIDImport = "IMP-B-42"
};
var referenceRows = new Dictionary<long, BatikModels.ParametreMetierRow>
{
[2] = new() { PAPKID = 2, PACode = "T", PAValeur = "1", PALibelle = "Maintenance" },
[3] = new() { PAPKID = 3, PACode = "S", PAValeur = "1", PALibelle = "En cours" }
};
CanonicalMappingResult<Intervention> result = BatikMappers.BatikInterventionMapper.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(2, intervention.Type!.LegacyId);
Assert.Equal("En cours", intervention.Etat!.Label);
Assert.Equal(3, intervention.Etat!.LegacyId);
Assert.Equal(new DateTime(2026, 3, 15), intervention.DatePrevueDebut);
Assert.True(intervention.EstArchive);
}
// ---- 4. Type et état ----------------------------------------------------------
[Fact]
public void InterventionBatik_Type_IsResolvedFromLegacyId()
{
var row = new BatikModels.InterventionRow { INPKID = 5, INIDTypeIntervention = 7 };
var referenceRows = new Dictionary<long, BatikModels.ParametreMetierRow>
{
[7] = new() { PAPKID = 7, PACode = "T", PAValeur = "2", PALibelle = "Urgence" }
};
CanonicalMappingResult<Intervention> result = BatikMappers.BatikInterventionMapper.Map(row, referenceRows);
Assert.NotNull(result.Value.Type);
Assert.Equal("Urgence", result.Value.Type!.Label);
Assert.Equal(7, result.Value.Type!.LegacyId);
Assert.Empty(result.Warnings);
}
[Fact]
public void InterventionBatik_UnresolvedType_KeepsLegacyIdAndWarns()
{
var row = new BatikModels.InterventionRow { INPKID = 6, INIDTypeIntervention = 77 };
CanonicalMappingResult<Intervention> result = BatikMappers.BatikInterventionMapper.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("IN_IDTypeIntervention", StringComparison.Ordinal)
&& w.Contains(MappingWarningCodes.UnresolvedReference, StringComparison.Ordinal));
}
[Fact]
public void InterventionBatik_ZeroType_ProducesNoReference()
{
var row = new BatikModels.InterventionRow { INPKID = 8, INIDTypeIntervention = 0 };
CanonicalMappingResult<Intervention> result = BatikMappers.BatikInterventionMapper.Map(row);
Assert.Null(result.Value.Type);
Assert.Empty(result.Warnings);
}
[Fact]
public void InterventionBatik_UnresolvedEtat_KeepsLegacyIdAndWarns()
{
var row = new BatikModels.InterventionRow { INPKID = 9, INIDEtatIntervention = 88 };
CanonicalMappingResult<Intervention> result = BatikMappers.BatikInterventionMapper.Map(row);
Assert.NotNull(result.Value.Etat);
Assert.Equal(88, result.Value.Etat!.LegacyId);
Assert.Contains(result.Warnings, w => w.Contains("IN_IDEtatIntervention", StringComparison.Ordinal)
&& w.Contains(MappingWarningCodes.UnresolvedReference, StringComparison.Ordinal));
}
// ---- 5. Champs ignorés --------------------------------------------------------
[Fact]
public void InterventionBatik_FieldsWithoutCanonicalEquivalent_AreReportedAsIgnored()
{
var row = new BatikModels.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 = BatikMappers.BatikInterventionMapper.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 InterventionBatik_EmptyOrNullStrings_NeverThrow()
{
var row = new BatikModels.InterventionRow
{
INTitre = "",
INRefInterne = " ",
INDescription = "",
INNoteInterne = " ",
INRefExterne = "",
INIDImport = " "
};
CanonicalMappingResult<Intervention> result = BatikMappers.BatikInterventionMapper.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 InterventionBatik_DefaultRow_NeverThrows()
{
var row = new BatikModels.InterventionRow();
CanonicalMappingResult<Intervention> result = BatikMappers.BatikInterventionMapper.Map(row);
Assert.NotNull(result.Value);
Assert.False(result.Value.EstArchive);
}
// ---- 7. Absence de noms physiques HFSQL dans le domaine canonique -------------
[Fact]
public void InterventionBatik_CanonicalDomain_DoesNotExposeHfSqlNames()
{
var row = new BatikModels.InterventionRow
{
INPKID = 40,
INTitre = "Titre",
INRefInterne = "INT-040",
INDescription = "desc",
INIDTypeIntervention = 2,
INIDEtatIntervention = 3,
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"
};
var referenceRows = new Dictionary<long, BatikModels.ParametreMetierRow>
{
[2] = new() { PAPKID = 2, PACode = "T", PAValeur = "1", PALibelle = "Maintenance" },
[3] = new() { PAPKID = 3, PACode = "S", PAValeur = "1", PALibelle = "En cours" }
};
CanonicalMappingResult<Intervention> result = BatikMappers.BatikInterventionMapper.Map(row, referenceRows);
Intervention intervention = result.Value;
string[] forbidden =
[
"IN_", "PA_", "PKID", "HfSql", "HFSQL", "GUID"
];
AssertHasNoPhysicalNames(intervention, forbidden);
if (intervention.SourceReference is not null)
{
AssertHasNoPhysicalNames(intervention.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));
}
}
}