Files
PleinR/tests/mail-recipients.test.ts
Claude 0c8542f894 Tests : file d'envoi, destinataires, navigation et invariants
`tests/security.test.ts` gagne cinq blocs qui verrouillent ce qui compte
et qu'aucun type ne protège :

- les cinq actions qui émettent un mot de passe appellent `sendNow` et
  jamais `queueMail`, et la trace journalisée porte un corps vide ;
- l'écran Boîte mail ne déchiffre aucun secret, saisit en champ masqué et
  annonce qu'un champ vide conserve la valeur enregistrée ;
- les trois fonctions qui écrivent un secret en base le chiffrent ;
- ni copie ni copie cachée dans la chaîne d'envoi, et `to_address` reste
  une adresse unique et obligatoire ;
- aucune lecture publique ne touche aux informations, et rien de ce qui
  les rend n'emprunte `dangerouslySetInnerHTML` ;
- chaque boucle de fond garde son interrupteur et reste hors du bundle
  edge.

`tests/backend-nav.test.ts` attrape l'oubli qu'on ne voit qu'à l'écran :
ajouter une page demande de toucher aux titres, aux sections et au jeu
d'icônes, et rater l'un des trois donne un en-tête vide ou une icône
absente.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QsLjRAnuLwivqxCbM4WeP
2026-09-14 16:22:07 +00:00

54 lines
2.2 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { dedupeRecipients, isPlausibleEmail } from "../src/lib/mail-recipients";
test("une adresse manifestement cassée est écartée", () => {
assert.equal(isPlausibleEmail("contact@pleinr.fr"), true);
assert.equal(isPlausibleEmail(" contact@pleinr.fr "), true);
assert.equal(isPlausibleEmail(""), false);
assert.equal(isPlausibleEmail("pas-une-adresse"), false);
assert.equal(isPlausibleEmail("contact@pleinr"), false);
assert.equal(isPlausibleEmail("a@b.fr, c@d.fr"), false);
assert.equal(isPlausibleEmail("victime@pleinr.fr>\r\nBcc: espion@x.fr"), false);
});
test("N destinataires donnent N entrées, jamais une seule fusionnée", () => {
const rows = [
{ email: "a@pleinr.fr", name: "A", memberId: 1 },
{ email: "b@pleinr.fr", name: "B", memberId: 2 },
{ email: "c@pleinr.fr", name: "C", memberId: 3 },
];
const out = dedupeRecipients(rows);
assert.equal(out.length, 3);
assert.deepEqual(out.map((r) => r.email), ["a@pleinr.fr", "b@pleinr.fr", "c@pleinr.fr"]);
});
test("un doublon ne reçoit pas deux fois, quelle que soit la casse", () => {
const out = dedupeRecipients([
{ email: "Contact@PleinR.fr", name: "Premier", memberId: 1 },
{ email: "contact@pleinr.fr", name: "Second", memberId: 2 },
{ email: " CONTACT@pleinr.FR ", name: "Troisième", memberId: 3 },
]);
assert.equal(out.length, 1);
// La première occurrence gagne, avec son nom et son rattachement.
assert.equal(out[0].name, "Premier");
assert.equal(out[0].memberId, 1);
assert.equal(out[0].email, "Contact@PleinR.fr");
});
test("les lignes sans adresse utilisable disparaissent", () => {
const out = dedupeRecipients([
{ email: null, name: "Sans adresse", memberId: 1 },
{ email: " ", name: "Vide", memberId: 2 },
{ email: "cassée", name: "Invalide", memberId: 3 },
{ email: "bon@pleinr.fr", name: "Bon", memberId: 4 },
]);
assert.deepEqual(out, [{ email: "bon@pleinr.fr", name: "Bon", memberId: 4 }]);
});
test("un nom absent devient une chaîne vide, pas « null »", () => {
const out = dedupeRecipients([{ email: "bon@pleinr.fr", name: null, memberId: null }]);
assert.deepEqual(out, [{ email: "bon@pleinr.fr", name: "", memberId: null }]);
});