Files
mabibli/MaBibli.Tests/RapprochementAuteursTests.cs
T
mathieuandClaude Opus 5 b97cf33494 Ramener les ligatures « œ » et « æ » à « oe » et « ae »
L'Œuvre de Zola, que la BnF écrit avec la ligature, était introuvable à
qui tape « oeuvre » : NFD sépare les accents mais laisse les ligatures.

Table explicite plutôt que NFKD, qui aurait aussi transformé les exposants,
les chiffres romains et les espaces insécables. Une normalisation de
recherche doit rester prévisible.

Sept colonnes normalisées en dépendent, dont cinq portent une unicité. Le
rattrapage au démarrage est donc étendu aux envies, séries, revues et
numéros — qui ne calculaient leurs formes qu'à l'écriture — et refuse toute
collision AVANT d'écrire : sinon l'exception tomberait au démarrage et le
serveur ne se lancerait plus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:25:59 +02:00

128 lines
5.4 KiB
C#

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