import { describe, it, expect, beforeAll } from "vitest"; import PocketBase from "pocketbase"; import { recomputeMetrics } from "@/lib/metrics/store"; import { APP_URL, DEMO, adminPb, requireServices, signIn } from "./helpers"; let admin: PocketBase; let projectId: string; beforeAll(async () => { await requireServices(); admin = await adminPb(); const project = await admin .collection("projects") .getFirstListItem(`slug = "${DEMO.projectSlug}"`); projectId = project.id; }); describe("cached metrics agree with the stored commits", () => { it("counts the same non-merge commits the database holds", async () => { const metrics = await recomputeMetrics(projectId); const all = await admin.collection("commits").getFullList({ filter: `project = "${projectId}"`, fields: "is_merge", }); const merges = all.filter((c) => c.is_merge).length; expect(metrics.totalCommits).toBe(all.length - merges); expect(metrics.mergeCommits).toBe(merges); }); it("stores the constants alongside the figures", async () => { const cached = await admin .collection("project_metrics") .getFirstListItem(`project = "${projectId}"`); const payload = cached.payload as { constants?: Record }; expect(payload.constants).toBeDefined(); expect(payload.constants!.sessionGapMinutes).toBeGreaterThan(0); }); it("never estimates fewer minutes than sessions times padding", async () => { // A sanity floor: every session contributes at least its padding, so a // total below that means clustering silently dropped commits. const cached = await admin .collection("project_metrics") .getFirstListItem(`project = "${projectId}"`); const p = cached.payload as { estimatedMinutes: number; sessionCount: number; constants: { preSessionMinutes: number; postSessionMinutes: number }; }; const floor = p.sessionCount * (p.constants.preSessionMinutes + p.constants.postSessionMinutes); expect(p.estimatedMinutes).toBeGreaterThanOrEqual(floor); }); }); describe("the dashboard is visible to a client manager", () => { it("renders with the methodology panel", async () => { // Effort figures are shown to clients by explicit decision, so the panel // that explains them must render for them too. const cookie = await signIn(DEMO.managerEmail, DEMO.managerPassword); const response = await fetch( `${APP_URL}/p/${DEMO.clientSlug}/${DEMO.projectSlug}/dashboard`, { headers: { cookie } } ); expect(response.status).toBe(200); const html = await response.text(); expect(html).toContain("How these numbers are worked out"); expect(html.toLowerCase()).toContain("estimated"); }); });