streaming.test.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import assert from 'assert';
  2. import { _iterSSEMessages } from '@opencode-ai/sdk/core/streaming';
  3. import { ReadableStreamFrom } from '@opencode-ai/sdk/internal/shims';
  4. describe('streaming decoding', () => {
  5. test('basic', async () => {
  6. async function* body(): AsyncGenerator<Buffer> {
  7. yield Buffer.from('event: completion\n');
  8. yield Buffer.from('data: {"foo":true}\n');
  9. yield Buffer.from('\n');
  10. }
  11. const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
  12. Symbol.asyncIterator
  13. ]();
  14. let event = await stream.next();
  15. assert(event.value);
  16. expect(JSON.parse(event.value.data)).toEqual({ foo: true });
  17. event = await stream.next();
  18. expect(event.done).toBeTruthy();
  19. });
  20. test('data without event', async () => {
  21. async function* body(): AsyncGenerator<Buffer> {
  22. yield Buffer.from('data: {"foo":true}\n');
  23. yield Buffer.from('\n');
  24. }
  25. const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
  26. Symbol.asyncIterator
  27. ]();
  28. let event = await stream.next();
  29. assert(event.value);
  30. expect(event.value.event).toBeNull();
  31. expect(JSON.parse(event.value.data)).toEqual({ foo: true });
  32. event = await stream.next();
  33. expect(event.done).toBeTruthy();
  34. });
  35. test('event without data', async () => {
  36. async function* body(): AsyncGenerator<Buffer> {
  37. yield Buffer.from('event: foo\n');
  38. yield Buffer.from('\n');
  39. }
  40. const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
  41. Symbol.asyncIterator
  42. ]();
  43. let event = await stream.next();
  44. assert(event.value);
  45. expect(event.value.event).toEqual('foo');
  46. expect(event.value.data).toEqual('');
  47. event = await stream.next();
  48. expect(event.done).toBeTruthy();
  49. });
  50. test('multiple events', async () => {
  51. async function* body(): AsyncGenerator<Buffer> {
  52. yield Buffer.from('event: foo\n');
  53. yield Buffer.from('\n');
  54. yield Buffer.from('event: ping\n');
  55. yield Buffer.from('\n');
  56. }
  57. const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
  58. Symbol.asyncIterator
  59. ]();
  60. let event = await stream.next();
  61. assert(event.value);
  62. expect(event.value.event).toEqual('foo');
  63. expect(event.value.data).toEqual('');
  64. event = await stream.next();
  65. assert(event.value);
  66. expect(event.value.event).toEqual('ping');
  67. expect(event.value.data).toEqual('');
  68. event = await stream.next();
  69. expect(event.done).toBeTruthy();
  70. });
  71. test('multiple events with data', async () => {
  72. async function* body(): AsyncGenerator<Buffer> {
  73. yield Buffer.from('event: foo\n');
  74. yield Buffer.from('data: {"foo":true}\n');
  75. yield Buffer.from('\n');
  76. yield Buffer.from('event: ping\n');
  77. yield Buffer.from('data: {"bar":false}\n');
  78. yield Buffer.from('\n');
  79. }
  80. const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
  81. Symbol.asyncIterator
  82. ]();
  83. let event = await stream.next();
  84. assert(event.value);
  85. expect(event.value.event).toEqual('foo');
  86. expect(JSON.parse(event.value.data)).toEqual({ foo: true });
  87. event = await stream.next();
  88. assert(event.value);
  89. expect(event.value.event).toEqual('ping');
  90. expect(JSON.parse(event.value.data)).toEqual({ bar: false });
  91. event = await stream.next();
  92. expect(event.done).toBeTruthy();
  93. });
  94. test('multiple data lines with empty line', async () => {
  95. async function* body(): AsyncGenerator<Buffer> {
  96. yield Buffer.from('event: ping\n');
  97. yield Buffer.from('data: {\n');
  98. yield Buffer.from('data: "foo":\n');
  99. yield Buffer.from('data: \n');
  100. yield Buffer.from('data:\n');
  101. yield Buffer.from('data: true}\n');
  102. yield Buffer.from('\n\n');
  103. }
  104. const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
  105. Symbol.asyncIterator
  106. ]();
  107. let event = await stream.next();
  108. assert(event.value);
  109. expect(event.value.event).toEqual('ping');
  110. expect(JSON.parse(event.value.data)).toEqual({ foo: true });
  111. expect(event.value.data).toEqual('{\n"foo":\n\n\ntrue}');
  112. event = await stream.next();
  113. expect(event.done).toBeTruthy();
  114. });
  115. test('data json escaped double new line', async () => {
  116. async function* body(): AsyncGenerator<Buffer> {
  117. yield Buffer.from('event: ping\n');
  118. yield Buffer.from('data: {"foo": "my long\\n\\ncontent"}');
  119. yield Buffer.from('\n\n');
  120. }
  121. const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
  122. Symbol.asyncIterator
  123. ]();
  124. let event = await stream.next();
  125. assert(event.value);
  126. expect(event.value.event).toEqual('ping');
  127. expect(JSON.parse(event.value.data)).toEqual({ foo: 'my long\n\ncontent' });
  128. event = await stream.next();
  129. expect(event.done).toBeTruthy();
  130. });
  131. test('special new line characters', async () => {
  132. async function* body(): AsyncGenerator<Buffer> {
  133. yield Buffer.from('data: {"content": "culpa "}\n');
  134. yield Buffer.from('\n');
  135. yield Buffer.from('data: {"content": "');
  136. yield Buffer.from([0xe2, 0x80, 0xa8]);
  137. yield Buffer.from('"}\n');
  138. yield Buffer.from('\n');
  139. yield Buffer.from('data: {"content": "foo"}\n');
  140. yield Buffer.from('\n');
  141. }
  142. const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
  143. Symbol.asyncIterator
  144. ]();
  145. let event = await stream.next();
  146. assert(event.value);
  147. expect(JSON.parse(event.value.data)).toEqual({ content: 'culpa ' });
  148. event = await stream.next();
  149. assert(event.value);
  150. expect(JSON.parse(event.value.data)).toEqual({ content: Buffer.from([0xe2, 0x80, 0xa8]).toString() });
  151. event = await stream.next();
  152. assert(event.value);
  153. expect(JSON.parse(event.value.data)).toEqual({ content: 'foo' });
  154. event = await stream.next();
  155. expect(event.done).toBeTruthy();
  156. });
  157. test('multi-byte characters across chunks', async () => {
  158. async function* body(): AsyncGenerator<Buffer> {
  159. yield Buffer.from('event: completion\n');
  160. yield Buffer.from('data: {"content": "');
  161. // bytes taken from the string 'известни' and arbitrarily split
  162. // so that some multi-byte characters span multiple chunks
  163. yield Buffer.from([0xd0]);
  164. yield Buffer.from([0xb8, 0xd0, 0xb7, 0xd0]);
  165. yield Buffer.from([0xb2, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0xd0, 0xbd, 0xd0, 0xb8]);
  166. yield Buffer.from('"}\n');
  167. yield Buffer.from('\n');
  168. }
  169. const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
  170. Symbol.asyncIterator
  171. ]();
  172. let event = await stream.next();
  173. assert(event.value);
  174. expect(event.value.event).toEqual('completion');
  175. expect(JSON.parse(event.value.data)).toEqual({ content: 'известни' });
  176. event = await stream.next();
  177. expect(event.done).toBeTruthy();
  178. });
  179. });