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.
TypeScript
at incident-api
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998import { eq } from "@openstatus/db";import { monitor, monitorStatusTable, privateLocation, privateLocationToMonitors,} from "@openstatus/db/src/schema";import { expect } from "@std/expect";import { beforeAll, describe, test } from "@std/testing/bdd";
import { createWorkspaceFixture, makeUserCtx, withTestTransaction,} from "../../../test/helpers";import type { ServiceContext } from "../../context";import { ForbiddenError, NotFoundError, ValidationError } from "../../errors";import { createMonitor } from "../create";import { fetchMonitorDailyStats } from "../get-daily-summary";import { getMonitorStatus } from "../get-monitor-status";import { getMonitorSummary } from "../get-monitor-summary";import { getResponseLog } from "../get-response-log";import { listResponseLogs } from "../list-response-logs";import { listResponseLogsInfinite } from "../list-response-logs-infinite";import { getPrivateLocationIdsByMonitor } from "../private-locations";
const TEST_PREFIX = "svc-monitor-reads-test";
let teamCtx: ServiceContext;let freeCtx: ServiceContext;
beforeAll(async () => { const team = (await createWorkspaceFixture("team")).workspace; const free = (await createWorkspaceFixture("free")).workspace; teamCtx = makeUserCtx(team, { userId: 1 }); freeCtx = makeUserCtx(free, { userId: 2 });});
describe("getMonitorStatus", () => { test("throws NotFoundError for cross-workspace monitorId", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-cross-ws-status`, jobType: "http", url: "https://example.com", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); await expect( getMonitorStatus({ ctx: { ...freeCtx, db: tx }, input: { monitorId: row.id }, }), ).rejects.toBeInstanceOf(NotFoundError); }); });
test("returns empty regions when monitor has no configured regions", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-empty-regions`, jobType: "http", url: "https://example.com", method: "GET", headers: [], assertions: [], active: false, regions: [], }, }); const result = await getMonitorStatus({ ctx: { ...teamCtx, db: tx }, input: { monitorId: row.id }, }); expect(result).toEqual({ id: row.id, regions: [] }); }); });
test("returns one row per configured region (filtered to monitor.regions)", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-region-rows`, jobType: "http", url: "https://example.com", method: "GET", headers: [], assertions: [], active: true, regions: ["ams", "iad"], }, }); // Seed status rows: two configured + one stale (gru) that should be ignored. await tx.insert(monitorStatusTable).values([ { monitorId: row.id, region: "ams", status: "active" }, { monitorId: row.id, region: "iad", status: "error" }, { monitorId: row.id, region: "gru", status: "degraded" }, ]);
const result = await getMonitorStatus({ ctx: { ...teamCtx, db: tx }, input: { monitorId: row.id }, }); expect(result.id).toBe(row.id); const sorted = [...result.regions].sort((a, b) => a.region.localeCompare(b.region), ); expect(sorted).toEqual([ { region: "ams", status: "active" }, { region: "iad", status: "error" }, ]); }); });});
describe("getMonitorSummary", () => { test("throws NotFoundError for cross-workspace monitorId", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-cross-ws-summary`, jobType: "http", url: "https://example.com", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); await expect( getMonitorSummary({ ctx: { ...freeCtx, db: tx }, input: { monitorId: row.id, timeRange: "1d" }, }), ).rejects.toBeInstanceOf(NotFoundError); }); });
test("throws ValidationError for unsupported jobType (udp)", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-udp-summary`, jobType: "udp", url: "1.1.1.1", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); await expect( getMonitorSummary({ ctx: { ...teamCtx, db: tx }, input: { monitorId: row.id, timeRange: "1d" }, }), ).rejects.toBeInstanceOf(ValidationError); }); });
test("returns a summary for icmp jobType", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-icmp-summary`, jobType: "icmp", url: "1.1.1.1", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); const summary = await getMonitorSummary({ ctx: { ...teamCtx, db: tx }, input: { monitorId: row.id, timeRange: "1d" }, }); expect(summary.monitorId).toBe(row.id); }); });});
describe("fetchMonitorDailyStats", () => { const emptyTb = { httpStatus45d: () => Promise.resolve({ data: [] }), tcpStatus45d: () => Promise.resolve({ data: [] }), dnsStatus45d: () => Promise.resolve({ data: [] }), icmpStatus45d: () => Promise.resolve({ data: [] }), } as unknown as NonNullable<ServiceContext["tb"]>;
test("skips cross-workspace monitorId (returns empty, no throw)", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-cross-ws-daily`, jobType: "http", url: "https://example.com", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); const stats = await fetchMonitorDailyStats({ db: tx, tb: emptyTb, monitorIds: [row.id], workspaceId: freeCtx.workspace.id, }); expect(stats).toEqual([]); }); });
test("skips unknown monitorId (returns empty)", async () => { await withTestTransaction(async (tx) => { const stats = await fetchMonitorDailyStats({ db: tx, tb: emptyTb, monitorIds: [999_999_999], workspaceId: teamCtx.workspace.id, }); expect(stats).toEqual([]); }); });
test("skips unsupported jobType (udp)", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-udp-daily`, jobType: "udp", url: "1.1.1.1", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); const stats = await fetchMonitorDailyStats({ db: tx, tb: emptyTb, monitorIds: [row.id], workspaceId: teamCtx.workspace.id, }); expect(stats).toEqual([]); }); });
test("queries the icmp pipe for icmp jobType", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-icmp-daily`, jobType: "icmp", url: "1.1.1.1", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); const icmpTb = { httpStatus45d: () => Promise.resolve({ data: [] }), tcpStatus45d: () => Promise.resolve({ data: [] }), dnsStatus45d: () => Promise.resolve({ data: [] }), icmpStatus45d: ({ monitorIds }: { monitorIds: string[] }) => Promise.resolve({ data: monitorIds.map((monitorId) => ({ day: "2024-01-01T00:00:00.000Z", count: 1, ok: 1, degraded: 0, error: 0, monitorId, })), }), } as unknown as NonNullable<ServiceContext["tb"]>; const stats = await fetchMonitorDailyStats({ db: tx, tb: icmpTb, monitorIds: [row.id], workspaceId: teamCtx.workspace.id, }); expect(stats).toHaveLength(1); expect(stats[0]?.monitorId).toBe(String(row.id)); }); });
test("queries each pipe with its own job type's ids and merges the rows", async () => { await withTestTransaction(async (tx) => { const httpMon = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-daily-http`, jobType: "http", url: "https://example.com", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); const tcpMon = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-daily-tcp`, jobType: "tcp", url: "example.com:443", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, });
const today = new Date( Math.floor(Date.now() / 86_400_000) * 86_400_000, ).toISOString(); const bucket = (monitorId: number) => ({ day: today, count: 10, ok: 9, degraded: 1, error: 0, monitorId: String(monitorId), });
const queried: Record<string, string[]> = {}; const fakeTb = { httpStatus45d: ({ monitorIds }: { monitorIds: string[] }) => { queried.http = monitorIds; return Promise.resolve({ data: [bucket(httpMon.id)] }); }, tcpStatus45d: ({ monitorIds }: { monitorIds: string[] }) => { queried.tcp = monitorIds; return Promise.resolve({ data: [bucket(tcpMon.id)] }); }, dnsStatus45d: () => Promise.resolve({ data: [] }), } as unknown as NonNullable<ServiceContext["tb"]>;
const stats = await fetchMonitorDailyStats({ db: tx, tb: fakeTb, monitorIds: [httpMon.id, tcpMon.id], workspaceId: teamCtx.workspace.id, });
expect(queried.http).toEqual([String(httpMon.id)]); expect(queried.tcp).toEqual([String(tcpMon.id)]); expect(stats).toEqual([bucket(httpMon.id), bucket(tcpMon.id)]); }); });});
describe("getResponseLog", () => { test("throws ForbiddenError when plan disables response-logs", async () => { await withTestTransaction(async (tx) => { // Free plan: limits["response-logs"] === false. Guard fires before // any monitor lookup, so a fake id is fine. await expect( getResponseLog({ ctx: { ...freeCtx, db: tx }, input: { monitorId: 999_999, logId: "noop" }, }), ).rejects.toBeInstanceOf(ForbiddenError); }); });
test("throws ValidationError for non-http monitor", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-tcp-detail`, jobType: "tcp", url: "example.com:443", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); await expect( getResponseLog({ ctx: { ...teamCtx, db: tx }, input: { monitorId: row.id, logId: "noop" }, }), ).rejects.toBeInstanceOf(ValidationError); }); });
test("throws NotFoundError for unknown monitorId in own workspace", async () => { await withTestTransaction(async (tx) => { await expect( getResponseLog({ ctx: { ...teamCtx, db: tx }, input: { monitorId: 999_999_999, logId: "noop" }, }), ).rejects.toBeInstanceOf(NotFoundError); }); });});
describe("listResponseLogs", () => { test("throws ForbiddenError when plan disables response-logs", async () => { await withTestTransaction(async (tx) => { await expect( listResponseLogs({ ctx: { ...freeCtx, db: tx }, input: { monitorId: 999_999, limit: 10, offset: 0 }, }), ).rejects.toBeInstanceOf(ForbiddenError); }); });
test("throws ValidationError for non-http monitor", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-dns-list`, jobType: "dns", url: "example.com", method: "GET", headers: [], assertions: [ { version: "v1", type: "dnsRecord", key: "A", compare: "eq", target: "1.1.1.1", }, ], active: false, regions: ["ams"], }, }); await expect( listResponseLogs({ ctx: { ...teamCtx, db: tx }, input: { monitorId: row.id, limit: 10, offset: 0 }, }), ).rejects.toBeInstanceOf(ValidationError); }); });
test("throws NotFoundError for unknown monitorId in own workspace", async () => { await withTestTransaction(async (tx) => { await expect( listResponseLogs({ ctx: { ...teamCtx, db: tx }, input: { monitorId: 999_999_999, limit: 10, offset: 0 }, }), ).rejects.toBeInstanceOf(NotFoundError); }); });});
describe("getPrivateLocationIdsByMonitor", () => { async function createHttpMonitor(ctx: ServiceContext, name: string) { return createMonitor({ ctx, input: { name: `${TEST_PREFIX}-${name}`, jobType: "http", url: "https://example.com", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); }
test("groups private location ids by monitor across the input", async () => { await withTestTransaction(async (tx) => { const ctx: ServiceContext = { ...teamCtx, db: tx }; const mon1 = await createHttpMonitor(ctx, "group-mon1"); const mon2 = await createHttpMonitor(ctx, "group-mon2"); const mon3 = await createHttpMonitor(ctx, "group-mon3");
const [pl1] = await tx .insert(privateLocation) .values({ workspaceId: teamCtx.workspace.id, name: `${TEST_PREFIX}-group-pl1`, token: `${TEST_PREFIX}-group-pl1-token`, }) .returning(); const [pl2] = await tx .insert(privateLocation) .values({ workspaceId: teamCtx.workspace.id, name: `${TEST_PREFIX}-group-pl2`, token: `${TEST_PREFIX}-group-pl2-token`, }) .returning();
await tx.insert(privateLocationToMonitors).values([ { privateLocationId: pl1.id, monitorId: mon1.id }, { privateLocationId: pl2.id, monitorId: mon1.id }, { privateLocationId: pl1.id, monitorId: mon2.id }, ]);
const map = await getPrivateLocationIdsByMonitor({ ctx, input: { monitorIds: [mon1.id, mon2.id, mon3.id] }, });
expect(map.get(mon1.id)).toEqual( [pl1.id, pl2.id].sort((a, b) => a - b).map(String), ); expect(map.get(mon2.id)).toEqual([String(pl1.id)]); // monitor with no associations is absent from the map expect(map.has(mon3.id)).toBe(false); }); });
test("excludes private locations from another workspace", async () => { await withTestTransaction(async (tx) => { const teamTxCtx: ServiceContext = { ...teamCtx, db: tx }; const mon = await createHttpMonitor(teamTxCtx, "cross-ws-mon");
// A private location owned by the free workspace, wired to a team // monitor. The workspace-scoped join must not surface it. const [foreignPl] = await tx .insert(privateLocation) .values({ workspaceId: freeCtx.workspace.id, name: `${TEST_PREFIX}-cross-ws-pl`, token: `${TEST_PREFIX}-cross-ws-pl-token`, }) .returning(); await tx .insert(privateLocationToMonitors) .values({ privateLocationId: foreignPl.id, monitorId: mon.id });
const map = await getPrivateLocationIdsByMonitor({ ctx: teamTxCtx, input: { monitorIds: [mon.id] }, });
expect(map.has(mon.id)).toBe(false); }); });
test("excludes soft-deleted attachments", async () => { await withTestTransaction(async (tx) => { const ctx: ServiceContext = { ...teamCtx, db: tx }; const mon = await createHttpMonitor(ctx, "soft-deleted");
const [pl] = await tx .insert(privateLocation) .values({ workspaceId: teamCtx.workspace.id, name: `${TEST_PREFIX}-soft-deleted-pl`, token: `${TEST_PREFIX}-soft-deleted-pl-token`, }) .returning(); // A soft-deleted join row: the location no longer runs the monitor. await tx.insert(privateLocationToMonitors).values({ privateLocationId: pl.id, monitorId: mon.id, deletedAt: new Date(), });
const map = await getPrivateLocationIdsByMonitor({ ctx, input: { monitorIds: [mon.id] }, });
expect(map.has(mon.id)).toBe(false); }); });
test("excludes attachments whose monitor was soft-deleted", async () => { await withTestTransaction(async (tx) => { const ctx: ServiceContext = { ...teamCtx, db: tx }; const mon = await createHttpMonitor(ctx, "deleted-monitor");
const [pl] = await tx .insert(privateLocation) .values({ workspaceId: teamCtx.workspace.id, name: `${TEST_PREFIX}-deleted-monitor-pl`, token: `${TEST_PREFIX}-deleted-monitor-pl-token`, }) .returning(); await tx .insert(privateLocationToMonitors) .values({ privateLocationId: pl.id, monitorId: mon.id }); await tx .update(monitor) .set({ deletedAt: new Date() }) .where(eq(monitor.id, mon.id));
const map = await getPrivateLocationIdsByMonitor({ ctx, input: { monitorIds: [mon.id] }, });
expect(map.has(mon.id)).toBe(false); }); });
test("excludes attachments whose monitor belongs to another workspace", async () => { await withTestTransaction(async (tx) => { // Legacy/corrupt pivot: a team private location wired to a free-workspace // monitor. The workspace-scoped monitor join must drop it. const foreignMon = await createHttpMonitor( { ...freeCtx, db: tx }, "foreign-monitor", ); const [pl] = await tx .insert(privateLocation) .values({ workspaceId: teamCtx.workspace.id, name: `${TEST_PREFIX}-foreign-monitor-pl`, token: `${TEST_PREFIX}-foreign-monitor-pl-token`, }) .returning(); await tx .insert(privateLocationToMonitors) .values({ privateLocationId: pl.id, monitorId: foreignMon.id });
const map = await getPrivateLocationIdsByMonitor({ ctx: { ...teamCtx, db: tx }, input: { monitorIds: [foreignMon.id] }, });
expect(map.has(foreignMon.id)).toBe(false); }); });
test("returns an empty map for empty input", async () => { await withTestTransaction(async (tx) => { const map = await getPrivateLocationIdsByMonitor({ ctx: { ...teamCtx, db: tx }, input: { monitorIds: [] }, }); expect(map.size).toBe(0); }); });});
describe("listResponseLogsInfinite", () => { const REGIONS = ["iad", "ams", "gru", "syd", "sin"];
/** One `tb` stand-in per pipe getter, recording the params it was called with. */ function makeFakeTb(rows: Record<string, unknown>[]) { const calls: Record<string, Record<string, unknown>> = {}; const record = (name: string) => (params: Record<string, unknown>) => { calls[name] = params; return Promise.resolve({ data: rows }); }; const tb = { httpListV2Daily: record("httpListV2Daily"), httpListV2Weekly: record("httpListV2Weekly"), httpListV2Biweekly: record("httpListV2Biweekly"), tcpListV2Daily: record("tcpListV2Daily"), tcpListV2Weekly: record("tcpListV2Weekly"), tcpListV2Biweekly: record("tcpListV2Biweekly"), icmpListV2Daily: record("icmpListV2Daily"), icmpListV2Weekly: record("icmpListV2Weekly"), icmpListV2Biweekly: record("icmpListV2Biweekly"), dnsListV2Biweekly: record("dnsListV2Biweekly"), grpcListV2Biweekly: record("grpcListV2Biweekly"), } as unknown as NonNullable<ServiceContext["tb"]>; return { tb, calls }; }
function httpRows(ticks: number, perTick: number) { const rows: Record<string, unknown>[] = []; for (let tick = 0; tick < ticks; tick++) { const cronTimestamp = 1_700_000_000_000 - tick * 30_000; for (let i = 0; i < perTick; i++) { rows.push({ id: `log-${tick}-${i}`, monitorId: "1", region: REGIONS[i % REGIONS.length], requestStatus: "success", trigger: "cron", statusCode: 200, latency: 100, cronTimestamp, timestamp: cronTimestamp, timing: null, }); } } return rows; }
/** Like `makeFakeTb`, but honours the `limit` the pipe was asked for. */ function makeLimitedFakeTb(rows: Record<string, unknown>[]) { const limits: number[] = []; const tb = { httpListV2Biweekly: (params: Record<string, unknown>) => { const limit = Number(params.limit); limits.push(limit); return Promise.resolve({ data: rows.slice(0, limit) }); }, } as unknown as NonNullable<ServiceContext["tb"]>; return { tb, limits }; }
test("throws ForbiddenError when plan disables response-logs", async () => { await withTestTransaction(async (tx) => { await expect( listResponseLogsInfinite({ ctx: { ...freeCtx, db: tx }, input: { monitorId: 999_999, limit: 50, direction: "next" }, }), ).rejects.toBeInstanceOf(ForbiddenError); }); });
test("throws ValidationError for an unsupported job type", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-udp-infinite`, jobType: "udp", url: "1.1.1.1", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); await expect( listResponseLogsInfinite({ ctx: { ...teamCtx, db: tx, tb: makeFakeTb([]).tb }, input: { monitorId: row.id, limit: 50, direction: "next" }, }), ).rejects.toBeInstanceOf(ValidationError); }); });
test("overfetches by the location count and trims to a whole tick", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-http-infinite`, jobType: "http", url: "https://example.com", method: "GET", headers: [], assertions: [], active: false, regions: REGIONS, }, }); // 53 rows = ten full five-region ticks plus a partial eleventh. const rows = httpRows(11, 5).slice(0, 53); const { tb, calls } = makeFakeTb(rows);
const result = await listResponseLogsInfinite({ ctx: { ...teamCtx, db: tx, tb }, input: { monitorId: row.id, limit: 50, direction: "next" }, });
expect(calls.httpListV2Biweekly?.limit).toBe(55); expect(calls.httpListV2Biweekly?.monitorId).toBe(String(row.id)); expect(result.data.length).toBe(50); expect(result.nextCursor).toBe( Math.min(...result.data.map((log) => log.cronTimestamp)), ); }); });
test("widens the fetch when one tick fills the ceiling", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-http-wide-tick`, jobType: "http", url: "https://example.com", method: "GET", headers: [], assertions: [], active: false, regions: REGIONS, }, }); // Ten rows on the newest tick: the MV is a plain MergeTree, so a retry // can land more rows on a tick than the monitor has locations, and the // location-based overfetch then no longer spans it. const rows = [...httpRows(1, 10), ...httpRows(2, 5).slice(5)]; const { tb, limits } = makeLimitedFakeTb(rows);
const result = await listResponseLogsInfinite({ ctx: { ...teamCtx, db: tx, tb }, input: { monitorId: row.id, limit: 2, direction: "next" }, });
// 2 + 5 locations only reaches into the tick; the retry doubles it. expect(limits).toEqual([7, 14]); // The tick comes back whole, so the exclusive cursor skips nothing. expect(result.data.length).toBe(10); expect(new Set(result.data.map((log) => log.cronTimestamp)).size).toBe(1); expect(result.nextCursor).toBe(result.data[0].cronTimestamp); }); });
test("routes tcp monitors to the tcp pipe with null http columns", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-tcp-infinite`, jobType: "tcp", url: "example.com:443", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); const { tb, calls } = makeFakeTb([ { id: "tcp-1", monitorId: String(row.id), region: "ams", requestStatus: "success", trigger: "cron", latency: 12, cronTimestamp: 1_700_000_000_000, timestamp: 1_700_000_000_000, }, ]);
const result = await listResponseLogsInfinite({ ctx: { ...teamCtx, db: tx, tb }, input: { monitorId: row.id, limit: 50, direction: "next" }, });
expect(calls.tcpListV2Biweekly).toBeDefined(); expect(calls.httpListV2Biweekly).toBeUndefined(); expect(result.data[0].statusCode).toBe(null); expect(result.data[0].timing).toBe(null); expect(result.nextCursor).toBe(null); }); });
test("routes icmp monitors to the icmp pipe", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-icmp-infinite`, jobType: "icmp", url: "1.1.1.1", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); const { tb, calls } = makeFakeTb([ { id: "icmp-1", monitorId: String(row.id), region: "ams", requestStatus: "success", trigger: "cron", latency: 12, cronTimestamp: 1_700_000_000_000, timestamp: 1_700_000_000_000, }, ]);
const result = await listResponseLogsInfinite({ ctx: { ...teamCtx, db: tx, tb }, input: { monitorId: row.id, limit: 50, direction: "next" }, });
expect(calls.icmpListV2Biweekly).toBeDefined(); expect(calls.tcpListV2Biweekly).toBeUndefined(); expect(result.data[0].statusCode).toBe(null); expect(result.data[0].timing).toBe(null); expect(result.nextCursor).toBe(null); }); });
test("routes grpc monitors to the grpc pipe, keeping its phase timings", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-grpc-infinite`, jobType: "grpc", url: "example.com:443", method: "GET", headers: [], assertions: [], active: false, regions: ["ams"], }, }); const timing = { dns: 1, connect: 2, tls: 3, ttfb: 4, transfer: 5 }; const { tb, calls } = makeFakeTb([ { id: "grpc-1", monitorId: String(row.id), region: "ams", requestStatus: "success", trigger: "cron", latency: 12, cronTimestamp: 1_700_000_000_000, timestamp: 1_700_000_000_000, timing, }, ]);
const result = await listResponseLogsInfinite({ ctx: { ...teamCtx, db: tx, tb }, input: { monitorId: row.id, limit: 50, direction: "next" }, });
expect(calls.grpcListV2Biweekly).toBeDefined(); expect(calls.httpListV2Biweekly).toBeUndefined(); // gRPC has no HTTP status code, but it does carry HTTP's phase shape. expect(result.data[0].statusCode).toBe(null); expect(result.data[0].timing).toEqual(timing); expect(result.nextCursor).toBe(null); }); });
test("routes dns monitors to the dns pipe", async () => { await withTestTransaction(async (tx) => { const row = await createMonitor({ ctx: { ...teamCtx, db: tx }, input: { name: `${TEST_PREFIX}-dns-infinite`, jobType: "dns", url: "example.com", method: "GET", headers: [], assertions: [ { version: "v1", type: "dnsRecord", key: "A", compare: "eq", target: "1.1.1.1", }, ], active: false, regions: ["ams"], }, }); const { tb, calls } = makeFakeTb([]);
const result = await listResponseLogsInfinite({ ctx: { ...teamCtx, db: tx, tb }, input: { monitorId: row.id, limit: 50, direction: "next" }, });
expect(calls.dnsListV2Biweekly).toBeDefined(); expect(result.data).toEqual([]); expect(result.nextCursor).toBe(null); }); });});