diff --git a/tests/integration/log-work.test.ts b/tests/integration/log-work.test.ts new file mode 100644 index 0000000..ee48fae --- /dev/null +++ b/tests/integration/log-work.test.ts @@ -0,0 +1,95 @@ +import { describe, it, expect, beforeAll, afterAll } from "vitest"; +import PocketBase from "pocketbase"; + +import { DEMO, adminPb, requireServices } 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("logWorkAction", () => { + const created: string[] = []; + + afterAll(async () => { + for (const id of created) { + await admin.collection("commits").delete(id).catch(() => {}); + } + }); + + it("refuses without a valid owner session", async () => { + // The action reads the session cookie via next/headers, which is not + // available outside a request in this harness — it must return a failure + // rather than silently succeed with no session. + const { logWorkAction } = await import("@/app/actions/log-work"); + const result = await logWorkAction({ + projectId, + clientSlug: DEMO.clientSlug, + projectSlug: DEMO.projectSlug, + date: "2026-09-19", + hours: 2, + message: "Should never be written", + }); + + expect(result.ok).toBe(false); + + const rows = await admin.collection("commits").getFullList({ + filter: `project = "${projectId}" && subject = "Should never be written"`, + }); + expect(rows).toHaveLength(0); + }); +}); + +describe("computeProjectMetrics via recomputeMetrics", () => { + let commitId: string; + + afterAll(async () => { + if (commitId) await admin.collection("commits").delete(commitId).catch(() => {}); + }); + + it("adds a manual entry's declared minutes to estimatedMinutes", async () => { + const before = await admin + .collection("project_metrics") + .getFirstListItem(`project = "${projectId}"`) + .catch(() => null); + const beforeMinutes = (before?.payload as { estimatedMinutes?: number } | undefined) + ?.estimatedMinutes ?? 0; + + const commit = await admin.collection("commits").create({ + project: projectId, + sha: `manual-test-${Date.now()}`, + short_sha: "manual", + message: "Integration test entry", + subject: "Integration test entry", + type: "", + scope: "", + authored_at: "2026-09-19 12:00:00.000Z", + is_merge: false, + additions: 0, + deletions: 0, + card: "", + link_source: "none", + url: "", + summary: "Integration test entry", + summary_version: 0, + summary_attempts: 0, + source: "manual", + manual_minutes: 90, + }); + commitId = commit.id; + + const { recomputeMetrics } = await import("@/lib/metrics/store"); + const metrics = await recomputeMetrics(projectId); + + expect(metrics.manualMinutes).toBeGreaterThanOrEqual(90); + expect(metrics.estimatedMinutes).toBeGreaterThanOrEqual(beforeMinutes + 90); + }); +});