feat(marketing-pages): sendWelcomeEmail job + handler enqueues it
sendWelcomeEmailJob takes IMailerService, validates the typed input (userId + email), and delegates to mailer.sendWelcome. The onAuthUserSignedUpHandler now takes IJobQueue and enqueues "marketing-pages.send-welcome-email" with the event payload. Both binders bind a RecordingMailerService at the IMailerService symbol (production placeholder until a real adapter ships) and pass mailer / queue into the wrapped factories. Dev-seed additionally queue.register()s the slug so the in-memory queue dispatches via the wrapped job; production relies on the generated Payload event-task to resolve the wrapped handler from the container.
This commit is contained in:
@@ -1,15 +1,25 @@
|
||||
// packages/marketing-pages/src/events/handlers/on-auth-user-signed-up.handler.test.ts
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { RecordingJobQueue } from "@repo/core-testing/instrumentation";
|
||||
import { onAuthUserSignedUpHandler } from "@/events/handlers/on-auth-user-signed-up.handler";
|
||||
|
||||
describe("onAuthUserSignedUpHandler", () => {
|
||||
it("returns a function (factory shape)", () => {
|
||||
const handler = onAuthUserSignedUpHandler();
|
||||
expect(typeof handler).toBe("function");
|
||||
});
|
||||
it("enqueues marketing-pages.send-welcome-email with userId + email", async () => {
|
||||
const queue = new RecordingJobQueue();
|
||||
const handler = onAuthUserSignedUpHandler(queue);
|
||||
|
||||
it("does not throw on a valid stub event", async () => {
|
||||
const handler = onAuthUserSignedUpHandler();
|
||||
await expect(handler({} as never)).resolves.toBeUndefined();
|
||||
await handler({
|
||||
userId: "u-42",
|
||||
email: "alice@example.com",
|
||||
signedUpAt: "2026-05-08T12:00:00.000Z",
|
||||
});
|
||||
|
||||
expect(queue.enqueued).toHaveLength(1);
|
||||
expect(queue.enqueued[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
taskSlug: "marketing-pages.send-welcome-email",
|
||||
input: { userId: "u-42", email: "alice@example.com" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
// packages/marketing-pages/src/events/handlers/on-auth-user-signed-up.handler.ts
|
||||
import type { UserSignedUpEvent } from "@repo/auth";
|
||||
import type { IJobQueue } from "@repo/core-shared/jobs";
|
||||
|
||||
export type IOnAuthUserSignedUpHandler = ReturnType<
|
||||
typeof onAuthUserSignedUpHandler
|
||||
>;
|
||||
|
||||
export const onAuthUserSignedUpHandler =
|
||||
() =>
|
||||
async (_event: UserSignedUpEvent): Promise<void> => {
|
||||
// TODO: implement the reaction. Inject dependencies via the factory's
|
||||
// constructor and use them here. The handler runs inside the consumer's
|
||||
// span+capture sandwich, so just throwing on failure is the right shape.
|
||||
(queue: IJobQueue) =>
|
||||
async (event: UserSignedUpEvent): Promise<void> => {
|
||||
await queue.enqueue("marketing-pages.send-welcome-email", {
|
||||
userId: event.userId,
|
||||
email: event.email,
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user