diff --git a/lib/email.test.ts b/lib/email.test.ts new file mode 100644 index 00000000..d15f1218 --- /dev/null +++ b/lib/email.test.ts @@ -0,0 +1,79 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { getGmailCredentials, sendEmail, type MailTransport } from "./email"; + +const env = { + GMAIL_SMTP_USER: "rjbulk01@gmail.com", + GMAIL_SMTP_APP_PASSWORD: "test-app-password", +}; + +test("getGmailCredentials rejects missing Gmail configuration", () => { + assert.throws( + () => getGmailCredentials({}), + { message: "Gmail email credentials are not configured" }, + ); +}); + +test("sendEmail uses the authenticated Gmail user and forwards the message", async () => { + const sent: unknown[] = []; + const transport: MailTransport = { + async sendMail(message) { + sent.push(message); + return {}; + }, + }; + + await sendEmail( + { + to: "customer@example.com", + subject: "Delivery statement", + html: "
Statement attached.
", + attachments: [ + { + filename: "statement.pdf", + content: Buffer.from("pdf-bytes"), + }, + ], + }, + { env, transport }, + ); + + assert.deepEqual(sent, [ + { + from: "rjbulk01@gmail.com", + to: "customer@example.com", + subject: "Delivery statement", + html: "Statement attached.
", + attachments: [ + { + filename: "statement.pdf", + content: Buffer.from("pdf-bytes"), + }, + ], + }, + ]); +}); + +test("sendEmail hides provider error details and credentials", async () => { + const secret = "test-app-password"; + const transport: MailTransport = { + async sendMail() { + throw new Error(`authentication failed for ${secret}`); + }, + }; + + await assert.rejects( + sendEmail( + { + to: "customer@example.com", + subject: "Delivery statement", + html: "Statement attached.
", + }, + { env, transport }, + ), + (error: unknown) => + error instanceof Error && + error.message === "Gmail email send failed" && + !error.message.includes(secret), + ); +});