2026-06-15 08:06:08 +03:00
|
|
|
// tools/plan-skills.test.mjs
|
|
|
|
|
import { describe, it, expect } from 'vitest';
|
2026-06-18 13:19:22 +03:00
|
|
|
import { parsePlanSkills, extractPlanGoal, parsePlanDelivery, parsePlanKind } from './plan-skills.mjs';
|
|
|
|
|
|
|
|
|
|
describe('parsePlanKind (D1)', () => {
|
|
|
|
|
it('**Kind:** deploy → "deploy"', () => {
|
|
|
|
|
expect(parsePlanKind('# План\n**Kind:** deploy\n')).toBe('deploy');
|
|
|
|
|
});
|
|
|
|
|
it('нет пометки / мусор → "normal" (fail-safe)', () => {
|
|
|
|
|
expect(parsePlanKind('# План без пометки')).toBe('normal');
|
|
|
|
|
expect(parsePlanKind('**Kind:** wat')).toBe('normal');
|
|
|
|
|
});
|
|
|
|
|
});
|
2026-06-17 10:53:25 +03:00
|
|
|
|
|
|
|
|
describe('parsePlanDelivery', () => {
|
|
|
|
|
it('читает user-result из маркера', () => {
|
|
|
|
|
expect(parsePlanDelivery('текст\n**Delivery:** user-result\nещё')).toBe('user-result');
|
|
|
|
|
});
|
|
|
|
|
it('читает internal', () => {
|
|
|
|
|
expect(parsePlanDelivery('**Delivery:** internal')).toBe('internal');
|
|
|
|
|
});
|
|
|
|
|
it('по умолчанию internal (нет маркера)', () => {
|
|
|
|
|
expect(parsePlanDelivery('план без пометки')).toBe('internal');
|
|
|
|
|
});
|
|
|
|
|
it('мусорное значение → internal (fail-safe)', () => {
|
|
|
|
|
expect(parsePlanDelivery('**Delivery:** whatever')).toBe('internal');
|
|
|
|
|
});
|
|
|
|
|
});
|
2026-06-15 08:06:08 +03:00
|
|
|
|
|
|
|
|
describe('parsePlanSkills', () => {
|
|
|
|
|
const md = ['# План', '```skills-json', '["executing-plans","test-driven-development"]', '```', '## Цель', 'x'].join('\n');
|
|
|
|
|
it('достаёт список скилов из ```skills-json блока', () => {
|
|
|
|
|
expect(parsePlanSkills(md)).toEqual(['executing-plans', 'test-driven-development']);
|
|
|
|
|
});
|
|
|
|
|
it('нет блока → пустой массив', () => { expect(parsePlanSkills('# План\nтекст')).toEqual([]); });
|
|
|
|
|
it('битый JSON → пустой массив (fail-safe)', () => {
|
|
|
|
|
expect(parsePlanSkills('```skills-json\n[не json\n```')).toEqual([]);
|
|
|
|
|
});
|
|
|
|
|
it('не-массив / не-строки отфильтрованы', () => {
|
|
|
|
|
expect(parsePlanSkills('```skills-json\n["ok", 5, null, "two"]\n```')).toEqual(['ok', 'two']);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('extractPlanGoal (зеркало extractGoal судьи)', () => {
|
|
|
|
|
it('секция ## Цель → её текст до след. заголовка', () => {
|
|
|
|
|
const md = ['# План', '## Цель', 'починить парсер X', '', '## Шаги', 'шаг 1'].join('\n');
|
|
|
|
|
expect(extractPlanGoal(md)).toBe('починить парсер X');
|
|
|
|
|
});
|
|
|
|
|
it('секция ## Goal → её текст', () => {
|
|
|
|
|
expect(extractPlanGoal('## Goal\nfix the bug\n## Steps')).toBe('fix the bug');
|
|
|
|
|
});
|
|
|
|
|
it('нет секции цели → первый непустой не-заголовок абзац', () => {
|
|
|
|
|
expect(extractPlanGoal('# План\n\nделаем фичу\n')).toBe('делаем фичу');
|
|
|
|
|
});
|
|
|
|
|
it('пусто → пустая строка', () => { expect(extractPlanGoal('')).toBe(''); });
|
2026-06-21 07:51:22 +03:00
|
|
|
it('(D1) многострочное тело цели — целиком, не первая строка', () => {
|
|
|
|
|
expect(extractPlanGoal('## Цель\nстрока один\nстрока два\n\n## Шаги\nx')).toBe('строка один\nстрока два');
|
|
|
|
|
});
|
|
|
|
|
it('(D2) пустая строка + fenced-блок перед целью → текст цели, не блок', () => {
|
|
|
|
|
const md = '# Заголовок\n\n```skills-json\n["a"]\n```\n\n## Цель\n\nнастоящая цель.';
|
|
|
|
|
expect(extractPlanGoal(md)).toBe('настоящая цель.');
|
|
|
|
|
});
|
|
|
|
|
it('(D2) маркер шапки **Kind:** не попадает в цель', () => {
|
|
|
|
|
expect(extractPlanGoal('# T\n\n**Kind:** normal\n\n## Цель\n\nцель тут.')).toBe('цель тут.');
|
|
|
|
|
});
|
|
|
|
|
it('(D2) несколько секций ## Цель → берётся первая', () => {
|
|
|
|
|
expect(extractPlanGoal('## Цель\nпервая\n\n## Цель\nвторая')).toBe('первая');
|
|
|
|
|
});
|
2026-06-15 08:06:08 +03:00
|
|
|
});
|