Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
7.1 kB ยท 216 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217import "./test-preload.ts";import { db, eq } from "@openstatus/db";import { pageSubscriber, pageSubscriberToPageComponent,} from "@openstatus/db/src/schema";import { EmailClient } from "@openstatus/emails";import { expect } from "@std/expect";import { afterAll, afterEach, beforeAll, beforeEach, describe, test,} from "@std/testing/bdd";import { assertSpyCalls, type Stub, stub } from "@std/testing/mock";
import { dispatchPageUpdate } from "./dispatcher";import type { PageUpdate } from "./types";
// RESEND_API_KEY is set in test-preload.ts (see bunfig.toml) so @openstatus/emails// loads successfully and EmailClient prototype methods can be spied on.
let sendStatusReportUpdateMock: Stub<EmailClient>;let rejectNextSend: Error | null = null;
// IDs present in the seeded databaseconst PAGE_ID = 1; // slug: "status"const COMPONENT_1 = 1;const COMPONENT_2 = 2;
const EMAILS = { entirePage: "dispatcher-page-test@example.com", component1: "dispatcher-comp1-test@example.com", component2: "dispatcher-comp2-test@example.com",};
// Captured IDs after insertlet _subEntirePageId: number;let subComponent1Id: number;let subComponent2Id: number;
function makePageUpdate(overrides: Partial<PageUpdate> = {}): PageUpdate { return { id: 1, pageId: PAGE_ID, title: "Test Incident", status: "investigating", message: "We are investigating.", pageComponentIds: [], pageComponents: [], date: new Date().toISOString(), ...overrides, };}
async function cleanAll() { for (const email of Object.values(EMAILS)) { await db.delete(pageSubscriber).where(eq(pageSubscriber.email, email)); }}
beforeAll(async () => { await cleanAll();
const insertAccepted = async (email: string) => { return db .insert(pageSubscriber) .values({ channelType: "email", email, pageId: PAGE_ID, token: crypto.randomUUID(), acceptedAt: new Date(), expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), }) .returning() .get(); };
const subEntirePage = await insertAccepted(EMAILS.entirePage); const subComponent1 = await insertAccepted(EMAILS.component1); const subComponent2 = await insertAccepted(EMAILS.component2);
_subEntirePageId = subEntirePage.id; subComponent1Id = subComponent1.id; subComponent2Id = subComponent2.id;
// subEntirePage has no component associations โ entire-page scope // subComponent1 subscribes to component 1 only await db .insert(pageSubscriberToPageComponent) .values({ pageSubscriberId: subComponent1Id, pageComponentId: COMPONENT_1 }) .run();
// subComponent2 subscribes to component 2 only await db .insert(pageSubscriberToPageComponent) .values({ pageSubscriberId: subComponent2Id, pageComponentId: COMPONENT_2 }) .run();});
afterAll(cleanAll);
beforeEach(() => { rejectNextSend = null; sendStatusReportUpdateMock = stub( EmailClient.prototype, "sendStatusReportUpdate", () => { if (rejectNextSend) { const error = rejectNextSend; rejectNextSend = null; return Promise.reject(error); } return Promise.resolve(undefined); }, );});
afterEach(() => { sendStatusReportUpdateMock.restore();});
// โโโ dispatchPageUpdate - component filtering โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
describe("dispatchPageUpdate - component filtering", () => { test("notifies entire-page and component-1 subscriber when update affects component 1", async () => { await dispatchPageUpdate( makePageUpdate({ pageComponentIds: [COMPONENT_1] }), );
assertSpyCalls(sendStatusReportUpdateMock, 1); const { subscribers } = sendStatusReportUpdateMock.calls[0].args[0]; const emails = subscribers.map((s: { email: string }) => s.email);
expect(emails).toContain(EMAILS.entirePage); expect(emails).toContain(EMAILS.component1); expect(emails).not.toContain(EMAILS.component2); });
test("notifies entire-page and component-2 subscriber when update affects component 2", async () => { await dispatchPageUpdate( makePageUpdate({ pageComponentIds: [COMPONENT_2] }), );
const { subscribers } = sendStatusReportUpdateMock.calls[0].args[0]; const emails = subscribers.map((s: { email: string }) => s.email);
expect(emails).toContain(EMAILS.entirePage); expect(emails).toContain(EMAILS.component2); expect(emails).not.toContain(EMAILS.component1); });
test("notifies all subscribers when update affects both components", async () => { await dispatchPageUpdate( makePageUpdate({ pageComponentIds: [COMPONENT_1, COMPONENT_2] }), );
const { subscribers } = sendStatusReportUpdateMock.calls[0].args[0]; const emails = subscribers.map((s: { email: string }) => s.email);
expect(emails).toContain(EMAILS.entirePage); expect(emails).toContain(EMAILS.component1); expect(emails).toContain(EMAILS.component2); });
test("notifies only the entire-page subscriber when update has no affected components", async () => { await dispatchPageUpdate(makePageUpdate({ pageComponentIds: [] }));
assertSpyCalls(sendStatusReportUpdateMock, 1); const { subscribers } = sendStatusReportUpdateMock.calls[0].args[0]; const emails = subscribers.map((s: { email: string }) => s.email);
// Assert only against this suite's own subscribers โ package test suites // run in parallel against the same seeded page, so an exact count over // all recipients races with concurrently inserted subscribers. const ownEmails = emails.filter((e: string) => Object.values(EMAILS).includes(e), ); expect(ownEmails).toEqual([EMAILS.entirePage]); });
test("does not notify component subscribers when update affects a different component", async () => { // Only component 1 is affected โ component 2 subscriber should be skipped await dispatchPageUpdate( makePageUpdate({ pageComponentIds: [COMPONENT_1] }), );
const { subscribers } = sendStatusReportUpdateMock.calls[0].args[0]; const emails = subscribers.map((s: { email: string }) => s.email);
expect(emails).not.toContain(EMAILS.component2); });});
// โโโ dispatchPageUpdate - edge cases โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
describe("dispatchPageUpdate - edge cases", () => { test("does not call sendNotifications for a non-existent page", async () => { await dispatchPageUpdate( makePageUpdate({ pageId: 99999, pageComponentIds: [COMPONENT_1] }), );
assertSpyCalls(sendStatusReportUpdateMock, 0); });
test("does not propagate channel failure โ resolves even when sendStatusReportUpdate throws", async () => { rejectNextSend = new Error("SMTP failure");
await expect( dispatchPageUpdate(makePageUpdate({ pageComponentIds: [] })), ).resolves.toBeUndefined(); });});