2.4 KiB
2.4 KiB
fake-smtp-client.ts
Purpose
Deterministic test double for SmtpClientLike. Tests queue scripted
responses, invoke the wrapped SmtpTransport, and inspect the captured
calls — without vi.mock, without a real SMTP server, and without
monkey-patching globals. Shipping the fake from the package itself means
downstream consumers can test their integration against the same exact
shape SmtpTransport produces, without re-implementing the protocol fake.
Features
push(response)queues a scripted outcome (infofor success,rejectfor failure).callscaptures everysendMailinvocation in FIFO order so assertions can target individual recipients / headers / attachments.scheduleVerify(true | false | Error)programs the nextverify()call.verifyCallsandcloseCallstrack lifecycle so tests can assert cleanup behaviour.
Usage: Basic
import { FakeSmtpClient } from "@suaveplan/emailer/fakes/fake-smtp-client";
import { SmtpTransport } from "@suaveplan/emailer/transports/smtp";
const adapter = new FakeSmtpClient();
adapter.push({ info: { messageId: "<abc@host>" } });
const transport = new SmtpTransport();
await transport.initialize({ client: adapter });
const r = await transport.send({ to: "u@x.y", subject: "hi", text: "hi" });
Usage: Advanced
import { FakeSmtpClient } from "@suaveplan/emailer/fakes/fake-smtp-client";
const adapter = new FakeSmtpClient();
adapter.push({ reject: new Error("connection refused") });
adapter.push({ info: { messageId: "<1@h>", accepted: ["a@b.c"] } });
adapter.scheduleVerify(true);
// First send fails, second succeeds, verify returns true.
API Reference
class FakeSmtpClient implements SmtpClientLikeinterface FakeSmtpResponse { info?, reject? }interface FakeSmtpCall { payload: SmtpSendPayload }
Implementation Notes
- The fake is intentionally minimal — it carries no SMTP state, no auth,
no connection pool. The point is that
SmtpTransportonly ever calls three methods (sendMail,verify,close), and the fake reproduces exactly those three semantics with FIFO scripting. scheduleVerify(Error)is single-use — after the nextverifythrows, the reject queue clears. Subsequent calls return the most-recently scheduled boolean (defaulttrue).closeCallsincrements on everyclose, allowing tests to assert idempotent close behaviour without inspecting private state.