reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
161
vendor-new/rev-skills/tests/validate.test.mjs
Normal file
161
vendor-new/rev-skills/tests/validate.test.mjs
Normal file
@@ -0,0 +1,161 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { parseFrontmatter, checkSkillDir, collectSkills, SKILL_PREFIX } from '../validate.mjs';
|
||||
|
||||
// Windows 下 URL.pathname 产出 /D:/... 形式,不能直接当本地路径;用 fileURLToPath 统一转换
|
||||
const FIX = fileURLToPath(new URL('./fixtures/', import.meta.url));
|
||||
|
||||
test('parseFrontmatter 解析合法 frontmatter', () => {
|
||||
const md = `---\nname: re-abc\ndescription: 测试。\n---\n\n# 标题\n\n正文`;
|
||||
const { name, description, body } = parseFrontmatter(md);
|
||||
assert.equal(name, 're-abc');
|
||||
assert.equal(description, '测试。');
|
||||
assert.match(body, /^# 标题/);
|
||||
});
|
||||
|
||||
test('parseFrontmatter 拒绝缺失 name/description', () => {
|
||||
assert.throws(() => parseFrontmatter('---\nname: x\n---\n'), /description/);
|
||||
assert.throws(() => parseFrontmatter('# no frontmatter'), /frontmatter/);
|
||||
});
|
||||
|
||||
// 校验用例共用的能力注册表子集(原子技能必须声明能力,声明值须在注册表内)
|
||||
const CAPS = { knownCapabilities: new Set(['triage']) };
|
||||
|
||||
test('good-skill 通过全部检查', () => {
|
||||
const { errors } = checkSkillDir(FIX + 'good-skill', CAPS);
|
||||
assert.deepEqual(errors, []);
|
||||
});
|
||||
|
||||
test('bad-name 报名字不一致错误', () => {
|
||||
const { errors } = checkSkillDir(FIX + 'bad-name');
|
||||
assert.ok(errors.some(e => e.includes('name') && e.includes('wrong-name')));
|
||||
});
|
||||
|
||||
test('no-tools 叶子技能报缺工具准备', () => {
|
||||
const { errors } = checkSkillDir(FIX + 'no-tools');
|
||||
assert.ok(errors.some(e => e.includes('工具准备')));
|
||||
});
|
||||
|
||||
test('bad-frontmatter 顶格游离行报错(校验闸口不再放过非法 frontmatter)', () => {
|
||||
const { errors } = checkSkillDir(FIX + 'bad-frontmatter');
|
||||
assert.ok(errors.some(e => e.includes('frontmatter line')), `未报错:${JSON.stringify(errors)}`);
|
||||
});
|
||||
|
||||
test('链接规则:技能死链与跨技能裸引用报错,本技能 references 放行', () => {
|
||||
const { errors } = checkSkillDir(FIX + 'broken-link', { knownSkills: ['re-abc'] });
|
||||
assert.ok(errors.some(e => e.includes('re-does-not-exist')), '技能死链应报错');
|
||||
assert.ok(errors.some(e => e.includes('platform-tips')), '跨技能裸引用应报错');
|
||||
assert.ok(!errors.some(e => e.includes('local-note')), '本技能 references 链接应放行');
|
||||
});
|
||||
|
||||
test('跨技能 references 用 [[re-xxx/name]] 限定后放行,指向不存在则报错', () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'link-'));
|
||||
const mkSkill = (skill, body, refs = {}) => {
|
||||
mkdirSync(join(root, skill, 'references'), { recursive: true });
|
||||
writeFileSync(join(root, skill, 'SKILL.md'),
|
||||
`---\nname: ${skill}\ndescription: 测试。test。\ncapabilities: [triage]\n---\n\n# 标题\n\n## 工具准备\n\n${body}`);
|
||||
for (const [n, c] of Object.entries(refs)) writeFileSync(join(root, skill, 'references', `${n}.md`), c);
|
||||
};
|
||||
const opts = { ...CAPS, knownSkills: ['re-a', 're-b'] };
|
||||
try {
|
||||
mkSkill('re-a', '跨技能见 [[re-b/shared]]');
|
||||
mkSkill('re-b', '本技能内见 [[shared]]', { shared: 'x' });
|
||||
assert.deepEqual(checkSkillDir(join(root, 're-a'), opts).errors, []);
|
||||
assert.deepEqual(checkSkillDir(join(root, 're-b'), opts).errors, []);
|
||||
mkSkill('re-a', '跨技能见 [[re-b/nope]]');
|
||||
assert.ok(checkSkillDir(join(root, 're-a'), opts).errors.some(e => e.includes('re-b/nope')));
|
||||
} finally { rmSync(root, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
test('gateway-skill 豁免工具准备检查', () => {
|
||||
const { errors } = checkSkillDir(FIX + 'gateway-skill');
|
||||
assert.ok(!errors.some(e => e.includes('工具准备')));
|
||||
});
|
||||
|
||||
test('collectSkills 收集全部技能目录名', () => {
|
||||
const names = collectSkills(FIX);
|
||||
assert.deepEqual([...names].sort(), ['bad-frontmatter', 'bad-name', 'broken-link', 'gateway-skill', 'good-skill', 'no-tools']);
|
||||
});
|
||||
|
||||
test('parseFrontmatter 解析 capabilities list', () => {
|
||||
const md = `---\nname: re-abc\ndescription: 测试。\ncapabilities: [elf-parser, unpack]\n---\n\n# 标题\n\n正文`;
|
||||
const { capabilities } = parseFrontmatter(md);
|
||||
assert.deepEqual(capabilities, ['elf-parser', 'unpack']);
|
||||
});
|
||||
|
||||
test('guard 合法 JSON 通过、结构非法报错', () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'guard-test-'));
|
||||
const name = dir.split(/[\\/]/).pop();
|
||||
const body = `---\nname: ${name}\ndescription: 测试。test。\ncapabilities: [triage]\nguard: %GUARD%\n---\n\n# 标题\n\n## 工具准备\n\n正文`;
|
||||
try {
|
||||
// 合法
|
||||
writeFileSync(join(dir, 'SKILL.md'), body.replace('%GUARD%', '{"require_authorization": true, "forbidden": ["unauthorized_api_testing"]}'));
|
||||
assert.deepEqual(checkSkillDir(dir, CAPS).errors, []);
|
||||
// 非 JSON
|
||||
writeFileSync(join(dir, 'SKILL.md'), body.replace('%GUARD%', 'require_authorization: true'));
|
||||
const { errors } = checkSkillDir(dir, CAPS);
|
||||
assert.ok(errors.some(e => e.includes('guard must be')));
|
||||
// 结构缺字段
|
||||
writeFileSync(join(dir, 'SKILL.md'), body.replace('%GUARD%', '{"require_authorization": "yes"}'));
|
||||
const { errors: e2 } = checkSkillDir(dir, CAPS);
|
||||
assert.ok(e2.some(e => e.includes('guard must be')));
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('description 须中英双含(触发词约定)', () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'desc-i18n-'));
|
||||
const name = dir.split(/[\\/]/).pop();
|
||||
const withDesc = (d) => `---\nname: ${name}\ndescription: ${d}\ncapabilities: [triage]\n---\n\n# 标题\n\n## 工具准备\n\n正文`;
|
||||
try {
|
||||
// 双语俱全 → 通过
|
||||
writeFileSync(join(dir, 'SKILL.md'), withDesc('测试技能:test skill 触发词'));
|
||||
assert.deepEqual(checkSkillDir(dir, CAPS).errors, []);
|
||||
// 仅中文 → 报错(英文环境命中不了)
|
||||
writeFileSync(join(dir, 'SKILL.md'), withDesc('只有中文触发词'));
|
||||
assert.ok(checkSkillDir(dir, CAPS).errors.some(e => e.includes('CJK')));
|
||||
// 仅英文 → 报错
|
||||
writeFileSync(join(dir, 'SKILL.md'), withDesc('english triggers only'));
|
||||
assert.ok(checkSkillDir(dir, CAPS).errors.some(e => e.includes('CJK')));
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('原子技能未声明能力即报错(能力层完整性)', () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'no-caps-'));
|
||||
const name = dir.split(/[\\/]/).pop();
|
||||
try {
|
||||
writeFileSync(join(dir, 'SKILL.md'), `---\nname: ${name}\ndescription: 测试。test。\n---\n\n# 标题\n\n## 工具准备\n\n正文`);
|
||||
assert.ok(checkSkillDir(dir, CAPS).errors.some(e => e.includes('must declare capabilities')));
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('capabilities 合法通过、未知标签与非法写法报错', () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'cap-test-'));
|
||||
const name = dir.split(/[\\/]/).pop();
|
||||
const caps = new Set(['elf-parser', 'unpack', 'tracing']);
|
||||
const body = `---\nname: ${name}\ndescription: 测试。test。\ncapabilities: %CAPS%\n---\n\n# 标题\n\n## 工具准备\n\n正文`;
|
||||
try {
|
||||
// 合法 list 通过
|
||||
writeFileSync(join(dir, 'SKILL.md'), body.replace('%CAPS%', '[elf-parser, unpack]'));
|
||||
assert.deepEqual(checkSkillDir(dir, { knownCapabilities: caps }).errors, []);
|
||||
// 未知标签报错
|
||||
writeFileSync(join(dir, 'SKILL.md'), body.replace('%CAPS%', '[elf-parser, nope-cap]'));
|
||||
const { errors } = checkSkillDir(dir, { knownCapabilities: caps });
|
||||
assert.ok(errors.some(e => e.includes("unknown capability 'nope-cap'")));
|
||||
// 非法写法(非 list)报错
|
||||
writeFileSync(join(dir, 'SKILL.md'), body.replace('%CAPS%', 'elf-parser'));
|
||||
const { errors: e2 } = checkSkillDir(dir, { knownCapabilities: caps });
|
||||
assert.ok(e2.some(e => e.includes('non-empty list')));
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user