using MaBibli.Api.Data;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
namespace MaBibli.Tests;
///
/// La migration RangDesEnvies, appliquée pour de vrai à une base déjà peuplée.
///
///
/// Ce test ne double pas ServiceSouhaitsTests : celui-ci crée son schéma avec
/// EnsureCreated, qui ne joue aucune migration. Le remplissage de la colonne
/// Rang n'y passerait donc jamais, et une erreur dans son SQL ne se verrait qu'en
/// production — sur les listes d'envies réelles, qui se réordonneraient sous les yeux de leur
/// propriétaire.
///
public class MigrationRangDesEnviesTests : IDisposable
{
private const string AvantLeRang = "20260818052834_ListeDEnvies";
private readonly SqliteConnection _connexion;
private readonly MaBibliDbContext _db;
public MigrationRangDesEnviesTests()
{
// Connexion maintenue ouverte : une base SQLite « :memory: » disparaît avec elle.
_connexion = new SqliteConnection("Data Source=:memory:");
_connexion.Open();
_db = new MaBibliDbContext(
new DbContextOptionsBuilder().UseSqlite(_connexion).Options);
}
private IMigrator Migrateur => _db.GetInfrastructure().GetRequiredService();
///
/// Le cœur du test : la migration doit reconduire l'ordre affiché jusqu'ici —
/// auteurs renseignés d'abord, puis par auteur, puis par titre — et repartir de zéro pour
/// chaque utilisateur, le rang étant personnel comme le reste de la table.
///
[Fact]
public async Task Reconduit_lancien_ordre_daffichage_utilisateur_par_utilisateur()
{
await Migrateur.MigrateAsync(AvantLeRang);
// Insertion en SQL brut : à ce point du schéma, la colonne Rang n'existe pas encore,
// et l'entité C# la porte déjà. Passer par EF échouerait.
await ExecuterAsync("""
INSERT INTO LivresSouhaites
(Id, Utilisateur, Titre, TitreNormalise, Auteur, AuteurNormalise, DateAjout)
VALUES
(1, 'mathieu', 'Germinal', 'germinal', 'Émile Zola', 'emile zola', '2026-01-01'),
(2, 'mathieu', 'Sans nom', 'sans nom', NULL, '', '2026-01-02'),
(3, 'mathieu', 'Au bonheur', 'au bonheur', 'Émile Zola', 'emile zola', '2026-01-03'),
(4, 'mathieu', 'Les fourmis','les fourmis','Bernard Werber', 'bernard werber', '2026-01-04'),
(5, 'camille', 'Sa liste', 'sa liste', 'Bernard Werber', 'bernard werber', '2026-01-05'),
(6, 'camille', 'Sans nom', 'sans nom', NULL, '', '2026-01-06');
""");
await Migrateur.MigrateAsync();
// Werber avant Zola, « Au bonheur » avant « Germinal », et l'envie sans auteur en
// dernier : exactement ce que la liste montrait avant la colonne.
Assert.Equal(
[(0, "Les fourmis"), (1, "Au bonheur"), (2, "Germinal"), (3, "Sans nom")],
await RangsAsync("mathieu"));
// ⚠️ La numérotation de Camille repart de zéro : le rang n'a de sens qu'à l'intérieur
// d'une liste. Continuer celle de Mathieu ferait dépendre l'ordre de l'un de l'autre.
Assert.Equal([(0, "Sa liste"), (1, "Sans nom")], await RangsAsync("camille"));
}
/// Une base sans aucune envie doit traverser la migration sans broncher.
[Fact]
public async Task Sapplique_aussi_a_une_base_vide()
{
await Migrateur.MigrateAsync();
Assert.Empty(await _db.LivresSouhaites.ToListAsync());
}
private async Task ExecuterAsync(string sql)
{
await using var commande = _connexion.CreateCommand();
commande.CommandText = sql;
await commande.ExecuteNonQueryAsync();
}
private async Task> RangsAsync(string utilisateur)
{
// Contexte neuf : le précédent a pu mettre en cache le schéma d'avant la migration.
await using var db = new MaBibliDbContext(
new DbContextOptionsBuilder().UseSqlite(_connexion).Options);
return await db.LivresSouhaites
.Where(s => s.Utilisateur == utilisateur)
.OrderBy(s => s.Rang)
.Select(s => new ValueTuple(s.Rang, s.Titre))
.ToListAsync();
}
public void Dispose()
{
_db.Dispose();
_connexion.Dispose();
GC.SuppressFinalize(this);
}
}