115 lines
7.7 KiB
Gherkin
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 |
|