1
0

protocol-mirror.e2e.ts 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. import { execFile } from 'node:child_process'
  2. import { existsSync } from 'node:fs'
  3. import { fileURLToPath } from 'node:url'
  4. import { promisify } from 'node:util'
  5. import { describe, expect, it } from 'vitest'
  6. import { logTruncationMarker } from '../src/protocol.ts'
  7. /**
  8. * Cross-language mirror check for the two protocol surfaces the host and the
  9. * CPython subprocess share at runtime, spawning a real `python3` to read them
  10. * from `py/protocol.py`. `src/protocol.ts` and `py/protocol.py` declare the same
  11. * frame vocabulary on two sides of the wire; the only values both sides EXECUTE
  12. * against are `PROTOCOL_FD` (the fd the channel is pinned to) and the log
  13. * truncation marker text (emitted verbatim by whichever ledger exhausts first),
  14. * so a drift there silently corrupts a live run. Self-skips when no `python3` is
  15. * on PATH — CI provides one; the pure-TS `protocol.spec.ts` covers the host
  16. * codec unconditionally.
  17. */
  18. const execFileAsync = promisify(execFile)
  19. const pyDir = fileURLToPath(new URL('../py', import.meta.url))
  20. async function hasPython3(): Promise<boolean> {
  21. try {
  22. await execFileAsync('python3', ['--version'])
  23. return true
  24. } catch {
  25. return false
  26. }
  27. }
  28. const python3Available = await hasPython3()
  29. describe.skipIf(!python3Available)('protocol.py mirrors protocol.ts at runtime', () => {
  30. it('agrees on PROTOCOL_FD and the log truncation marker across byte budgets', async () => {
  31. const budgets = [1, 65536, 1048576]
  32. const probe = [
  33. 'import json, sys',
  34. `sys.path.insert(0, ${JSON.stringify(pyDir)})`,
  35. 'from protocol import PROTOCOL_FD, log_truncation_marker',
  36. `budgets = ${JSON.stringify(budgets)}`,
  37. 'print(json.dumps({',
  38. ' "fd": PROTOCOL_FD,',
  39. ' "markers": [log_truncation_marker(b) for b in budgets],',
  40. '}))',
  41. ].join('\n')
  42. const { stdout } = await execFileAsync('python3', ['-I', '-c', probe])
  43. const seen = JSON.parse(stdout) as { fd: number; markers: string[] }
  44. // fd 3 is the wire contract, not a tunable: index.ts pins it positionally.
  45. expect(seen.fd).toBe(3)
  46. expect(seen.markers).toEqual(budgets.map(budget => logTruncationMarker(budget)))
  47. })
  48. })
  49. it('names the py/ directory that ships with the package', () => {
  50. // Resolves py/ relative to this test file; the same directory ships in the
  51. // package.json `files` whitelist (`py/**/*.py`). The tests/ directory itself
  52. // is not published — this asserts the source-tree layout the mirror test
  53. // depends on, so it holds even when python3 is absent from the runner.
  54. expect(existsSync(pyDir)).toBe(true)
  55. })