io-terminal.test.mjs (2762B)
1 import test from 'node:test'; 2 import assert from 'node:assert/strict'; 3 import {Terminal} from '../dist/io-terminal.js'; 4 const read=(id,limit=4096)=>({run:'1',id,kind:'read',limit}); 5 test('line input is scalar bounded, strict, LF framed and explicitly distinct from EOF',()=>{ 6 const t=new Terminal('1');assert.equal(t.handle(read(1)),undefined); 7 for(const value of ['x\ny','\ud800','😀'.repeat(257),null])assert.equal(t.line('1',1,value),undefined); 8 assert.equal(t.line('2',1,'x'),undefined);assert.deepEqual(t.waiting,{run:'1',id:1}); 9 assert.deepEqual(t.line('1',1,''),{run:'1',id:1,kind:'read',bytes:[10],eof:false}); 10 t.handle(read(2));assert.deepEqual(t.endInput('1',2),{run:'1',id:2,kind:'read',bytes:[],eof:true}); 11 assert.equal(t.handle(read(3)).eof,true);t.finish(); 12 }); 13 test('encoded line is split at requested byte bounds, without loss or invented EOF',()=>{ 14 const t=new Terminal('1');t.handle(read(1,1));const parts=[...t.line('1',1,'😀'.repeat(256)).bytes]; 15 for(let id=2;id<=1025;id++) {const r=t.handle(read(id,1));assert.equal(r.eof,false);parts.push(...r.bytes);} 16 assert.deepEqual(parts,[...new TextEncoder().encode('😀'.repeat(256)),10]); 17 assert.equal(t.handle(read(1026)),undefined);assert.throws(()=>t.finish(),/pending/); 18 }); 19 test('cancellation retires exactly the pending prompt; stale submissions do not consume its successor',()=>{ 20 const t=new Terminal('1');t.handle(read(1)); 21 assert.deepEqual(t.handle({run:'1',id:1,kind:'cancel',operation:'read'}),{run:'1',id:1,kind:'cancelled'}); 22 t.handle(read(2));assert.equal(t.line('1',1,'stale'),undefined);assert.equal(t.endInput('1',1),undefined); 23 assert.equal(t.handle({run:'1',id:1,kind:'cancel',operation:'read'}),undefined); 24 assert.equal(t.waiting.id,2);assert.deepEqual(t.line('1',2,'ok').bytes,[111,107,10]);t.finish(); 25 }); 26 test('binary write acknowledgement is independent of display decoding and abort races never duplicate it',()=>{ 27 const t=new Terminal('1'),event={run:'1',id:1,kind:'write',stream:'output',bytes:[255,240,159]}; 28 assert.deepEqual(t.handle(event),{run:'1',id:1,kind:'write',count:3}); 29 assert.equal(t.handle({run:'1',id:1,kind:'cancel',operation:'write'}),undefined); 30 assert.throws(()=>t.handle(event),/stale/);t.finish(); 31 assert.deepEqual(t.output.snapshot().output.bytes,event.bytes);assert.equal(t.output.snapshot().output.tokens.at(-1).reason,'incomplete'); 32 }); 33 test('invalid input events preserve current waits and do not consume future identities',()=>{ 34 const t=new Terminal('1');assert.throws(()=>t.handle(read(2,0)),/read/);t.handle(read(1)); 35 assert.throws(()=>t.handle(read(2)),/read/);assert.throws(()=>t.handle({...read(2),run:'2'}),/event/); 36 assert.equal(t.waiting.id,1);t.endInput('1',1);assert.equal(t.handle(read(2)).eof,true); 37 });