La liste d'envies vit dans une table séparée (LivreSouhaite) plutôt que dans un statut de plus sur Livre : un livre souhaité n'est pas possédé, et le loger dans Livres l'aurait fait entrer dans le catalogue, les compteurs et les prêts, au prix d'un « et qui n'est pas souhaité » à répéter dans chaque lecture. La portée est personnelle, comme le statut de lecture — mais ici Utilisateur est une vraie frontière : toute lecture filtre dessus. Bibliographie : SRU BnF interrogé par bib.author, vérifié le 2026-08-18. Deux filtres mesurés sur des réponses réelles sont indispensables — le type de document (l'index mêle livres audio, jeux et spectacles) et surtout l'auteur réel de la notice, « all » rapprochant les mots sur l'ensemble des auteurs : « Émile Zola » remonte sinon toute l'œuvre de sa fille Denise Le Blond-Zola. Les rééditions sont regroupées par clé d'œuvre (183 notices Werber -> 51 œuvres). Le rapprochement avec l'étagère se fait par titre, pas par ISBN, qui désigne une édition et non une œuvre ; ses limites sont dites à l'écran. Export en deux formats, tous deux du texte sans dépendance : .txt groupé par auteur pour la librairie, .csv à séparateur point-virgule et BOM pour le tableur. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
83 lines
3.6 KiB
C#
83 lines
3.6 KiB
C#
using MaBibli.Shared.Textes;
|
|
|
|
namespace MaBibli.Tests;
|
|
|
|
/// <summary>
|
|
/// Clé d'œuvre : ce qu'elle réunit, et surtout <b>ce qu'elle rate</b>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Les cas d'échec sont testés au même titre que les réussites : ils documentent la limite du
|
|
/// rapprochement, que l'interface annonce à l'utilisateur. Si l'un d'eux se met à passer un
|
|
/// jour, c'est que la règle a changé — et le message affiché doit changer avec.
|
|
/// </remarks>
|
|
public class CleOeuvreTests
|
|
{
|
|
[Theory]
|
|
// Les trois formes rencontrées pour un même roman de Werber.
|
|
[InlineData("Les thanatonautes", "Les thanatonautes : roman")]
|
|
[InlineData("Les thanatonautes", "Les thanatonautes / Bernard Werber")]
|
|
[InlineData("Les thanatonautes : roman", "Les thanatonautes / Bernard Werber")]
|
|
// Casse et accents, comme partout ailleurs dans le projet.
|
|
[InlineData("Germinal", "GERMINAL")]
|
|
public void Reunit_les_variantes_d_un_meme_titre(string a, string b) =>
|
|
Assert.Equal(CleOeuvre.Cle(a), CleOeuvre.Cle(b));
|
|
|
|
[Theory]
|
|
[InlineData("Germinal", "La bête humaine")]
|
|
// Un tome n'est pas rapproché de l'œuvre entière : le point n'est pas coupé.
|
|
[InlineData("Troisième humanité", "Troisième humanité. Tome 1")]
|
|
public void Distingue_deux_oeuvres_differentes(string a, string b) =>
|
|
Assert.NotEqual(CleOeuvre.Cle(a), CleOeuvre.Cle(b));
|
|
|
|
/// <summary>
|
|
/// Limite assumée : deux œuvres distinctes partageant leur titre principal se confondent.
|
|
/// Le risque est borné, la comparaison n'ayant lieu qu'à auteur donné.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Confond_deux_oeuvres_partageant_le_titre_principal() =>
|
|
Assert.Equal(
|
|
CleOeuvre.Cle("Nouvelles : premier recueil"),
|
|
CleOeuvre.Cle("Nouvelles : second recueil"));
|
|
|
|
/// <summary>
|
|
/// Limite <b>héritée</b> de <see cref="NormalisationTexte"/>, pas propre à la clé d'œuvre :
|
|
/// la ligature « œ » n'est pas ramenée à « oe ».
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <c>NormalisationTexte</c> décompose en NFD, qui sépare les accents mais laisse les
|
|
/// ligatures intactes — seul NFKD les défait. Conséquence concrète : <i>L'Œuvre</i> de Zola,
|
|
/// que la BnF écrit avec la ligature, n'est pas rapproché d'un « L'oeuvre » saisi au clavier.
|
|
/// <para>
|
|
/// Le défaut ne vient pas d'ici et ne se corrige pas ici : la même fonction alimente la
|
|
/// recherche du catalogue et la clé unique des auteurs, et en changer déplacerait ces deux
|
|
/// invariants. Consigné dans IDEES.md ; ce test existe pour que la limite soit visible plutôt
|
|
/// que découverte en usage.
|
|
/// </para>
|
|
/// </remarks>
|
|
[Fact]
|
|
public void Ne_reduit_pas_la_ligature_oe() =>
|
|
Assert.NotEqual(CleOeuvre.Cle("L'Œuvre"), CleOeuvre.Cle("L'oeuvre"));
|
|
|
|
/// <summary>
|
|
/// Limite assumée : un titre retraduit n'est pas rapproché du tout.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Ne_rapproche_pas_un_titre_retraduit() =>
|
|
Assert.NotEqual(CleOeuvre.Cle("Le Seigneur des anneaux"), CleOeuvre.Cle("The Lord of the Rings"));
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData(" ")]
|
|
public void Rend_une_cle_vide_pour_une_saisie_vide(string? titre) =>
|
|
Assert.Equal(string.Empty, CleOeuvre.Cle(titre));
|
|
|
|
/// <summary>
|
|
/// Un titre réduit à son seul sous-titre ne doit pas produire une clé vide : elle se
|
|
/// confondrait avec toutes les autres clés vides.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Ne_vide_pas_un_titre_qui_commence_par_le_separateur() =>
|
|
Assert.NotEqual(string.Empty, CleOeuvre.Cle(": roman"));
|
|
}
|