Gestion de bibliothèque personnelle auto-hébergée : catalogue, prêts, scan de code-barres, consultation hors-ligne. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
238 lines
8.6 KiB
C#
238 lines
8.6 KiB
C#
using MaBibli.Api.Data;
|
|
using MaBibli.Api.Services.Catalogue;
|
|
using MaBibli.Shared.Catalogue;
|
|
using MaBibli.Shared.Dtos;
|
|
using MaBibli.Shared.Entites;
|
|
using Microsoft.Data.Sqlite;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace MaBibli.Tests;
|
|
|
|
/// <summary>
|
|
/// Table des auteurs : regroupement automatique, suggestions et fusions.
|
|
/// </summary>
|
|
public class ServiceAuteursTests : IDisposable
|
|
{
|
|
private readonly SqliteConnection _connexion;
|
|
private readonly MaBibliDbContext _db;
|
|
private readonly ServiceAuteurs _auteurs;
|
|
private readonly ServiceCatalogue _catalogue;
|
|
|
|
public ServiceAuteursTests()
|
|
{
|
|
_connexion = new SqliteConnection("Data Source=:memory:");
|
|
_connexion.Open();
|
|
|
|
_db = new MaBibliDbContext(
|
|
new DbContextOptionsBuilder<MaBibliDbContext>().UseSqlite(_connexion).Options);
|
|
_db.Database.EnsureCreated();
|
|
|
|
_auteurs = new ServiceAuteurs(_db);
|
|
_catalogue = new ServiceCatalogue(_db, _auteurs);
|
|
}
|
|
|
|
private async Task<LivreDto> AjouterAsync(string titre, string auteurs) =>
|
|
(await _catalogue.CreerAsync(
|
|
new EnregistrementLivre { Titre = titre, Auteur = auteurs }, "mathieu")).Livre!;
|
|
|
|
[Fact]
|
|
public async Task Un_auteur_nest_cree_quune_fois()
|
|
{
|
|
await AjouterAsync("Germinal", "Émile Zola");
|
|
await AjouterAsync("Nana", "Émile Zola");
|
|
|
|
var liste = await _auteurs.ListerAsync();
|
|
var zola = Assert.Single(liste);
|
|
Assert.Equal("Émile Zola", zola.Nom);
|
|
Assert.Equal(2, zola.NombreDeLivres);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Le_nom_daffichage_reste_celui_de_la_premiere_saisie()
|
|
{
|
|
await AjouterAsync("Germinal", "Émile Zola");
|
|
await AjouterAsync("Nana", "ZOLA, emile");
|
|
|
|
// Les deux livres pointent la même fiche, sous le nom d'origine : l'accentuation correcte
|
|
// ne doit pas être écrasée par une saisie plus négligée.
|
|
Assert.Equal("Émile Zola", Assert.Single(await _auteurs.ListerAsync()).Nom);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Aucune_suggestion_sur_des_auteurs_sans_rapport()
|
|
{
|
|
await AjouterAsync("Germinal", "Émile Zola");
|
|
await AjouterAsync("Le Horla", "Guy de Maupassant");
|
|
|
|
Assert.Empty(await _auteurs.SuggestionsAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Un_patronyme_seul_est_propose_vers_le_nom_complet()
|
|
{
|
|
await AjouterAsync("Pandora's Star", "Peter F. Hamilton");
|
|
await AjouterAsync("Un autre", "Hamilton");
|
|
|
|
var suggestion = Assert.Single(await _auteurs.SuggestionsAsync());
|
|
|
|
// La fusion efface la source : c'est le nom le moins complet qui doit disparaître.
|
|
Assert.Equal("Hamilton", suggestion.Source.Nom);
|
|
Assert.Equal("Peter F. Hamilton", suggestion.Cible.Nom);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Accepter_un_rapprochement_reunit_les_livres()
|
|
{
|
|
var complet = await AjouterAsync("Pandora's Star", "Peter F. Hamilton");
|
|
var court = await AjouterAsync("Judas Unchained", "Hamilton");
|
|
|
|
var suggestion = Assert.Single(await _auteurs.SuggestionsAsync());
|
|
Assert.True(await _auteurs.FusionnerAsync(suggestion.Source.Id, suggestion.Cible.Id));
|
|
|
|
var restant = Assert.Single(await _auteurs.ListerAsync());
|
|
Assert.Equal("Peter F. Hamilton", restant.Nom);
|
|
Assert.Equal(2, restant.NombreDeLivres);
|
|
|
|
// Les deux livres remontent maintenant sous le même auteur.
|
|
var groupe = await _catalogue.ListerAsync(new CritereLivres { AuteurId = restant.Id }, "mathieu");
|
|
Assert.Equal([complet.Id, court.Id], groupe.Select(l => l.Id).Order());
|
|
|
|
// Et la suggestion ne peut plus revenir : l'une des deux fiches n'existe plus.
|
|
Assert.Empty(await _auteurs.SuggestionsAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Refuser_un_rapprochement_le_fait_taire_definitivement()
|
|
{
|
|
await AjouterAsync("Pandora's Star", "Peter F. Hamilton");
|
|
await AjouterAsync("Un autre", "Hamilton");
|
|
|
|
var suggestion = Assert.Single(await _auteurs.SuggestionsAsync());
|
|
Assert.True(await _auteurs.RefuserAsync(suggestion.Source.Id, suggestion.Cible.Id));
|
|
|
|
Assert.Empty(await _auteurs.SuggestionsAsync());
|
|
|
|
// Les deux auteurs restent bien distincts : refuser n'est pas fusionner.
|
|
Assert.Equal(2, (await _auteurs.ListerAsync()).Count);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Un_refus_vaut_dans_les_deux_sens()
|
|
{
|
|
await AjouterAsync("Pandora's Star", "Peter F. Hamilton");
|
|
await AjouterAsync("Un autre", "Hamilton");
|
|
|
|
var suggestion = Assert.Single(await _auteurs.SuggestionsAsync());
|
|
|
|
// Refus enregistré dans l'ordre inverse de celui proposé : il doit compter quand même.
|
|
Assert.True(await _auteurs.RefuserAsync(suggestion.Cible.Id, suggestion.Source.Id));
|
|
Assert.Empty(await _auteurs.SuggestionsAsync());
|
|
|
|
// Idempotent : redire non ne doit pas empiler les lignes.
|
|
Assert.True(await _auteurs.RefuserAsync(suggestion.Source.Id, suggestion.Cible.Id));
|
|
Assert.Equal(1, await _db.RapprochementsRefuses.CountAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Fusionner_un_livre_cosigne_ne_cree_pas_de_lien_en_double()
|
|
{
|
|
// Le même livre signé par les deux variantes : après fusion il ne doit rester qu'un lien.
|
|
var livre = await AjouterAsync("Œuvre commune", "Peter F. Hamilton; Hamilton");
|
|
Assert.Equal(2, livre.Auteurs.Count);
|
|
|
|
var suggestion = Assert.Single(await _auteurs.SuggestionsAsync());
|
|
Assert.True(await _auteurs.FusionnerAsync(suggestion.Source.Id, suggestion.Cible.Id));
|
|
|
|
var relu = await _catalogue.ObtenirAsync(livre.Id, "mathieu");
|
|
Assert.Single(relu!.Auteurs);
|
|
Assert.Equal(1, await _db.LivreAuteurs.CountAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Fusionner_avec_un_auteur_inexistant_echoue_sans_rien_casser()
|
|
{
|
|
var livre = await AjouterAsync("Germinal", "Émile Zola");
|
|
var zola = livre.Auteurs.Single().Id;
|
|
|
|
Assert.False(await _auteurs.FusionnerAsync(zola, 4242));
|
|
Assert.False(await _auteurs.FusionnerAsync(zola, zola));
|
|
Assert.Equal(1, await _db.Auteurs.CountAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Retirer_un_auteur_dun_livre_supprime_sa_fiche_devenue_vide()
|
|
{
|
|
var livre = await AjouterAsync("Germinal", "Émile Zola");
|
|
|
|
await _catalogue.ModifierAsync(
|
|
livre.Id,
|
|
new EnregistrementLivre { Titre = "Germinal", Auteur = "Guy de Maupassant" },
|
|
"mathieu");
|
|
|
|
var restant = Assert.Single(await _auteurs.ListerAsync());
|
|
Assert.Equal("Guy de Maupassant", restant.Nom);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Une_saisie_sans_lettre_ne_cree_pas_dauteur()
|
|
{
|
|
await AjouterAsync("Sans auteur", " ; ; .,; ");
|
|
|
|
Assert.Empty(await _auteurs.ListerAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Le_meme_auteur_saisi_deux_fois_ne_donne_quun_lien()
|
|
{
|
|
var livre = await AjouterAsync("Germinal", "Émile Zola; ZOLA, Emile");
|
|
|
|
Assert.Single(livre.Auteurs);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Renommer_un_auteur_recalcule_ses_formes()
|
|
{
|
|
var livre = await AjouterAsync("Pandora's Star", "Hamilton");
|
|
|
|
var resultat = await _auteurs.RenommerAsync(
|
|
livre.Auteurs.Single().Id, "Peter F. Hamilton");
|
|
|
|
Assert.False(resultat.EstCollision);
|
|
Assert.Equal("Peter F. Hamilton", resultat.Auteur!.Nom);
|
|
|
|
var auteur = await _db.Auteurs.SingleAsync();
|
|
Assert.Equal("peter f hamilton", auteur.NomNormalise);
|
|
Assert.Equal("f hamilton peter", auteur.CleRegroupement);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Renommer_avec_collision_demande_puis_fusionne()
|
|
{
|
|
var court = await AjouterAsync("Judas Unchained", "Hamilton");
|
|
var complet = await AjouterAsync("Pandora's Star", "Peter F. Hamilton");
|
|
var idCourt = court.Auteurs.Single().Id;
|
|
var idComplet = complet.Auteurs.Single().Id;
|
|
|
|
var avertissement = await _auteurs.RenommerAsync(idCourt, "Peter F. Hamilton");
|
|
|
|
Assert.True(avertissement.EstCollision);
|
|
Assert.Equal(idComplet, avertissement.Collision!.Id);
|
|
Assert.Equal(2, await _db.Auteurs.CountAsync());
|
|
|
|
var fusion = await _auteurs.RenommerAsync(
|
|
idCourt, "Peter F. Hamilton", confirmerFusion: true);
|
|
|
|
Assert.False(fusion.EstCollision);
|
|
Assert.Single(await _db.Auteurs.ToListAsync());
|
|
Assert.Equal(2, await _db.LivreAuteurs.CountAsync());
|
|
Assert.Equal(2, (await _auteurs.ListerAsync()).Single().NombreDeLivres);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_db.Dispose();
|
|
_connexion.Dispose();
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|