Files
2026-05-27 23:29:53 +01:00

57 lines
3.0 KiB
Markdown

## ADDED Requirements
### Requirement: Mailpit image policy
The `@suaveplan/testing-integration-mailpit` plugin SHALL pin to `axllent/mailpit` at a fixed minor tag (e.g. `axllent/mailpit:v1.20`). The plugin MUST NOT use the `latest` tag. A follow-up operational change tracked at `docs/superpowers/specs/2026-04-25-test-suite-audit.md §5` will pin to `sha256:<digest>`.
#### Scenario: Image tag pinned to minor
- **WHEN** the container factory is invoked without injection
- **THEN** the resulting container uses an `axllent/mailpit:v<major>.<minor>` tag — never `axllent/mailpit:latest`
### Requirement: Env-bridge contract
The plugin SHALL declare and forward both `MAILPIT_SMTP_URL` and `MAILPIT_API_URL`. When BOTH are set on the parent process at construction time, the fixture MUST enter env-bridge passthrough mode. A partial-set (only one of the two) MUST throw `INTEGRATION.MAILPIT.PARTIAL_ENV` at construction.
#### Scenario: env() returns the full set
- **WHEN** the fixture has started in container mode
- **THEN** `fixture.env()` returns a record with exactly the keys `MAILPIT_SMTP_URL` and `MAILPIT_API_URL`
#### Scenario: Partial env-bridge rejected
- **WHEN** only `MAILPIT_SMTP_URL` is set on `process.env` at construction
- **THEN** the constructor throws with `code === "INTEGRATION.MAILPIT.PARTIAL_ENV"`
### Requirement: Healthcheck contract — API ping
`isHealthy()` SHALL issue an HTTP `GET` to the Mailpit API health endpoint and treat a 2xx response as healthy. The fixture constructor SHALL accept an optional `fetch` parameter for test injection.
#### Scenario: 2xx is healthy
- **WHEN** the API health endpoint returns 200
- **THEN** `isHealthy()` resolves `true`
### Requirement: assertMail() helper
The package SHALL export `assertMail(opts: { to?: string | string[]; from?: string; subject?: string; subjectMatch?: "exact" | "partial" })` that queries the Mailpit API and asserts at least one matching message exists. **Address matching MUST be exact.** **Subject matching defaults to `"partial"`** (substring on `subject`); callers MAY opt into exact matching. On no match, the helper MUST throw with `code === "INTEGRATION.MAILPIT.NO_MATCH"` and `context` listing the candidates that were considered.
#### Scenario: Partial subject match by default
- **WHEN** Mailpit holds a message with subject `"Welcome to Suaveplan, please confirm your email"` and `assertMail({ subject: "Welcome to Suaveplan" })` is invoked
- **THEN** the helper resolves without throwing
#### Scenario: Exact address match required
- **WHEN** Mailpit holds a message addressed to `user@example.com` and `assertMail({ to: "USER@example.com" })` is invoked without case-insensitivity opt-in
- **THEN** the helper rejects with `INTEGRATION.MAILPIT.NO_MATCH`
### Requirement: Error-code prefix
Errors thrown from the plugin SHALL carry codes matching `^INTEGRATION\.MAILPIT\.[A-Z_]+$`.
#### Scenario: Coded helper failure
- **WHEN** `assertMail()` finds no match
- **THEN** the rejection's `code === "INTEGRATION.MAILPIT.NO_MATCH"` AND `context.candidates` is an array