Files

144 lines
4.8 KiB
JavaScript
Raw Permalink Normal View History

import { describe, expect, it } from 'vitest';
import { normalizeWalkthrough, parseModelJson, MAX_STOPS } from './schema.js';
const ALIASES = new Map([
['h1', 'working:src/a.ts:aaaa1111'],
['h2', 'working:src/a.ts:bbbb2222'],
['h3', 'working:src/b.ts:cccc3333'],
]);
const walkthrough = (chapters) => ({ title: 'Change', focus: 'why', chapters });
describe('normalizeWalkthrough', () => {
it('maps aliases to real hunk ids and assigns stable local ids', () => {
const result = normalizeWalkthrough(walkthrough([
{
title: 'Data',
icon: 'doc',
blurb: 'shape first',
stops: [
{ title: 'New field', hunks: ['h1', 'h2'], importance: 'critical', prose: 'Adds a field.' },
],
},
]), ALIASES);
expect(result.chapters[0].id).toBe('chapter-1');
expect(result.chapters[0].stops[0]).toMatchObject({
id: 'stop-1-1',
hunkIds: ['working:src/a.ts:aaaa1111', 'working:src/a.ts:bbbb2222'],
importance: 'critical',
});
});
it('drops invented aliases instead of rendering a broken anchor', () => {
const result = normalizeWalkthrough(walkthrough([
{
title: 'Data',
icon: 'doc',
blurb: '',
stops: [
{ title: 'Mixed', hunks: ['h1', 'h99', 'nonsense'], importance: 'normal', prose: 'Something.' },
],
},
]), ALIASES);
expect(result.chapters[0].stops[0].hunkIds).toEqual(['working:src/a.ts:aaaa1111']);
expect(result.droppedAnchors).toBe(2);
});
it('anchors each hunk to a single stop', () => {
const result = normalizeWalkthrough(walkthrough([
{
title: 'Data',
icon: 'doc',
blurb: '',
stops: [
{ title: 'First', hunks: ['h1'], importance: 'normal', prose: 'One.' },
{ title: 'Second', hunks: ['h1', 'h2'], importance: 'normal', prose: 'Two.' },
],
},
]), ALIASES);
expect(result.chapters[0].stops[0].hunkIds).toEqual(['working:src/a.ts:aaaa1111']);
expect(result.chapters[0].stops[1].hunkIds).toEqual(['working:src/a.ts:bbbb2222']);
});
it('discards stops left with no anchor or no prose', () => {
const result = normalizeWalkthrough(walkthrough([
{
title: 'Data',
icon: 'doc',
blurb: '',
stops: [
{ title: 'Ghost', hunks: ['h99'], importance: 'normal', prose: 'About nothing.' },
{ title: 'Silent', hunks: ['h1'], importance: 'normal', prose: ' ' },
{ title: 'Real', hunks: ['h2'], importance: 'normal', prose: 'Actual explanation.' },
],
},
]), ALIASES);
expect(result.chapters[0].stops.map((stop) => stop.title)).toEqual(['Real']);
});
it('rejects a response whose stops all fall away', () => {
expect(() => normalizeWalkthrough(walkthrough([
{ title: 'Empty', icon: 'doc', blurb: '', stops: [{ title: 'Ghost', hunks: ['h99'], importance: 'normal', prose: 'x' }] },
]), ALIASES)).toThrow('no usable stops');
});
it('clamps chapter titles and falls back on unknown enums', () => {
const result = normalizeWalkthrough(walkthrough([
{
title: 'An extremely long chapter title that will not fit the column',
icon: 'rocket',
blurb: '',
stops: [{ title: 'A', hunks: ['h1'], importance: 'urgent', prose: 'Text.' }],
},
]), ALIASES);
expect(result.chapters[0].title.length).toBeLessThanOrEqual(24);
expect(result.chapters[0].icon).toBe('doc');
expect(result.chapters[0].stops[0].importance).toBe('normal');
});
it('caps the total number of stops', () => {
const many = Array.from({ length: 30 }, (_, index) => ({
title: `Stop ${index}`,
hunks: [['h1', 'h2', 'h3'][index % 3]],
importance: 'normal',
prose: 'Text.',
}));
const result = normalizeWalkthrough(
walkthrough([{ title: 'All', icon: 'doc', blurb: '', stops: many }]),
ALIASES,
);
const total = result.chapters.reduce((sum, chapter) => sum + chapter.stops.length, 0);
expect(total).toBeLessThanOrEqual(MAX_STOPS);
// Only three aliases exist and each is used once, so the real cap here is
// the alias pool, not the stop limit.
expect(total).toBe(3);
});
});
describe('parseModelJson', () => {
it('parses a clean object', () => {
expect(parseModelJson('{"title":"x"}')).toEqual({ title: 'x' });
});
it('unwraps a fenced block', () => {
expect(parseModelJson('```json\n{"title":"x"}\n```')).toEqual({ title: 'x' });
});
it('recovers an object followed by stray prose', () => {
expect(parseModelJson('{"title":"x"}\n\nHope that helps!')).toEqual({ title: 'x' });
});
it('fails loudly on unusable output', () => {
expect(() => parseModelJson('')).toThrow('empty response');
expect(() => parseModelJson('no json at all')).toThrow('no JSON object');
expect(() => parseModelJson('{"broken":')).toThrow('not valid JSON');
});
});