Repository Public
1import { appendFileSync } from "node:fs";2import { SMTPServer } from "smtp-server";3import { simpleParser } from "mailparser";45/**6 * Test-only SMTP server for the end-to-end run: accepts every message on 127.0.0.1 and appends what it received7 * (parsed) to a JSON-lines file. The application talks to it through its real SMTP transport.8 * Recipients starting with "bounce@" are rejected, to exercise the retry path.9 */10const file = process.env.SPCAL_TEST_OUTBOX ?? "/tmp/spcal-e2e-outbox.jsonl";11const port = Number(process.env.SMTP_SINK_PORT ?? 2526);12const server = new SMTPServer({13 authOptional: true, disabledCommands: ["STARTTLS"],14 onRcptTo(address, _session, cb) { cb(address.address.startsWith("bounce@") ? Object.assign(new Error("mailbox unavailable"), { responseCode: 550 }) : undefined); },15 onData(stream, _session, cb) {16 simpleParser(stream).then((m) => {17 const cal = m.attachments.find((a) => a.contentType.startsWith("text/calendar"));18 const to = Array.isArray(m.to) ? m.to[0]?.value[0]?.address : m.to?.value[0]?.address;19 appendFileSync(file, JSON.stringify({20 to, subject: m.subject, text: m.text ?? "", html: typeof m.html === "string" ? m.html : "", replyTo: m.replyTo?.value[0]?.address ?? null,21 ics: cal ? { method: /METHOD:(\w+)/.exec(cal.content.toString("utf8"))?.[1] ?? "", content: cal.content.toString("utf8") } : null,22 }) + "\n");23 cb();24 }, (e) => cb(e));25 },26});27server.listen(port, "127.0.0.1", () => console.log(`smtp sink on 127.0.0.1:${port} → ${file}`));28process.on("SIGTERM", () => server.close(() => process.exit(0)));29