diff --git a/CLAUDE.md b/CLAUDE.md
index 7cf1c6d..0766649 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1786,6 +1786,30 @@ Une œuvre bibliographique non possédée n'a pas de fiche locale. Son titre est
qui déplie inline son ISBN et son nombre d'éditions, tandis que les œuvres possédées ouvrent
leur fiche via la carte partagée.
+## Lot E — trouver ce qu'on veut souhaiter (2026-08-20)
+
+L'écran `/souhaits/ajout` affiche désormais la couverture et l'ISBN formaté de chaque résultat
+BnF. Le tri est local, sans nouvel appel réseau : date (récent en premier), titre ou éditeur.
+L'ISBN reste nu dans les données envoyées et seul `FormatageIsbn.Afficher` intervient à l'écran.
+
+La recherche par auteur expose un bouton **Nouveautés** depuis la bibliographie. Elle utilise un
+endpoint dédié (`/api/auteurs/{id}/bibliographie/nouveautes`) afin de ne pas modifier le contrat
+de la bibliographie générale. Les résultats sont les œuvres non possédées et non déjà souhaitées
+dont la dernière édition BnF connue est postérieure à l'édition la plus récente possédée.
+
+⚠️ Le représentant d'une œuvre reste la notice la plus ancienne : `OeuvreBibliographie.Annee`
+reste donc cette date, tandis que `AnneeDerniereEdition` conserve le maximum des dates de toutes
+les notices regroupées. C'est cette seconde valeur qui sert au filtre « paru depuis ». Le seuil
+est recherché d'abord par ISBN (ISBN-13 et ISBN-10 sont rapprochés), puis par clé d'œuvre.
+
+La bibliographie générale conserve son plafond de deux pages (200 notices). Pour les nouveautés,
+la BnF est paginée jusqu'à **10 pages de 100 notices (1000 maximum)**, ou moins si le nombre
+annoncé est inférieur. La première page donne le total, puis les pages nécessaires sont lancées
+en parallèle. Ce bornage est volontaire : le SRU est classé par pertinence et ne permet pas de
+déduire qu'une page suivante ne contient plus de date récente ; au-delà de 1000, la liste reste
+un extrait signalé par `Tronquee`, sans boucle ni appels indéfinis. Une source muette conserve
+les mêmes `EtatSourceBibliographie` et motifs que la bibliographie normale.
+
## Installation YunoHost — éprouvée en production le 2026-08-18
Le paquet vit dans le dépôt `mabibli_ynh` (voir « Deux dépôts distincts »). Installé,
diff --git a/IDEES.md b/IDEES.md
index 07a6430..6fe81e8 100644
--- a/IDEES.md
+++ b/IDEES.md
@@ -20,9 +20,9 @@ Le travail visuel de fond (couleurs, typographie, mise en page) est repoussé vo
La bibliographie par auteur est implémentée (voir `CLAUDE.md`). Deux pistes non traitées :
-- **Plafond de 200 notices.** Suffisant pour un auteur contemporain (Werber : 203, Nothomb : 277),
- très insuffisant pour un classique (Zola : 2692). L'écran le dit, mais on pourrait charger les
- pages suivantes à la demande plutôt que d'annoncer un extrait.
+- **Plafond de 200 notices.** Traité pour la recherche « nouveautés d'un auteur » du lot E :
+ elle charge jusqu'à 1000 notices avec un bornage explicite. La bibliographie générale reste
+ plafonnée à 200 et continue d'annoncer honnêtement qu'elle est tronquée.
- **OpenLibrary en second rideau.** La BnF ne connaît pas les auteurs étrangers non traduits ;
l'écran affiche alors une liste vide et l'explique. OpenLibrary expose les œuvres d'un auteur
(`/authors/{id}/works.json`) et pourrait prendre le relais — à ne faire que si le cas se
@@ -211,6 +211,8 @@ Aujourd'hui elle se consulte ; les cinq items la font servir à **constituer** u
## Lot E — Trouver ce qu'on veut souhaiter
+✅ Traité le 2026-08-20 : voir `CLAUDE.md`, section « Lot E — trouver ce qu'on veut souhaiter ».
+
L'écran `/souhaits/ajout` rend aujourd'hui une liste plate.
- **E1. ISBN et couverture dans les résultats** de recherche. ⚠️ L'ISBN existe déjà dans la
diff --git a/MaBibli.Api/Endpoints/SouhaitsEndpoints.cs b/MaBibli.Api/Endpoints/SouhaitsEndpoints.cs
index dc35248..5d7a967 100644
--- a/MaBibli.Api/Endpoints/SouhaitsEndpoints.cs
+++ b/MaBibli.Api/Endpoints/SouhaitsEndpoints.cs
@@ -180,6 +180,27 @@ public static class SouhaitsEndpoints
.Produces()
.Produces(StatusCodes.Status404NotFound);
+ routes.MapGet("/api/auteurs/{id:int}/bibliographie/nouveautes", async (
+ int id,
+ IServiceBibliographie service,
+ IFournisseurUtilisateur utilisateurs,
+ CancellationToken ct) =>
+ {
+ var bibliographie = await service.ObtenirNouveautesAsync(
+ id, utilisateurs.Obtenir().Identifiant, ct);
+
+ return bibliographie is null ? Results.NotFound() : Results.Ok(bibliographie);
+ })
+ .WithName("NouveautesAuteur")
+ .WithTags("Auteurs")
+ .WithSummary("Les œuvres de cet auteur parues depuis le livre le plus récent possédé.")
+ .WithDescription(
+ "La date du représentant d'une œuvre est celle de l'édition la plus ancienne ; "
+ + "la sélection compare donc la date la plus récente des éditions connues. "
+ + "La recherche étendue est bornée à 1000 notices BnF.")
+ .Produces()
+ .Produces(StatusCodes.Status404NotFound);
+
routes.MapPost("/api/auteurs/{id:int}/bibliographie/masque", async (
int id,
MasquageBibliographie saisie,
diff --git a/MaBibli.Api/Services/Catalogue/ServiceBibliographie.cs b/MaBibli.Api/Services/Catalogue/ServiceBibliographie.cs
index 8875eec..1f288a8 100644
--- a/MaBibli.Api/Services/Catalogue/ServiceBibliographie.cs
+++ b/MaBibli.Api/Services/Catalogue/ServiceBibliographie.cs
@@ -2,6 +2,7 @@ using MaBibli.Api.Data;
using MaBibli.Api.Services.Isbn;
using MaBibli.Shared.Dtos;
using MaBibli.Shared.Entites;
+using MaBibli.Shared.Isbn;
using MaBibli.Shared.Textes;
using Microsoft.EntityFrameworkCore;
@@ -15,6 +16,9 @@ public interface IServiceBibliographie
///
Task ObtenirAsync(int auteurId, string? utilisateur, CancellationToken ct = default);
+ Task ObtenirNouveautesAsync(
+ int auteurId, string? utilisateur, CancellationToken ct = default);
+
Task MasquerAsync(int auteurId, string titre, string? utilisateur, CancellationToken ct = default);
Task DemasquerAsync(int auteurId, string titre, string? utilisateur, CancellationToken ct = default);
@@ -101,6 +105,70 @@ public sealed class ServiceBibliographie(
};
}
+ public async Task ObtenirNouveautesAsync(
+ int auteurId, string? utilisateur, CancellationToken ct = default)
+ {
+ var auteur = await auteurs.ObtenirAsync(auteurId, ct);
+ if (auteur is null)
+ {
+ return null;
+ }
+
+ var recherche = bnf is IBnfClientNouveautes etendu
+ ? await etendu.RechercherParAuteurNouveautesAsync(auteur.Nom, ct)
+ : await bnf.RechercherParAuteurAsync(auteur.Nom, ct);
+ var resultat = recherche.Resultat;
+
+ var possedes = await db.LivreAuteurs
+ .AsNoTracking()
+ .Where(la => la.AuteurId == auteurId)
+ .Select(la => new { la.LivreId, la.Livre!.Titre, la.Livre.Isbn })
+ .ToListAsync(ct);
+
+ var parTitre = new Dictionary();
+ var parIsbn = new Dictionary();
+ foreach (var livre in possedes)
+ {
+ var cle = CleOeuvre.Cle(livre.Titre);
+ if (cle.Length > 0)
+ {
+ parTitre.TryAdd(cle, livre.LivreId);
+ }
+
+ if (!string.IsNullOrWhiteSpace(livre.Isbn))
+ {
+ parIsbn.TryAdd(livre.Isbn, livre.LivreId);
+ }
+ }
+
+ var souhaits = await SouhaitsAsync(utilisateur, ct);
+ var masquees = await MasqueesAsync(auteurId, utilisateur, ct);
+ var oeuvres = Regrouper(resultat)
+ .Select(o => Confronter(o, parTitre, parIsbn, souhaits, masquees))
+ .ToList();
+ var seuil = AnneeSeuilNouveautes(
+ possedes.Select(l => (l.Titre, l.Isbn)), resultat.Notices);
+
+ // Une notice d'édition originale ne date pas la dernière édition de l'œuvre. Le
+ // regroupement conserve donc AnneeDerniereEdition, qui est la seule date pertinente
+ // pour répondre à « paru depuis ».
+ var nouveautes = FiltrerNouveautes(oeuvres, seuil);
+
+ return new BibliographieDto
+ {
+ Auteur = auteur,
+ Oeuvres = nouveautes,
+ NoticesAnnoncees = resultat.NombreAnnonce,
+ NoticesLues = resultat.NombreLu,
+ NoticesEcartees = resultat.EcarteesAutreAuteur + resultat.EcarteesTypeNonLivre,
+ Avertissement = recherche.Avertissement,
+ Etat = resultat.Etat,
+ EstNouveautes = true,
+ AnneeSeuilNouveautes = seuil,
+ SeuilNouveautesInconnu = seuil is null && possedes.Count > 0,
+ };
+ }
+
public async Task MasquerAsync(
int auteurId, string titre, string? utilisateur, CancellationToken ct = default)
{
@@ -222,6 +290,12 @@ public sealed class ServiceBibliographie(
{
Titre = reference.Titre,
Annee = reference.Annee,
+ AnneeDerniereEdition = ordonnees
+ .Select(n => AnneeTriable(n.Annee))
+ .Where(a => a is not null)
+ .Select(a => a!.Value)
+ .DefaultIfEmpty()
+ .Max() is var annee && annee > 0 ? annee : null,
Editeur = reference.Editeur,
Isbn = ordonnees.Select(n => n.Isbn).FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)),
NombreEditions = groupe.Count(),
@@ -258,6 +332,61 @@ public sealed class ServiceBibliographie(
};
}
+ public static int? AnneeSeuilNouveautes(
+ IEnumerable<(string Titre, string? Isbn)> possedes,
+ IReadOnlyList notices)
+ {
+ var annees = new List();
+ foreach (var livre in possedes)
+ {
+ var correspondantes = !string.IsNullOrWhiteSpace(livre.Isbn)
+ ? notices.Where(n => MemesIsbn(n.Isbn, livre.Isbn))
+ : notices.Where(n => n.CleTitre == CleOeuvre.Cle(livre.Titre));
+
+ annees.AddRange(correspondantes
+ .Select(n => AnneeTriable(n.Annee))
+ .Where(a => a is not null)
+ .Select(a => a!.Value));
+ }
+
+ return annees.Count == 0 ? null : annees.Max();
+ }
+
+ private static bool MemesIsbn(string? gauche, string? droite)
+ {
+ var a = IsbnUtils.Normaliser(gauche);
+ var b = IsbnUtils.Normaliser(droite);
+ if (string.IsNullOrWhiteSpace(a) || string.IsNullOrWhiteSpace(b))
+ {
+ return false;
+ }
+
+ if (string.Equals(a, b, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ return IsbnUtils.TryConvertirEnIsbn10(a, out var a10)
+ && string.Equals(a10, b, StringComparison.OrdinalIgnoreCase)
+ || IsbnUtils.TryConvertirEnIsbn10(b, out var b10)
+ && string.Equals(b10, a, StringComparison.OrdinalIgnoreCase);
+ }
+
+ public static IReadOnlyList FiltrerNouveautes(
+ IEnumerable oeuvres, int? seuil)
+ {
+ if (seuil is null)
+ {
+ return [];
+ }
+
+ return oeuvres
+ .Where(o => o.AnneeDerniereEdition > seuil && o.ADecouvrir)
+ .OrderByDescending(o => o.AnneeDerniereEdition)
+ .ThenBy(o => o.Titre, StringComparer.CurrentCultureIgnoreCase)
+ .ToList();
+ }
+
///
/// Année exploitable pour le tri, extraite d'une date BnF.
///
diff --git a/MaBibli.Api/Services/Isbn/BnfClient.cs b/MaBibli.Api/Services/Isbn/BnfClient.cs
index bf040a6..ae66d5e 100644
--- a/MaBibli.Api/Services/Isbn/BnfClient.cs
+++ b/MaBibli.Api/Services/Isbn/BnfClient.cs
@@ -45,10 +45,19 @@ public interface IBnfClient
string titre, string? auteur, CancellationToken ct = default);
}
+///
+/// Extension de la recherche par auteur utilisée uniquement pour les nouveautés.
+///
+public interface IBnfClientNouveautes
+{
+ Task<(ResultatBibliographie Resultat, string? Avertissement)>
+ RechercherParAuteurNouveautesAsync(string auteur, CancellationToken ct = default);
+}
+
///
/// Client de l'API SRU du catalogue général de la BnF (gratuite, sans clé).
///
-public sealed class BnfClient(HttpClient http, ILogger logger) : IBnfClient
+public sealed class BnfClient(HttpClient http, ILogger logger) : IBnfClient, IBnfClientNouveautes
{
/// Nom du client typé enregistré dans IHttpClientFactory.
public const string NomHttpClient = "bnf";
@@ -211,6 +220,12 @@ public sealed class BnfClient(HttpClient http, ILogger logger) : IBnf
public const int NoticesParPage = 100;
public const int PagesMaximum = 2;
+ public const int PagesMaximumNouveautes = 10;
+
+ public static int NombrePagesNouveautes(int nombreAnnonce) =>
+ Math.Min(
+ PagesMaximumNouveautes,
+ Math.Max(1, (nombreAnnonce + NoticesParPage - 1) / NoticesParPage));
public async Task<(ResultatBibliographie Resultat, string? Avertissement)> RechercherParAuteurAsync(
string auteur, CancellationToken ct = default)
@@ -257,6 +272,52 @@ public sealed class BnfClient(HttpClient http, ILogger logger) : IBnf
}
}
+ public async Task<(ResultatBibliographie Resultat, string? Avertissement)>
+ RechercherParAuteurNouveautesAsync(string auteur, CancellationToken ct = default)
+ {
+ if (string.IsNullOrWhiteSpace(auteur))
+ {
+ return (new ResultatBibliographie(), null);
+ }
+
+ try
+ {
+ var premiere = await LirePageAsync(auteur, 1, ct);
+ if (premiere is null)
+ {
+ return Echec(EtatSourceBibliographie.Injoignable, auteur);
+ }
+
+ var nombrePages = NombrePagesNouveautes(premiere.NombreAnnonce);
+
+ var pages = new List { premiere };
+ if (nombrePages > 1)
+ {
+ var suivantes = await Task.WhenAll(
+ Enumerable.Range(1, nombrePages - 1)
+ .Select(i => LirePageAsync(auteur, (i * NoticesParPage) + 1, ct)));
+ pages.AddRange(suivantes.Where(p => p is not null).Select(p => p!));
+ }
+
+ return (Fusionner(pages), null);
+ }
+ catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
+ {
+ logger.LogWarning(ex, "Délai dépassé par la BnF pour les nouveautés de {Auteur}", auteur);
+ return Echec(EtatSourceBibliographie.DelaiDepasse, auteur);
+ }
+ catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException)
+ {
+ logger.LogWarning(ex, "BnF injoignable pour les nouveautés de {Auteur}", auteur);
+ return Echec(EtatSourceBibliographie.Injoignable, auteur);
+ }
+ catch (System.Xml.XmlException ex)
+ {
+ logger.LogWarning(ex, "Réponse BnF illisible pour les nouveautés de {Auteur}", auteur);
+ return Echec(EtatSourceBibliographie.ReponseIllisible, auteur);
+ }
+ }
+
///
/// Motif lisible d'une bibliographie que la BnF n'a pas pu fournir.
///
diff --git a/MaBibli.Client/Pages/AjoutSouhait.razor b/MaBibli.Client/Pages/AjoutSouhait.razor
index e3264d2..915873b 100644
--- a/MaBibli.Client/Pages/AjoutSouhait.razor
+++ b/MaBibli.Client/Pages/AjoutSouhait.razor
@@ -98,6 +98,16 @@
Choisissez celle qui correspond, ou complétez à la main.
@*
⚠️ Les deux cas ci-dessous s'excluent, et c'est TOUT l'objet de ce bloc.
@@ -65,13 +74,27 @@ else if (_bibliographie is { } biblio)
}
else if (biblio.Oeuvres.Count == 0)
{
- @* La source a répondu, et elle ne connaît rien : là, et là seulement, on peut
- l'expliquer. Le cas est fréquent et n'a rien d'anormal. *@
-
- La BnF ne connaît aucun livre de cet auteur. C'est le cas attendu pour les auteurs
- étrangers : le dépôt légal français ne couvre que l'édition francophone, et un
- auteur traduit n'y a souvent pas de notice à son nom.
-
+ Impossible de dater le livre le plus récent que vous possédez dans les notices
+ reçues. La BnF a répondu, mais aucune nouveauté fiable ne peut être déduite.
+
+ }
+ else if (biblio.EstNouveautes)
+ {
+
Aucune nouveauté trouvée depuis @biblio.AnneeSeuilNouveautes.
+ }
+ else
+ {
+ @* La source a répondu, et elle ne connaît rien : là, et là seulement, on peut
+ l'expliquer. *@
+
+ La BnF ne connaît aucun livre de cet auteur. C'est le cas attendu pour les auteurs
+ étrangers : le dépôt légal français ne couvre que l'édition francophone, et un
+ auteur traduit n'y a souvent pas de notice à son nom.
+
+ }
}
else if (biblio.Avertissement is not null)
{
@@ -92,6 +115,13 @@ else if (_bibliographie is { } biblio)
}
+ @if (biblio.EstNouveautes)
+ {
+
+ Œuvres parues après @biblio.AnneeSeuilNouveautes, d'après les éditions BnF reçues.
+
+ }
+
@* Deux phrases distinctes plutôt qu'une seule à trous : enchaîner des fragments
conditionnels dans une même phrase produit une ponctuation bancale dès qu'un des
fragments manque (« exister. .34 notices ont été écartées »). *@
@@ -290,6 +320,19 @@ else if (_bibliographie is { } biblio)
@if (_bibliographie is { } b)
{
}
@@ -298,6 +341,9 @@ else if (_bibliographie is { } biblio)
[Parameter]
public int AuteurId { get; set; }
+ [SupplyParameterFromQuery(Name = "nouveautes")]
+ public bool Nouveautes { get; set; }
+
private BibliographieDto? _bibliographie;
private int _modePossession;
private bool _filtresOuverts;
@@ -337,7 +383,9 @@ else if (_bibliographie is { } biblio)
try
{
- _bibliographie = await Api.ObtenirBibliographieAsync(AuteurId);
+ _bibliographie = Nouveautes
+ ? await Api.ObtenirNouveautesAsync(AuteurId)
+ : await Api.ObtenirBibliographieAsync(AuteurId);
_livresCatalogue = await Api.ListerAsync(new CritereLivres());
if (_bibliographie is null)
diff --git a/MaBibli.Client/Services/ServiceLivresApi.cs b/MaBibli.Client/Services/ServiceLivresApi.cs
index 41f5ce0..c8408eb 100644
--- a/MaBibli.Client/Services/ServiceLivresApi.cs
+++ b/MaBibli.Client/Services/ServiceLivresApi.cs
@@ -775,6 +775,16 @@ public sealed class ServiceLivresApi(HttpClient http, CacheHorsLigne cache, Etat
return bibliographie;
}
+ public async Task ObtenirNouveautesAsync(
+ int auteurId, CancellationToken ct = default)
+ {
+ var reponse = await http.GetAsync(
+ $"api/auteurs/{auteurId}/bibliographie/nouveautes", ct);
+ return reponse.StatusCode == HttpStatusCode.NotFound
+ ? null
+ : await LireAsync(reponse, ct);
+ }
+
public async Task MasquerOeuvreBibliographieAsync(
int auteurId, string titre, CancellationToken ct = default) =>
await ModifierBibliographieAsync(
diff --git a/MaBibli.Shared/Dtos/SouhaitDto.cs b/MaBibli.Shared/Dtos/SouhaitDto.cs
index bc5a19d..9a29cc6 100644
--- a/MaBibli.Shared/Dtos/SouhaitDto.cs
+++ b/MaBibli.Shared/Dtos/SouhaitDto.cs
@@ -97,6 +97,13 @@ public record OeuvreBibliographie
public string? Annee { get; init; }
+ /// Année la plus récente des éditions BnF réunies sous cette œuvre.
+ ///
+ /// L'année affichée reste celle de l'édition la plus ancienne, qui représente l'œuvre.
+ /// Cette seconde valeur sert uniquement à rechercher les nouveautés.
+ ///
+ public int? AnneeDerniereEdition { get; init; }
+
public string? Editeur { get; init; }
/// ISBN d'une des éditions relevées, quand la notice en portait un.
@@ -193,6 +200,15 @@ public record BibliographieDto
///
public EtatSourceBibliographie Etat { get; init; }
+ /// Vrai quand la liste est limitée aux œuvres postérieures au seuil possédé.
+ public bool EstNouveautes { get; init; }
+
+ /// Année de publication la plus récente retrouvée pour un livre possédé.
+ public int? AnneeSeuilNouveautes { get; init; }
+
+ /// Vrai quand aucun ISBN ou titre possédé n'a permis de dater le seuil.
+ public bool SeuilNouveautesInconnu { get; init; }
+
///
/// Vrai quand la BnF n'a pas répondu : la liste vide ne prouve alors rien.
///
diff --git a/MaBibli.Tests/BnfBibliographieParserTests.cs b/MaBibli.Tests/BnfBibliographieParserTests.cs
index b5d5eff..da92dee 100644
--- a/MaBibli.Tests/BnfBibliographieParserTests.cs
+++ b/MaBibli.Tests/BnfBibliographieParserTests.cs
@@ -150,6 +150,57 @@ public class BnfBibliographieParserTests
}
}
+ [Fact]
+ public void Conserve_aussi_la_date_de_la_derniere_edition()
+ {
+ var resultat = BnfBibliographieParser.Parser(Fixture.Lire(Werber), "Bernard Werber");
+ var oeuvre = ServiceBibliographie.Regrouper(resultat)
+ .First(o => o.NombreEditions > 1
+ && o.AnneeDerniereEdition is not null
+ && o.Annee is not null);
+
+ var cle = MaBibli.Shared.Textes.CleOeuvre.Cle(oeuvre.Titre);
+ var maximum = resultat.Notices
+ .Where(n => n.CleTitre == cle)
+ .Select(n => ServiceBibliographie.AnneeTriable(n.Annee))
+ .Where(a => a is not null)
+ .Max();
+
+ Assert.Equal(maximum, oeuvre.AnneeDerniereEdition);
+ Assert.True(oeuvre.AnneeDerniereEdition >= ServiceBibliographie.AnneeTriable(oeuvre.Annee));
+ }
+
+ [Fact]
+ public void Les_nouveautes_comparent_la_derniere_edition_et_non_le_representant()
+ {
+ var anciennes = new OeuvreBibliographie
+ {
+ Titre = "Ancienne œuvre",
+ Annee = "1980",
+ AnneeDerniereEdition = 2024,
+ };
+ var recente = new OeuvreBibliographie
+ {
+ Titre = "Nouvelle œuvre",
+ Annee = "2023",
+ AnneeDerniereEdition = 2023,
+ };
+
+ var resultat = ServiceBibliographie.FiltrerNouveautes([anciennes, recente], 2023);
+
+ Assert.Single(resultat);
+ Assert.Equal("Ancienne œuvre", resultat[0].Titre);
+ }
+
+ [Theory]
+ [InlineData(0, 1)]
+ [InlineData(203, 3)]
+ [InlineData(1000, 10)]
+ [InlineData(2692, 10)]
+ public void La_recherche_de_nouveautes_est_bornee_a_dix_pages(
+ int annonces, int pages) =>
+ Assert.Equal(pages, BnfClient.NombrePagesNouveautes(annonces));
+
[Fact]
public void Une_reponse_vide_ne_fait_pas_echouer_la_lecture()
{