125 lines
3.8 KiB
TypeScript
125 lines
3.8 KiB
TypeScript
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
import { buildRetryJobOptions } from "@hivegrid/queue";
|
|
import { buildLegacyEventWebhookAttemptDeliveryJob, queueLegacyEventWebhookAttemptDelivery } from "./webhookDeliveryQueue.js";
|
|
import type { LegacyEventWebhookAttempt } from "@hivegrid/db/event-webhook-attempts.server";
|
|
|
|
|
|
const attempt = {
|
|
id: "attempt_123",
|
|
endpoint: "https://example.test/webhooks/events",
|
|
tenantId: "tenant_123",
|
|
} satisfies LegacyEventWebhookAttempt;
|
|
|
|
const body = {
|
|
id: "evt_123",
|
|
type: "member.created",
|
|
};
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("buildLegacyEventWebhookAttemptDeliveryJob", () => {
|
|
test("builds a deliver-webhook descriptor for legacy attempts", () => {
|
|
expect(buildLegacyEventWebhookAttemptDeliveryJob(attempt, body, "req_webhook_123")).toEqual({
|
|
queueName: "hivegrid.email",
|
|
jobName: "deliver-webhook",
|
|
payload: {
|
|
kind: "webhook.deliver",
|
|
url: attempt.endpoint,
|
|
method: "POST",
|
|
eventName: "legacy.event_webhook_attempt",
|
|
body,
|
|
tenantId: attempt.tenantId,
|
|
metadata: {
|
|
legacyAttemptId: attempt.id,
|
|
legacyAttemptTenantId: attempt.tenantId,
|
|
requestId: "req_webhook_123",
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
test("omits requestId metadata when requestId is absent", () => {
|
|
expect(buildLegacyEventWebhookAttemptDeliveryJob(attempt, body).payload.metadata).toEqual({
|
|
legacyAttemptId: attempt.id,
|
|
legacyAttemptTenantId: attempt.tenantId,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("queueLegacyEventWebhookAttemptDelivery", () => {
|
|
test("returns typed non-enqueued result when Redis env is absent", async () => {
|
|
const result = await queueLegacyEventWebhookAttemptDelivery({
|
|
attempt,
|
|
body,
|
|
env: {},
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
enqueued: false,
|
|
reason: "missing-redis-connection",
|
|
});
|
|
});
|
|
|
|
test("enqueues on an injected queue without touching process environment", async () => {
|
|
const queue = buildQueue();
|
|
const job = { id: "job_123" };
|
|
queue.add.mockResolvedValue(job);
|
|
|
|
const result = await queueLegacyEventWebhookAttemptDelivery({
|
|
attempt,
|
|
body,
|
|
requestId: "req_enqueue_123",
|
|
queue,
|
|
});
|
|
|
|
const descriptor = buildLegacyEventWebhookAttemptDeliveryJob(attempt, body, "req_enqueue_123");
|
|
expect(queue.add).toHaveBeenCalledWith(
|
|
"deliver-webhook",
|
|
descriptor.payload,
|
|
// priority is resolved from attempt.tenantId; without a registered
|
|
// plan lookup it falls back to NORMAL (5).
|
|
{ ...buildRetryJobOptions(descriptor), removeOnComplete: 10_000, jobId: attempt.id, priority: 5 }
|
|
);
|
|
expect(result).toEqual({
|
|
enqueued: true,
|
|
job,
|
|
});
|
|
expect(queue.close).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test("propagates enqueue failures for injected queues", async () => {
|
|
const error = new Error("enqueue failed");
|
|
const queue = buildQueue();
|
|
queue.add.mockRejectedValue(error);
|
|
|
|
await expect(
|
|
queueLegacyEventWebhookAttemptDelivery({
|
|
attempt,
|
|
body,
|
|
queue,
|
|
})
|
|
).rejects.toThrow(error);
|
|
const descriptor = buildLegacyEventWebhookAttemptDeliveryJob(attempt, body);
|
|
expect(queue.add).toHaveBeenCalledWith(
|
|
"deliver-webhook",
|
|
expect.objectContaining({
|
|
kind: "webhook.deliver",
|
|
}),
|
|
{ ...buildRetryJobOptions(descriptor), removeOnComplete: 10_000, jobId: attempt.id, priority: 5 }
|
|
);
|
|
expect(queue.close).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
function buildQueue() {
|
|
return {
|
|
add: vi.fn(),
|
|
close: vi.fn().mockResolvedValue(undefined),
|
|
} as unknown as Parameters<typeof queueLegacyEventWebhookAttemptDelivery>[0]["queue"] & {
|
|
add: ReturnType<typeof vi.fn>;
|
|
close: ReturnType<typeof vi.fn>;
|
|
};
|
|
}
|