diff --git a/packages/api/src/router/statusPage.order.test.ts b/packages/api/src/router/statusPage.order.test.ts new file mode 100644 index 000000000..dcf692cda --- /dev/null +++ b/packages/api/src/router/statusPage.order.test.ts @@ -0,0 +1,173 @@ +import { db, eq } from "@openstatus/db"; +import { + page, + statusReport, + statusReportUpdate, +} from "@openstatus/db/src/schema"; +import { + createPage, + createTestWorkspace, +} from "@openstatus/db/src/test/factories"; +import { NextRequest } from "next/server.js"; +import { afterAll, beforeAll, describe, expect, test } from "vitest"; + +import { appRouter } from "../root"; +import { createInnerTRPCContext } from "../trpc"; + +let workspaceId: number; +let pageId: number; +let slug: string; + +function caller() { + return appRouter.createCaller( + createInnerTRPCContext({ + req: new NextRequest("http://rsc.internal"), + session: null, + }), + ); +} + +beforeAll(async () => { + const fixture = await createTestWorkspace(); + workspaceId = fixture.workspace.id; + const createdPage = await createPage(workspaceId); + pageId = createdPage.id; + slug = createdPage.slug; +}); + +afterAll(async () => { + await db.delete(statusReport).where(eq(statusReport.pageId, pageId)); + await db.delete(page).where(eq(page.workspaceId, workspaceId)); +}); + +describe("statusPage.get report order", () => { + test("uses each report's newest update and puts reports without updates first", async () => { + const olderActivity = await db + .insert(statusReport) + .values({ + workspaceId, + pageId, + title: "Older activity", + status: "investigating", + }) + .returning() + .get(); + const recentActivity = await db + .insert(statusReport) + .values({ + workspaceId, + pageId, + title: "Recent activity", + status: "investigating", + }) + .returning() + .get(); + const noUpdates = await db + .insert(statusReport) + .values({ + workspaceId, + pageId, + title: "No updates", + status: "investigating", + }) + .returning() + .get(); + + await db.insert(statusReportUpdate).values([ + { + statusReportId: recentActivity.id, + status: "investigating", + message: "Initial update", + date: new Date("2026-01-01T00:00:00Z"), + }, + { + statusReportId: olderActivity.id, + status: "investigating", + message: "Other report update", + date: new Date("2026-02-01T00:00:00Z"), + }, + { + statusReportId: recentActivity.id, + status: "investigating", + message: "Newest update", + date: new Date("2026-03-01T00:00:00Z"), + }, + ]); + + const result = await caller().statusPage.get({ slug }); + expect(result?.statusReports.map((report) => report.id)).toEqual([ + noUpdates.id, + recentActivity.id, + olderActivity.id, + ]); + }); + + test("breaks equal activity dates and reports without updates by id", async () => { + const first = await db + .insert(statusReport) + .values({ + workspaceId, + pageId, + title: "First tie", + status: "investigating", + }) + .returning() + .get(); + const second = await db + .insert(statusReport) + .values({ + workspaceId, + pageId, + title: "Second tie", + status: "investigating", + }) + .returning() + .get(); + const firstNoUpdates = await db + .insert(statusReport) + .values({ + workspaceId, + pageId, + title: "First without updates", + status: "investigating", + }) + .returning() + .get(); + const secondNoUpdates = await db + .insert(statusReport) + .values({ + workspaceId, + pageId, + title: "Second without updates", + status: "investigating", + }) + .returning() + .get(); + + const date = new Date("2026-04-01T00:00:00Z"); + await db.insert(statusReportUpdate).values([ + { + statusReportId: first.id, + status: "investigating", + message: "First", + date, + }, + { + statusReportId: second.id, + status: "investigating", + message: "Second", + date, + }, + ]); + + const result = await caller().statusPage.get({ slug }); + const ids = result?.statusReports.map((report) => report.id); + expect( + ids?.filter((id) => + [first.id, second.id, firstNoUpdates.id, secondNoUpdates.id].includes( + id, + ), + ), + ).toEqual([secondNoUpdates.id, firstNoUpdates.id, second.id, first.id]); + }); +}); diff --git a/packages/api/src/router/statusPage.ts b/packages/api/src/router/statusPage.ts index fa5e7aa69..36e412aa1 100644 --- a/packages/api/src/router/statusPage.ts +++ b/packages/api/src/router/statusPage.ts @@ -490,14 +490,12 @@ export const statusPageRouter = createTRPCRouter({ a.statusReportUpdates.length === 0 && b.statusReportUpdates.length === 0 ) - return 0; + return b.id - a.id; if (a.statusReportUpdates.length === 0) return -1; - if (b.statusReportUpdates.length === 0) return -1; + if (b.statusReportUpdates.length === 0) return 1; return ( - b.statusReportUpdates[ - b.statusReportUpdates.length - 1 - ].date.getTime() - - a.statusReportUpdates[a.statusReportUpdates.length - 1].date.getTime() + b.statusReportUpdates[0].date.getTime() - + a.statusReportUpdates[0].date.getTime() || b.id - a.id ); });