This commit is contained in:
@@ -92,6 +92,9 @@ describe('GET /api/skills/:name', () => {
|
||||
expect(res.body.name).toBe('sys-skill');
|
||||
expect(res.body.source).toBe('system');
|
||||
expect(res.body.content).toContain('body');
|
||||
// raw is the full file (incl. frontmatter) the editor must edit/save.
|
||||
expect(res.body.raw).toContain('name: sys-skill');
|
||||
expect(res.body.raw).toContain('body');
|
||||
expect(res.body.files).toContain('SKILL.md');
|
||||
expect(res.body).toHaveProperty('maxSeverity');
|
||||
});
|
||||
@@ -256,6 +259,45 @@ describe('PUT /api/skills/:name (update)', () => {
|
||||
.send({ content: 'x' });
|
||||
expect(res.status).toBe(400);
|
||||
});
|
||||
|
||||
// Regression: editing the body-only `content` (frontmatter dropped) used to
|
||||
// overwrite SKILL.md without frontmatter, making the skill vanish from the
|
||||
// catalog. The PUT now rejects frontmatter-less content and leaves the file
|
||||
// intact.
|
||||
it('rejects content without valid frontmatter and leaves the skill intact', async () => {
|
||||
addUserSkill('user-1', 'keepme');
|
||||
const filePath = join(userRoot, 'user-1', 'skills', 'keepme', 'SKILL.md');
|
||||
const before = readFileSync(filePath, 'utf-8');
|
||||
const res = await request(makeApp(makeCatalog(), { id: 'user-1' }))
|
||||
.put('/api/skills/keepme?scope=user')
|
||||
.send({ content: '# keepme\njust the body, no frontmatter' });
|
||||
expect(res.status).toBe(400);
|
||||
// File untouched → still has frontmatter → still loadable.
|
||||
expect(readFileSync(filePath, 'utf-8')).toBe(before);
|
||||
const list = await request(makeApp(makeCatalog(), { id: 'user-1' })).get('/api/skills?scope=user');
|
||||
expect(list.body.skills.map((s: { name: string }) => s.name)).toContain('keepme');
|
||||
});
|
||||
|
||||
it('rejects a frontmatter name that does not match the skill (no rename via edit)', async () => {
|
||||
addUserSkill('user-1', 'orig');
|
||||
const res = await request(makeApp(makeCatalog(), { id: 'user-1' }))
|
||||
.put('/api/skills/orig?scope=user')
|
||||
.send({ content: SKILL_MD('renamed') });
|
||||
expect(res.status).toBe(400);
|
||||
expect(existsSync(join(userRoot, 'user-1', 'skills', 'orig', 'SKILL.md'))).toBe(true);
|
||||
});
|
||||
|
||||
it('saves full content (frontmatter preserved) round-trip', async () => {
|
||||
addUserSkill('user-1', 'rt');
|
||||
const newFull = SKILL_MD('rt') + '\nmore body';
|
||||
const res = await request(makeApp(makeCatalog(), { id: 'user-1' }))
|
||||
.put('/api/skills/rt?scope=user')
|
||||
.send({ content: newFull });
|
||||
expect(res.status).toBe(200);
|
||||
const saved = readFileSync(join(userRoot, 'user-1', 'skills', 'rt', 'SKILL.md'), 'utf-8');
|
||||
expect(saved).toContain('name: rt'); // frontmatter kept
|
||||
expect(saved).toContain('more body'); // body updated
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE /api/skills/:name', () => {
|
||||
|
||||
Reference in New Issue
Block a user