stray-fragments.spec.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import { Context } from '@deepseek-ai/cordis'
  2. import { expect, it, vi } from 'vitest'
  3. import { logTruncationMarker } from '../src/protocol.ts'
  4. // Keep the interpreter and pipe lifecycle real; only OS-dependent read sizes
  5. // change. Each byte reaches the runtime as its own data event.
  6. vi.mock('node:child_process', async (importOriginal) => {
  7. const actual = await importOriginal<typeof import('node:child_process')>()
  8. return {
  9. ...actual,
  10. spawn: vi.fn(actual.spawn).mockImplementation((...args) => {
  11. const child = actual.spawn(...args)
  12. const stdout = child.stdout!
  13. const emit = stdout.emit.bind(stdout)
  14. stdout.emit = (event: string | symbol, ...values: unknown[]) => {
  15. if (event !== 'data') return emit(event, ...values)
  16. const chunk = values[0] as Buffer
  17. for (let offset = 0; offset < chunk.length; offset++) {
  18. emit('data', chunk.subarray(offset, offset + 1))
  19. }
  20. return true
  21. }
  22. return child
  23. }),
  24. }
  25. })
  26. const { PythonPtcRuntime } = await import('../src/index.ts')
  27. it('seals stray fragments without recopying the sealed prefix', async () => {
  28. const ctx = new Context()
  29. const fiber = await ctx.plugin(PythonPtcRuntime, { maxLogBytes: 200_000, maxWallMs: 30_000 })
  30. const realConcat = Buffer.concat.bind(Buffer)
  31. let copied = 0
  32. let maxFragments = 0
  33. const concat = vi.spyOn(Buffer, 'concat').mockImplementation((list, total) => {
  34. maxFragments = Math.max(maxFragments, list.length)
  35. for (const part of list) copied += part.length
  36. return realConcat(list, total)
  37. })
  38. try {
  39. const result = await ctx.ptcRuntime.run(ctx.ptcRuntime.resolve({
  40. program: 'import os\nos.write(1, b"x" * 60000 + b"\\n")\nreturn "done"',
  41. bindings: [],
  42. }))
  43. expect(result.error).toBeUndefined()
  44. expect(result.value).toBe('done')
  45. expect(result.logs).toEqual(['x'.repeat(60_000)])
  46. // Sealing copies each byte at most twice; merging every accumulated prefix
  47. // instead copies over a megabyte for these 60,001 controlled fragments.
  48. expect(maxFragments).toBeLessThanOrEqual(1024)
  49. expect(copied).toBeLessThan(256 * 1024)
  50. } finally {
  51. concat.mockRestore()
  52. await fiber.dispose()
  53. }
  54. }, 40_000)
  55. it.each([
  56. { name: 'illegal UTF-8 bytes', payload: 'b"\\xff" * 3200' },
  57. { name: 'CESU-8 lone surrogates', payload: 'b"\\xed\\xa0\\x80" * 1100' },
  58. ])('bounds $name by their U+FFFD-decoded cost', async ({ payload }) => {
  59. const ctx = new Context()
  60. const fiber = await ctx.plugin(PythonPtcRuntime, { maxLogBytes: 3072, maxWallMs: 30_000 })
  61. const realConcat = Buffer.concat.bind(Buffer)
  62. let maxConcat = 0
  63. const concat = vi.spyOn(Buffer, 'concat').mockImplementation((list, total) => {
  64. const merged = realConcat(list, total)
  65. maxConcat = Math.max(maxConcat, merged.length)
  66. return merged
  67. })
  68. try {
  69. const result = await ctx.ptcRuntime.run(ctx.ptcRuntime.resolve({
  70. program: `import os\nos.write(1, ${payload})\nreturn None`,
  71. bindings: [],
  72. }))
  73. expect(result.error).toBeUndefined()
  74. expect(result.logs.at(-1)).toBe(logTruncationMarker(3072))
  75. // Each raw byte decodes to U+FFFD (three UTF-8 bytes), so a 3072-byte
  76. // budget flushes near 1024 raw bytes. Charging raw or structural widths
  77. // instead retains over 2048 bytes before flushing these payloads.
  78. expect(maxConcat).toBeLessThan(2048)
  79. } finally {
  80. concat.mockRestore()
  81. await fiber.dispose()
  82. }
  83. }, 20_000)