-
-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathlint-skill-entry.test.mjs
More file actions
110 lines (96 loc) · 3.63 KB
/
Copy pathlint-skill-entry.test.mjs
File metadata and controls
110 lines (96 loc) · 3.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { afterEach, describe, test } from 'node:test';
const LINTER = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'..',
'.github',
'scripts',
'lint-skill-entry.mjs',
);
const roots = [];
function makeRoot() {
const root = mkdtempSync(path.join(os.tmpdir(), 'skill-lint-'));
roots.push(root);
return root;
}
afterEach(() => {
while (roots.length > 0) {
rmSync(roots.pop(), { recursive: true, force: true });
}
});
function writeSkill(root, domain, name, frontmatter, body) {
const dir = path.join(root, 'domains', domain, 'skills', name);
mkdirSync(dir, { recursive: true });
const defaultBody = '## When To Use\n\n- always\n\n## Workflow\n\n1. do the thing\n';
writeFileSync(path.join(dir, 'skill.md'), `---\n${frontmatter}\n---\n\n${body ?? defaultBody}`);
return dir;
}
function lint(root) {
const result = spawnSync(process.execPath, [LINTER], {
env: { ...process.env, SKILLS_LINT_ROOT: root },
encoding: 'utf8',
});
return { code: result.status, output: `${result.stdout}${result.stderr}` };
}
describe('lint-skill-entry', () => {
test('a well-formed skill passes', () => {
const root = makeRoot();
writeSkill(root, 'testing', 'unit-testing', 'name: unit-testing\ndescription: Write unit tests');
assert.equal(lint(root).code, 0);
});
test('missing name fails', () => {
const root = makeRoot();
writeSkill(root, 'testing', 'unit-testing', 'description: x');
const { code, output } = lint(root);
assert.equal(code, 1);
assert.match(output, /missing required `name`/u);
});
test('name not matching the directory fails', () => {
const root = makeRoot();
writeSkill(root, 'testing', 'unit-testing', 'name: wrong-name\ndescription: x');
const { code, output } = lint(root);
assert.equal(code, 1);
assert.match(output, /must match the directory/u);
});
test('a knowledge/ sibling directory fails (the conversion guarantee)', () => {
const root = makeRoot();
const dir = writeSkill(root, 'perps', 'fix-bug', 'name: fix-bug\ndescription: x');
mkdirSync(path.join(dir, 'knowledge'));
const { code, output } = lint(root);
assert.equal(code, 1);
assert.match(output, /knowledge/u);
});
test('an mms- prefix in the source name fails', () => {
const root = makeRoot();
writeSkill(root, 'testing', 'mms-unit', 'name: mms-unit\ndescription: x');
const { code, output } = lint(root);
assert.equal(code, 1);
assert.match(output, /prefix/u);
});
test('a description over the operator ceiling fails', () => {
const root = makeRoot();
writeSkill(root, 'testing', 'unit-testing', `name: unit-testing\ndescription: ${'x'.repeat(1100)}`);
const { code, output } = lint(root);
assert.equal(code, 1);
assert.match(output, /operator ceiling/u);
});
test('an invalid maturity value fails', () => {
const root = makeRoot();
writeSkill(root, 'testing', 'unit-testing', 'name: unit-testing\ndescription: x\nmaturity: beta');
const { code, output } = lint(root);
assert.equal(code, 1);
assert.match(output, /maturity/u);
});
test('alwaysApply: true fails (on-demand-only contract)', () => {
const root = makeRoot();
writeSkill(root, 'testing', 'unit-testing', 'name: unit-testing\ndescription: x\nalwaysApply: true');
const { code, output } = lint(root);
assert.equal(code, 1);
assert.match(output, /on-demand/u);
});
});