mirror of
https://github.com/affaan-m/ECC.git
synced 2026-08-17 21:15:40 +02:00
* feat(install): add guided Claude plugin setup * fix: support Claude command shims on Windows * feat: support safe Claude plugin scope migration * fix(install): preserve interactive setup terminal * fix(install): auto-migrate setup scope changes * feat(install): add guided multi-harness installer * fix(install): sync Yarn binary metadata * fix(install): handle wizard EOF on Node 18 * ci: allow installer matrix tests to finish * test(install): allow slower PowerShell delegation * fix(install): harden guided provider reconciliation * test(install): harden packaged and local compatibility * chore: prepare guided installer release 2.2.0 * fix(install): report refreshed Codex marketplace state * fix(install): verify managed content provenance * test(install): allow empty Yarn smoke fixture * test(install): invoke Windows package shims safely * fix(install): close cross-platform release gaps * fix(install): require trusted GitHub origins * fix(install): preserve hook profile precedence * refactor(install): centralize trusted GitHub origins * ci: retrigger workflow run after merge of main Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
581 lines
20 KiB
JavaScript
581 lines
20 KiB
JavaScript
/**
|
|
* Tests for scripts/lib/hook-flags.js
|
|
*
|
|
* Run with: node tests/hooks/hook-flags.test.js
|
|
*/
|
|
|
|
const assert = require('assert');
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
const { spawnSync } = require('child_process');
|
|
|
|
// Import the module
|
|
const {
|
|
VALID_PROFILES,
|
|
normalizeId,
|
|
parseBoolean,
|
|
readManagedHookConfig,
|
|
areHooksEnabled,
|
|
getHookProfile,
|
|
getDisabledHookIds,
|
|
parseProfiles,
|
|
isHookEnabled,
|
|
} = require('../../scripts/lib/hook-flags');
|
|
|
|
// Test helper
|
|
function test(name, fn) {
|
|
try {
|
|
fn();
|
|
console.log(` ✓ ${name}`);
|
|
return true;
|
|
} catch (err) {
|
|
console.log(` ✗ ${name}`);
|
|
console.log(` Error: ${err.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Helper to save and restore env vars
|
|
function withEnv(vars, fn) {
|
|
const saved = {};
|
|
for (const key of Object.keys(vars)) {
|
|
saved[key] = process.env[key];
|
|
if (vars[key] === undefined) {
|
|
delete process.env[key];
|
|
} else {
|
|
process.env[key] = vars[key];
|
|
}
|
|
}
|
|
try {
|
|
fn();
|
|
} finally {
|
|
for (const key of Object.keys(saved)) {
|
|
if (saved[key] === undefined) {
|
|
delete process.env[key];
|
|
} else {
|
|
process.env[key] = saved[key];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test suite
|
|
function runTests() {
|
|
console.log('\n=== Testing hook-flags.js ===\n');
|
|
|
|
let passed = 0;
|
|
let failed = 0;
|
|
|
|
// VALID_PROFILES tests
|
|
console.log('VALID_PROFILES:');
|
|
|
|
if (test('is a Set', () => {
|
|
assert.ok(VALID_PROFILES instanceof Set);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('contains minimal, standard, strict', () => {
|
|
assert.ok(VALID_PROFILES.has('minimal'));
|
|
assert.ok(VALID_PROFILES.has('standard'));
|
|
assert.ok(VALID_PROFILES.has('strict'));
|
|
})) passed++; else failed++;
|
|
|
|
if (test('contains exactly 3 profiles', () => {
|
|
assert.strictEqual(VALID_PROFILES.size, 3);
|
|
})) passed++; else failed++;
|
|
|
|
console.log('\nHook preference sources:');
|
|
|
|
if (test('hooks default enabled when no preference source exists', () => {
|
|
withEnv({
|
|
ECC_HOOKS_ENABLED: undefined,
|
|
CLAUDE_PLUGIN_OPTION_HOOKS_ENABLED: undefined,
|
|
ECC_HOOK_CONFIG: undefined,
|
|
CLAUDE_PLUGIN_ROOT: undefined,
|
|
ECC_PLUGIN_ROOT: undefined,
|
|
}, () => {
|
|
assert.strictEqual(areHooksEnabled(), true);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('Claude plugin options control enabled state and profile', () => {
|
|
withEnv({
|
|
ECC_HOOKS_ENABLED: undefined,
|
|
ECC_HOOK_PROFILE: undefined,
|
|
CLAUDE_PLUGIN_OPTION_HOOKS_ENABLED: 'false',
|
|
CLAUDE_PLUGIN_OPTION_HOOK_PROFILE: 'minimal',
|
|
ECC_HOOK_CONFIG: undefined,
|
|
}, () => {
|
|
assert.strictEqual(areHooksEnabled(), false);
|
|
assert.strictEqual(getHookProfile(), 'minimal');
|
|
assert.strictEqual(
|
|
isHookEnabled('pre:test', { profiles: 'minimal,standard,strict' }),
|
|
false
|
|
);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('explicit ECC environment overrides Claude plugin options', () => {
|
|
withEnv({
|
|
ECC_HOOKS_ENABLED: 'true',
|
|
ECC_HOOK_PROFILE: 'strict',
|
|
CLAUDE_PLUGIN_OPTION_HOOKS_ENABLED: 'false',
|
|
CLAUDE_PLUGIN_OPTION_HOOK_PROFILE: 'minimal',
|
|
}, () => {
|
|
assert.strictEqual(areHooksEnabled(), true);
|
|
assert.strictEqual(getHookProfile(), 'strict');
|
|
});
|
|
assert.strictEqual(
|
|
getHookProfile({
|
|
ECC_HOOK_PROFILE: '',
|
|
CLAUDE_PLUGIN_OPTION_HOOK_PROFILE: 'minimal',
|
|
}),
|
|
'standard',
|
|
'an explicit empty ECC profile must not fall through to plugin config'
|
|
);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('managed hook config is used after explicit and plugin preferences', () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-hook-flags-'));
|
|
const configPath = path.join(root, 'ecc', 'setup.json');
|
|
try {
|
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
fs.writeFileSync(configPath, JSON.stringify({
|
|
hooks: { enabled: false, profile: 'minimal' },
|
|
}));
|
|
withEnv({
|
|
ECC_HOOKS_ENABLED: undefined,
|
|
ECC_HOOK_PROFILE: undefined,
|
|
CLAUDE_PLUGIN_OPTION_HOOKS_ENABLED: undefined,
|
|
CLAUDE_PLUGIN_OPTION_HOOK_PROFILE: undefined,
|
|
ECC_HOOK_CONFIG: configPath,
|
|
}, () => {
|
|
assert.deepStrictEqual(readManagedHookConfig(), {
|
|
enabled: false,
|
|
profile: 'minimal',
|
|
});
|
|
assert.strictEqual(areHooksEnabled(), false);
|
|
assert.strictEqual(getHookProfile(), 'minimal');
|
|
});
|
|
} finally {
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
}
|
|
})) passed++; else failed++;
|
|
|
|
if (test('a hook evaluation reads managed config only once', () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-hook-flags-read-once-'));
|
|
const configPath = path.join(root, 'setup.json');
|
|
const originalReadFileSync = fs.readFileSync;
|
|
let configReadCount = 0;
|
|
try {
|
|
fs.writeFileSync(configPath, JSON.stringify({
|
|
hooks: { enabled: true, profile: 'minimal' },
|
|
}));
|
|
fs.readFileSync = (...args) => {
|
|
if (args[0] === configPath) configReadCount += 1;
|
|
return originalReadFileSync(...args);
|
|
};
|
|
assert.strictEqual(isHookEnabled('pre:test', {
|
|
env: { ECC_HOOK_CONFIG: configPath },
|
|
profiles: ['minimal'],
|
|
}), true);
|
|
assert.strictEqual(configReadCount, 1);
|
|
} finally {
|
|
fs.readFileSync = originalReadFileSync;
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
}
|
|
})) passed++; else failed++;
|
|
|
|
if (test('malformed managed config emits one sanitized diagnostic', () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-hook-flags-invalid-'));
|
|
const configPath = path.join(root, 'setup.json');
|
|
const originalWrite = process.stderr.write;
|
|
const diagnostics = [];
|
|
try {
|
|
fs.writeFileSync(configPath, '{"hooks":\u001b[31m');
|
|
process.stderr.write = value => {
|
|
diagnostics.push(String(value));
|
|
return true;
|
|
};
|
|
assert.deepStrictEqual(readManagedHookConfig({ ECC_HOOK_CONFIG: configPath }), {});
|
|
assert.strictEqual(diagnostics.length, 1);
|
|
assert.match(diagnostics[0], /Warning: unable to read managed ECC hook config/);
|
|
assert.strictEqual(diagnostics[0].includes('\u001b'), false);
|
|
assert.match(diagnostics[0], /setup\.json/);
|
|
} finally {
|
|
process.stderr.write = originalWrite;
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
}
|
|
})) passed++; else failed++;
|
|
|
|
if (test('boolean parsing recognizes supported values and uses its fallback', () => {
|
|
for (const value of ['1', 'true', 'yes', 'on']) {
|
|
assert.strictEqual(parseBoolean(value, false), true);
|
|
}
|
|
for (const value of ['0', 'false', 'no', 'off']) {
|
|
assert.strictEqual(parseBoolean(value, true), false);
|
|
}
|
|
assert.strictEqual(parseBoolean('invalid', false), false);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('run-with-flags suppresses wrapper hooks when plugin hooks are off', () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-hook-wrapper-'));
|
|
const markerPath = path.join(root, 'ran.txt');
|
|
const hookPath = path.join(root, 'marker.js');
|
|
const runner = path.join(__dirname, '..', '..', 'scripts', 'hooks', 'run-with-flags.js');
|
|
const raw = JSON.stringify({ hook_event_name: 'PreToolUse', tool_name: 'Write' });
|
|
try {
|
|
fs.writeFileSync(
|
|
hookPath,
|
|
`'use strict';\nconst fs=require('fs');\nmodule.exports.run=function(raw){fs.writeFileSync(${JSON.stringify(markerPath)},'ran');return raw;};\n`
|
|
);
|
|
const env = {
|
|
...process.env,
|
|
CLAUDE_PLUGIN_ROOT: root,
|
|
CLAUDE_PLUGIN_OPTION_HOOKS_ENABLED: 'false',
|
|
};
|
|
delete env.ECC_HOOKS_ENABLED;
|
|
const result = spawnSync(process.execPath, [
|
|
runner,
|
|
'pre:test:marker',
|
|
'marker.js',
|
|
'minimal,standard,strict',
|
|
], {
|
|
cwd: root,
|
|
env,
|
|
input: raw,
|
|
encoding: 'utf8',
|
|
});
|
|
assert.strictEqual(result.status, 0, result.stderr);
|
|
assert.strictEqual(result.stdout, raw);
|
|
assert.ok(!fs.existsSync(markerPath), 'disabled wrapper hook must not execute');
|
|
} finally {
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
}
|
|
})) passed++; else failed++;
|
|
|
|
// normalizeId tests
|
|
console.log('\nnormalizeId:');
|
|
|
|
if (test('returns empty string for null', () => {
|
|
assert.strictEqual(normalizeId(null), '');
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns empty string for undefined', () => {
|
|
assert.strictEqual(normalizeId(undefined), '');
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns empty string for empty string', () => {
|
|
assert.strictEqual(normalizeId(''), '');
|
|
})) passed++; else failed++;
|
|
|
|
if (test('trims whitespace', () => {
|
|
assert.strictEqual(normalizeId(' hello '), 'hello');
|
|
})) passed++; else failed++;
|
|
|
|
if (test('converts to lowercase', () => {
|
|
assert.strictEqual(normalizeId('MyHook'), 'myhook');
|
|
})) passed++; else failed++;
|
|
|
|
if (test('handles mixed case with whitespace', () => {
|
|
assert.strictEqual(normalizeId(' My-Hook-ID '), 'my-hook-id');
|
|
})) passed++; else failed++;
|
|
|
|
if (test('converts numbers to string', () => {
|
|
assert.strictEqual(normalizeId(123), '123');
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns empty string for whitespace-only input', () => {
|
|
assert.strictEqual(normalizeId(' '), '');
|
|
})) passed++; else failed++;
|
|
|
|
// getHookProfile tests
|
|
console.log('\ngetHookProfile:');
|
|
|
|
if (test('defaults to standard when env var not set', () => {
|
|
withEnv({
|
|
ECC_HOOK_PROFILE: undefined,
|
|
CLAUDE_PLUGIN_OPTION_HOOK_PROFILE: undefined,
|
|
ECC_HOOK_CONFIG: undefined,
|
|
CLAUDE_PLUGIN_ROOT: undefined,
|
|
ECC_PLUGIN_ROOT: undefined,
|
|
}, () => {
|
|
assert.strictEqual(getHookProfile(), 'standard');
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns minimal when set to minimal', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'minimal' }, () => {
|
|
assert.strictEqual(getHookProfile(), 'minimal');
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns standard when set to standard', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'standard' }, () => {
|
|
assert.strictEqual(getHookProfile(), 'standard');
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns strict when set to strict', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'strict' }, () => {
|
|
assert.strictEqual(getHookProfile(), 'strict');
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('is case-insensitive', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'STRICT' }, () => {
|
|
assert.strictEqual(getHookProfile(), 'strict');
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('trims whitespace from env var', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: ' minimal ' }, () => {
|
|
assert.strictEqual(getHookProfile(), 'minimal');
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('defaults to standard for invalid value', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'invalid' }, () => {
|
|
assert.strictEqual(getHookProfile(), 'standard');
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('defaults to standard for empty string', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: '' }, () => {
|
|
assert.strictEqual(getHookProfile(), 'standard');
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
// getDisabledHookIds tests
|
|
console.log('\ngetDisabledHookIds:');
|
|
|
|
if (test('returns empty Set when env var not set', () => {
|
|
withEnv({ ECC_DISABLED_HOOKS: undefined }, () => {
|
|
const result = getDisabledHookIds();
|
|
assert.ok(result instanceof Set);
|
|
assert.strictEqual(result.size, 0);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns empty Set for empty string', () => {
|
|
withEnv({ ECC_DISABLED_HOOKS: '' }, () => {
|
|
assert.strictEqual(getDisabledHookIds().size, 0);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns empty Set for whitespace-only string', () => {
|
|
withEnv({ ECC_DISABLED_HOOKS: ' ' }, () => {
|
|
assert.strictEqual(getDisabledHookIds().size, 0);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('parses single hook id', () => {
|
|
withEnv({ ECC_DISABLED_HOOKS: 'my-hook' }, () => {
|
|
const result = getDisabledHookIds();
|
|
assert.strictEqual(result.size, 1);
|
|
assert.ok(result.has('my-hook'));
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('parses multiple comma-separated hook ids', () => {
|
|
withEnv({ ECC_DISABLED_HOOKS: 'hook-a,hook-b,hook-c' }, () => {
|
|
const result = getDisabledHookIds();
|
|
assert.strictEqual(result.size, 3);
|
|
assert.ok(result.has('hook-a'));
|
|
assert.ok(result.has('hook-b'));
|
|
assert.ok(result.has('hook-c'));
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('trims whitespace around hook ids', () => {
|
|
withEnv({ ECC_DISABLED_HOOKS: ' hook-a , hook-b ' }, () => {
|
|
const result = getDisabledHookIds();
|
|
assert.strictEqual(result.size, 2);
|
|
assert.ok(result.has('hook-a'));
|
|
assert.ok(result.has('hook-b'));
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('normalizes hook ids to lowercase', () => {
|
|
withEnv({ ECC_DISABLED_HOOKS: 'MyHook,ANOTHER' }, () => {
|
|
const result = getDisabledHookIds();
|
|
assert.ok(result.has('myhook'));
|
|
assert.ok(result.has('another'));
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('filters out empty entries from trailing commas', () => {
|
|
withEnv({ ECC_DISABLED_HOOKS: 'hook-a,,hook-b,' }, () => {
|
|
const result = getDisabledHookIds();
|
|
assert.strictEqual(result.size, 2);
|
|
assert.ok(result.has('hook-a'));
|
|
assert.ok(result.has('hook-b'));
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
// parseProfiles tests
|
|
console.log('\nparseProfiles:');
|
|
|
|
if (test('returns fallback for null input', () => {
|
|
const result = parseProfiles(null);
|
|
assert.deepStrictEqual(result, ['standard', 'strict']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns fallback for undefined input', () => {
|
|
const result = parseProfiles(undefined);
|
|
assert.deepStrictEqual(result, ['standard', 'strict']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('uses custom fallback when provided', () => {
|
|
const result = parseProfiles(null, ['minimal']);
|
|
assert.deepStrictEqual(result, ['minimal']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('parses comma-separated string', () => {
|
|
const result = parseProfiles('minimal,strict');
|
|
assert.deepStrictEqual(result, ['minimal', 'strict']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('parses single string value', () => {
|
|
const result = parseProfiles('strict');
|
|
assert.deepStrictEqual(result, ['strict']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('parses array of profiles', () => {
|
|
const result = parseProfiles(['minimal', 'standard']);
|
|
assert.deepStrictEqual(result, ['minimal', 'standard']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('filters invalid profiles from string', () => {
|
|
const result = parseProfiles('minimal,invalid,strict');
|
|
assert.deepStrictEqual(result, ['minimal', 'strict']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('filters invalid profiles from array', () => {
|
|
const result = parseProfiles(['minimal', 'bogus', 'strict']);
|
|
assert.deepStrictEqual(result, ['minimal', 'strict']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns fallback when all string values are invalid', () => {
|
|
const result = parseProfiles('invalid,bogus');
|
|
assert.deepStrictEqual(result, ['standard', 'strict']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns fallback when all array values are invalid', () => {
|
|
const result = parseProfiles(['invalid', 'bogus']);
|
|
assert.deepStrictEqual(result, ['standard', 'strict']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('is case-insensitive for string input', () => {
|
|
const result = parseProfiles('MINIMAL,STRICT');
|
|
assert.deepStrictEqual(result, ['minimal', 'strict']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('is case-insensitive for array input', () => {
|
|
const result = parseProfiles(['MINIMAL', 'STRICT']);
|
|
assert.deepStrictEqual(result, ['minimal', 'strict']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('trims whitespace in string input', () => {
|
|
const result = parseProfiles(' minimal , strict ');
|
|
assert.deepStrictEqual(result, ['minimal', 'strict']);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('handles null values in array', () => {
|
|
const result = parseProfiles([null, 'strict']);
|
|
assert.deepStrictEqual(result, ['strict']);
|
|
})) passed++; else failed++;
|
|
|
|
// isHookEnabled tests
|
|
console.log('\nisHookEnabled:');
|
|
|
|
if (test('returns true by default for a hook (standard profile)', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: undefined, ECC_DISABLED_HOOKS: undefined }, () => {
|
|
assert.strictEqual(isHookEnabled('my-hook'), true);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns true for empty hookId', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: undefined, ECC_DISABLED_HOOKS: undefined }, () => {
|
|
assert.strictEqual(isHookEnabled(''), true);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns true for null hookId', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: undefined, ECC_DISABLED_HOOKS: undefined }, () => {
|
|
assert.strictEqual(isHookEnabled(null), true);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns false when hook is in disabled list', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: undefined, ECC_DISABLED_HOOKS: 'my-hook' }, () => {
|
|
assert.strictEqual(isHookEnabled('my-hook'), false);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('disabled check is case-insensitive', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: undefined, ECC_DISABLED_HOOKS: 'MY-HOOK' }, () => {
|
|
assert.strictEqual(isHookEnabled('my-hook'), false);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns true when hook is not in disabled list', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: undefined, ECC_DISABLED_HOOKS: 'other-hook' }, () => {
|
|
assert.strictEqual(isHookEnabled('my-hook'), true);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns false when current profile is not in allowed profiles', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'minimal', ECC_DISABLED_HOOKS: undefined }, () => {
|
|
assert.strictEqual(isHookEnabled('my-hook', { profiles: 'strict' }), false);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns true when current profile is in allowed profiles', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'strict', ECC_DISABLED_HOOKS: undefined }, () => {
|
|
assert.strictEqual(isHookEnabled('my-hook', { profiles: 'standard,strict' }), true);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns true when current profile matches single allowed profile', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'minimal', ECC_DISABLED_HOOKS: undefined }, () => {
|
|
assert.strictEqual(isHookEnabled('my-hook', { profiles: 'minimal' }), true);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('disabled hooks take precedence over profile match', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'strict', ECC_DISABLED_HOOKS: 'my-hook' }, () => {
|
|
assert.strictEqual(isHookEnabled('my-hook', { profiles: 'strict' }), false);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('uses default profiles (standard, strict) when none specified', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'minimal', ECC_DISABLED_HOOKS: undefined }, () => {
|
|
assert.strictEqual(isHookEnabled('my-hook'), false);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('allows standard profile by default', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'standard', ECC_DISABLED_HOOKS: undefined }, () => {
|
|
assert.strictEqual(isHookEnabled('my-hook'), true);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('allows strict profile by default', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'strict', ECC_DISABLED_HOOKS: undefined }, () => {
|
|
assert.strictEqual(isHookEnabled('my-hook'), true);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
if (test('accepts array profiles option', () => {
|
|
withEnv({ ECC_HOOK_PROFILE: 'minimal', ECC_DISABLED_HOOKS: undefined }, () => {
|
|
assert.strictEqual(isHookEnabled('my-hook', { profiles: ['minimal', 'standard'] }), true);
|
|
});
|
|
})) passed++; else failed++;
|
|
|
|
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
|
process.exit(failed > 0 ? 1 : 0);
|
|
}
|
|
|
|
runTests();
|