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:
mathieu
2026-08-19 22:37:20 +02:00
co-authored by Claude Opus 5
parent 9ab6f23b8c
commit 07cb48b2be
16 changed files with 895 additions and 22 deletions
+68
View File
@@ -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