Files
mabibli/MaBibli.Tests/FournisseurUtilisateurTests.cs
T
mathieuandClaude Opus 5 fd6438b912 Phase 3 : CRUD des livres, statuts de lecture et identite SSOwat
API
- CRUD complet sur /api/livres : liste filtrable (format, statut) et
  cherchable (titre, auteur), consultation, creation, modification,
  changement de statut isole, suppression.
- Aucune lecture ne filtre sur AjoutePar : la bibliotheque est commune au
  foyer. Le critere n'existe meme pas dans CritereLivres, pour qu'on ne
  puisse pas s'en servir par inadvertance. Deux tests verrouillent
  l'invariant, dont un par reflexion sur le type des criteres.
- AjoutePar est renseigne par le serveur a la creation, jamais par le
  client, et n'est pas reecrit a l'edition (pas plus que DateAjout).
- Identite lue dans les en-tetes SSOwat (YNH_USER, YNH_USER_EMAIL,
  YNH_USER_FULLNAME), avec repli sur un utilisateur simule configure en
  developpement uniquement. Sans en-tete ni simulation, l'identite reste
  inconnue plutot qu'inventee. Expose par GET /api/moi.
- Migration appliquee au demarrage : pas d'etape manuelle a l'installation.

Interface Blazor, pensee mobile d'abord
- Catalogue unique (physiques et numeriques melanges, distingues par le
  format) avec recherche, filtres et changement de statut directement dans
  la liste.
- Ajout par ISBN : quand la cascade rend plusieurs notices, un ecran de
  choix les presente en mettant en avant editeur et annee, seuls elements
  qui les departagent. Le formulaire reste ensuite entierement modifiable.
- Ajout manuel, edition, suppression avec confirmation explicite.
- Couvertures : l'URL OpenLibrary n'est jamais validee par un appel
  prealable (502 intermittents mesures en phase 2) ; un attribut onerror
  masque l'image cassee et laisse apparaitre un substitut.

Decisions prises la ou CLAUDE.md etait muet
- Tri alphabetique par titre.
- Un ISBN saisi doit etre valide, mais reste facultatif.
- Ecran de choix affiche seulement a partir de deux candidats.

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

111 lines
3.6 KiB
C#

using MaBibli.Api.Services.Identite;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
namespace MaBibli.Tests;
/// <summary>
/// Lecture de l'identité dans les en-têtes SSOwat, et repli sur l'utilisateur simulé
/// en développement.
/// </summary>
public class FournisseurUtilisateurTests
{
private static FournisseurUtilisateurSsowat Construire(
IDictionary<string, string>? entetes = null,
OptionsIdentite? options = null)
{
var contexte = new DefaultHttpContext();
foreach (var (nom, valeur) in entetes ?? new Dictionary<string, string>())
{
contexte.Request.Headers[nom] = valeur;
}
var accesseur = new HttpContextAccessor { HttpContext = contexte };
return new FournisseurUtilisateurSsowat(accesseur, Options.Create(options ?? new OptionsIdentite()));
}
[Fact]
public void Les_entetes_ssowat_sont_lus()
{
var fournisseur = Construire(new Dictionary<string, string>
{
["YNH_USER"] = "mathieu",
["YNH_USER_EMAIL"] = "mathieu@example.org",
["YNH_USER_FULLNAME"] = "Mathieu L.",
});
var utilisateur = fournisseur.Obtenir();
Assert.Equal("mathieu", utilisateur.Identifiant);
Assert.Equal("mathieu@example.org", utilisateur.Email);
Assert.Equal("Mathieu L.", utilisateur.NomComplet);
Assert.False(utilisateur.Simule);
Assert.Equal("Mathieu L.", utilisateur.Affichage);
}
[Fact]
public void Lentete_utilisateur_seul_suffit()
{
var utilisateur = Construire(new Dictionary<string, string> { ["YNH_USER"] = "camille" }).Obtenir();
Assert.Equal("camille", utilisateur.Identifiant);
Assert.Null(utilisateur.Email);
Assert.Equal("camille", utilisateur.Affichage);
}
[Fact]
public void Lentete_ssowat_prime_sur_lutilisateur_simule()
{
// Sur le serveur, une configuration de dev oubliée ne doit jamais masquer l'identité réelle.
var fournisseur = Construire(
new Dictionary<string, string> { ["YNH_USER"] = "camille" },
new OptionsIdentite { UtilisateurSimule = "dev" });
var utilisateur = fournisseur.Obtenir();
Assert.Equal("camille", utilisateur.Identifiant);
Assert.False(utilisateur.Simule);
}
[Fact]
public void Sans_entete_lutilisateur_simule_prend_le_relais()
{
var fournisseur = Construire(options: new OptionsIdentite
{
UtilisateurSimule = "dev",
EmailSimule = "dev@localhost",
NomCompletSimule = "Utilisateur de développement",
});
var utilisateur = fournisseur.Obtenir();
Assert.Equal("dev", utilisateur.Identifiant);
Assert.Equal("Utilisateur de développement", utilisateur.NomComplet);
Assert.True(utilisateur.Simule);
}
[Fact]
public void Sans_entete_ni_simulation_lidentite_reste_inconnue()
{
// On n'invente pas d'identité : mieux vaut un livre sans traçabilité qu'un faux nom.
var utilisateur = Construire().Obtenir();
Assert.Null(utilisateur.Identifiant);
Assert.False(utilisateur.Simule);
Assert.Equal("inconnu", utilisateur.Affichage);
}
[Fact]
public void Un_entete_vide_est_traite_comme_absent()
{
var fournisseur = Construire(
new Dictionary<string, string> { ["YNH_USER"] = " " },
new OptionsIdentite { UtilisateurSimule = "dev" });
var utilisateur = fournisseur.Obtenir();
Assert.Equal("dev", utilisateur.Identifiant);
Assert.True(utilisateur.Simule);
}
}