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

115 lines
7.7 KiB
Gherkin

Feature: The two transactional email templates are replaced by the design's ready-to-use files
As the maintainer of RenewCMS
I want the verification and password-reset emails to use the exact Go html/template files from
docs/design/source/emails/, renamed to match the design's naming, instead of the old ad hoc
mailValidation.html and passwordReset.html files
So that account-verification and password-reset emails match the visual spec (bulletproof button,
preheader, dark-mode overrides), and the template name used at the call site reflects the file it
now loads
Background:
Given internal/infrastructure/mailer/templates/mailValidation.html is removed and replaced by
internal/infrastructure/mailer/templates/verification.html, copied verbatim from
docs/design/source/emails/verification.html, still driven by a Go struct with a Host field and
a VerificationCode field (the existing MailValidationData shape in CreateUser.go is unchanged)
And internal/infrastructure/mailer/templates/passwordReset.html is removed and replaced by
internal/infrastructure/mailer/templates/password-reset.html, copied verbatim from
docs/design/source/emails/password-reset.html, still driven by a Go struct with a Host field, a
VerificationCode field and an Email field (the existing MailPasswordResetData shape in
RequestPasswordReset.go is unchanged)
And MailRepository.Send (internal/infrastructure/mailer/mailRepository.go) is unchanged code: it
already loads "templates/" + templateName + ".html" via template.ParseFS, so it now resolves
the templateName "verification" to the new verification.html file and "password-reset" to the
new password-reset.html file, purely because of the rename, with no change to Send's own logic
And CreateUserUseCase.CreateUser (internal/infrastructure/useCases/CreateUser.go) now calls
mail.Repository.Send with template "verification" instead of the old "mailValidation"; nothing
else about that call changes: the receiver address, and the Host/VerificationCode data, are as
before
And RequestPasswordResetUseCase.RequestPasswordReset
(internal/infrastructure/useCases/RequestPasswordReset.go) now calls mail.Repository.Send with
template "password-reset" instead of the old "passwordReset"; nothing else about that call
changes: the receiver address, and the Host/VerificationCode/Email data, are as before
And the verification email's link is still built as
"{{.Host}}/register/validate?c={{.VerificationCode}}" and the password-reset email's link is
still built as
"{{.Host}}/register/reset/validate?c={{.VerificationCode}}&email={{.Email}}", matching both the
old templates and the use cases that populate them, so no handler, use case or route changes
as a result of this slice
And MailRepository (internal/infrastructure/mailer/mailRepository.go) gains an unexported render
step, e.g. render(templateName string, data any) (subject string, body string, err error),
that Send calls before dialing; render selects the subject by templateName ("Verify your
RenewCMS account" for "verification", "Reset your RenewCMS password" for "password-reset",
matching docs/design/emails.md's suggested subjects exactly) and parses/executes the
corresponding templates/<templateName>.html file, returning its rendered body; Send then uses
that subject and body to build and dial the message exactly as before, and any ParseFS or
Execute error from render is returned by Send unchanged, with the same log.Println call sites
as today
And an unrecognized templateName still reaches template.ParseFS, which fails to find
"templates/<templateName>.html" and returns its usual error; render does not special-case
unknown names beyond the two known subjects, so an unknown templateName's subject falls back to
the same literal "RenewCMS | Action required" used before this slice, and Send still returns
the ParseFS error without attempting to dial
And the two feature files that already assert a template-name string literal,
registration_gate_and_verification_email.feature and request_password_reset.feature, are
updated in this same slice so every occurrence of "mailValidation" reads "verification" and
every occurrence of "passwordReset" reads "password-reset"; their behaviour is otherwise
unchanged
Scenario: Registering a user sends the verification email under its new template name
Given the user repository is empty
When the client sends POST /v1/auth/register with username "alice", password "hunter2example12",
email "alice@example.com"
Then the response status is 200
And exactly one email was sent to "alice@example.com" using template "verification"
And that email's data has Host and VerificationCode populated exactly as before this slice
Scenario: Requesting a password reset sends the reset email under its new template name
Given the user repository contains a user "alice" with email "alice@example.com"
When the client sends POST /v1/auth/password-reset with body {"email": "alice@example.com"}
Then the response status is 200
And exactly one email was sent to "alice@example.com" using template "password-reset"
And that email's data has Host, VerificationCode and Email populated exactly as before this slice
Scenario: render() produces the verification email's subject, link and copy
Given a real *mailer.MailRepository
When its render step is called with templateName "verification" and data whose Host is
"https://cms.example.com" and whose VerificationCode is "abc123"
Then the returned subject is "Verify your RenewCMS account"
And the returned body contains the link "https://cms.example.com/register/validate?c=abc123"
And the returned body contains the button text "Verify my account"
And the returned body contains the text "This link expires in 2 hours."
Scenario: render() produces the password-reset email's subject, link and copy
Given a real *mailer.MailRepository
When its render step is called with templateName "password-reset" and data whose Host is
"https://cms.example.com", whose VerificationCode is "abc123" and whose Email is
"alice@example.com"
Then the returned subject is "Reset your RenewCMS password"
And the returned body contains the link
"https://cms.example.com/register/reset/validate?c=abc123&email=alice%40example.com"
And the returned body contains the button text "Choose a new password"
And the returned body contains the text "This link expires in 1 hour and works once."
Scenario: render() falls back to the old default subject for an unrecognized template name
Given a real *mailer.MailRepository
When its render step is called with templateName "does-not-exist" and any data
Then the returned subject is "RenewCMS | Action required"
And an error is returned because templates/does-not-exist.html cannot be found
And the returned body is empty
Scenario: A template execution failure is returned without sending
Given a real *mailer.MailRepository
And data that causes Execute to fail for a known templateName (e.g. a type without the fields
the template references)
When its render step is called with that templateName and that data
Then render returns an error and MailRepository.Send never reaches DialAndSend
Scenario Outline: The old template files no longer exist under their previous names
Then the file internal/infrastructure/mailer/templates/<old file> does not exist on disk
Examples:
| old file |
| mailValidation.html |
| passwordReset.html |