session-fixture-layout.spec.ts 3.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import { resolve } from 'node:path'
  2. import { describe, expect, it } from 'vitest'
  3. import { type SessionEvent } from '@deepseek-ai/dsh-session'
  4. import { parseSessionLog } from '@deepseek-ai/dsh-llm-replay'
  5. import {
  6. canonicalSessionFixture,
  7. inspectSessionFixtureLayouts,
  8. isPhysicalSessionFixture,
  9. } from './session-fixture-layout.ts'
  10. const HEADER = ' {"type":"session","version":0,"id":"fixture","createdAt":1,"delegationDepth":0} '
  11. const root = resolve(import.meta.dirname, '..')
  12. function chunkRun(): SessionEvent[] {
  13. return Array.from({ length: 4 }, (_, index) => ({
  14. type: 'assistant/chunk',
  15. seq: index,
  16. time: 10 + index,
  17. data: {
  18. turn: 1,
  19. step: 1,
  20. chunk: { type: 'text-delta', index: 0, text: `part-${index}` },
  21. },
  22. }))
  23. }
  24. function unpackedFixture(): string {
  25. return [HEADER, ...chunkRun().map(event => JSON.stringify(event)), ''].join('\n')
  26. }
  27. function decodedBody(content: string): SessionEvent[] {
  28. return parseSessionLog(content)
  29. }
  30. describe('canonicalSessionFixture', () => {
  31. it('preserves the header line and packs an unpacked event run losslessly', () => {
  32. const canonical = canonicalSessionFixture(unpackedFixture(), 'fixture.jsonl')
  33. expect(canonical).toBeDefined()
  34. expect(canonical?.split('\n')[0]).toBe(HEADER)
  35. const packed = JSON.parse(canonical?.split('\n')[1] ?? '{}') as Record<string, unknown>
  36. expect(packed).toMatchObject({ type: 'text-chunks' })
  37. expect(packed).not.toHaveProperty('seq0')
  38. expect(packed).not.toHaveProperty('time0')
  39. expect(decodedBody(canonical ?? '').map(({ seq: _seq, time: _time, ...event }) => event))
  40. .toStrictEqual(chunkRun().map(({ seq: _seq, time: _time, ...event }) => event))
  41. })
  42. it('ignores JSONL whose first record is not a session header', () => {
  43. expect(canonicalSessionFixture('{"type":"session_event"}\n{"value":1}\n')).toBeUndefined()
  44. })
  45. it('is idempotent for an already packed fixture', () => {
  46. const packed = canonicalSessionFixture(unpackedFixture())
  47. expect(packed).toBeDefined()
  48. expect(canonicalSessionFixture(packed ?? '')).toBe(packed)
  49. })
  50. it('is idempotent for an already projected fixture', () => {
  51. const projected = [
  52. HEADER,
  53. '{"type":"turn/start","data":{"turn":1,"seq":99,"time":100}}',
  54. '',
  55. ].join('\n')
  56. expect(canonicalSessionFixture(projected)).toBe(projected)
  57. })
  58. it('fails loud on malformed records after a session header', () => {
  59. expect(() => canonicalSessionFixture(`${HEADER}\n{not-json}\n`, 'broken.jsonl'))
  60. .toThrow(/broken\.jsonl: session snapshot line 2 contains invalid JSON/)
  61. })
  62. it('labels malformed packed rows with the fixture path and line', () => {
  63. expect(() => canonicalSessionFixture(`${HEADER}\n{"type":"text-chunks"}\n`, 'broken.jsonl'))
  64. .toThrow(/broken\.jsonl: session snapshot line 2: malformed text-chunks storage row/)
  65. })
  66. })
  67. describe('isPhysicalSessionFixture', () => {
  68. it('excludes only persisted logs under the WebWorker example root', () => {
  69. expect(isPhysicalSessionFixture(
  70. 'packages/experimental/webworker-runtime/tests/fixtures/vfs-example/home/sessions/--dsh-workspace--/main/session.jsonl',
  71. )).toBe(true)
  72. expect(isPhysicalSessionFixture(
  73. 'packages/experimental/webworker-runtime/tests/fixtures/vfs-example/home/sessions/README.jsonl',
  74. )).toBe(false)
  75. expect(isPhysicalSessionFixture('apps/web/tests/snapshots/example/session.jsonl')).toBe(false)
  76. })
  77. })
  78. it('keeps every session-format JSONL fixture projected into canonical packed layout', () => {
  79. const nonCanonical = inspectSessionFixtureLayouts(root)
  80. .filter(fixture => fixture.source !== fixture.canonical)
  81. .map(fixture => fixture.path)
  82. expect(
  83. nonCanonical,
  84. 'Run `pnpm run migrate:packed-session-fixtures` and commit the mechanical fixture rewrite.',
  85. ).toEqual([])
  86. })