import assert from 'node:assert/strict'; import { access, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises'; import path from 'node:path'; import test from 'node:test'; import { installHooks } from './install-hooks.mjs'; const fixtureRoot = path.join(process.cwd(), '.mosaic-test-work', `hooks-${process.pid}`); const quarantineRoot = path.join(fixtureRoot, 'quarantine'); async function fixture(name) { const root = path.join(fixtureRoot, name); await mkdir(path.join(root, '.husky'), { recursive: true }); return root; } async function exists(target) { try { await access(target); return true; } catch { return false; } } test.after(async () => { await rm(fixtureRoot, { recursive: true, force: true }); }); test('an interrupted install quarantines the partial active hook set and fails loudly', async () => { const root = await fixture('interrupted'); let restoredHooksPath = 'not-called'; await assert.rejects( installHooks({ root, quarantineRoot, runHusky: async (stagingHooks) => { await mkdir(path.join(stagingHooks, '_'), { recursive: true }); await writeFile(path.join(stagingHooks, '_', 'h'), 'partial'); throw new Error('simulated interruption'); }, activateHooks: async () => {}, readHooksPath: async () => null, restoreHooksPath: async (value) => { restoredHooksPath = value; }, }), (error) => { assert.match(error.message, /Hook installation failed/); assert.match(error.message, /pnpm install --frozen-lockfile/); return true; }, ); assert.equal(await exists(path.join(root, '.husky', '_')), false); assert.equal(restoredHooksPath, 'not-called'); const quarantined = await readdir(quarantineRoot); assert.equal(quarantined.length, 1); }); test('a failed replacement restores a previously complete active hook set', async () => { const root = await fixture('rollback'); const activeShim = path.join(root, '.husky', '_', 'h'); await mkdir(path.dirname(activeShim), { recursive: true }); await writeFile(activeShim, 'previous-complete'); let previousRemainedActiveDuringStaging = false; await assert.rejects( installHooks({ root, quarantineRoot: path.join(fixtureRoot, 'rollback-quarantine'), runHusky: async () => { previousRemainedActiveDuringStaging = (await readFile(activeShim, 'utf8')) === 'previous-complete'; throw new Error('simulated replacement failure'); }, activateHooks: async () => {}, readHooksPath: async () => '.husky/_', restoreHooksPath: async () => {}, }), /Hook installation failed/, ); assert.equal(previousRemainedActiveDuringStaging, true); assert.equal(await readFile(activeShim, 'utf8'), 'previous-complete'); }); test('a mismatched complete hook set fails loudly instead of reporting a stale install as current', async () => { const root = await fixture('mismatch'); const activeShim = path.join(root, '.husky', '_', 'h'); await mkdir(path.dirname(activeShim), { recursive: true }); await writeFile(activeShim, 'old-complete'); await assert.rejects( installHooks({ root, quarantineRoot: path.join(fixtureRoot, 'mismatch-quarantine'), runHusky: async (stagingHooks) => { await mkdir(path.join(stagingHooks, '_'), { recursive: true }); await writeFile(path.join(stagingHooks, '_', 'h'), 'new-complete'); }, activateHooks: async () => {}, readHooksPath: async () => '.husky/_', restoreHooksPath: async () => {}, }), /Hook installation failed.*pnpm install --frozen-lockfile/, ); assert.equal(await readFile(activeShim, 'utf8'), 'old-complete'); }); test('a competing successful installer is not removed by the losing process', async () => { const root = await fixture('concurrent'); const activeShim = path.join(root, '.husky', '_', 'h'); let restored = false; await assert.rejects( installHooks({ root, quarantineRoot: path.join(fixtureRoot, 'concurrent-quarantine'), runHusky: async (stagingHooks) => { await mkdir(path.join(stagingHooks, '_'), { recursive: true }); await writeFile(path.join(stagingHooks, '_', 'h'), 'ours'); await mkdir(path.dirname(activeShim), { recursive: true }); await writeFile(activeShim, 'peer'); }, activateHooks: async () => {}, readHooksPath: async () => null, restoreHooksPath: async () => { restored = true; }, }), /Hook installation failed/, ); assert.equal(await readFile(activeShim, 'utf8'), 'peer'); assert.equal(restored, false); }); test("a competing installer that replaces this installer's active set is preserved", async () => { const root = await fixture('concurrent-after-rename'); const active = path.join(root, '.husky', '_'); const activeShim = path.join(active, 'h'); let restored = false; await assert.rejects( installHooks({ root, quarantineRoot: path.join(fixtureRoot, 'concurrent-after-rename-quarantine'), runHusky: async (stagingHooks) => { await mkdir(path.join(stagingHooks, '_'), { recursive: true }); await writeFile(path.join(stagingHooks, '_', 'h'), 'ours'); }, activateHooks: async () => { await rm(active, { recursive: true, force: true }); await mkdir(active, { recursive: true }); await writeFile(activeShim, 'peer'); throw new Error('our activation lost to peer'); }, readHooksPath: async () => null, restoreHooksPath: async () => { restored = true; }, }), /Hook installation failed/, ); assert.equal(await readFile(activeShim, 'utf8'), 'peer'); assert.equal(restored, false); }); test('an explicit interactive HUSKY=0 opt-out preserves existing hooks without running installer', async () => { const root = await fixture('disabled'); const activeShim = path.join(root, '.husky', '_', 'h'); await mkdir(path.dirname(activeShim), { recursive: true }); await writeFile(activeShim, 'preserved'); let ran = false; await installHooks({ root, disabled: true, runHusky: async () => { ran = true; }, }); assert.equal(ran, false); assert.equal(await readFile(activeShim, 'utf8'), 'preserved'); }); test('a successful install leaves a complete active hook set', async () => { const root = await fixture('success'); await installHooks({ root, quarantineRoot, runHusky: async (stagingHooks) => { await mkdir(path.join(stagingHooks, '_'), { recursive: true }); await writeFile(path.join(stagingHooks, '_', 'h'), 'complete'); }, activateHooks: async () => {}, readHooksPath: async () => null, restoreHooksPath: async () => {}, }); assert.equal(await exists(path.join(root, '.husky', '_', 'h')), true); });