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