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:
157
.claude/hooks/test/config.test.mjs
Normal file
157
.claude/hooks/test/config.test.mjs
Normal file
@@ -0,0 +1,157 @@
|
||||
// Tests for lib/config.mjs: the harness.config.json loader. It merges the file
|
||||
// over the built-in defaults, validates the shape (fail-closed on malformed),
|
||||
// and degrades to defaults when the file is missing.
|
||||
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { afterEach, describe, test, expect } from "vitest";
|
||||
import {
|
||||
loadConfig,
|
||||
clearConfigCache,
|
||||
CONFIG_FILENAME,
|
||||
isDeployEnabled,
|
||||
deployGlobs,
|
||||
pipelineRoles,
|
||||
debounceRoles,
|
||||
roleModel,
|
||||
worktreeProvision,
|
||||
prePrSteps,
|
||||
} from "../lib/config.mjs";
|
||||
|
||||
const REPO_ROOT = join(
|
||||
dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
);
|
||||
|
||||
const tmpRepos = [];
|
||||
const makeRepo = (config) => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "harness-config-"));
|
||||
tmpRepos.push(dir);
|
||||
if (config !== undefined) {
|
||||
writeFileSync(
|
||||
join(dir, CONFIG_FILENAME),
|
||||
typeof config === "string" ? config : JSON.stringify(config),
|
||||
);
|
||||
}
|
||||
clearConfigCache();
|
||||
return dir;
|
||||
};
|
||||
|
||||
const VALID = {
|
||||
name: "sample",
|
||||
validation: {
|
||||
steps: [
|
||||
{ id: "typecheck", kind: "typecheck", command: "npm run typecheck" },
|
||||
{
|
||||
id: "unit",
|
||||
kind: "unit",
|
||||
runner: "vitest",
|
||||
config: "vitest.config.ts",
|
||||
projects: ["app"],
|
||||
},
|
||||
],
|
||||
},
|
||||
roles: {
|
||||
developer: { model: "sonnet", pipeline: true, debounce: true },
|
||||
planner: { model: "opus", pipeline: true, debounce: false },
|
||||
},
|
||||
deploy: { adapter: "supabase", relevantGlobs: ["supabase/**"] },
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
while (tmpRepos.length)
|
||||
rmSync(tmpRepos.pop(), { recursive: true, force: true });
|
||||
clearConfigCache();
|
||||
});
|
||||
|
||||
describe("config loader", () => {
|
||||
test("loads a valid config and applies defaults for absent keys", () => {
|
||||
const cfg = loadConfig(makeRepo(VALID));
|
||||
expect(cfg.name).toBe("sample");
|
||||
// default filled in when absent
|
||||
expect(worktreeProvision(cfg)).toBe("npm-link");
|
||||
expect(cfg.launcher.sessionDirEnv).toBe("CHAT_SESSION_DIR");
|
||||
});
|
||||
|
||||
test("missing file degrades to defaults (no deploy capability)", () => {
|
||||
const cfg = loadConfig(makeRepo(undefined));
|
||||
expect(isDeployEnabled(cfg)).toBe(false);
|
||||
expect(cfg.validation.steps).toEqual([]);
|
||||
});
|
||||
|
||||
test("typed readers reflect the config", () => {
|
||||
const cfg = loadConfig(makeRepo(VALID));
|
||||
expect(isDeployEnabled(cfg)).toBe(true);
|
||||
expect(deployGlobs(cfg)).toEqual(["supabase/**"]);
|
||||
expect(pipelineRoles(cfg).sort()).toEqual(["developer", "planner"]);
|
||||
expect(debounceRoles(cfg)).toEqual(["developer"]);
|
||||
expect(roleModel(cfg, "planner")).toBe("opus");
|
||||
});
|
||||
|
||||
test("malformed JSON throws a clear, prefixed error", () => {
|
||||
expect(() => loadConfig(makeRepo("{ not json"))).toThrow(
|
||||
new RegExp(`${CONFIG_FILENAME}: invalid JSON`),
|
||||
);
|
||||
});
|
||||
|
||||
test("validation.steps not an array is rejected", () => {
|
||||
expect(() =>
|
||||
loadConfig(makeRepo({ validation: { steps: {} }, roles: {} })),
|
||||
).toThrow(/validation\.steps` must be an array/);
|
||||
});
|
||||
|
||||
test("a role missing a model is rejected", () => {
|
||||
expect(() =>
|
||||
loadConfig(
|
||||
makeRepo({ validation: { steps: [] }, roles: { developer: {} } }),
|
||||
),
|
||||
).toThrow(/roles\.developer needs/);
|
||||
});
|
||||
|
||||
test("deploy present but without relevantGlobs is rejected", () => {
|
||||
expect(() =>
|
||||
loadConfig(
|
||||
makeRepo({ validation: { steps: [] }, roles: {}, deploy: {} }),
|
||||
),
|
||||
).toThrow(/deploy\.relevantGlobs/);
|
||||
});
|
||||
|
||||
test("prePrSteps takes typecheck/lint but excludes changedScoped steps", () => {
|
||||
const cfg = loadConfig(
|
||||
makeRepo({
|
||||
validation: {
|
||||
steps: [
|
||||
{
|
||||
id: "typecheck",
|
||||
kind: "typecheck",
|
||||
command: "npm run typecheck",
|
||||
},
|
||||
{
|
||||
id: "lint",
|
||||
kind: "lint",
|
||||
command: "npx eslint",
|
||||
changedScoped: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
roles: {},
|
||||
}),
|
||||
);
|
||||
// typecheck (whole-repo-safe) is on the human push path; the diff-scoped lint
|
||||
// step is not — it needs a per-worktree base the human path lacks.
|
||||
expect(prePrSteps(cfg).map((s) => s.id)).toEqual(["typecheck"]);
|
||||
});
|
||||
|
||||
test("the committed harness.config.json is valid", () => {
|
||||
clearConfigCache();
|
||||
const cfg = loadConfig(REPO_ROOT);
|
||||
expect(cfg.name).toBe("atomic-crm");
|
||||
expect(isDeployEnabled(cfg)).toBe(true);
|
||||
expect(roleModel(cfg, "quality-reviewer")).toBe("opus");
|
||||
expect(pipelineRoles(cfg)).toContain("test-writer");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user