Files
mabibli/MaBibli.Tests/GroupementCatalogueTests.cs
T
mathieuandClaude Opus 5 7d2f3dc5d9 Rassemble au catalogue les tomes qui racontent la même histoire
Les livres d'une même série se suivaient au hasard de l'alphabet, et rien
ne disait qu'ils allaient ensemble. Ils tiennent maintenant sous le nom de
leur série, à la place qu'occupait le premier d'entre eux — l'ordre général
ne bouge donc pas — et ce nom mène à la série, seul écran qui montre aussi
les tomes qui manquent.

Le regroupement ne cache ni ne duplique rien : une bascule le défait, et
un livre rattaché à deux séries ne paraît qu'une fois, toujours sous la
même. La règle est une fonction pure, avec ses tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 00:16:52 +02:00

137 lines
4.9 KiB
C#

using MaBibli.Client.Services;
using MaBibli.Shared.Dtos;
namespace MaBibli.Tests;
/// <summary>
/// Regroupement des tomes d'une série au catalogue (2026-08-22).
/// </summary>
/// <remarks>
/// L'écran n'est pas éprouvable ici, mais la règle l'est : ces tests portent sur ce qui a été
/// <b>décidé</b> — la place du bloc, l'ordre des tomes à l'intérieur, et le fait que rien ne
/// disparaisse ni ne se duplique.
/// </remarks>
public class GroupementCatalogueTests
{
private static LivreDto Livre(int id, string titre) =>
new()
{
Id = id,
Titre = titre,
Format = MaBibli.Shared.Entites.Format.Physique,
DateAjout = DateTime.UtcNow,
};
private static SerieDto Serie(int id, string titre, params (int Position, int LivreId)[] tomes) =>
new()
{
Id = id,
Titre = titre,
Elements =
[
.. tomes.Select(t => new ElementSerieDto
{
Id = t.LivreId,
Position = t.Position,
Titre = $"tome {t.Position}",
LivreId = t.LivreId,
}),
],
};
/// <summary>
/// ⚠️ Le cas qui a tranché la décision : le bloc se pose à la place du PREMIER tome, pour
/// que l'ordre général du catalogue ne bouge pas.
/// </summary>
[Fact]
public void Le_bloc_prend_la_place_de_son_premier_tome()
{
List<LivreDto> catalogue =
[
Livre(1, "Dracula"),
Livre(2, "L'Éclat de cristal"),
Livre(3, "Dune"),
Livre(4, "Terre natale"),
];
var entrees = GroupementCatalogue.Grouper(
catalogue, [Serie(9, "La Légende de Drizzt", (1, 2), (0, 4))]);
Assert.Collection(
entrees,
e => Assert.Equal("Dracula", Assert.Single(e.Livres).Titre),
e =>
{
Assert.Equal("La Légende de Drizzt", e.Serie!.Titre);
// À l'intérieur, l'ordre est celui de LECTURE, pas celui du catalogue.
Assert.Equal(["Terre natale", "L'Éclat de cristal"], e.Livres.Select(l => l.Titre));
},
e => Assert.Equal("Dune", Assert.Single(e.Livres).Titre));
}
/// <summary>Aucun livre ne disparaît, aucun n'apparaît deux fois : c'est la règle du catalogue.</summary>
[Fact]
public void Le_regroupement_ne_perd_ni_ne_duplique_aucun_livre()
{
List<LivreDto> catalogue = [Livre(1, "A"), Livre(2, "B"), Livre(3, "C")];
// Le livre 2 est rattaché à DEUX séries — le modèle l'autorise.
var entrees = GroupementCatalogue.Grouper(
catalogue, [Serie(1, "Zeta", (0, 2)), Serie(2, "Alpha", (0, 2), (1, 3))]);
Assert.Equal([1, 2, 3], entrees.SelectMany(e => e.Livres).Select(l => l.Id).Order());
}
/// <summary>
/// ⚠️ La série retenue est la première par ordre alphabétique, et non celle que l'API a
/// rendue en premier : sans cela, le bloc changerait de nom d'un chargement à l'autre.
/// </summary>
[Fact]
public void Un_livre_de_deux_series_est_range_sous_la_premiere_alphabetiquement()
{
var entrees = GroupementCatalogue.Grouper(
[Livre(2, "B")], [Serie(1, "Zeta", (0, 2)), Serie(2, "Alpha", (0, 2))]);
Assert.Equal("Alpha", Assert.Single(entrees).Serie!.Titre);
}
/// <summary>Sans série connue — hors-ligne sans instantané, ou groupement défait — la liste est plate.</summary>
[Fact]
public void Sans_series_la_liste_reste_celle_du_catalogue()
{
List<LivreDto> catalogue = [Livre(1, "A"), Livre(2, "B")];
var entrees = GroupementCatalogue.Grouper(catalogue, null);
Assert.All(entrees, e => Assert.Null(e.Serie));
Assert.Equal(["A", "B"], entrees.Select(e => e.Livres[0].Titre));
}
/// <summary>
/// Un seul tome possédé forme quand même un bloc : c'est ce qui dit à quoi ce livre
/// appartient, et ce qui mène à la série où figurent les tomes manquants.
/// </summary>
[Fact]
public void Un_seul_tome_possede_forme_quand_meme_un_bloc()
{
var entrees = GroupementCatalogue.Grouper(
[Livre(4, "Terre natale")], [Serie(9, "L'Elfe noir", (0, 4), (1, 5))]);
Assert.Equal("L'Elfe noir", Assert.Single(entrees).Serie!.Titre);
}
/// <summary>
/// Les tomes filtrés restent absents : le regroupement rassemble ce que la liste contient,
/// il ne va pas rechercher ce qu'un filtre a écarté.
/// </summary>
[Fact]
public void Un_filtre_ne_ramene_pas_les_tomes_ecartes()
{
var entrees = GroupementCatalogue.Grouper(
[Livre(5, "L'Éclat de cristal")], [Serie(9, "Drizzt", (0, 4), (1, 5))]);
Assert.Equal(["L'Éclat de cristal"], Assert.Single(entrees).Livres.Select(l => l.Titre));
}
}