pelagia-portal/App/tests/integration/project-codes.test.ts
Hardik 02c0806d35
All checks were successful
PR checks / checks (pull_request) Successful in 51s
PR checks / integration (pull_request) Successful in 32s
refactor(po): admin-managed Project Codes instead of a static list
Replaces the hardcoded PROJECT_CODES array with an admin-managed
`ProjectCode` model, mirroring the Delivery Locations pattern (PR #100):

- ProjectCode model (unique `code` + isActive) + migration seeding the
  five previously-hardcoded codes; PO.projectCode stays a free-text
  snapshot (no FK) so history/exports/imports are unchanged.
- manage_project_codes permission (Manager + SuperUser + Admin).
- /admin/project-codes CRUD screen (table + Add/Edit + activate/delete)
  and an Administration sidebar link.
- ProjectCodeField now takes `options` from the active codes; the three
  PO forms + pages fetch them from the DB. Static list removed.
- Unit test reworked to the options API; CRUD integration test added;
  documented in App/CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 02:52:03 +05:30

81 lines
3.2 KiB
TypeScript

/**
* Integration tests for the Project Codes admin CRUD (issue #124).
* Covers create/update/toggle/delete + the manage_project_codes guard.
*/
import { vi, describe, it, expect, afterAll } from "vitest";
vi.mock("@/auth", () => ({ auth: vi.fn() }));
vi.mock("next/cache", () => ({ revalidatePath: vi.fn() }));
import { auth } from "@/auth";
import { db } from "@/lib/db";
import {
createProjectCode,
updateProjectCode,
toggleProjectCodeActive,
deleteProjectCode,
} from "@/app/(portal)/admin/project-codes/actions";
import { makeSession, fd } from "./helpers";
const mockedAuth = vi.mocked(auth);
const PREFIX = "INTTEST_PROJCODE_";
const asManager = () => mockedAuth.mockResolvedValue(makeSession("u-mgr", "MANAGER") as never);
afterAll(async () => {
await db.projectCode.deleteMany({ where: { code: { startsWith: PREFIX } } });
});
describe("createProjectCode", () => {
it("persists an active project code", async () => {
asManager();
const result = await createProjectCode(fd({ code: `${PREFIX}Alpha` }));
expect(result).toEqual({ ok: true });
const code = await db.projectCode.findFirstOrThrow({ where: { code: `${PREFIX}Alpha` } });
expect(code.isActive).toBe(true);
});
it("requires a non-empty code", async () => {
asManager();
expect("error" in (await createProjectCode(fd({ code: " " })))).toBe(true);
});
it("rejects a duplicate code", async () => {
asManager();
await createProjectCode(fd({ code: `${PREFIX}Dup` }));
const result = await createProjectCode(fd({ code: `${PREFIX}Dup` }));
expect("error" in result).toBe(true);
});
it("refuses callers without manage_project_codes", async () => {
mockedAuth.mockResolvedValue(makeSession("u-tech", "TECHNICAL") as never);
expect(await createProjectCode(fd({ code: `${PREFIX}X` }))).toEqual({ error: "Forbidden" });
mockedAuth.mockResolvedValue(makeSession("u-acc", "ACCOUNTS") as never);
expect(await createProjectCode(fd({ code: `${PREFIX}X` }))).toEqual({ error: "Forbidden" });
});
});
describe("updateProjectCode / toggle / delete", () => {
it("edits, toggles active, then deletes a project code", async () => {
asManager();
await createProjectCode(fd({ code: `${PREFIX}Old` }));
const code = await db.projectCode.findFirstOrThrow({ where: { code: `${PREFIX}Old` } });
expect(await updateProjectCode(code.id, fd({ code: `${PREFIX}New` }))).toEqual({ ok: true });
expect((await db.projectCode.findUniqueOrThrow({ where: { id: code.id } })).code).toBe(`${PREFIX}New`);
expect(await toggleProjectCodeActive(code.id)).toEqual({ ok: true });
expect((await db.projectCode.findUniqueOrThrow({ where: { id: code.id } })).isActive).toBe(false);
expect(await deleteProjectCode(code.id)).toEqual({ ok: true });
expect(await db.projectCode.findUnique({ where: { id: code.id } })).toBeNull();
});
it("guards update/toggle/delete behind the permission", async () => {
mockedAuth.mockResolvedValue(makeSession("u-tech", "TECHNICAL") as never);
expect(await updateProjectCode("x", fd({ code: "y" }))).toEqual({ error: "Forbidden" });
expect(await toggleProjectCodeActive("x")).toEqual({ error: "Forbidden" });
expect(await deleteProjectCode("x")).toEqual({ error: "Forbidden" });
});
});