kanban-dev-webapp/tests/integration/metrics.test.ts

76 lines
2.7 KiB
TypeScript
Raw Normal View History

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<string, number> };
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");
});
});