import { describe, it, expect } from 'vitest'; import { stripDcpTags, makeDcpStreamStripper } from '../dcp-strip.js'; // Feed chunks through a fresh stripper and return the fully reassembled output // (everything emitted during streaming + the final flush) — i.e. what the // dispatcher would accumulate into the persisted message content. function run(chunks: string[]): string { const s = makeDcpStreamStripper(); let out = ''; for (const c of chunks) out += s.push(c); out += s.flush(); return out; } describe('stripDcpTags (one-shot)', () => { it('removes a complete tag', () => { expect(stripDcpTags('Yes — "Test".\n\nm0019')).toBe( 'Yes — "Test".\n\n', ); }); it('leaves text without a tag untouched', () => { expect(stripDcpTags('no tag here')).toBe('no tag here'); }); }); describe('per-chunk strip is INSUFFICIENT (documents the bug)', () => { it('a tag split across chunks survives a naive per-chunk .replace()', () => { const chunks = ['Yes.\n\nm0019']; const naive = chunks.map(stripDcpTags).join(''); // The reassembled content still contains the tag — this is the screenshot bug. expect(naive).toContain('m0019'); }); }); describe('makeDcpStreamStripper (cross-chunk fix)', () => { it('strips a tag split across chunks (the real opencode case)', () => { expect(run(['Yes.\n\nm0019'])).toBe('Yes.\n\n'); }); it('strips a tag split at EVERY character boundary', () => { const full = 'Answer.m0019'; expect(run([...full])).toBe('Answer.'); }); it('strips a tag delivered whole in one chunk', () => { expect(run(['Answer.m0019'])).toBe('Answer.'); }); it('passes through text with no tag', () => { expect(run(['hello ', 'world'])).toBe('hello world'); }); it('does NOT swallow legitimate < content (code/HTML/generics)', () => { expect(run(['use ', '
', ' and ', 'Array<', 'string>'])).toBe('use
and Array'); }); it('handles a lone < that is not a dcp tag, split across chunks', () => { expect(run(['a <', 'b c'])).toBe('a { expect(run(['before ', '', 'm1', '', ' after'])).toBe( 'before after', ); }); it('flushes a truncated/never-closed partial tag without leaking it as a complete tag', () => { // If the stream ends mid-tag, flush strips complete tags; an incomplete // remnant is returned as-is (no complete tag ever existed to render). const out = run(['done.m00']); expect(out).not.toContain(''); }); });