GitNexus/.devcontainer/verify-git-hooks.test.cjs
Gergő Magyar b961e695ca
Some checks failed
Devcontainer Smoke / Config-transform unit tests (push) Has been cancelled
Devcontainer Smoke / Build devcontainer image (default) (push) Has been cancelled
Publish Development Images / publish (core) (push) Has been cancelled
Publish Development Images / publish (ui) (push) Has been cancelled
Devcontainer Smoke / Build devcontainer image (ui) (push) Has been cancelled
CodeQL / Analyze (python) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / ci (push) Blocked by required conditions
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Skill copy sync / shipped skills drift guard (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
fix(devcontainer): refresh Node and streamline development (#3610)
2026-10-17 21:05:13 +00:00

158 lines
6.4 KiB
JavaScript

'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { execFileSync, spawnSync } = require('node:child_process');
const verifier = path.join(__dirname, 'verify-git-hooks.cjs');
const husky = path.resolve(__dirname, '../node_modules/husky/bin.js');
function fixture(t, install = true) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'gn-husky-'));
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
const globalConfig = path.join(root, 'global-gitconfig');
fs.writeFileSync(globalConfig, '');
const env = {
...process.env,
HUSKY: '',
GIT_CONFIG_GLOBAL: globalConfig,
GIT_CONFIG_NOSYSTEM: '1',
GIT_CONFIG_COUNT: '0',
XDG_CONFIG_HOME: path.join(root, 'xdg'),
};
for (const key of ['GIT_DIR', 'GIT_COMMON_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE'])
delete env[key];
const options = { cwd: root, env, encoding: 'utf8' };
const git = (...args) => execFileSync('git', args, options);
git('init', '-q');
git('config', 'user.name', 'Husky test');
git('config', 'user.email', 'husky-test@example.invalid');
fs.mkdirSync(path.join(root, '.husky'));
fs.copyFileSync(
path.resolve(__dirname, '../.husky/pre-commit'),
path.join(root, '.husky/pre-commit'),
);
if (install) execFileSync(process.execPath, [husky], options);
const verify = (overrides = {}) =>
spawnSync(process.execPath, [verifier], { ...options, env: { ...env, ...overrides } });
const commit = () => spawnSync('git', ['commit', '--allow-empty', '-m', 'hook test'], options);
const tool = (file, status) => {
const target = path.join(root, file);
fs.mkdirSync(path.dirname(target), { recursive: true });
fs.writeFileSync(target, `#!/bin/sh\nprintf called > "$0.called"\nexit ${status}\n`, {
mode: 0o755,
});
return `${target}.called`;
};
return { root, git, verify, commit, tool };
}
test('Husky verification accepts a real installation', (t) => {
const repo = fixture(t);
const result = repo.verify();
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /installed and enabled/);
});
test('Husky verification rejects missing Git hook configuration', (t) => {
const repo = fixture(t, false);
assert.equal(repo.verify().status, 1);
});
test('Husky verification rejects a cleared cache without removing the tracked hook', (t) => {
const repo = fixture(t);
fs.rmSync(path.join(repo.root, '.husky/_'), { recursive: true });
assert.equal(repo.verify().status, 1);
assert.ok(fs.existsSync(path.join(repo.root, '.husky/pre-commit')));
});
test('Husky verification rejects hooks redirected elsewhere', (t) => {
const repo = fixture(t);
repo.git('config', 'core.hooksPath', 'other-hooks');
assert.equal(repo.verify().status, 1);
});
test('Husky verification rejects a missing tracked pre-commit script', (t) => {
const repo = fixture(t);
fs.rmSync(path.join(repo.root, '.husky/pre-commit'));
assert.equal(repo.verify().status, 1);
});
test('Husky verification rejects a non-executable Git hook', (t) => {
const repo = fixture(t);
fs.chmodSync(path.join(repo.root, '.husky/_/pre-commit'), 0o644);
assert.equal(repo.verify().status, 1);
});
test('Husky verification rejects an explicitly disabled hook', (t) => {
const repo = fixture(t);
const result = repo.verify({ HUSKY: '0' });
assert.equal(result.status, 1);
assert.match(result.stderr, /HUSKY=0 disables/);
});
for (const init of ['export HUSKY=0\n', 'exit 0\n']) {
test(`Husky verification rejects startup configuration ${init.trim()}`, (t) => {
const repo = fixture(t);
const marker = repo.tool('node_modules/.bin/lint-staged', 1);
fs.mkdirSync(path.join(repo.root, 'xdg/husky'), { recursive: true });
fs.writeFileSync(path.join(repo.root, 'xdg/husky/init.sh'), init);
const result = repo.verify();
assert.equal(result.status, 1);
assert.match(result.stderr, /startup configuration disables or skips/);
// Demonstrate why the verifier must fail: real Git would skip the formatter.
assert.equal(repo.commit().status, 0);
assert.equal(fs.existsSync(marker), false);
});
}
test('Husky probe accepts enabled startup configuration without running the tracked hook', (t) => {
const repo = fixture(t);
const marker = repo.tool('node_modules/.bin/lint-staged', 1);
fs.mkdirSync(path.join(repo.root, 'xdg/husky'), { recursive: true });
fs.writeFileSync(path.join(repo.root, 'xdg/husky/init.sh'), 'export HUSKY=1\nprintf startup\n');
assert.equal(repo.verify().status, 0);
assert.equal(fs.existsSync(marker), false);
assert.equal(repo.commit().status, 1);
assert.equal(fs.existsSync(marker), true);
});
test('Git rejects a commit when the actual pre-commit formatter fails', (t) => {
const repo = fixture(t);
const marker = repo.tool('node_modules/.bin/lint-staged', 1);
assert.equal(repo.verify().status, 0);
const result = repo.commit();
assert.equal(result.status, 1, result.stderr);
assert.match(result.stdout + result.stderr, /pre-commit: formatting staged files/);
assert.equal(fs.readFileSync(marker, 'utf8'), 'called');
assert.throws(() => repo.git('rev-parse', '--verify', 'HEAD'));
});
for (const pkg of ['gitnexus', 'gitnexus-web']) {
test(`Git rejects a commit when the actual ${pkg} pre-commit typecheck fails`, (t) => {
const repo = fixture(t);
repo.tool('node_modules/.bin/lint-staged', 0);
const marker = repo.tool(`${pkg}/node_modules/.bin/tsc`, 1);
fs.writeFileSync(path.join(repo.root, pkg, 'change.ts'), 'export const value = 1;\n');
repo.git('add', `${pkg}/change.ts`);
assert.equal(repo.verify().status, 0);
const result = repo.commit();
assert.equal(result.status, 1, result.stderr);
assert.match(result.stdout + result.stderr, new RegExp(`pre-commit: typechecking ${pkg}`));
assert.equal(fs.readFileSync(marker, 'utf8'), 'called');
assert.throws(() => repo.git('rev-parse', '--verify', 'HEAD'));
});
}
test('Git accepts a commit when the actual pre-commit checks pass', (t) => {
const repo = fixture(t);
repo.tool('node_modules/.bin/lint-staged', 0);
assert.equal(repo.verify().status, 0);
const result = repo.commit();
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout + result.stderr, /pre-commit: all checks passed/);
assert.ok(repo.git('rev-parse', '--verify', 'HEAD').trim());
});