zerobyte/app/server/modules/notifications/__tests__/notifications.service.test.ts
Nico f83b765d04
Some checks failed
Release Workflow / determine-release-type (push) Has been cancelled
Release Workflow / checks (push) Has been cancelled
Release Workflow / e2e-tests (push) Has been cancelled
Release Workflow / build-images (push) Has been cancelled
Release Workflow / publish-release (push) Has been cancelled
Release Workflow / request-docs-version-update (push) Has been cancelled
fix(notifications): persist delivery health status (#850)
* fix(notifications): persist delivery health status

* fix: pr feedback double update
2026-05-02 11:51:16 +02:00

86 lines
2.8 KiB
TypeScript

import { afterEach, describe, expect, test, vi } from "vitest";
import { db } from "~/server/db/db";
import { notificationDestinationsTable } from "~/server/db/schema";
import { withContext } from "~/server/core/request-context";
import { createTestSession } from "~/test/helpers/auth";
import * as shoutrrr from "~/server/utils/shoutrrr";
import { notificationsService } from "../notifications.service";
import { serverEvents } from "~/server/core/events";
afterEach(() => {
vi.restoreAllMocks();
});
describe("notificationsService.testDestination", () => {
test("marks the destination as error when delivery fails", async () => {
const { organizationId, user } = await createTestSession();
await withContext({ organizationId, userId: user.id }, async () => {
const destination = await notificationsService.createDestination("Broken webhook", {
type: "custom",
shoutrrrUrl: "discord://token@webhookid",
});
vi.spyOn(shoutrrr, "sendNotification").mockResolvedValue({ success: false, error: "webhook rejected" });
const event = new Promise<Parameters<typeof serverEvents.emit>[1]>((resolve) => {
serverEvents.once("notification:updated", resolve);
});
await expect(notificationsService.testDestination(destination.id)).rejects.toThrow("webhook rejected");
await expect(event).resolves.toEqual(
expect.objectContaining({
organizationId,
notificationId: destination.id,
notificationName: "Broken webhook",
status: "error",
}),
);
const updated = await db.query.notificationDestinationsTable.findFirst({
where: { id: destination.id },
});
expect(updated).toEqual(
expect.objectContaining({
status: "error",
lastChecked: expect.any(Number),
lastError: expect.stringContaining("webhook rejected"),
}),
);
});
});
test("marks the destination as healthy when delivery succeeds", async () => {
const { organizationId, user } = await createTestSession();
await withContext({ organizationId, userId: user.id }, async () => {
const [destination] = await db
.insert(notificationDestinationsTable)
.values({
name: "Recovered webhook",
type: "custom",
config: { type: "custom", shoutrrrUrl: "discord://token@webhookid" },
organizationId,
status: "error",
lastError: "previous failure",
})
.returning();
vi.spyOn(shoutrrr, "sendNotification").mockResolvedValue({ success: true });
await expect(notificationsService.testDestination(destination.id)).resolves.toEqual({ success: true });
const updated = await db.query.notificationDestinationsTable.findFirst({
where: { id: destination.id },
});
expect(updated).toEqual(
expect.objectContaining({
status: "healthy",
lastChecked: expect.any(Number),
lastError: null,
}),
);
});
});
});