snapshot of https://github.com/marmelab/atomic-crm.git (main, 2026-09-18; blobs > 131072B omitted)
Some checks failed
✅ Check / 🔬 ESLint (push) Failing after 21s
✅ Check / 🏷️ Typecheck (push) Failing after 11s
✅ Check / 🔎 Test (push) Failing after 11s
✅ Check / e2e-test (push) Failing after 11s
✅ Check / 🔨 Build (push) Failing after 11s
🚀 Deploy / 🚀 Deploy (doc) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (demo) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (supabase) (push) Failing after 18s
Some checks failed
✅ Check / 🔬 ESLint (push) Failing after 21s
✅ Check / 🏷️ Typecheck (push) Failing after 11s
✅ Check / 🔎 Test (push) Failing after 11s
✅ Check / e2e-test (push) Failing after 11s
✅ Check / 🔨 Build (push) Failing after 11s
🚀 Deploy / 🚀 Deploy (doc) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (demo) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (supabase) (push) Failing after 18s
This commit is contained in:
119
.claude/hooks/test/record-review-verdict.test.mjs
Normal file
119
.claude/hooks/test/record-review-verdict.test.mjs
Normal file
@@ -0,0 +1,119 @@
|
||||
// Tests for record-review-verdict.mjs (vitest, "claude" Node project). A
|
||||
// reviewer's final contract line (APPROVED / REJECTED) becomes a per-ticket flag
|
||||
// under <sessionDir>/reviews/<TASK>-<role>, read later by
|
||||
// block-merger-without-review.mjs. Verdict source here is last_assistant_message.
|
||||
|
||||
import { mkdtempSync, existsSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { describe, test, expect, beforeAll, afterAll } from "vitest";
|
||||
import { sanitizePath } from "../lib/paths.mjs";
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const HOOK = join(HERE, "..", "record-review-verdict.mjs");
|
||||
const SESSION_ID = "deadbeef-1111-2222-3333-444455556666";
|
||||
|
||||
let TMP;
|
||||
let APP_DIR;
|
||||
let env;
|
||||
let reviewsDir;
|
||||
|
||||
const run = (payload) =>
|
||||
spawnSync("node", [HOOK], {
|
||||
input: JSON.stringify({ session_id: SESSION_ID, ...payload }),
|
||||
env,
|
||||
encoding: "utf8",
|
||||
});
|
||||
const flag = (name) => join(reviewsDir, name);
|
||||
|
||||
beforeAll(() => {
|
||||
TMP = mkdtempSync(join(tmpdir(), "record-verdict-test-"));
|
||||
APP_DIR = join(TMP, "app");
|
||||
const CRM_TMP_ROOT = join(TMP, "scratch");
|
||||
reviewsDir = join(CRM_TMP_ROOT, sanitizePath(APP_DIR), SESSION_ID, "reviews");
|
||||
env = { ...process.env, APP_DIR, CRM_TMP_ROOT };
|
||||
delete env.CLAUDE_AGENT_NAME;
|
||||
// These tests assert the flag lands under ctx.sessionDir (CRM_TMP_ROOT/...).
|
||||
// reviewsDir() prefers CHAT_SESSION_DIR when set (managed-launcher path), so a
|
||||
// CHAT_SESSION_DIR inherited from the ambient env (e.g. CRM Builder's container)
|
||||
// would redirect the flag and break the assertion — neutralise it here. The
|
||||
// CHAT_SESSION_DIR-present path is covered by reviews.test.mjs.
|
||||
delete env.CHAT_SESSION_DIR;
|
||||
});
|
||||
|
||||
afterAll(() => rmSync(TMP, { recursive: true, force: true }));
|
||||
|
||||
describe("record-review-verdict", () => {
|
||||
test("APPROVED writes the per-ticket flag", () => {
|
||||
run({
|
||||
agent_type: "quality-reviewer-TASK-007",
|
||||
last_assistant_message: "APPROVED",
|
||||
});
|
||||
expect(existsSync(flag("TASK-007-quality-reviewer"))).toBe(true);
|
||||
});
|
||||
|
||||
test("REJECTED clears a prior flag", () => {
|
||||
run({
|
||||
agent_type: "quality-reviewer-TASK-007",
|
||||
last_assistant_message: "REJECTED: do X",
|
||||
});
|
||||
expect(existsSync(flag("TASK-007-quality-reviewer"))).toBe(false);
|
||||
});
|
||||
|
||||
test("records the flag from a final APPROVED after preamble prose", () => {
|
||||
run({
|
||||
agent_type: "quality-reviewer-TASK-011",
|
||||
last_assistant_message: "Part C — integration: present\nAPPROVED",
|
||||
});
|
||||
expect(existsSync(flag("TASK-011-quality-reviewer"))).toBe(true);
|
||||
});
|
||||
|
||||
test("multi-line REJECTED (bulleted feedback) clears a prior flag", () => {
|
||||
// Arrange: a prior APPROVED flag exists for this ticket/role.
|
||||
run({
|
||||
agent_type: "quality-reviewer-TASK-009",
|
||||
last_assistant_message: "APPROVED",
|
||||
});
|
||||
expect(existsSync(flag("TASK-009-quality-reviewer"))).toBe(true);
|
||||
// Act: a re-review rejects using the contract's bulleted-list feedback, so
|
||||
// the REJECTED keyword is NOT on the final line.
|
||||
run({
|
||||
agent_type: "quality-reviewer-TASK-009",
|
||||
last_assistant_message:
|
||||
"Findings:\nREJECTED:\n- src/foo.ts: missing null check\n- src/bar.ts: wrong import",
|
||||
});
|
||||
// Assert: the stale APPROVED flag is cleared.
|
||||
expect(existsSync(flag("TASK-009-quality-reviewer"))).toBe(false);
|
||||
});
|
||||
|
||||
test("trailing prose starting with REJECTED does not flip a real APPROVED", () => {
|
||||
// A chatty reviewer that approves but adds a clarifying sentence which happens
|
||||
// to start with the keyword must NOT be recorded as REJECTED. The contract
|
||||
// REJECTED form requires a colon (`REJECTED:`), so this trailing prose is
|
||||
// ignored and the standalone APPROVED above wins.
|
||||
run({
|
||||
agent_type: "quality-reviewer-TASK-010",
|
||||
last_assistant_message:
|
||||
"APPROVED\nREJECTED concerns from the first pass are now resolved.",
|
||||
});
|
||||
expect(existsSync(flag("TASK-010-quality-reviewer"))).toBe(true);
|
||||
});
|
||||
|
||||
test("standalone APPROVED with a trailing note still approves", () => {
|
||||
run({
|
||||
agent_type: "quality-reviewer-TASK-012",
|
||||
last_assistant_message: "APPROVED\nNice work — all e2e specs present.",
|
||||
});
|
||||
expect(existsSync(flag("TASK-012-quality-reviewer"))).toBe(true);
|
||||
});
|
||||
|
||||
test("unknown verdict leaves state untouched (no flag written)", () => {
|
||||
run({
|
||||
agent_type: "quality-reviewer-TASK-008",
|
||||
last_assistant_message: "I am still thinking about it",
|
||||
});
|
||||
expect(existsSync(flag("TASK-008-quality-reviewer"))).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user