145 lines
3.8 KiB
TypeScript
145 lines
3.8 KiB
TypeScript
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
import { buildRetryJobOptions, buildTransactionalMailJob, queueNames, type TypedQueue } from "@toolctrl/queue";
|
|
import { sendEmailMailjet } from "./MailjetEmailService";
|
|
|
|
const runtimeQueue = {
|
|
add: vi.fn(async () => ({ id: "job_from_env" })),
|
|
close: vi.fn(async () => undefined),
|
|
};
|
|
|
|
vi.mock("@toolctrl/queue/runtime", () => ({
|
|
createQueue: vi.fn(() => runtimeQueue),
|
|
}));
|
|
|
|
afterEach(() => {
|
|
vi.clearAllMocks();
|
|
delete process.env.BULLMQ_REDIS_URL;
|
|
delete process.env.REDIS_URL;
|
|
});
|
|
|
|
function createQueueMock() {
|
|
return {
|
|
add: vi.fn(async () => ({ id: "job_123" })),
|
|
} as unknown as TypedQueue<typeof queueNames.email> & {
|
|
add: ReturnType<typeof vi.fn>;
|
|
};
|
|
}
|
|
|
|
describe("sendEmailMailjet", () => {
|
|
test("enqueues transactional email through the default queue provider", async () => {
|
|
const queue = createQueueMock();
|
|
|
|
const result = await sendEmailMailjet(
|
|
{
|
|
data: {
|
|
to: "user@example.com",
|
|
subject: "Welcome",
|
|
body: "<p>Hello</p>",
|
|
alias: "registration-welcome",
|
|
data: { firstName: "Ada" },
|
|
},
|
|
},
|
|
{
|
|
queue,
|
|
}
|
|
);
|
|
|
|
expect(result).toEqual({
|
|
provider: "queue",
|
|
status: "queued",
|
|
messageId: "job_123",
|
|
warnings: {
|
|
queueName: queueNames.email,
|
|
jobName: "send-transactional-email",
|
|
},
|
|
});
|
|
|
|
const descriptor = buildTransactionalMailJob({
|
|
to: ["user@example.com"],
|
|
subject: "Welcome",
|
|
html: "<p>Hello</p>",
|
|
templateData: { firstName: "Ada" },
|
|
metadata: { alias: "registration-welcome" },
|
|
});
|
|
expect(queue.add).toHaveBeenCalledWith(
|
|
"send-transactional-email",
|
|
descriptor.payload,
|
|
buildRetryJobOptions(descriptor)
|
|
);
|
|
});
|
|
|
|
test("fails closed when Redis is missing without falling back to synchronous mail", async () => {
|
|
await expect(sendEmailMailjet(
|
|
{
|
|
data: {
|
|
to: "user@example.com",
|
|
subject: "Welcome",
|
|
body: "<p>Hello</p>",
|
|
alias: "registration-welcome",
|
|
data: { firstName: "Ada" },
|
|
},
|
|
},
|
|
{
|
|
env: {},
|
|
}
|
|
)).rejects.toMatchObject({
|
|
provider: "queue",
|
|
message: "Set BULLMQ_REDIS_URL or REDIS_URL to enqueue transactional email jobs.",
|
|
responseBody: {
|
|
code: "missing_redis_connection",
|
|
message: "Set BULLMQ_REDIS_URL or REDIS_URL to enqueue transactional email jobs.",
|
|
requiredEnv: ["BULLMQ_REDIS_URL", "REDIS_URL"],
|
|
},
|
|
});
|
|
});
|
|
|
|
test("uses process Redis configuration by default", async () => {
|
|
process.env.BULLMQ_REDIS_URL = "redis://localhost:6379/2";
|
|
|
|
const result = await sendEmailMailjet({
|
|
data: {
|
|
to: "user@example.com",
|
|
subject: "Welcome",
|
|
body: "<p>Hello</p>",
|
|
alias: "registration-welcome",
|
|
data: { firstName: "Ada" },
|
|
},
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
provider: "queue",
|
|
status: "queued",
|
|
messageId: "job_from_env",
|
|
warnings: {
|
|
queueName: queueNames.email,
|
|
jobName: "send-transactional-email",
|
|
},
|
|
});
|
|
expect(runtimeQueue.add).toHaveBeenCalledOnce();
|
|
expect(runtimeQueue.close).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
test("propagates enqueue failures to the caller", async () => {
|
|
const queue = createQueueMock();
|
|
const error = new Error("queue add failed");
|
|
queue.add.mockRejectedValueOnce(error);
|
|
|
|
await expect(
|
|
sendEmailMailjet(
|
|
{
|
|
data: {
|
|
to: "user@example.com",
|
|
subject: "Welcome",
|
|
body: "<p>Hello</p>",
|
|
alias: "registration-welcome",
|
|
data: { firstName: "Ada" },
|
|
},
|
|
},
|
|
{
|
|
queue,
|
|
}
|
|
)
|
|
).rejects.toBe(error);
|
|
});
|
|
});
|