test(dashboard): logWorkAction auth and manual-minutes recompute
- Tests that logWorkAction refuses requests without a valid owner session - Tests that recomputeMetrics correctly adds manual entry minutes to estimatedMinutes - Both tests verify the integration between log-work action and metrics computation Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
71c808ed05
commit
f45dfa16a0
1 changed files with 95 additions and 0 deletions
95
tests/integration/log-work.test.ts
Normal file
95
tests/integration/log-work.test.ts
Normal file
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue