397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
117 lines
4.0 KiB
JavaScript
117 lines
4.0 KiB
JavaScript
// tools/graphify-safe-update.test.mjs
|
|
// Pure-helper tests for the post-commit safe-update wrapper.
|
|
// Covers scope filtering + code/doc partitioning — the parts that gated
|
|
// the spike-bloat incident (ADR-017 § "Стратегия обновлений").
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { filterInScope, partitionByExtension } from './graphify-safe-update.mjs';
|
|
|
|
const ALLOWED_SCOPES = ['docs/', '.claude/', 'app/'];
|
|
const SCAN_EXCLUDE_DIRS = ['node_modules/', 'vendor/', '__pycache__/', '.git/'];
|
|
const CODE_EXTS = new Set(['.php', '.ts', '.js', '.vue', '.mjs', '.cjs', '.py', '.go']);
|
|
|
|
describe('filterInScope', () => {
|
|
it('keeps files inside allowed scopes', () => {
|
|
const input = ['docs/foo.md', '.claude/skills/x/SKILL.md', 'app/Models/Deal.php'];
|
|
expect(filterInScope(input, ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS)).toEqual(input);
|
|
});
|
|
|
|
it('rejects files outside allowed scopes (tools/, root .md, bin/)', () => {
|
|
const input = [
|
|
'tools/graphify-safe-update.mjs',
|
|
'CHANGELOG.md',
|
|
'bin/something.exe',
|
|
'package.json',
|
|
'README.md',
|
|
];
|
|
expect(filterInScope(input, ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS)).toEqual([]);
|
|
});
|
|
|
|
it('rejects vendor/node_modules even if nominally under app/', () => {
|
|
const input = [
|
|
'app/vendor/laravel/framework/Foo.php',
|
|
'app/node_modules/foo/bar.js',
|
|
'app/__pycache__/x.pyc',
|
|
'app/.git/HEAD',
|
|
];
|
|
expect(filterInScope(input, ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS)).toEqual([]);
|
|
});
|
|
|
|
it('keeps real app/ files alongside vendor exclusions', () => {
|
|
const input = [
|
|
'app/app/Services/LedgerService.php',
|
|
'app/vendor/laravel/framework/Container.php',
|
|
'app/resources/js/views/Dashboard.vue',
|
|
];
|
|
expect(filterInScope(input, ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS)).toEqual([
|
|
'app/app/Services/LedgerService.php',
|
|
'app/resources/js/views/Dashboard.vue',
|
|
]);
|
|
});
|
|
|
|
it('returns empty array on empty input', () => {
|
|
expect(filterInScope([], ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS)).toEqual([]);
|
|
});
|
|
|
|
it('handles mixed in-scope and out-of-scope', () => {
|
|
const input = [
|
|
'docs/CLAUDE.md',
|
|
'tools/x.mjs',
|
|
'app/Models/Deal.php',
|
|
'CHANGELOG.md',
|
|
'.claude/skills/y/SKILL.md',
|
|
];
|
|
expect(filterInScope(input, ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS)).toEqual([
|
|
'docs/CLAUDE.md',
|
|
'app/Models/Deal.php',
|
|
'.claude/skills/y/SKILL.md',
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('partitionByExtension', () => {
|
|
it('puts .php/.ts/.vue/.mjs into code', () => {
|
|
const input = ['app/Foo.php', 'app/bar.ts', 'app/baz.vue', 'app/qux.mjs'];
|
|
const { codeFiles, docFiles } = partitionByExtension(input, CODE_EXTS);
|
|
expect(codeFiles).toEqual(input);
|
|
expect(docFiles).toEqual([]);
|
|
});
|
|
|
|
it('puts .md/.txt into doc', () => {
|
|
const input = ['docs/foo.md', '.claude/bar.md', 'app/README.txt'];
|
|
const { codeFiles, docFiles } = partitionByExtension(input, CODE_EXTS);
|
|
expect(codeFiles).toEqual([]);
|
|
expect(docFiles).toEqual(input);
|
|
});
|
|
|
|
it('partitions mixed list correctly', () => {
|
|
const input = [
|
|
'app/Models/Deal.php',
|
|
'docs/spec.md',
|
|
'app/resources/js/utils.ts',
|
|
'.claude/skills/x/SKILL.md',
|
|
'app/composer.json',
|
|
];
|
|
const { codeFiles, docFiles } = partitionByExtension(input, CODE_EXTS);
|
|
expect(codeFiles).toEqual(['app/Models/Deal.php', 'app/resources/js/utils.ts']);
|
|
expect(docFiles).toEqual([
|
|
'docs/spec.md',
|
|
'.claude/skills/x/SKILL.md',
|
|
'app/composer.json',
|
|
]);
|
|
});
|
|
|
|
it('is case-insensitive on extension', () => {
|
|
const input = ['app/Foo.PHP', 'app/Bar.TS', 'docs/README.MD'];
|
|
const { codeFiles, docFiles } = partitionByExtension(input, CODE_EXTS);
|
|
expect(codeFiles).toEqual(['app/Foo.PHP', 'app/Bar.TS']);
|
|
expect(docFiles).toEqual(['docs/README.MD']);
|
|
});
|
|
|
|
it('returns empty arrays on empty input', () => {
|
|
const { codeFiles, docFiles } = partitionByExtension([], CODE_EXTS);
|
|
expect(codeFiles).toEqual([]);
|
|
expect(docFiles).toEqual([]);
|
|
});
|
|
});
|