Files
admin b723e2bd7d Reorganize: Move all skills to skills/ folder
- Created skills/ directory
- Moved 272 skills to skills/ subfolder
- Kept agents/ at root level
- Kept installation scripts and docs at root level

Repository structure:
- skills/           - All 272 skills from skills.sh
- agents/           - Agent definitions
- *.sh, *.ps1       - Installation scripts
- README.md, etc.   - Documentation

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-23 18:05:17 +00:00

363 lines
11 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { verifyConfig as main, type VerifyConfigOptions } from '../src/bin/verify-config.ts';
describe('verify-config', () => {
let tempDir: string;
let userConfigPath: string;
let projectConfigPath: string;
let capturedStdout: string[];
let capturedStderr: string[];
let originalConsoleLog: typeof console.log;
let originalConsoleError: typeof console.error;
beforeEach(() => {
// Create unique temp directory
tempDir = join(
tmpdir(),
`verify-config-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
);
mkdirSync(tempDir, { recursive: true });
// Set up paths
userConfigPath = join(tempDir, '.cc-safety-net', 'config.json');
projectConfigPath = join(tempDir, '.safety-net.json');
// Capture console output
capturedStdout = [];
capturedStderr = [];
originalConsoleLog = console.log;
originalConsoleError = console.error;
console.log = (...args: unknown[]) => {
capturedStdout.push(args.map(String).join(' '));
};
console.error = (...args: unknown[]) => {
capturedStderr.push(args.map(String).join(' '));
};
});
afterEach(() => {
// Restore console
console.log = originalConsoleLog;
console.error = originalConsoleError;
// Clean up temp directory
if (existsSync(tempDir)) {
rmSync(tempDir, { recursive: true, force: true });
}
});
function writeUserConfig(content: string): void {
const dir = join(tempDir, '.cc-safety-net');
mkdirSync(dir, { recursive: true });
writeFileSync(userConfigPath, content, 'utf-8');
}
function writeProjectConfig(content: string): void {
writeFileSync(projectConfigPath, content, 'utf-8');
}
function runMain(): number {
const options: VerifyConfigOptions = {
userConfigPath,
projectConfigPath,
};
return main(options);
}
function getStdout(): string {
return capturedStdout.join('\n');
}
function getStderr(): string {
return capturedStderr.join('\n');
}
describe('no configs', () => {
test('returns zero when no configs exist', () => {
const result = runMain();
expect(result).toBe(0);
});
test('prints header', () => {
runMain();
const output = getStdout();
expect(output).toContain('Safety Net Config');
expect(output).toContain('═');
});
test('prints no configs message', () => {
runMain();
const output = getStdout();
expect(output).toContain('No config files found');
expect(output).toContain('Using built-in rules only');
});
});
describe('valid configs', () => {
test('user config only returns zero', () => {
writeUserConfig('{"version": 1}');
const result = runMain();
expect(result).toBe(0);
});
test('user config prints checkmark', () => {
writeUserConfig('{"version": 1}');
runMain();
const output = getStdout();
expect(output).toContain('✓ User config:');
});
test('user config shows rules none', () => {
writeUserConfig('{"version": 1}');
runMain();
const output = getStdout();
expect(output).toContain('Rules: (none)');
});
test('user config with rules shows numbered list', () => {
writeUserConfig(
JSON.stringify({
version: 1,
rules: [
{
name: 'block-foo',
command: 'foo',
block_args: ['-x'],
reason: 'Blocked',
},
{
name: 'block-bar',
command: 'bar',
block_args: ['-y'],
reason: 'Blocked',
},
],
}),
);
runMain();
const output = getStdout();
expect(output).toContain('Rules:');
expect(output).toContain('1. block-foo');
expect(output).toContain('2. block-bar');
});
test('project config only returns zero', () => {
writeProjectConfig('{"version": 1}');
const result = runMain();
expect(result).toBe(0);
});
test('project config prints checkmark', () => {
writeProjectConfig('{"version": 1}');
runMain();
const output = getStdout();
expect(output).toContain('✓ Project config:');
});
test('both configs returns zero', () => {
writeUserConfig('{"version": 1}');
writeProjectConfig('{"version": 1}');
const result = runMain();
expect(result).toBe(0);
});
test('both configs prints both checkmarks', () => {
writeUserConfig('{"version": 1}');
writeProjectConfig('{"version": 1}');
runMain();
const output = getStdout();
expect(output).toContain('✓ User config:');
expect(output).toContain('✓ Project config:');
});
test('valid config prints success message', () => {
writeProjectConfig('{"version": 1}');
runMain();
const output = getStdout();
expect(output).toContain('All configs valid.');
});
});
describe('invalid configs', () => {
test('invalid user config returns one', () => {
writeUserConfig('{"version": 2}');
const result = runMain();
expect(result).toBe(1);
});
test('invalid user config prints x mark', () => {
writeUserConfig('{"version": 2}');
runMain();
const output = getStderr();
expect(output).toContain('✗ User config:');
});
test('invalid config shows numbered errors', () => {
writeUserConfig('{"version": 2}');
runMain();
const output = getStderr();
expect(output).toContain('Errors:');
expect(output).toContain('1.');
expect(output).toContain('version');
});
test('invalid project config returns one', () => {
writeProjectConfig('{"rules": []}');
const result = runMain();
expect(result).toBe(1);
});
test('invalid project config prints x mark', () => {
writeProjectConfig('{"rules": []}');
runMain();
const output = getStderr();
expect(output).toContain('✗ Project config:');
});
test('both invalid returns one', () => {
writeUserConfig('{"version": 2}');
writeProjectConfig('{"rules": []}');
const result = runMain();
expect(result).toBe(1);
});
test('both invalid prints both errors', () => {
writeUserConfig('{"version": 2}');
writeProjectConfig('{"rules": []}');
runMain();
const output = getStderr();
expect(output).toContain('✗ User config:');
expect(output).toContain('✗ Project config:');
});
test('invalid json prints error', () => {
writeProjectConfig('{ not valid json }');
runMain();
const output = getStderr();
expect(output).toContain('✗ Project config:');
});
test('validation failed message', () => {
writeProjectConfig('{"version": 2}');
runMain();
const output = getStderr();
expect(output).toContain('Config validation failed.');
});
});
describe('mixed validity', () => {
test('valid user invalid project returns one', () => {
writeUserConfig('{"version": 1}');
writeProjectConfig('{"version": 2}');
const result = runMain();
expect(result).toBe(1);
});
test('valid user invalid project shows both', () => {
writeUserConfig('{"version": 1}');
writeProjectConfig('{"version": 2}');
runMain();
const stdout = getStdout();
const stderr = getStderr();
expect(stdout).toContain('✓ User config:');
expect(stderr).toContain('✗ Project config:');
});
test('invalid user valid project returns one', () => {
writeUserConfig('{"version": 2}');
writeProjectConfig('{"version": 1}');
const result = runMain();
expect(result).toBe(1);
});
test('invalid user valid project shows both', () => {
writeUserConfig('{"version": 2}');
writeProjectConfig('{"version": 1}');
runMain();
const stdout = getStdout();
const stderr = getStderr();
expect(stderr).toContain('✗ User config:');
expect(stdout).toContain('✓ Project config:');
});
});
describe('schema auto-add', () => {
function readProjectConfig(): Record<string, unknown> {
return JSON.parse(readFileSync(projectConfigPath, 'utf-8'));
}
function readUserConfig(): Record<string, unknown> {
return JSON.parse(readFileSync(userConfigPath, 'utf-8'));
}
test('adds $schema to valid project config missing it', () => {
writeProjectConfig('{"version": 1}');
runMain();
const config = readProjectConfig();
expect(config.$schema).toBe(
'https://raw.githubusercontent.com/kenryu42/claude-code-safety-net/main/assets/cc-safety-net.schema.json',
);
});
test('adds $schema to valid user config missing it', () => {
writeUserConfig('{"version": 1}');
runMain();
const config = readUserConfig();
expect(config.$schema).toBe(
'https://raw.githubusercontent.com/kenryu42/claude-code-safety-net/main/assets/cc-safety-net.schema.json',
);
});
test('does not modify config that already has $schema', () => {
const originalConfig = {
$schema:
'https://raw.githubusercontent.com/kenryu42/claude-code-safety-net/main/assets/cc-safety-net.schema.json',
version: 1,
};
writeProjectConfig(JSON.stringify(originalConfig, null, 2));
runMain();
const config = readProjectConfig();
expect(config).toEqual(originalConfig);
});
test('preserves existing rules when adding $schema', () => {
const originalConfig = {
version: 1,
rules: [
{
name: 'block-foo',
command: 'foo',
block_args: ['-x'],
reason: 'Blocked',
},
],
};
writeProjectConfig(JSON.stringify(originalConfig));
runMain();
const config = readProjectConfig();
expect(config.$schema).toBe(
'https://raw.githubusercontent.com/kenryu42/claude-code-safety-net/main/assets/cc-safety-net.schema.json',
);
expect(config.version).toBe(1);
expect(config.rules).toEqual(originalConfig.rules);
});
test('does not add $schema to invalid config', () => {
writeProjectConfig('{"version": 2}');
runMain();
const config = readProjectConfig();
expect(config.$schema).toBeUndefined();
});
test('prints message when $schema is added', () => {
writeProjectConfig('{"version": 1}');
runMain();
const output = getStdout();
expect(output).toContain('Added $schema');
});
});
});