Files
2026-07-13 12:30:22 +08:00

48 lines
1.6 KiB
TypeScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { spawnAgent } from '../dist/index.js';
import type { AgentDef } from '../dist/index.js';
// Regression coverage for issue #9 mojibake (◆◆◆): a multi-byte UTF-8 char
// split across two stdout `data` chunks must not become U+FFFD. spawn.ts now
// decodes through StringDecoder, which buffers an incomplete trailing sequence.
// A child that prints CJK text ONE BYTE AT A TIME with a tick between bytes, so
// every 3-byte glyph is guaranteed to straddle multiple `data` events.
const BYTE_AT_A_TIME = `
const s = Buffer.from('设计引擎:每次使用都是一次进化。', 'utf8');
let i = 0;
(function next() {
if (i >= s.length) { process.exit(0); return; }
process.stdout.write(Buffer.from([s[i++]]));
setTimeout(next, 1);
})();
`;
function makeNodeDef(code: string): AgentDef {
return {
id: 'test-echo',
name: 'test-echo',
bin: process.execPath, // node
versionArgs: ['--version'],
buildArgs: () => ['-e', code],
streamFormat: 'plain',
};
}
test('CJK split across stdout chunks is not corrupted', async () => {
let collected = '';
const handle = spawnAgent({
def: makeNodeDef(BYTE_AT_A_TIME),
prompt: '',
context: { cwd: process.cwd() },
onEvent: (ev) => {
if (ev.type === 'text') collected += ev.chunk;
},
});
const { exitCode } = await handle.done;
assert.equal(exitCode, 0);
assert.equal(collected, '设计引擎:每次使用都是一次进化。');
assert.ok(!collected.includes(''), 'must contain no U+FFFD replacement chars');
});