mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-11 04:00:38 +00:00
`session[.vN].jsonl` was assembled independently by the JSONL provider (twice), the session-log export archive, and the recorded-session fixture helpers. `dsh-session-format` now owns `sessionFormatLogFilename` and `parseSessionFormatLogFilename`; the three consumers append only their compression suffix, and the migration note names the owner. `snapshotSessionFormatJson` re-implemented the lossless JSON walk that `dsh-util-values` already publishes as `snapshotJsonValue` + `deepFreeze`, and the frozen v0 relationship validator carried a third structural JSON comparison next to `deepEqualJson`. Both now delegate; the format package reports one `is not lossless JSON` diagnostic per labelled subject instead of ten member-specific ones, and enumerable accessors whose values survive a JSON round trip are accepted like `JSON.stringify` accepts them.
72 lines
3.4 KiB
TypeScript
72 lines
3.4 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
inspectSessionFormatVersion,
|
|
sessionFormatCount,
|
|
sessionFormatSafeInteger,
|
|
snapshotSessionFormatArtifact,
|
|
snapshotSessionFormatHeader,
|
|
snapshotSessionFormatJson,
|
|
} from '../src/index.ts'
|
|
|
|
describe('lossless Session format JSON snapshots', () => {
|
|
it.each([
|
|
['negative zero', -0],
|
|
['non-finite number', Number.POSITIVE_INFINITY],
|
|
['undefined member', { value: undefined }],
|
|
['sparse array', Array(1)],
|
|
['symbol member', { [Symbol('hidden')]: true }],
|
|
['non-enumerable member', Object.defineProperty({}, 'hidden', { value: true })],
|
|
['array property', Object.assign([], { extra: true })],
|
|
])('refuses %s that JSON cannot preserve', (_name, value) => {
|
|
expect(() => snapshotSessionFormatJson(value, 'payload')).toThrow('payload is not lossless JSON')
|
|
})
|
|
|
|
it('detaches, freezes, and retains repeated non-cyclic values and __proto__ keys', () => {
|
|
const shared = { value: 1 }
|
|
const source = JSON.parse('{"__proto__":{"safe":true}}') as Record<string, unknown>
|
|
source['values'] = [shared, shared]
|
|
|
|
const snapshot = snapshotSessionFormatJson(source) as Record<string, unknown>
|
|
|
|
expect(snapshot).toEqual(source)
|
|
expect(snapshot).not.toBe(source)
|
|
expect(Object.isFrozen(snapshot)).toBe(true)
|
|
expect(Object.isFrozen(snapshot['values'])).toBe(true)
|
|
expect(Object.getPrototypeOf(snapshot)).toBe(Object.prototype)
|
|
})
|
|
|
|
it('refuses invalid scalar coordinates, cycles, and custom prototypes', () => {
|
|
const cyclic: { self?: unknown } = {}
|
|
cyclic.self = cyclic
|
|
class RecordValue { value = 1 }
|
|
class ArrayValue extends Array<number> {}
|
|
|
|
expect(() => sessionFormatCount(-1, 'count')).toThrow(/non-negative/)
|
|
expect(() => sessionFormatSafeInteger(1.5, 'integer')).toThrow(/safe integer/)
|
|
expect(() => inspectSessionFormatVersion([])).toThrow(/header/)
|
|
expect(() => snapshotSessionFormatJson(cyclic)).toThrow(/not lossless JSON/)
|
|
expect(() => snapshotSessionFormatJson(new RecordValue())).toThrow(/not lossless JSON/)
|
|
expect(() => snapshotSessionFormatJson(new ArrayValue(1))).toThrow(/not lossless JSON/)
|
|
})
|
|
|
|
it.each([
|
|
['non-object header', { header: null, inheritedEventCount: 0, events: [] }],
|
|
['non-array events', { header: { version: 1 }, inheritedEventCount: 0, events: null }],
|
|
['non-object event', { header: { version: 1 }, inheritedEventCount: 0, events: [null] }],
|
|
['non-dense seq', { header: { version: 1 }, inheritedEventCount: 0, events: [{ type: 'x', seq: 1, time: 1, data: {} }] }],
|
|
['empty type', { header: { version: 1 }, inheritedEventCount: 0, events: [{ type: '', seq: 0, time: 1, data: {} }] }],
|
|
['invalid time', { header: { version: 1 }, inheritedEventCount: 0, events: [{ type: 'x', seq: 0, time: 1.5, data: {} }] }],
|
|
['missing data', { header: { version: 1 }, inheritedEventCount: 0, events: [{ type: 'x', seq: 0, time: 1 }] }],
|
|
['oversized cut', { header: { version: 1 }, inheritedEventCount: 1, events: [] }],
|
|
])('refuses an artifact with %s', (_name, artifact) => {
|
|
expect(() => snapshotSessionFormatArtifact(artifact as never)).toThrow()
|
|
})
|
|
|
|
it('refuses a non-object header snapshot', () => {
|
|
expect(() => snapshotSessionFormatHeader(null as never)).toThrow(/header|object/)
|
|
expect(() => snapshotSessionFormatHeader({
|
|
version: 1, id: 'missing-seeded', createdAt: 1, delegationDepth: 0,
|
|
} as never)).toThrow(/isSeeded/)
|
|
})
|
|
})
|