Distinguer scénario, dessin et couleurs sur chaque livre
Le rôle vit sur le LIEN livre-auteur, jamais sur l'auteur : le même auteur scénarise un album et dessine le suivant, et il ne doit pas exister en deux fiches pour autant — ce que l'index unique sur la clé de regroupement interdit de toute façon. Les rôles se retrouvent par clé d'auteur résolue et non par position : ResoudreAsync écarte les noms vides et fusionne les variantes, donc sa liste peut être plus courte que la saisie. Piège traité : le champ des noms reconstruit la liste à chaque frappe. Decouper reprend les rôles connus en rapprochant sur la forme normalisée, sans quoi corriger une faute de frappe les effacerait tous. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
using MaBibli.Shared.Dtos;
|
||||
using MaBibli.Shared.Entites;
|
||||
using MaBibli.Shared.Textes;
|
||||
|
||||
namespace MaBibli.Tests;
|
||||
@@ -117,6 +119,41 @@ public class RapprochementAuteursTests
|
||||
public void La_normalisation_met_a_plat_casse_accents_et_ponctuation(string? brut, string attendu)
|
||||
=> Assert.Equal(attendu, NormalisationTexte.Normaliser(brut));
|
||||
|
||||
/// <summary>
|
||||
/// Retaper le champ des noms ne doit pas effacer les rôles déjà posés.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[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);
|
||||
}
|
||||
|
||||
/// <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.
|
||||
|
||||
@@ -390,6 +390,74 @@ public class ServiceCatalogueTests : IDisposable
|
||||
Assert.Equal(["Germinal"], nonPrecises.Select(l => l.Titre));
|
||||
}
|
||||
|
||||
// ── Rôles des auteurs : ils vivent sur le LIEN, pas sur la personne ──────
|
||||
|
||||
[Fact]
|
||||
public async Task Les_roles_sont_enregistres_par_livre_et_pas_par_personne()
|
||||
{
|
||||
var bd = Saisie("Le Horla", format: Format.Physique);
|
||||
bd.TypeDocument = TypeDocument.BandeDessinee;
|
||||
bd.Auteurs =
|
||||
[
|
||||
new AuteurSaisi("Guy de Maupassant", RoleAuteur.Scenario),
|
||||
new AuteurSaisi("Guillaume Sorel", RoleAuteur.Dessin),
|
||||
];
|
||||
|
||||
var premier = (await _service.CreerAsync(bd, "mathieu")).Livre!;
|
||||
|
||||
// Le MÊME dessinateur, scénariste ailleurs : une seule fiche d'auteur, deux rôles.
|
||||
var autre = Saisie("Autre album");
|
||||
autre.Auteurs = [new AuteurSaisi("Guillaume Sorel", RoleAuteur.Scenario)];
|
||||
var second = (await _service.CreerAsync(autre, "mathieu", confirmerDoublon: true)).Livre!;
|
||||
|
||||
Assert.Equal(
|
||||
[RoleAuteur.Scenario, RoleAuteur.Dessin],
|
||||
premier.Auteurs.Select(a => a.Role));
|
||||
|
||||
Assert.Equal(RoleAuteur.Scenario, second.Auteurs.Single().Role);
|
||||
Assert.Equal(2, await _db.Auteurs.CountAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Un_livre_sans_role_precise_reste_non_precise()
|
||||
{
|
||||
var livre = (await _service.CreerAsync(Saisie("Germinal", "Émile Zola"), "mathieu")).Livre!;
|
||||
|
||||
Assert.Equal(RoleAuteur.NonPrecise, livre.Auteurs.Single().Role);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Le_role_suit_lauteur_meme_si_son_nom_est_ecrit_autrement()
|
||||
{
|
||||
var saisie = Saisie("Le Horla");
|
||||
saisie.Auteurs =
|
||||
[
|
||||
// Variante sûre d'un auteur déjà connu : le serveur la rapproche, et le rôle doit
|
||||
// suivre la fiche RÉSOLUE, pas la position dans la saisie.
|
||||
new AuteurSaisi("Maupassant, Guy de", RoleAuteur.Scenario),
|
||||
];
|
||||
|
||||
await _service.CreerAsync(Saisie("Bel-Ami", "Guy de Maupassant"), "mathieu");
|
||||
var livre = (await _service.CreerAsync(saisie, "mathieu")).Livre!;
|
||||
|
||||
Assert.Single(await _db.Auteurs.ToListAsync());
|
||||
Assert.Equal(RoleAuteur.Scenario, livre.Auteurs.Single().Role);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Modifier_un_livre_change_le_role_sans_toucher_a_lauteur()
|
||||
{
|
||||
var saisie = Saisie("Le Horla");
|
||||
saisie.Auteurs = [new AuteurSaisi("Guillaume Sorel", RoleAuteur.Dessin)];
|
||||
var cree = (await _service.CreerAsync(saisie, "mathieu")).Livre!;
|
||||
|
||||
saisie.Auteurs = [new AuteurSaisi("Guillaume Sorel", RoleAuteur.Couleurs)];
|
||||
var modifie = (await _service.ModifierAsync(cree.Id, saisie, "mathieu")).Livre!;
|
||||
|
||||
Assert.Equal(RoleAuteur.Couleurs, modifie.Auteurs.Single().Role);
|
||||
Assert.Single(await _db.Auteurs.ToListAsync());
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Doublons : un AVERTISSEMENT, jamais un refus. Posséder deux exemplaires est légitime —
|
||||
// on garde le sien et on prête l'autre — et l'ISBN est facultatif, donc il ne peut pas
|
||||
|
||||
Reference in New Issue
Block a user