Normaliser les textes et poser les regles de rapprochement d'auteurs
SQLite ne sait pas comparer sans tenir compte des accents : sa collation NOCASE ne couvre que l'ASCII et il n'existe pas de unaccent() integre. La seule facon d'obtenir « Emile » vers « Émile » sans charger tout le catalogue en memoire est de stocker la forme mise a plat, et d'y chercher un terme passe par exactement la meme fonction. NormalisationTexte fait cette mise a plat. La ponctuation y devient une espace au lieu de disparaitre : « P.F. Hamilton » doit donner trois mots, sans quoi le rapprochement par initiales serait impossible. RapprochementAuteurs porte la frontiere decidee dans CLAUDE.md. La cle de regroupement — mots normalises tries — couvre casse, accents et ordre inverse, et servira d'index unique. Les initiales abregees lui echappent : elles demandent d'aligner les mots un a un, avec retour arriere, parce qu'une correspondance gloutonne echouerait sur « P. Peter » face a « Peter Paul ». Le verdict est a trois niveaux, et l'asymetrie est deliberee : ne pas fusionner laisse deux fiches a reunir d'un clic, alors qu'une fusion erronee melange les livres de deux personnes et se defait mal. Un patronyme seul face a un nom complet ressort donc « suggere », jamais « certain ». Meme exigence sur l'alignement complet : « P. H. » et « Peter Hamilton » s'alignent par les seules initiales, ce qui ne concorde sur rien de verifiable — un mot entier commun est exige. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
using MaBibli.Shared.Textes;
|
||||
|
||||
namespace MaBibli.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Règles de regroupement des noms d'auteurs.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// La frontière testée ici est celle que CLAUDE.md pose : <b>ce qui est sûr est fusionné, ce qui
|
||||
/// est ambigu est proposé</b>. 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.
|
||||
/// </remarks>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// La clé ne sait pas rapprocher les initiales : c'est justement pour ça que
|
||||
/// <see cref="RapprochementAuteurs.Comparer"/> existe en plus de l'index unique.
|
||||
/// </summary>
|
||||
[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")]
|
||||
[InlineData("", "")]
|
||||
[InlineData(null, "")]
|
||||
public void La_normalisation_met_a_plat_casse_accents_et_ponctuation(string? brut, string attendu)
|
||||
=> Assert.Equal(attendu, NormalisationTexte.Normaliser(brut));
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void La_ponctuation_separe_les_mots()
|
||||
=> Assert.Equal(["p", "f", "hamilton"], NormalisationTexte.Mots("P.F. Hamilton"));
|
||||
}
|
||||
Reference in New Issue
Block a user