Files
RenewCMS/features/transactional_email_templates.qa.md
2026-09-26 05:41:34 +02:00

72 lines
4.9 KiB
Markdown

# QA procedure: transactional email templates
## Automated (Go, `test/`)
This slice touches a use-case call site (a string literal) and two static template files; it has
no new HTTP surface of its own, so no new handler/router wiring is exercised here. Reuse the
existing fakes already in `test/` (`mockMailRepo` from `test/mail_test.go`, the fake `user.Repository`
used by the registration and password-reset tests) rather than building new ones.
1. **Registration sends "verification".**
In the existing registration test file (or its replacement after this slice), build
`CreateUserUseCase` over an empty fake `user.Repository`, a real hasher, and `mockMailRepo`, call
`CreateUser` with username `alice`, password `hunter2example12`, email `alice@example.com`, assert
the call succeeds, then assert `mockMailRepo.SentEmails` has exactly one entry whose `Template`
field equals `"verification"` (not `"mailValidation"`).
2. **Password reset sends "password-reset".**
Build `RequestPasswordResetUseCase` over a fake `user.Repository` seeded with a user whose email is
`alice@example.com`, a real hasher, `mockMailRepo` and a public URL, call `RequestPasswordReset("alice@example.com")`,
assert it returns `nil`, then assert `mockMailRepo.SentEmails` has exactly one entry whose
`Template` field equals `"password-reset"` (not `"passwordReset"`).
3. **render() produces the right subject, link and copy for each known template.**
Construct a real `mailer.NewMailRepository(config.SmtpSettings{})` (SMTP fields are irrelevant
since `DialAndSend` is never called here) and call its render step directly (test lives in
package `test`, so this exercises the exported behavior surface of a real `*mailer.MailRepository`
without opening a network connection):
- `render("verification", <data with Host "https://cms.example.com", VerificationCode "abc123">)`
→ subject `"Verify your RenewCMS account"`, body contains
`https://cms.example.com/register/validate?c=abc123`, `Verify my account`,
`This link expires in 2 hours.`
- `render("password-reset", <data with Host "https://cms.example.com", VerificationCode "abc123",
Email "alice@example.com">)` → subject `"Reset your RenewCMS password"`, body contains
`https://cms.example.com/register/reset/validate?c=abc123&email=alice%40example.com`,
`Choose a new password`, `This link expires in 1 hour and works once.`
(`html/template` auto-escapes `Email` in the query string, turning `@` into `%40`; assert the
escaped form, matching what `MailRepository.Send` will actually produce.)
3b. **render() fallback and failure paths.**
- `render("does-not-exist", nil)` returns an error (template file not found) and the returned
subject is still the old default `"RenewCMS | Action required"`, with an empty body.
- A `render(<known templateName>, <data that fails Execute>)` call returns an error; assert
`MailRepository.Send` with the same inputs returns an error too and never gets to
`DialAndSend` (no network dependency needed to prove this: a failing `render` return is
sufficient to prove `Send` short-circuits before dialing, since `Send`'s existing structure
calls render, then returns immediately on its error).
4. **Old template files are gone.**
`os.Stat("internal/infrastructure/mailer/templates/mailValidation.html")` and the `passwordReset.html`
equivalent each return an error satisfying `os.IsNotExist`.
5. **Existing slice tests still pass with updated literals.**
Run the full suite (`task test`) and confirm the tests backing
`registration_gate_and_verification_email.feature` and `request_password_reset.feature` still pass
now that their scenario text and any Go assertions inside their test files use `"verification"` and
`"password-reset"` instead of the old strings. If those test files still contain the old literals
as Go string constants (not just prose in the `.feature` files), they need the same rename; point
this out to `coder` rather than silently doing a code edit here.
## Manual verification (not covered by the automated Go tests above)
- **Rendered appearance in an actual mail client.** The bulletproof button (table-cell background +
padded link), the hidden preheader text, and the `prefers-color-scheme: dark` overrides can only be
judged by opening the rendered HTML in a real or preview mail client (or the board file
`docs/design/source/RenewCMS Emails.dc.html` next to a manually triggered send through a real SMTP
server) — this is not something `os.ReadFile` + `html/template.Execute` can verify. Confirm both
light and dark rendering for both templates.
- **Real SMTP delivery end to end.** Sending an actual email via `SMTP_HOST`/`SMTP_PORT` and eyeballing
the arrived message (subject line, sender name "RenewCMS <...>", clickable button, monospace
fallback URL) is a manual item; nothing here exercises `gomail.Dialer.DialAndSend`.