Configurer les rôles et leurs capacités

Critère d'acceptation de WP-01 resté sans écran : « un rôle personnalisé créé
par un client modifie effectivement l'accès, sans changement de code ». Le
catalogue de capacités existait, l'autorisation s'y adossait déjà, mais seul le
semis pouvait attribuer quoi que ce soit.

Deux dangers opposés, et il faut se protéger des deux. L'escalade d'abord : on
ne peut accorder qu'une capacité qu'on détient soi-même, sans quoi la première
personne autorisée à éditer un rôle s'accorde l'accès aux rémunérations en
trois clics et le catalogue ne sert plus à rien. Le retrait, lui, reste permis
même sur une capacité qu'on n'a pas — réduire un droit n'a jamais élargi le
sien, et l'interdire empêcherait de corriger un rôle trop large.

Le verrouillage ensuite : au moins un rôle doit conserver la gestion des
droits. La condition se juge sur l'ensemble des rôles après coup, non sur celui
qu'on édite — ce qui compte n'est pas que ce rôle-ci garde la capacité, mais
qu'un rôle la garde. Sans cela une organisation se fermerait dehors, et le seul
recours serait une intervention en base.

Un rôle naît sans aucune capacité : en hériter de celles de son créateur
distribuerait des droits que personne n'a demandés. Le niveau propriétaire se
délègue à part. Un rôle système ne se supprime pas, un rôle attribué non plus —
ses membres se retrouveraient dehors sans que personne l'ait décidé pour eux.

Le test va jusqu'au bout : créer le rôle, l'attribuer, se connecter, constater
qu'un écran s'ouvre et qu'un autre reste fermé. S'arrêter à « la case est
cochée » n'aurait rien dit du critère.

Trois défauts d'isolation des tests corrigés au passage, tous du même genre —
un état qui s'accumule d'une exécution à l'autre :

- la file des congés désignait « la première ligne de ce salarié » pour son
  nettoyage, et tombait sur une ligne laissée par un passage antérieur ;
- l'écran de conservation tronquait à quarante pièces triées par ancienneté,
  faisant disparaître les pièces échues derrière celles dont il n'y a rien à
  dire — corrigé dans le produit, pas seulement dans le test ;
- le test de verrouillage de période consommait un mois par exécution sans
  jamais le rendre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv
This commit is contained in:
Claude committed 2026-08-09 08:07:36 +00:00
1 parent 0b82283397
commit 92a0ebf853
12 files changed
+1097 -12

No files matched your search

+5 -6
View File
@@ -180,15 +180,14 @@ test('une pièce échue est effectivement effacée', async ({ page }) => {
await upload.getByRole('button', { name: 'Déposer' }).click();
await expect(page.getByText(`« ${fileName} » déposé.`)).toBeVisible();
// Fraîchement déposée, elle n'est pas échue : la purge ne doit pas l'emporter.
await page.goto('/reglages/conservation');
await expect(
page.locator('li').filter({ hasText: fileName }).getByText('Échéance non atteinte'),
).toBeVisible();
// Fraîchement déposée, elle n'est pas échue. Constaté sur le dossier, qui ne
// liste que les pièces de ce salarié : l'écran de conservation tronque, et la
// pièce d'un test neuf n'y figure pas tant qu'elle n'appelle aucune décision.
await expect(page.getByText(/conservée jusqu’au/)).toBeVisible();
// Seule l'échéance déclenche l'effacement, et elle se compte en mois.
await backdateDocument(fileName, 3);
await page.reload();
await page.goto('/reglages/conservation');
await expect(
page.locator('li').filter({ hasText: fileName }).getByText('À purger'),
).toBeVisible();