mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-29 21:43:29 +08:00
test: cover CI catalog validator
This commit is contained in:
@@ -33,8 +33,8 @@ function normalizePathSegments(relativePath) {
|
|||||||
return relativePath.split(path.sep).join('/');
|
return relativePath.split(path.sep).join('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
function listMatchingFiles(relativeDir, matcher) {
|
function listMatchingFiles(root, relativeDir, matcher) {
|
||||||
const directory = path.join(ROOT, relativeDir);
|
const directory = path.join(root, relativeDir);
|
||||||
if (!fs.existsSync(directory)) {
|
if (!fs.existsSync(directory)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -45,11 +45,11 @@ function listMatchingFiles(relativeDir, matcher) {
|
|||||||
.sort();
|
.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildCatalog() {
|
function buildCatalog(root = ROOT) {
|
||||||
const agents = listMatchingFiles('agents', entry => entry.isFile() && entry.name.endsWith('.md'));
|
const agents = listMatchingFiles(root, 'agents', entry => entry.isFile() && entry.name.endsWith('.md'));
|
||||||
const commands = listMatchingFiles('commands', entry => entry.isFile() && entry.name.endsWith('.md'));
|
const commands = listMatchingFiles(root, 'commands', entry => entry.isFile() && entry.name.endsWith('.md'));
|
||||||
const skills = listMatchingFiles('skills', entry => (
|
const skills = listMatchingFiles(root, 'skills', entry => (
|
||||||
entry.isDirectory() && fs.existsSync(path.join(ROOT, 'skills', entry.name, 'SKILL.md'))
|
entry.isDirectory() && fs.existsSync(path.join(root, 'skills', entry.name, 'SKILL.md'))
|
||||||
)).map(skillDir => `${skillDir}/SKILL.md`);
|
)).map(skillDir => `${skillDir}/SKILL.md`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -540,33 +540,55 @@ function syncZhAgents(content, catalog) {
|
|||||||
return nextContent;
|
return nextContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DOCUMENT_SPECS = [
|
function createDocumentSpecs(paths = {}) {
|
||||||
|
const {
|
||||||
|
readmePath = README_PATH,
|
||||||
|
agentsPath = AGENTS_PATH,
|
||||||
|
zhRootReadmePath = README_ZH_CN_PATH,
|
||||||
|
zhDocsReadmePath = DOCS_ZH_CN_README_PATH,
|
||||||
|
zhDocsAgentsPath = DOCS_ZH_CN_AGENTS_PATH,
|
||||||
|
} = paths;
|
||||||
|
|
||||||
|
return [
|
||||||
{
|
{
|
||||||
filePath: README_PATH,
|
filePath: readmePath,
|
||||||
parseExpectations: parseReadmeExpectations,
|
parseExpectations: parseReadmeExpectations,
|
||||||
syncContent: syncEnglishReadme,
|
syncContent: syncEnglishReadme,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
filePath: AGENTS_PATH,
|
filePath: agentsPath,
|
||||||
parseExpectations: parseAgentsDocExpectations,
|
parseExpectations: parseAgentsDocExpectations,
|
||||||
syncContent: syncEnglishAgents,
|
syncContent: syncEnglishAgents,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
filePath: README_ZH_CN_PATH,
|
filePath: zhRootReadmePath,
|
||||||
parseExpectations: parseZhRootReadmeExpectations,
|
parseExpectations: parseZhRootReadmeExpectations,
|
||||||
syncContent: syncZhRootReadme,
|
syncContent: syncZhRootReadme,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
filePath: DOCS_ZH_CN_README_PATH,
|
filePath: zhDocsReadmePath,
|
||||||
parseExpectations: parseZhDocsReadmeExpectations,
|
parseExpectations: parseZhDocsReadmeExpectations,
|
||||||
syncContent: syncZhDocsReadme,
|
syncContent: syncZhDocsReadme,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
filePath: DOCS_ZH_CN_AGENTS_PATH,
|
filePath: zhDocsAgentsPath,
|
||||||
parseExpectations: parseZhAgentsDocExpectations,
|
parseExpectations: parseZhAgentsDocExpectations,
|
||||||
syncContent: syncZhAgents,
|
syncContent: syncZhAgents,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDocumentSpecsForRoot(root) {
|
||||||
|
return createDocumentSpecs({
|
||||||
|
readmePath: path.join(root, 'README.md'),
|
||||||
|
agentsPath: path.join(root, 'AGENTS.md'),
|
||||||
|
zhRootReadmePath: path.join(root, 'README.zh-CN.md'),
|
||||||
|
zhDocsReadmePath: path.join(root, 'docs', 'zh-CN', 'README.md'),
|
||||||
|
zhDocsAgentsPath: path.join(root, 'docs', 'zh-CN', 'AGENTS.md'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const DOCUMENT_SPECS = createDocumentSpecs();
|
||||||
|
|
||||||
function renderText(result) {
|
function renderText(result) {
|
||||||
console.log('Catalog counts:');
|
console.log('Catalog counts:');
|
||||||
@@ -608,11 +630,16 @@ function renderMarkdown(result) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() {
|
function runCatalogCheck(options = {}) {
|
||||||
const catalog = buildCatalog();
|
const root = options.root || ROOT;
|
||||||
|
const writeMode = options.writeMode ?? WRITE_MODE;
|
||||||
|
const documentSpecs = options.documentSpecs || (
|
||||||
|
root === ROOT ? DOCUMENT_SPECS : createDocumentSpecsForRoot(root)
|
||||||
|
);
|
||||||
|
const catalog = buildCatalog(root);
|
||||||
|
|
||||||
if (WRITE_MODE) {
|
if (writeMode) {
|
||||||
for (const spec of DOCUMENT_SPECS) {
|
for (const spec of documentSpecs) {
|
||||||
const currentContent = readFileOrThrow(spec.filePath);
|
const currentContent = readFileOrThrow(spec.filePath);
|
||||||
const nextContent = spec.syncContent(currentContent, catalog);
|
const nextContent = spec.syncContent(currentContent, catalog);
|
||||||
if (nextContent !== currentContent) {
|
if (nextContent !== currentContent) {
|
||||||
@@ -621,28 +648,55 @@ function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const expectations = DOCUMENT_SPECS.flatMap(spec => (
|
const expectations = documentSpecs.flatMap(spec => (
|
||||||
spec.parseExpectations(readFileOrThrow(spec.filePath))
|
spec.parseExpectations(readFileOrThrow(spec.filePath))
|
||||||
));
|
));
|
||||||
const checks = evaluateExpectations(catalog, expectations);
|
const checks = evaluateExpectations(catalog, expectations);
|
||||||
const result = { catalog, checks };
|
return { catalog, checks };
|
||||||
|
}
|
||||||
|
|
||||||
if (OUTPUT_MODE === 'json') {
|
function main(options = {}) {
|
||||||
|
const outputMode = options.outputMode || OUTPUT_MODE;
|
||||||
|
const result = runCatalogCheck(options);
|
||||||
|
|
||||||
|
if (outputMode === 'json') {
|
||||||
console.log(JSON.stringify(result, null, 2));
|
console.log(JSON.stringify(result, null, 2));
|
||||||
} else if (OUTPUT_MODE === 'md') {
|
} else if (outputMode === 'md') {
|
||||||
renderMarkdown(result);
|
renderMarkdown(result);
|
||||||
} else {
|
} else {
|
||||||
renderText(result);
|
renderText(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checks.some(check => !check.ok)) {
|
if (result.checks.some(check => !check.ok)) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
try {
|
try {
|
||||||
main();
|
main();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`ERROR: ${error.message}`);
|
console.error(`ERROR: ${error.message}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
buildCatalog,
|
||||||
|
createDocumentSpecs,
|
||||||
|
createDocumentSpecsForRoot,
|
||||||
|
evaluateExpectations,
|
||||||
|
formatExpectation,
|
||||||
|
main,
|
||||||
|
parseAgentsDocExpectations,
|
||||||
|
parseReadmeExpectations,
|
||||||
|
parseZhAgentsDocExpectations,
|
||||||
|
parseZhDocsReadmeExpectations,
|
||||||
|
parseZhRootReadmeExpectations,
|
||||||
|
runCatalogCheck,
|
||||||
|
syncEnglishAgents,
|
||||||
|
syncEnglishReadme,
|
||||||
|
syncZhAgents,
|
||||||
|
syncZhDocsReadme,
|
||||||
|
syncZhRootReadme,
|
||||||
|
};
|
||||||
|
|||||||
265
tests/ci/catalog.test.js
Normal file
265
tests/ci/catalog.test.js
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
/**
|
||||||
|
* Direct coverage for scripts/ci/catalog.js.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const {
|
||||||
|
buildCatalog,
|
||||||
|
formatExpectation,
|
||||||
|
runCatalogCheck,
|
||||||
|
} = require('../../scripts/ci/catalog');
|
||||||
|
|
||||||
|
function createTestDir() {
|
||||||
|
return fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-ci-catalog-'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupTestDir(testDir) {
|
||||||
|
fs.rmSync(testDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeCountedFiles(root, category, count) {
|
||||||
|
const dir = path.join(root, category);
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
|
||||||
|
for (let index = 1; index <= count; index += 1) {
|
||||||
|
if (category === 'skills') {
|
||||||
|
const skillDir = path.join(dir, `skill-${index}`);
|
||||||
|
fs.mkdirSync(skillDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(skillDir, 'SKILL.md'), `# Skill ${index}\n`);
|
||||||
|
} else {
|
||||||
|
fs.writeFileSync(path.join(dir, `${category}-${index}.md`), `# ${category} ${index}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeEnglishReadme(root, counts, options = {}) {
|
||||||
|
const tableCounts = options.tableCounts || counts;
|
||||||
|
const parityCounts = options.parityCounts || counts;
|
||||||
|
const unrelatedSkillsCount = options.unrelatedSkillsCount || 16;
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(root, 'README.md'), `Access to ${counts.agents} agents, ${counts.skills} skills, and ${counts.commands} commands.
|
||||||
|
| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| Agents | PASS: ${tableCounts.agents} agents |
|
||||||
|
| Commands | PASS: ${tableCounts.commands} commands |
|
||||||
|
| Skills | PASS: ${tableCounts.skills} skills |
|
||||||
|
|
||||||
|
| Feature | Count | Format |
|
||||||
|
| --- | ---: | --- |
|
||||||
|
| Skills | ${unrelatedSkillsCount} | .agents/skills/ |
|
||||||
|
|
||||||
|
## Cross-Tool Feature Parity
|
||||||
|
|
||||||
|
| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| **Agents** | ${parityCounts.agents} | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |
|
||||||
|
| **Commands** | ${parityCounts.commands} | Shared | Instruction-based | 31 |
|
||||||
|
| **Skills** | ${parityCounts.skills} | Shared | 10 (native format) | 37 |
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeEnglishAgents(root, counts, options = {}) {
|
||||||
|
const plus = options.skillsMinimum ? '+' : '';
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(root, 'AGENTS.md'), `This is a production plugin providing ${counts.agents} specialized agents, ${counts.skills}${plus} skills, ${counts.commands} commands.
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
agents/ - ${counts.agents} specialized subagents
|
||||||
|
skills/ - ${counts.skills}${plus} workflow skills and domain knowledge
|
||||||
|
commands/ - ${counts.commands} slash commands
|
||||||
|
\`\`\`
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeZhRootReadme(root, counts) {
|
||||||
|
fs.writeFileSync(path.join(root, 'README.zh-CN.md'), `你现在可以使用 ${counts.agents} 个代理、${counts.skills} 个技能和 ${counts.commands} 个命令。\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeZhDocsReadme(root, counts, options = {}) {
|
||||||
|
const tableCounts = options.tableCounts || counts;
|
||||||
|
const parityCounts = options.parityCounts || counts;
|
||||||
|
const unrelatedSkillsCount = options.unrelatedSkillsCount || 16;
|
||||||
|
const dir = path.join(root, 'docs', 'zh-CN');
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(dir, 'README.md'), `你现在可以使用 ${counts.agents} 个智能体、${counts.skills} 项技能和 ${counts.commands} 个命令了。
|
||||||
|
| 功能特性 | Claude Code | OpenCode | 状态 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 智能体 | PASS: ${tableCounts.agents} 个 |
|
||||||
|
| 命令 | PASS: ${tableCounts.commands} 个 |
|
||||||
|
| 技能 | PASS: ${tableCounts.skills} 项 |
|
||||||
|
|
||||||
|
| 功能特性 | 数量 | 格式 |
|
||||||
|
| --- | ---: | --- |
|
||||||
|
| 技能 | ${unrelatedSkillsCount} | .agents/skills/ |
|
||||||
|
|
||||||
|
## 跨工具功能对等
|
||||||
|
|
||||||
|
| 功能特性 | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| **智能体** | ${parityCounts.agents} | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
|
||||||
|
| **命令** | ${parityCounts.commands} | 共享 | 基于指令 | 31 |
|
||||||
|
| **技能** | ${parityCounts.skills} | 共享 | 10 (原生格式) | 37 |
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeZhAgents(root, counts, options = {}) {
|
||||||
|
const plus = options.skillsMinimum ? '+' : '';
|
||||||
|
const dir = path.join(root, 'docs', 'zh-CN');
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(dir, 'AGENTS.md'), `这是一个生产就绪的 AI 编码插件,提供 ${counts.agents} 个专业代理、${counts.skills}${plus} 项技能、${counts.commands} 条命令。
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
agents/ - ${counts.agents} 个专业子代理
|
||||||
|
skills/ - ${counts.skills}${plus} 个工作流技能和领域知识
|
||||||
|
commands/ - ${counts.commands} 个斜杠命令
|
||||||
|
\`\`\`
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeCatalogFixture(root, options = {}) {
|
||||||
|
const actualCounts = options.actualCounts || { agents: 1, skills: 1, commands: 1 };
|
||||||
|
const documentedCounts = options.documentedCounts || actualCounts;
|
||||||
|
const skillsMinimum = Boolean(options.skillsMinimum);
|
||||||
|
const unrelatedSkillsCount = options.unrelatedSkillsCount || 16;
|
||||||
|
|
||||||
|
writeCountedFiles(root, 'agents', actualCounts.agents);
|
||||||
|
writeCountedFiles(root, 'commands', actualCounts.commands);
|
||||||
|
writeCountedFiles(root, 'skills', actualCounts.skills);
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(root, 'agents', 'notes.txt'), 'not counted\n');
|
||||||
|
fs.writeFileSync(path.join(root, 'commands', 'notes.txt'), 'not counted\n');
|
||||||
|
fs.mkdirSync(path.join(root, 'skills', 'missing-skill-file'), { recursive: true });
|
||||||
|
|
||||||
|
writeEnglishReadme(root, documentedCounts, { unrelatedSkillsCount });
|
||||||
|
writeEnglishAgents(root, documentedCounts, { skillsMinimum });
|
||||||
|
writeZhRootReadme(root, documentedCounts);
|
||||||
|
writeZhDocsReadme(root, documentedCounts, { unrelatedSkillsCount });
|
||||||
|
writeZhAgents(root, documentedCounts, { skillsMinimum });
|
||||||
|
}
|
||||||
|
|
||||||
|
function test(name, fn) {
|
||||||
|
try {
|
||||||
|
fn();
|
||||||
|
console.log(` PASS ${name}`);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(` FAIL ${name}`);
|
||||||
|
console.log(` Error: ${error.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTests() {
|
||||||
|
console.log('\n=== Testing CI catalog.js ===\n');
|
||||||
|
|
||||||
|
let passed = 0;
|
||||||
|
let failed = 0;
|
||||||
|
|
||||||
|
if (test('builds catalog counts from a supplied root', () => {
|
||||||
|
const testDir = createTestDir();
|
||||||
|
try {
|
||||||
|
writeCatalogFixture(testDir, {
|
||||||
|
actualCounts: { agents: 2, skills: 1, commands: 3 },
|
||||||
|
documentedCounts: { agents: 2, skills: 1, commands: 3 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const catalog = buildCatalog(testDir);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
{
|
||||||
|
agents: catalog.agents.count,
|
||||||
|
skills: catalog.skills.count,
|
||||||
|
commands: catalog.commands.count,
|
||||||
|
},
|
||||||
|
{ agents: 2, skills: 1, commands: 3 }
|
||||||
|
);
|
||||||
|
assert.ok(catalog.agents.files.every(file => file.endsWith('.md')));
|
||||||
|
assert.ok(catalog.skills.files.every(file => file.endsWith('/SKILL.md')));
|
||||||
|
} finally {
|
||||||
|
cleanupTestDir(testDir);
|
||||||
|
}
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('reports mismatches from every tracked catalog document', () => {
|
||||||
|
const testDir = createTestDir();
|
||||||
|
try {
|
||||||
|
writeCatalogFixture(testDir, {
|
||||||
|
actualCounts: { agents: 1, skills: 1, commands: 1 },
|
||||||
|
documentedCounts: { agents: 9, skills: 9, commands: 9 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = runCatalogCheck({ root: testDir });
|
||||||
|
const formatted = result.checks
|
||||||
|
.filter(check => !check.ok)
|
||||||
|
.map(formatExpectation)
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
assert.ok(formatted.includes('README.md quick-start summary'));
|
||||||
|
assert.ok(formatted.includes('AGENTS.md summary'));
|
||||||
|
assert.ok(formatted.includes('README.zh-CN.md quick-start summary'));
|
||||||
|
assert.ok(formatted.includes('docs/zh-CN/README.md parity table'));
|
||||||
|
assert.ok(formatted.includes('docs/zh-CN/AGENTS.md project structure'));
|
||||||
|
} finally {
|
||||||
|
cleanupTestDir(testDir);
|
||||||
|
}
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('write mode syncs counts while preserving plus suffixes and unrelated tables', () => {
|
||||||
|
const testDir = createTestDir();
|
||||||
|
try {
|
||||||
|
writeCatalogFixture(testDir, {
|
||||||
|
actualCounts: { agents: 1, skills: 1, commands: 1 },
|
||||||
|
documentedCounts: { agents: 7, skills: 7, commands: 7 },
|
||||||
|
skillsMinimum: true,
|
||||||
|
unrelatedSkillsCount: 42,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = runCatalogCheck({ root: testDir, writeMode: true });
|
||||||
|
|
||||||
|
assert.strictEqual(result.checks.filter(check => !check.ok).length, 0);
|
||||||
|
|
||||||
|
const readme = fs.readFileSync(path.join(testDir, 'README.md'), 'utf8');
|
||||||
|
const agentsDoc = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf8');
|
||||||
|
const zhReadme = fs.readFileSync(path.join(testDir, 'docs', 'zh-CN', 'README.md'), 'utf8');
|
||||||
|
const zhAgentsDoc = fs.readFileSync(path.join(testDir, 'docs', 'zh-CN', 'AGENTS.md'), 'utf8');
|
||||||
|
|
||||||
|
assert.ok(readme.includes('Access to 1 agents, 1 skills, and 1 legacy command shims'));
|
||||||
|
assert.ok(readme.includes('| Skills | 42 | .agents/skills/ |'));
|
||||||
|
assert.ok(agentsDoc.includes('providing 1 specialized agents, 1+ skills, 1 commands'));
|
||||||
|
assert.ok(agentsDoc.includes('skills/ - 1+ workflow skills and domain knowledge'));
|
||||||
|
assert.ok(zhReadme.includes('| 技能 | 42 | .agents/skills/ |'));
|
||||||
|
assert.ok(zhAgentsDoc.includes('提供 1 个专业代理、1+ 项技能、1 条命令'));
|
||||||
|
assert.ok(zhAgentsDoc.includes('skills/ - 1+ 个工作流技能和领域知识'));
|
||||||
|
} finally {
|
||||||
|
cleanupTestDir(testDir);
|
||||||
|
}
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('throws a clear error for missing tracked documents', () => {
|
||||||
|
const testDir = createTestDir();
|
||||||
|
try {
|
||||||
|
writeCatalogFixture(testDir);
|
||||||
|
fs.rmSync(path.join(testDir, 'docs', 'zh-CN', 'AGENTS.md'));
|
||||||
|
|
||||||
|
assert.throws(
|
||||||
|
() => runCatalogCheck({ root: testDir }),
|
||||||
|
/Failed to read AGENTS\.md/
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
cleanupTestDir(testDir);
|
||||||
|
}
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||||
|
process.exit(failed > 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests();
|
||||||
Reference in New Issue
Block a user