using MaBibli.Shared.Dtos;
using MaBibli.Shared.Entites;
using MaBibli.Shared.Textes;
namespace MaBibli.Tests;
///
/// Règles de regroupement des noms d'auteurs.
///
///
/// La frontière testée ici est celle que CLAUDE.md pose : ce qui est sûr est fusionné, ce qui
/// est ambigu est proposé. Les tests sont écrits par catégorie plutôt que par cas, pour que
/// l'ajout d'une règle oblige à dire de quel côté de la frontière elle tombe.
///
public class RapprochementAuteursTests
{
[Theory]
// Casse et accents.
[InlineData("Émile Zola", "emile zola")]
[InlineData("Émile Zola", "EMILE ZOLA")]
[InlineData("Émile Zola", "Emile Zola")]
// Ordre inversé, avec ou sans virgule.
[InlineData("Zola, Émile", "Émile Zola")]
[InlineData("Zola, Emile", "emile zola")]
// Initiales abrégées, le reste concordant.
[InlineData("Peter F. Hamilton", "P.F. Hamilton")]
[InlineData("Peter F. Hamilton", "P. F. Hamilton")]
[InlineData("P.F. Hamilton", "P. F. Hamilton")]
[InlineData("Thomas H. Cormen", "T. H. Cormen")]
// Combinaison des trois.
[InlineData("Hamilton, Peter F.", "p f hamilton")]
// Auteur mononyme.
[InlineData("Homère", "homere")]
public void Les_variantes_sures_sont_reconnues(string a, string b)
{
Assert.Equal(NiveauRapprochement.Certain, RapprochementAuteurs.Comparer(a, b));
Assert.Equal(NiveauRapprochement.Certain, RapprochementAuteurs.Comparer(b, a));
}
[Theory]
// Le cas d'école de CLAUDE.md : un patronyme seul face à un nom complet.
[InlineData("Hamilton", "Peter F. Hamilton")]
[InlineData("Zola", "Émile Zola")]
[InlineData("Cormen", "Thomas H. Cormen")]
// Un prénom en plus, ou en moins.
[InlineData("Émile Zola", "Émile Édouard Zola")]
public void Les_rapprochements_ambigus_sont_seulement_proposes(string a, string b)
{
Assert.Equal(NiveauRapprochement.Suggere, RapprochementAuteurs.Comparer(a, b));
Assert.Equal(NiveauRapprochement.Suggere, RapprochementAuteurs.Comparer(b, a));
// Et surtout : jamais fusionnés d'office.
Assert.False(RapprochementAuteurs.SontLeMemeAuteur(a, b));
}
[Theory]
// Deux personnes qui partagent un patronyme : rien ne doit les rapprocher automatiquement.
[InlineData("Peter Hamilton", "Paul Hamilton")]
[InlineData("Peter F. Hamilton", "Peter G. Hamilton")]
// Homonymes partiels sans mot entier commun.
[InlineData("Émile Zola", "Émile Ajar")]
[InlineData("Voltaire", "Molière")]
// Une initiale seule ne concorde sur rien de vérifiable.
[InlineData("P. H.", "Peter Hamilton")]
[InlineData("P. Hamilton", "Peter Paul")]
public void Les_auteurs_distincts_ne_sont_pas_rapproches(string a, string b)
{
Assert.Equal(NiveauRapprochement.Aucun, RapprochementAuteurs.Comparer(a, b));
Assert.Equal(NiveauRapprochement.Aucun, RapprochementAuteurs.Comparer(b, a));
}
[Fact]
public void Un_nom_vide_ne_se_rapproche_de_rien()
{
Assert.Equal(NiveauRapprochement.Aucun, RapprochementAuteurs.Comparer(null, "Émile Zola"));
Assert.Equal(NiveauRapprochement.Aucun, RapprochementAuteurs.Comparer(" ", "Émile Zola"));
Assert.Equal(NiveauRapprochement.Aucun, RapprochementAuteurs.Comparer(".,;", "Émile Zola"));
}
[Fact]
public void La_cle_est_identique_pour_les_variantes_dordre_et_daccents()
{
var attendue = RapprochementAuteurs.Cle("Émile Zola");
Assert.Equal(attendue, RapprochementAuteurs.Cle("Zola, Émile"));
Assert.Equal(attendue, RapprochementAuteurs.Cle("emile zola"));
Assert.Equal(attendue, RapprochementAuteurs.Cle(" ZOLA, Emile "));
Assert.Equal("emile zola", attendue);
}
///
/// La clé ne sait pas rapprocher les initiales : c'est justement pour ça que
/// existe en plus de l'index unique.
///
[Fact]
public void La_cle_ne_couvre_pas_les_initiales()
{
Assert.NotEqual(
RapprochementAuteurs.Cle("Peter F. Hamilton"),
RapprochementAuteurs.Cle("P.F. Hamilton"));
Assert.True(RapprochementAuteurs.SontLeMemeAuteur("Peter F. Hamilton", "P.F. Hamilton"));
}
[Theory]
[InlineData("Émile ZOLA", "emile zola")]
[InlineData("P.F. Hamilton", "p f hamilton")]
[InlineData(" Zola, Émile ", "zola emile")]
[InlineData("Saint-Exupéry", "saint exupery")]
[InlineData("L'Écume", "l ecume")]
// Ligatures : NFD ne les défait pas, une table explicite s'en charge (2026-08-19).
[InlineData("L'Œuvre", "l oeuvre")]
[InlineData("Sœur Emmanuelle", "soeur emmanuelle")]
[InlineData("Ex æquo", "ex aequo")]
// La ligature vaut deux lettres, elle ne coupe donc PAS le mot en deux.
[InlineData("cœur", "coeur")]
[InlineData("", "")]
[InlineData(null, "")]
public void La_normalisation_met_a_plat_casse_accents_et_ponctuation(string? brut, string attendu)
=> Assert.Equal(attendu, NormalisationTexte.Normaliser(brut));
///
/// Retaper le champ des noms ne doit pas effacer les rôles déjà posés.
///
///
/// Le champ se relie à chaque frappe : sans reprise des rôles connus, corriger une faute de
/// frappe dans le nom d'un auteur remettrait tous ses voisins à « non précisé », sous les
/// yeux de l'utilisateur et sans qu'il comprenne pourquoi.
///
[Fact]
public void Redecouper_la_ligne_dauteurs_preserve_les_roles()
{
var saisie = new EnregistrementLivre
{
Auteurs =
[
new AuteurSaisi("Dobbs", RoleAuteur.Scenario),
new AuteurSaisi("Christophe", RoleAuteur.Dessin),
],
};
// L'utilisateur ajoute un accent au second nom : la ligne est redécoupée entièrement.
saisie.Auteur = "Dobbs; Christophé";
// Les deux rôles survivent : le rapprochement se fait sur la forme NORMALISÉE, donc un
// accent ajouté après coup ne casse rien.
Assert.Equal(
[RoleAuteur.Scenario, RoleAuteur.Dessin],
saisie.Auteurs.Select(a => a.Role));
// Un nom réellement différent, lui, n'est pas reconnu : son rôle repart à zéro, ce qui
// est honnête — on ne devine pas qu'il s'agit de la même personne.
saisie.Auteur = "Dobbs; Marchand";
Assert.Equal(RoleAuteur.NonPrecise, saisie.Auteurs[1].Role);
}
///
/// La ponctuation devient une espace et ne disparaît pas : sans ça « P.F. » donnerait un seul
/// mot et les initiales ne pourraient plus s'aligner.
///
[Fact]
public void La_ponctuation_separe_les_mots()
=> Assert.Equal(["p", "f", "hamilton"], NormalisationTexte.Mots("P.F. Hamilton"));
}