Files
redefined-designs/backend/tests/unit/mailOutcome.test.ts
T
bermudalambandClaude Opus 5 8a35c213fc feat(mail): have sendMail say what it actually did (#260)
It returned Promise<void> and returned early in two cases that were indistinguishable from success at the call site: no SMTP credentials, and a recipient outside MAIL_ALLOWLIST. A caller could therefore report that it had emailed somebody a message nobody would ever receive, and in QA — which restricts delivery deliberately, as its entire safety property — that is the normal case rather than an edge one.

It now returns a MailOutcome saying which of the three happened. No existing caller changes: there are seven and every one ignores the result, so this is additive. Re-deriving the answer at a second site would have duplicated isAllowedRecipient and the SMTP check, which is exactly the drift the guard-in-one-place comment above them exists to prevent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 17:39:59 -05:00

55 lines
1.9 KiB
TypeScript

import { sendMail } from '../../src/mailer';
/**
* What sendMail says it did.
*
* It returns early in two cases that are indistinguishable from success at the
* call site — SMTP unconfigured, and the recipient not on MAIL_ALLOWLIST — and
* #260 needs to tell them apart so the admin is not told a link was emailed
* when it was not.
*
* Only the two skip paths are covered. A real send needs an SMTP server, which
* a unit test has no business starting; the integration test in Task 4 covers
* the route's behaviour instead.
*/
describe('what sendMail reports', () => {
const original = { ...process.env };
afterEach(() => {
process.env = { ...original };
});
it('says so when SMTP is not configured', async () => {
delete process.env.SMTP_USER;
delete process.env.SMTP_PASSWORD;
await expect(sendMail('someone@example.com', 'subject', '<p>body</p>')).resolves.toBe(
'skipped-unconfigured'
);
});
// The case that matters most: QA restricts delivery, and a blocked address
// previously returned exactly as though it had sent.
it('says so when the recipient is not on the allowlist', async () => {
process.env.SMTP_USER = 'user';
process.env.SMTP_PASSWORD = 'password';
process.env.MAIL_ALLOWLIST = 'allowed@example.com';
await expect(sendMail('someone-else@example.com', 'subject', '<p>body</p>')).resolves.toBe(
'skipped-blocked'
);
});
it('does not report blocked for an address that is on the allowlist', async () => {
process.env.SMTP_USER = 'user';
process.env.SMTP_PASSWORD = 'password';
process.env.MAIL_ALLOWLIST = 'allowed@example.com';
// Not asserting 'sent': that would need a live SMTP server. Asserting only
// that the allowlist did not refuse it, which is this test's subject.
await expect(
sendMail('allowed@example.com', 'subject', '<p>body</p>').catch(() => 'threw')
).resolves.not.toBe('skipped-blocked');
});
});