129 lines
4.3 KiB
TypeScript
129 lines
4.3 KiB
TypeScript
import { describe, test, expect } from "vitest";
|
|
import { Webhook } from "@trigger.dev/core/v3/schemas";
|
|
import { generateErrorGroupWebhookPayload } from "~/v3/services/alerts/errorGroupWebhook.server";
|
|
|
|
type ErrorData = {
|
|
fingerprint: string;
|
|
environmentId: string;
|
|
environmentName: string;
|
|
taskIdentifier: string;
|
|
errorType: string;
|
|
errorMessage: string;
|
|
sampleStackTrace: string;
|
|
firstSeen: string;
|
|
lastSeen: string;
|
|
occurrenceCount: number;
|
|
};
|
|
|
|
const TEST_ORG = { id: "org_test_123", slug: "webhook-test-org", name: "Webhook Test Org" };
|
|
const TEST_PROJECT = {
|
|
id: "proj_test_456",
|
|
externalRef: "proj_webhook_test",
|
|
slug: "webhook-test-project",
|
|
name: "Webhook Test Project",
|
|
};
|
|
const DASHBOARD_URL = "https://cloud.trigger.dev/test";
|
|
|
|
function createMockError(overrides: Partial<ErrorData> = {}): ErrorData {
|
|
return {
|
|
fingerprint: "fp_test_default",
|
|
environmentId: "env_test_dev",
|
|
environmentName: "Development",
|
|
taskIdentifier: "process-payment",
|
|
errorType: "TypeError",
|
|
errorMessage: "Cannot read property 'id' of undefined",
|
|
sampleStackTrace: `TypeError: Cannot read property 'id' of undefined
|
|
at processPayment (src/tasks/payment.ts:42:15)
|
|
at Object.run (src/tasks/payment.ts:15:20)
|
|
at TaskExecutor.execute (node_modules/@trigger.dev/core/dist/index.js:234:18)`,
|
|
firstSeen: Date.now().toString(),
|
|
lastSeen: Date.now().toString(),
|
|
occurrenceCount: 42,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function buildPayload(classification: "new_issue" | "regression" | "unignored", error: ErrorData) {
|
|
return generateErrorGroupWebhookPayload({
|
|
classification,
|
|
error,
|
|
organization: TEST_ORG,
|
|
project: TEST_PROJECT,
|
|
dashboardUrl: DASHBOARD_URL,
|
|
});
|
|
}
|
|
|
|
describe("Webhook Error Alert Payload", () => {
|
|
test("payload structure is valid and parseable", () => {
|
|
const payload = buildPayload("new_issue", createMockError());
|
|
const parsed = Webhook.parse(payload);
|
|
|
|
expect(parsed.type).toBe("alert.error");
|
|
if (parsed.type === "alert.error") {
|
|
expect(parsed.object.classification).toBe("new_issue");
|
|
expect(parsed.object.error.type).toBe("TypeError");
|
|
expect(parsed.object.organization.slug).toBe("webhook-test-org");
|
|
expect(parsed.object.project.ref).toBe("proj_webhook_test");
|
|
}
|
|
});
|
|
|
|
test("payload survives JSON round-trip", () => {
|
|
const error = createMockError();
|
|
const payload = buildPayload("regression", error);
|
|
|
|
const deserialized = JSON.parse(JSON.stringify(payload));
|
|
const parsed = Webhook.parse(deserialized);
|
|
|
|
expect(parsed.type).toBe("alert.error");
|
|
if (parsed.type === "alert.error") {
|
|
expect(parsed.object.classification).toBe("regression");
|
|
expect(parsed.object.error.fingerprint).toBe(error.fingerprint);
|
|
}
|
|
});
|
|
|
|
test("all classifications are valid", () => {
|
|
const classifications = ["new_issue", "regression", "unignored"] as const;
|
|
|
|
for (const classification of classifications) {
|
|
const payload = buildPayload(classification, createMockError());
|
|
const parsed = Webhook.parse(payload);
|
|
if (parsed.type === "alert.error") {
|
|
expect(parsed.object.classification).toBe(classification);
|
|
}
|
|
}
|
|
});
|
|
|
|
test("error details are preserved", () => {
|
|
const error = createMockError({
|
|
fingerprint: "fp_custom_123",
|
|
errorType: "CustomError",
|
|
errorMessage: "Custom error message",
|
|
sampleStackTrace: "CustomError: at line 42",
|
|
taskIdentifier: "my-custom-task",
|
|
occurrenceCount: 999,
|
|
});
|
|
|
|
const payload = buildPayload("new_issue", error);
|
|
const parsed = Webhook.parse(payload);
|
|
|
|
if (parsed.type === "alert.error") {
|
|
expect(parsed.object.error.fingerprint).toBe("fp_custom_123");
|
|
expect(parsed.object.error.type).toBe("CustomError");
|
|
expect(parsed.object.error.message).toBe("Custom error message");
|
|
expect(parsed.object.error.stackTrace).toBe("CustomError: at line 42");
|
|
expect(parsed.object.error.taskIdentifier).toBe("my-custom-task");
|
|
expect(parsed.object.error.occurrenceCount).toBe(999);
|
|
}
|
|
});
|
|
|
|
test("empty stack trace becomes undefined", () => {
|
|
const error = createMockError({ sampleStackTrace: "" });
|
|
const payload = buildPayload("new_issue", error);
|
|
const parsed = Webhook.parse(payload);
|
|
|
|
if (parsed.type === "alert.error") {
|
|
expect(parsed.object.error.stackTrace).toBeUndefined();
|
|
}
|
|
});
|
|
});
|