// tests/mutator/claude.test.ts — Test Claude settings hook injection import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import fs from "fs"; import os from "os"; import path from "path"; import { SCRIPT_NAME } from "../../src/utils/config"; describe("Claude settings hook format", () => { const testDir = fs.mkdtempSync(path.join(os.tmpdir(), "claude-test-")); const settingsPath = path.join(testDir, ".claude", "settings.json"); beforeEach(() => { fs.mkdirSync(path.dirname(settingsPath), { recursive: true }); }); afterEach(() => { fs.rmSync(testDir, { recursive: true, force: true }); }); test("creates nested hook structure from empty settings", () => { const hookCommand = "bun run ~/.claude/package/opensearch_init.js"; // Start with empty settings fs.writeFileSync(settingsPath, "{}", "utf-8"); const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8")); settings.hooks = {}; settings.hooks.SessionStart = []; settings.hooks.SessionStart.push({ matcher: "*", hooks: [{ type: "command", command: hookCommand }], }); fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8"); const result = JSON.parse(fs.readFileSync(settingsPath, "utf-8")); expect(result.hooks).toBeDefined(); expect(result.hooks.SessionStart).toBeInstanceOf(Array); expect(result.hooks.SessionStart[0]).toEqual({ matcher: "*", hooks: [{ type: "command", command: hookCommand }], }); }); test("appends to existing nested hook", () => { const hookCommand1 = "bun run ~/.claude/package/opensearch_init.js"; const hookCommand2 = "bun run ~/.codex/package/opensearch_init.js"; const existing = { hooks: { SessionStart: [ { matcher: "*", hooks: [ { type: "command", command: ".vscode/setup.mjs" }, ], }, ], }, }; fs.writeFileSync(settingsPath, JSON.stringify(existing, null, 2), "utf-8"); // Simulate adding two hooks const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8")); const entry = settings.hooks.SessionStart[0]; entry.hooks.push({ type: "command", command: hookCommand1 }); entry.hooks.push({ type: "command", command: hookCommand2 }); fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8"); const result = JSON.parse(fs.readFileSync(settingsPath, "utf-8")); expect(result.hooks.SessionStart[0].hooks.length).toBe(3); expect(result.hooks.SessionStart[0].hooks[1]).toEqual({ type: "command", command: hookCommand1, }); expect(result.hooks.SessionStart[0].hooks[2]).toEqual({ type: "command", command: hookCommand2, }); }); test("creates matcher entry when SessionStart is empty array", () => { const hookCommand = "bun run ~/.codex/package/opensearch_init.js"; fs.writeFileSync(settingsPath, "{}", "utf-8"); const settings = { hooks: { SessionStart: [] as any[] } }; settings.hooks.SessionStart.push({ matcher: "*", hooks: [{ type: "command", command: hookCommand }], }); fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8"); const result = JSON.parse(fs.readFileSync(settingsPath, "utf-8")); expect(result.hooks.SessionStart[0]).toEqual({ matcher: "*", hooks: [{ type: "command", command: hookCommand }], }); }); });