Files
mabibli/MaBibli.Tests/ServiceSouhaitsTests.cs
T
mathieuandClaude Opus 5 5cf566bc33 Traiter les retours d'usage du 2026-08-18 (2ᵉ série)
Six lots, issus d'IDEES.md. Les décisions actées sont reportées dans
CLAUDE.md, et les entrées traitées retirées d'IDEES.md.

1. Douchette USB, ISSN et enchaînement du scan
   - Champ ISBN focalisé à l'ouverture : c'est tout ce qu'exige une
     douchette USB, qui se présente au système comme un clavier. Remède
     au scan caméra, qui rate sur la webcam d'un PC (optique, pas
     décodage).
   - Un EAN en 977 est un périodique : il porte un ISSN, donc un titre de
     revue. Intercepté AVANT la validation ISBN (c'est un EAN-13
     parfaitement valide), l'ISSN est déduit du code puis nommé via
     bib.issn. Auparavant la cascade s'exécutait en entier pour finir sur
     « aucun résultat ».
   - L'enchaînement après scan existait déjà mais était invisible : une
     étape « Recherche » affiche désormais le code interrogé.

2. ISBN affichés avec des tirets
   Tables extraites du RangeMessage.xml officiel — plusieurs tranches ne
   sont pas celles qu'on suppose. Le francophone est découpé en entier,
   ailleurs on s'arrête au groupe et à la clé : aucune coupure fausse.
   Corrige au passage l'export CSV, où un ISBN nu était lu comme un
   nombre par Excel.

3. Hors-ligne : la liste d'envies
   ListerSouhaitsAsync était le seul point de lecture hors du dispositif
   hors-ligne, d'où le « 404 Not Found » brut à l'écran. Cinquième
   instantané, écritures refusées, plus aucun message HTTP. Même défaut
   corrigé sur la bibliographie.

4. Navigation par onglets
   Catalogue / Auteurs / Prêts / Envies dans MainLayout ; les barres
   d'actions ne portent plus que des actions. Filtres repliés derrière un
   bouton compteur, ligne « format » masquée quand le fonds n'a qu'un
   format. Une seule entrée d'ajout, désactivée hors-ligne — pas masquée.

5. Liste d'envies : ordre, recherche, couvertures
   Migration RangDesEnvies. Le remplissage reconduit l'ordre affiché
   jusqu'ici : sans lui, les listes existantes se seraient réordonnées
   toutes seules. Réordonnancement par flèches et glisser-déposer (le
   drag & drop HTML5 ne marche pas au doigt). Ajout dans son propre
   écran, avec recherche par titre (bib.title) et couvertures enfin
   alimentées.

6. Bibliographie : une source muette n'est pas une liste vide
   L'écran affichait « BnF injoignable » PUIS « la BnF ne connaît aucun
   livre de cet auteur » — la seconde phrase étant fausse. Les deux cas
   s'excluent désormais, et un bouton Réessayer est offert.

   Vérifié en exécution : le diagnostic d'IDEES.md était faux sur un
   point. Robert A. Harper a bien 7 œuvres à la BnF (85 notices
   annoncées) ; c'était le même délai dépassé observé deux fois, pris
   pour deux causes distinctes.

380 tests, dont un qui applique réellement la migration (EnsureCreated
n'en joue aucune) et un qui verrouille les messages atteignant
l'utilisateur.

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

564 lines
22 KiB
C#

using MaBibli.Api.Data;
using MaBibli.Api.Services.Catalogue;
using MaBibli.Api.Services.Isbn;
using MaBibli.Api.Services.Souhaits;
using MaBibli.Shared.Catalogue;
using MaBibli.Shared.Dtos;
using MaBibli.Shared.Entites;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
namespace MaBibli.Tests;
/// <summary>
/// Liste d'envies, sur une base SQLite <b>en mémoire</b> : l'unicité « une œuvre par personne »
/// n'existe qu'en base, elle ne se vérifierait pas sur un faux dépôt.
/// </summary>
/// <remarks>
/// L'invariant central testé ici est la <b>portée personnelle</b> : deux utilisateurs ne doivent
/// jamais se voir. C'est l'exact contraire du catalogue, commun au foyer — et une régression y
/// serait discrète, puisque tout continuerait de « marcher » avec un seul compte.
/// </remarks>
public class ServiceSouhaitsTests : IDisposable
{
private readonly SqliteConnection _connexion;
private readonly MaBibliDbContext _db;
private readonly ServiceSouhaits _souhaits;
private readonly ServiceCatalogue _catalogue;
private const string Mathieu = "mathieu";
private const string Camille = "camille";
public ServiceSouhaitsTests()
{
_connexion = new SqliteConnection("Data Source=:memory:");
_connexion.Open();
var options = new DbContextOptionsBuilder<MaBibliDbContext>()
.UseSqlite(_connexion)
.Options;
_db = new MaBibliDbContext(options);
_db.Database.EnsureCreated();
_souhaits = new ServiceSouhaits(_db);
_catalogue = new ServiceCatalogue(_db, new ServiceAuteurs(_db));
}
private Task<ResultatSouhait> AjouterAsync(
string titre, string? auteur = "Bernard Werber", string utilisateur = Mathieu,
string? isbn = null, string? note = null) =>
_souhaits.AjouterAsync(
new EnregistrementSouhait { Titre = titre, Auteur = auteur, Isbn = isbn, Note = note },
utilisateur);
// ── Ordre choisi à la main ───────────────────────────────────────────────
private async Task<IReadOnlyList<string>> TitresAsync(string utilisateur = Mathieu) =>
(await _souhaits.ListerAsync(utilisateur)).Select(s => s.Titre).ToList();
/// <summary>
/// L'ordre d'affichage est celui de la liste, pas l'alphabet : classer une liste d'envies
/// par titre répondait à une question que personne ne se pose.
/// </summary>
[Fact]
public async Task Les_envies_sortent_dans_lordre_dajout_par_defaut()
{
await AjouterAsync("Zèbre", auteur: "Zoé Zed");
await AjouterAsync("Abricot", auteur: "Anne Alpha");
Assert.Equal(["Zèbre", "Abricot"], await TitresAsync());
}
[Fact]
public async Task Une_envie_nouvelle_se_pose_en_fin_de_liste()
{
await AjouterAsync("Premier");
await AjouterAsync("Deuxième");
await AjouterAsync("Troisième");
Assert.Equal(["Premier", "Deuxième", "Troisième"], await TitresAsync());
}
[Fact]
public async Task Reordonner_fixe_lordre_demande()
{
var a = (await AjouterAsync("Premier")).Souhait!.Id;
var b = (await AjouterAsync("Deuxième")).Souhait!.Id;
var c = (await AjouterAsync("Troisième")).Souhait!.Id;
Assert.True(await _souhaits.ReordonnerAsync([c, a, b], Mathieu));
Assert.Equal(["Troisième", "Premier", "Deuxième"], await TitresAsync());
}
/// <summary>Réenvoyer le même ordre ne doit rien bouger.</summary>
[Fact]
public async Task Reordonner_est_idempotent()
{
var a = (await AjouterAsync("Premier")).Souhait!.Id;
var b = (await AjouterAsync("Deuxième")).Souhait!.Id;
await _souhaits.ReordonnerAsync([b, a], Mathieu);
await _souhaits.ReordonnerAsync([b, a], Mathieu);
Assert.Equal(["Deuxième", "Premier"], await TitresAsync());
}
/// <summary>
/// ⚠️ Le cas qui perdrait des données : la liste du client est périmée (une envie ajoutée
/// depuis un autre appareil n'y figure pas). L'absente doit <b>survivre</b>, rangée à la
/// suite — mal classée vaut infiniment mieux que disparue.
/// </summary>
[Fact]
public async Task Une_envie_absente_de_lordre_envoye_est_conservee()
{
var a = (await AjouterAsync("Premier")).Souhait!.Id;
var b = (await AjouterAsync("Deuxième")).Souhait!.Id;
await AjouterAsync("Ajoutée ailleurs");
await _souhaits.ReordonnerAsync([b, a], Mathieu);
Assert.Equal(["Deuxième", "Premier", "Ajoutée ailleurs"], await TitresAsync());
}
/// <summary>
/// ⚠️ L'invariant de la table : réordonner ne doit pas offrir un moyen détourné de toucher
/// à la liste de quelqu'un d'autre en devinant un identifiant.
/// </summary>
[Fact]
public async Task Reordonner_ne_touche_jamais_la_liste_dun_autre()
{
var sienne = (await AjouterAsync("À Camille", utilisateur: Camille)).Souhait!.Id;
var a = (await AjouterAsync("Premier", utilisateur: Mathieu)).Souhait!.Id;
var b = (await AjouterAsync("Deuxième", utilisateur: Mathieu)).Souhait!.Id;
// Mathieu glisse l'identifiant de Camille en tête de SA demande.
await _souhaits.ReordonnerAsync([sienne, b, a], Mathieu);
Assert.Equal(["Deuxième", "Premier"], await TitresAsync(Mathieu));
Assert.Equal(["À Camille"], await TitresAsync(Camille));
}
[Fact]
public async Task Reordonner_sans_identite_ne_fait_rien()
{
await AjouterAsync("Premier");
Assert.False(await _souhaits.ReordonnerAsync([1], null));
}
/// <summary>
/// Supprimer laisse des trous dans les rangs ; ils ne doivent pas déranger l'ordre, et le
/// prochain ajout doit tout de même atterrir en dernier.
/// </summary>
[Fact]
public async Task Une_suppression_ne_desordonne_pas_la_liste()
{
await AjouterAsync("Premier");
var b = (await AjouterAsync("Deuxième")).Souhait!.Id;
await AjouterAsync("Troisième");
Assert.True(await _souhaits.SupprimerAsync(b, Mathieu));
await AjouterAsync("Quatrième");
Assert.Equal(["Premier", "Troisième", "Quatrième"], await TitresAsync());
}
// ── Portée personnelle ───────────────────────────────────────────────────
[Fact]
public async Task Deux_utilisateurs_ont_deux_listes_distinctes()
{
await AjouterAsync("Les fourmis", utilisateur: Mathieu);
await AjouterAsync("Germinal", "Émile Zola", Camille);
var deMathieu = await _souhaits.ListerAsync(Mathieu);
var deCamille = await _souhaits.ListerAsync(Camille);
Assert.Equal("Les fourmis", Assert.Single(deMathieu).Titre);
Assert.Equal("Germinal", Assert.Single(deCamille).Titre);
}
[Fact]
public async Task Deux_utilisateurs_peuvent_souhaiter_le_meme_livre()
{
var premier = await AjouterAsync("Les fourmis", utilisateur: Mathieu);
var second = await AjouterAsync("Les fourmis", utilisateur: Camille);
// L'unicité porte sur (utilisateur, œuvre) : elle ne doit pas empêcher deux personnes
// d'avoir la même envie — c'est même le cas normal dans un foyer.
Assert.Null(premier.Erreur);
Assert.Null(second.Erreur);
}
[Fact]
public async Task On_ne_supprime_pas_l_envie_d_un_autre()
{
var souhait = await AjouterAsync("Les fourmis", utilisateur: Mathieu);
var supprime = await _souhaits.SupprimerAsync(souhait.Souhait!.Id, Camille);
Assert.False(supprime);
Assert.Single(await _souhaits.ListerAsync(Mathieu));
}
[Fact]
public async Task Sans_identite_il_n_y_a_ni_liste_ni_ecriture()
{
var ajout = await AjouterAsync("Les fourmis", utilisateur: null!);
Assert.NotNull(ajout.Erreur);
Assert.Empty(await _souhaits.ListerAsync(null));
}
// ── Cloisonnement d'avec le catalogue ────────────────────────────────────
/// <summary>
/// L'invariant qui justifie l'entité séparée : une envie n'entre ni dans le catalogue, ni
/// dans les compteurs, ni dans la table des auteurs.
/// </summary>
[Fact]
public async Task Une_envie_n_apparait_pas_dans_le_catalogue()
{
await AjouterAsync("La prophétie des abeilles", "Bernard Werber");
var catalogue = await _catalogue.ListerAsync(new CritereLivres(), Mathieu);
Assert.Empty(catalogue);
}
[Fact]
public async Task Une_envie_ne_cree_pas_de_fiche_auteur()
{
await AjouterAsync("La prophétie des abeilles", "Bernard Werber");
// Sinon « Bernard Werber » apparaîtrait sur l'écran des auteurs avec « 0 livre »,
// et trahirait au passage la liste d'envies de son propriétaire.
Assert.Empty(await new ServiceAuteurs(_db).ListerAsync());
}
[Fact]
public async Task Une_envie_recherchee_dans_le_catalogue_ne_remonte_pas()
{
await _catalogue.CreerAsync(
new EnregistrementLivre { Titre = "Les fourmis", Auteur = "Bernard Werber" }, Mathieu);
await AjouterAsync("La prophétie des abeilles", "Bernard Werber");
var trouves = await _catalogue.ListerAsync(
new CritereLivres { Recherche = "Werber" }, Mathieu);
Assert.Equal("Les fourmis", Assert.Single(trouves).Titre);
}
// ── Validation et doublons ───────────────────────────────────────────────
[Fact]
public async Task Le_titre_est_obligatoire()
{
var resultat = await AjouterAsync(" ");
Assert.NotNull(resultat.Erreur);
}
[Fact]
public async Task La_meme_oeuvre_ne_s_ajoute_pas_deux_fois()
{
await AjouterAsync("Les thanatonautes");
// Variante d'écriture du même titre : c'est exactement ce que produit un double clic
// sur deux lignes de bibliographie qui n'ont pas été regroupées côté BnF.
var doublon = await AjouterAsync("Les thanatonautes : roman");
Assert.NotNull(doublon.Erreur);
Assert.Single(await _souhaits.ListerAsync(Mathieu));
}
[Fact]
public async Task Deux_auteurs_peuvent_avoir_une_oeuvre_de_meme_titre()
{
var premier = await AjouterAsync("Nouvelles", "Bernard Werber");
var second = await AjouterAsync("Nouvelles", "Émile Zola");
Assert.Null(premier.Erreur);
Assert.Null(second.Erreur);
Assert.Equal(2, (await _souhaits.ListerAsync(Mathieu)).Count);
}
[Fact]
public async Task Un_ISBN_invalide_est_refuse()
{
var resultat = await AjouterAsync("Les fourmis", isbn: "1234567890123");
Assert.NotNull(resultat.Erreur);
}
[Fact]
public async Task L_ISBN_reste_facultatif()
{
var resultat = await AjouterAsync("Les fourmis", isbn: null);
Assert.Null(resultat.Erreur);
Assert.Null(resultat.Souhait!.Isbn);
}
/// <summary>
/// ⚠️ <b>Contrat changé le 2026-08-18</b> : la liste était classée par auteur puis titre,
/// elle suit désormais le <b>rang choisi par son propriétaire</b>. L'alphabet répondait à
/// une question que personne ne se pose devant une liste d'envies ; « qu'est-ce que je veux
/// le plus ? » en est une.
/// <para>
/// L'ancien ordre n'est pas perdu pour autant : la migration <c>RangDesEnvies</c> le
/// reconduit dans les rangs initiaux, pour que les listes existantes ne se réordonnent pas
/// toutes seules. C'est <c>MigrationRangDesEnviesTests</c> qui le vérifie.
/// </para>
/// </summary>
[Fact]
public async Task La_liste_ne_se_classe_plus_par_ordre_alphabetique()
{
await AjouterAsync("Les fourmis", "Bernard Werber");
await AjouterAsync("Sans auteur", null);
await AjouterAsync("Germinal", "Émile Zola");
await AjouterAsync("Demain les chats", "Bernard Werber");
Assert.Equal(
["Les fourmis", "Sans auteur", "Germinal", "Demain les chats"],
await TitresAsync());
}
[Fact]
public async Task Une_envie_supprimee_disparait()
{
var souhait = await AjouterAsync("Les fourmis");
Assert.True(await _souhaits.SupprimerAsync(souhait.Souhait!.Id, Mathieu));
Assert.Empty(await _souhaits.ListerAsync(Mathieu));
}
// ── Bibliographie : confrontation à l'étagère ────────────────────────────
/// <summary>
/// Client BnF simulé : ces tests portent sur le rapprochement, pas sur le réseau.
/// </summary>
private sealed class BnfBibliographieFake(string xml) : IBnfClient
{
public Task<(IReadOnlyList<CandidatLivre>, string?)> RechercherAsync(
string isbn, string? urlCouverture, CancellationToken ct = default) =>
Task.FromResult(((IReadOnlyList<CandidatLivre>)[], (string?)null));
public Task<(ResultatBibliographie, string?)> RechercherParAuteurAsync(
string auteur, CancellationToken ct = default) =>
Task.FromResult((BnfBibliographieParser.Parser(xml, auteur), (string?)null));
/// <summary>Les périodiques n'interviennent pas dans la bibliographie par auteur.</summary>
public Task<(PeriodiqueDetecte?, string?)> RechercherPeriodiqueAsync(
string issn, CancellationToken ct = default) =>
Task.FromResult(((PeriodiqueDetecte?)null, (string?)null));
/// <summary>Recherche par titre : couverte par ses propres tests.</summary>
public Task<(IReadOnlyList<CandidatLivre>, string?)> RechercherParTitreAsync(
string titre, string? auteur, CancellationToken ct = default) =>
Task.FromResult(((IReadOnlyList<CandidatLivre>)[], (string?)null));
}
/// <summary>
/// Client BnF qui <b>ne répond pas</b> : c'est le cas « Robert A. Harper » remonté en usage.
/// </summary>
private sealed class BnfMuetFake(EtatSourceBibliographie etat, string message) : IBnfClient
{
public Task<(IReadOnlyList<CandidatLivre>, string?)> RechercherAsync(
string isbn, string? urlCouverture, CancellationToken ct = default) =>
Task.FromResult(((IReadOnlyList<CandidatLivre>)[], (string?)null));
public Task<(ResultatBibliographie, string?)> RechercherParAuteurAsync(
string auteur, CancellationToken ct = default) =>
Task.FromResult((new ResultatBibliographie { Etat = etat }, (string?)message));
public Task<(PeriodiqueDetecte?, string?)> RechercherPeriodiqueAsync(
string issn, CancellationToken ct = default) =>
Task.FromResult(((PeriodiqueDetecte?)null, (string?)null));
public Task<(IReadOnlyList<CandidatLivre>, string?)> RechercherParTitreAsync(
string titre, string? auteur, CancellationToken ct = default) =>
Task.FromResult(((IReadOnlyList<CandidatLivre>)[], (string?)null));
}
/// <summary>
/// ⚠️ <b>Le défaut que ce lot corrige.</b> Une BnF muette rend une liste vide, exactement
/// comme un auteur réellement absent de son catalogue. Sans <c>SourceMuette</c>, l'écran
/// affirmait « la BnF ne connaît aucun livre de cet auteur » à propos d'une question qu'il
/// n'avait pas pu poser.
/// </summary>
[Theory]
[InlineData(EtatSourceBibliographie.DelaiDepasse)]
[InlineData(EtatSourceBibliographie.Injoignable)]
[InlineData(EtatSourceBibliographie.ReponseIllisible)]
public async Task Une_BnF_muette_ne_se_confond_pas_avec_un_auteur_sans_oeuvre(
EtatSourceBibliographie etat)
{
var biblio = await BibliographieAsync(new BnfMuetFake(etat, "La BnF n'a pas répondu."));
Assert.Empty(biblio.Oeuvres);
Assert.True(biblio.SourceMuette);
Assert.Equal(etat, biblio.Etat);
Assert.NotNull(biblio.Avertissement);
}
/// <summary>
/// Le pendant : la BnF <b>a</b> répondu, elle ne connaît simplement rien. Là, et là
/// seulement, l'écran a le droit d'expliquer l'absence.
/// </summary>
[Fact]
public async Task Une_BnF_qui_repond_sans_rien_connaitre_nest_pas_muette()
{
var biblio = await BibliographieAsync(new BnfBibliographieFake(ReponseVide));
Assert.Empty(biblio.Oeuvres);
Assert.False(biblio.SourceMuette);
Assert.Equal(EtatSourceBibliographie.Ok, biblio.Etat);
Assert.Null(biblio.Avertissement);
}
/// <summary>Une bibliographie qui a abouti n'est jamais signalée comme muette.</summary>
[Fact]
public async Task Une_bibliographie_obtenue_nest_pas_muette()
{
var biblio = await BibliographieAsync(
new BnfBibliographieFake(Fixture.Lire("bnf-auteur-werber.xml")));
Assert.NotEmpty(biblio.Oeuvres);
Assert.False(biblio.SourceMuette);
}
private const string ReponseVide = """
<srw:searchRetrieveResponse xmlns:srw="http://www.loc.gov/zing/srw/">
<srw:numberOfRecords>0</srw:numberOfRecords>
</srw:searchRetrieveResponse>
""";
/// <summary>Bibliographie de Werber avec le client BnF fourni : seule la source varie.</summary>
/// <summary>
/// Bibliographie de Werber avec le client BnF fourni : <b>seule la source varie</b>.
/// L'auteur doit exister au catalogue, une bibliographie partant toujours d'un auteur à soi.
/// </summary>
private async Task<BibliographieDto> BibliographieAsync(IBnfClient bnf)
{
await _catalogue.CreerAsync(
new EnregistrementLivre { Titre = "Les fourmis", Auteur = "Bernard Werber" }, Mathieu);
var auteurs = new ServiceAuteurs(_db);
var service = new ServiceBibliographie(_db, bnf, auteurs);
var werber = (await auteurs.ListerAsync()).Single(a => a.Nom.Contains("Werber"));
return (await service.ObtenirAsync(werber.Id, Mathieu))!;
}
private async Task<BibliographieDto> BibliographieWerberAsync(string utilisateur = Mathieu)
{
var auteurs = new ServiceAuteurs(_db);
var service = new ServiceBibliographie(
_db, new BnfBibliographieFake(Fixture.Lire("bnf-auteur-werber.xml")), auteurs);
var werber = (await auteurs.ListerAsync()).Single(a => a.Nom.Contains("Werber"));
return (await service.ObtenirAsync(werber.Id, utilisateur))!;
}
[Fact]
public async Task La_bibliographie_grise_les_livres_deja_possedes()
{
// Titre saisi sans le sous-titre « : roman » que porte la notice BnF : c'est le cas
// courant, et c'est précisément ce que la clé d'œuvre doit rattraper.
await _catalogue.CreerAsync(
new EnregistrementLivre { Titre = "Les thanatonautes", Auteur = "Bernard Werber" }, Mathieu);
var biblio = await BibliographieWerberAsync();
var oeuvre = biblio.Oeuvres.Single(
o => o.Titre.StartsWith("Les thanatonautes", StringComparison.OrdinalIgnoreCase));
Assert.True(oeuvre.Possede);
Assert.NotNull(oeuvre.LivreId);
// Les autres restent à découvrir : posséder un livre n'en grise pas d'autres.
Assert.Contains(biblio.Oeuvres, o => o.ADecouvrir);
}
[Fact]
public async Task La_bibliographie_signale_ce_qui_est_deja_souhaite()
{
await _catalogue.CreerAsync(
new EnregistrementLivre { Titre = "Les fourmis", Auteur = "Bernard Werber" }, Mathieu);
var avant = await BibliographieWerberAsync();
var cible = avant.Oeuvres.First(o => o.ADecouvrir);
await AjouterAsync(cible.Titre, "Bernard Werber");
var apres = await BibliographieWerberAsync();
var revue = apres.Oeuvres.Single(o => o.Titre == cible.Titre);
Assert.True(revue.Souhaite);
Assert.False(revue.ADecouvrir);
}
/// <summary>
/// « Possédé » vaut pour le foyer, « souhaité » seulement pour l'appelant : c'est ce qui
/// permet d'offrir un livre à quelqu'un sans qu'il le voie venir.
/// </summary>
[Fact]
public async Task Le_souhait_d_un_autre_n_apparait_pas_dans_ma_bibliographie()
{
await _catalogue.CreerAsync(
new EnregistrementLivre { Titre = "Les fourmis", Auteur = "Bernard Werber" }, Mathieu);
var biblio = await BibliographieWerberAsync(Mathieu);
var cible = biblio.Oeuvres.First(o => o.ADecouvrir);
await AjouterAsync(cible.Titre, "Bernard Werber", Camille);
var vueDeMathieu = await BibliographieWerberAsync(Mathieu);
var vueDeCamille = await BibliographieWerberAsync(Camille);
Assert.False(vueDeMathieu.Oeuvres.Single(o => o.Titre == cible.Titre).Souhaite);
Assert.True(vueDeCamille.Oeuvres.Single(o => o.Titre == cible.Titre).Souhaite);
}
[Fact]
public async Task Un_auteur_inconnu_n_a_pas_de_bibliographie()
{
var service = new ServiceBibliographie(
_db, new BnfBibliographieFake("<x/>"), new ServiceAuteurs(_db));
Assert.Null(await service.ObtenirAsync(4242, Mathieu));
}
/// <summary>
/// Le livre d'un homonyme ne doit pas griser l'œuvre d'un autre : la comparaison se fait
/// à auteur donné, pas sur tout le catalogue.
/// </summary>
[Fact]
public async Task Un_livre_d_un_autre_auteur_ne_grise_rien()
{
await _catalogue.CreerAsync(
new EnregistrementLivre { Titre = "Les fourmis", Auteur = "Bernard Werber" }, Mathieu);
// Un homonyme parfait, mais signé de quelqu'un d'autre.
await _catalogue.CreerAsync(
new EnregistrementLivre { Titre = "Les thanatonautes", Auteur = "Éric Corbeyran" }, Mathieu);
var biblio = await BibliographieWerberAsync();
Assert.False(
biblio.Oeuvres
.Single(o => o.Titre.StartsWith("Les thanatonautes", StringComparison.OrdinalIgnoreCase))
.Possede);
}
public void Dispose()
{
_db.Dispose();
_connexion.Dispose();
GC.SuppressFinalize(this);
}
}