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;
///
/// CRUD du catalogue, sur une base SQLite en mémoire : même moteur qu'en production,
/// aucun fichier, aucun réseau.
///
public class ServiceCatalogueTests : IDisposable
{
private readonly SqliteConnection _connexion;
private readonly MaBibliDbContext _db;
private readonly ServiceCatalogue _service;
public ServiceCatalogueTests()
{
// La connexion doit rester ouverte : SQLite détruit une base « :memory: » à sa fermeture.
_connexion = new SqliteConnection("Data Source=:memory:");
_connexion.Open();
var options = new DbContextOptionsBuilder()
.UseSqlite(_connexion)
.Options;
_db = new MaBibliDbContext(options);
_db.Database.EnsureCreated();
_service = new ServiceCatalogue(_db);
}
private static EnregistrementLivre Saisie(
string titre,
string? auteur = null,
string? isbn = null,
Format format = Format.Physique,
Statut statut = Statut.ALire) => new()
{
Titre = titre,
Auteur = auteur,
Isbn = isbn,
Format = format,
Statut = statut,
};
[Fact]
public async Task Creer_enregistre_le_livre_et_trace_lauteur_de_la_saisie()
{
var resultat = await _service.CreerAsync(Saisie("Germinal", "Émile Zola"), "mathieu");
Assert.Null(resultat.Erreur);
var livre = resultat.Livre!;
Assert.True(livre.Id > 0);
Assert.Equal("Germinal", livre.Titre);
Assert.Equal("mathieu", livre.AjoutePar);
Assert.NotEqual(default, livre.DateAjout);
}
[Fact]
public async Task Creer_normalise_lisbn()
{
var resultat = await _service.CreerAsync(Saisie("Germinal", isbn: "978-2-253-00422-6"), "mathieu");
Assert.Equal("9782253004226", resultat.Livre!.Isbn);
}
[Fact]
public async Task Creer_refuse_un_titre_vide()
{
var resultat = await _service.CreerAsync(Saisie(" "), "mathieu");
Assert.Null(resultat.Livre);
Assert.NotNull(resultat.Erreur);
}
[Fact]
public async Task Creer_refuse_un_isbn_invalide_mais_accepte_labsence_disbn()
{
var invalide = await _service.CreerAsync(Saisie("Sans titre", isbn: "1234567890123"), "mathieu");
Assert.Null(invalide.Livre);
Assert.NotNull(invalide.Erreur);
// Certains livres n'ont pas d'ISBN : l'absence doit rester parfaitement légitime.
var sansIsbn = await _service.CreerAsync(Saisie("Carnet de famille"), "mathieu");
Assert.Null(sansIsbn.Erreur);
Assert.Null(sansIsbn.Livre!.Isbn);
}
[Fact]
public async Task Le_catalogue_est_unique_physiques_et_numeriques_cohabitent()
{
await _service.CreerAsync(Saisie("Papier", format: Format.Physique), "mathieu");
await _service.CreerAsync(Saisie("Epub", format: Format.Numerique), "mathieu");
var tout = await _service.ListerAsync(new CritereLivres());
Assert.Equal(2, tout.Count);
var numeriques = await _service.ListerAsync(new CritereLivres { Format = Format.Numerique });
Assert.Equal(["Epub"], numeriques.Select(l => l.Titre));
}
[Fact]
public async Task La_recherche_traverse_bien_SQLite()
{
await _service.CreerAsync(Saisie("Germinal", "Émile Zola"), "mathieu");
await _service.CreerAsync(Saisie("Le Horla", "Guy de Maupassant"), "mathieu");
var parAuteur = await _service.ListerAsync(new CritereLivres { Recherche = "maupassant" });
Assert.Equal(["Le Horla"], parAuteur.Select(l => l.Titre));
var parTitre = await _service.ListerAsync(new CritereLivres { Recherche = "GERMI" });
Assert.Equal(["Germinal"], parTitre.Select(l => l.Titre));
}
///
/// Invariant central : la bibliothèque est commune. Des livres saisis par des comptes
/// différents doivent tous ressortir, quelle que soit la personne qui consulte.
///
[Fact]
public async Task La_liste_renvoie_les_livres_de_tous_les_utilisateurs()
{
await _service.CreerAsync(Saisie("Livre de Mathieu"), "mathieu");
await _service.CreerAsync(Saisie("Livre de Camille"), "camille");
await _service.CreerAsync(Saisie("Livre anonyme"), null);
var tout = await _service.ListerAsync(new CritereLivres());
Assert.Equal(3, tout.Count);
Assert.Contains(tout, l => l.AjoutePar == "mathieu");
Assert.Contains(tout, l => l.AjoutePar == "camille");
Assert.Contains(tout, l => l.AjoutePar is null);
}
[Fact]
public async Task Modifier_ne_reecrit_ni_AjoutePar_ni_DateAjout()
{
var cree = (await _service.CreerAsync(Saisie("Titre initial", "Auteur"), "camille")).Livre!;
var modifie = await _service.ModifierAsync(cree.Id, new EnregistrementLivre
{
Titre = "Titre corrigé",
Auteur = "Auteur corrigé",
Format = Format.Numerique,
Statut = Statut.Lu,
});
Assert.Null(modifie.Erreur);
Assert.Equal("Titre corrigé", modifie.Livre!.Titre);
Assert.Equal(Format.Numerique, modifie.Livre.Format);
// La trace de la saisie d'origine survit à l'édition, y compris par quelqu'un d'autre.
Assert.Equal("camille", modifie.Livre.AjoutePar);
Assert.Equal(cree.DateAjout, modifie.Livre.DateAjout);
}
[Fact]
public async Task Modifier_un_livre_inexistant_est_signale_comme_introuvable()
{
var resultat = await _service.ModifierAsync(4242, Saisie("Fantôme"));
Assert.True(resultat.EstIntrouvable);
}
[Fact]
public async Task Changer_le_statut_ne_touche_que_le_statut()
{
var cree = (await _service.CreerAsync(Saisie("Germinal", "Émile Zola"), "mathieu")).Livre!;
var apres = await _service.ChangerStatutAsync(cree.Id, Statut.EnCours);
Assert.NotNull(apres);
Assert.Equal(Statut.EnCours, apres!.Statut);
Assert.Equal(cree.Titre, apres.Titre);
Assert.Equal(cree.Auteur, apres.Auteur);
Assert.Equal(cree.AjoutePar, apres.AjoutePar);
}
[Fact]
public async Task Changer_le_statut_dun_livre_inexistant_renvoie_null()
=> Assert.Null(await _service.ChangerStatutAsync(4242, Statut.Lu));
[Fact]
public async Task Supprimer_retire_le_livre_du_catalogue()
{
var cree = (await _service.CreerAsync(Saisie("À jeter"), "mathieu")).Livre!;
Assert.True(await _service.SupprimerAsync(cree.Id));
Assert.Null(await _service.ObtenirAsync(cree.Id));
Assert.Empty(await _service.ListerAsync(new CritereLivres()));
// Seconde suppression : plus rien à supprimer.
Assert.False(await _service.SupprimerAsync(cree.Id));
}
[Fact]
public async Task Obtenir_renvoie_la_fiche_complete()
{
var cree = (await _service.CreerAsync(
Saisie("Le Horla", "Guy de Maupassant", "9782080704092"), "mathieu")).Livre!;
var relu = await _service.ObtenirAsync(cree.Id);
Assert.NotNull(relu);
Assert.Equal("Le Horla", relu!.Titre);
Assert.Equal("9782080704092", relu.Isbn);
}
public void Dispose()
{
_db.Dispose();
_connexion.Dispose();
GC.SuppressFinalize(this);
}
}