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

4.9 KiB

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).
  1. 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.

  2. 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.