539d063c22
The first end-to-end smoke spec, run live against the Tier-1 stack
(§22 M3-0 Task 7). Drives the §6.2 email OTC sign-in: loads the app,
requests a code, reads it from Mailpit, verifies it, and asserts the
verify returns ok:true and sets the rfc_session cookie.
Supporting harness fixes:
- Makefile: add .PHONY so `make e2e` actually runs (the e2e/ directory
was shadowing the target, making it a no-op).
- mailpit.js: guard the per-message detail fetch (if !full.ok continue)
and scan the plain-text part before HTML so the \d{6} match can't
latch onto a stray number.
- spec uses a unique per-run email to avoid the per-email request
cooldown (OTC_REQUEST_COOLDOWN_SECONDS) on re-runs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
33 lines
1.3 KiB
JavaScript
33 lines
1.3 KiB
JavaScript
const MAILSINK = process.env.MAILSINK_URL || 'http://localhost:8025'
|
|
|
|
export async function waitForLatestOtc(toAddress, { attempts = 20, delayMs = 500 } = {}) {
|
|
for (let i = 0; i < attempts; i++) {
|
|
const res = await fetch(`${MAILSINK}/api/v1/messages`)
|
|
if (res.ok) {
|
|
const data = await res.json()
|
|
const msg = (data.messages || []).find(
|
|
(m) => (m.To || []).some((t) => t.Address === toAddress),
|
|
)
|
|
if (msg) {
|
|
const full = await fetch(`${MAILSINK}/api/v1/message/${msg.ID}`)
|
|
if (!full.ok) continue
|
|
const body = await full.json()
|
|
// Search the plain-text part first — the OTC mail is plain text
|
|
// (see backend/app/email_otc.py), and scanning Text before HTML
|
|
// keeps the \d{6} match from latching onto a stray number that a
|
|
// future HTML template might carry (style widths, year, etc.).
|
|
const text = body.Text || ''
|
|
const html = body.HTML || ''
|
|
const code = text.match(/\b(\d{6})\b/) || html.match(/\b(\d{6})\b/)
|
|
if (code) return code[1]
|
|
}
|
|
}
|
|
await new Promise((r) => setTimeout(r, delayMs))
|
|
}
|
|
throw new Error(`no OTC email for ${toAddress} arrived in Mailpit`)
|
|
}
|
|
|
|
export async function clearMailpit() {
|
|
await fetch(`${MAILSINK}/api/v1/messages`, { method: 'DELETE' })
|
|
}
|