fix(code-runtime): contain deep output accounting
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { CodeJsonValue } from '@deepseek-ai/dsh-code-runtime'
|
||||
import { jsonStringBytesUpTo, jsonValueBytesUpTo, truncateJsonStringBytes } from '../src/output-json.ts'
|
||||
|
||||
describe('truncateJsonStringBytes', () => {
|
||||
@@ -45,6 +46,8 @@ describe('jsonValueBytesUpTo', () => {
|
||||
expect(jsonValueBytesUpTo(value, bytes)).toBe(bytes)
|
||||
expect(jsonValueBytesUpTo(value, bytes - 1)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo({}, 1)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo([], 1)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo([], 2)).toBe(2)
|
||||
expect(jsonValueBytesUpTo(null, 3)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo(10, 1)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo(false, 4)).toBeUndefined()
|
||||
@@ -55,5 +58,14 @@ describe('jsonValueBytesUpTo', () => {
|
||||
expect(jsonValueBytesUpTo({ long: null }, 2)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo({ '': null }, 4)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo({ a: null }, 9)).toBeUndefined()
|
||||
expect(jsonValueBytesUpTo({ a: undefined } as unknown as CodeJsonValue, 100)).toBeUndefined()
|
||||
})
|
||||
|
||||
it('meters deeply nested arrays without recursive stack growth', () => {
|
||||
let value: CodeJsonValue = null
|
||||
for (let depth = 0; depth < 5_000; depth++) value = [value]
|
||||
|
||||
expect(jsonValueBytesUpTo(value, 10_004)).toBe(10_004)
|
||||
expect(jsonValueBytesUpTo(value, 10_003)).toBeUndefined()
|
||||
})
|
||||
})
|
||||
@@ -430,6 +430,29 @@ describe('WorkerCodeRuntime — hostile programs (real workers)', () => {
|
||||
expect(result).toEqual({ logs: [], error: { kind: 'exception', message: 'fake failure' } })
|
||||
})
|
||||
|
||||
it('contains a deeply nested forged completion without overflowing the host meter', async () => {
|
||||
const { runtime } = await setup()
|
||||
const result = await runtime.run({
|
||||
program: `
|
||||
const { parentPort } = await import('node:worker_threads');
|
||||
let value = null;
|
||||
for (let depth = 0; depth < 3_000; depth++) value = [value];
|
||||
parentPort.postMessage({ type: 'done', value });
|
||||
`,
|
||||
bindings: [],
|
||||
})
|
||||
expect(result.error).toBeUndefined()
|
||||
let value = result.value
|
||||
let depth = 0
|
||||
while (Array.isArray(value)) {
|
||||
expect(value).toHaveLength(1)
|
||||
value = value[0]
|
||||
depth += 1
|
||||
}
|
||||
expect(depth).toBe(3_000)
|
||||
expect(value).toBeNull()
|
||||
})
|
||||
|
||||
it('turns forged over-limit error text into output-limit at the host', async () => {
|
||||
const { runtime } = await setup({ maxOutputBytes: 64 })
|
||||
const result = await runtime.run({
|
||||
|
||||
@@ -79,6 +79,8 @@ describe('snapshotCodeJsonValue', () => {
|
||||
Object.defineProperty(symbolDecorated, Symbol('extra'), { value: true })
|
||||
const hiddenObject = Object.defineProperty({}, 'hidden', { value: true })
|
||||
const symbolObject = { [Symbol('extra')]: true }
|
||||
const customPrototype = Object.create(null) as Record<string, unknown>
|
||||
const customPrototypeObject = Object.assign(Object.create(customPrototype) as Record<string, unknown>, { value: 1 })
|
||||
const forgedPrototype: unknown[] = []
|
||||
Object.setPrototypeOf(forgedPrototype, null)
|
||||
const forgedArray = [1]
|
||||
@@ -94,6 +96,7 @@ describe('snapshotCodeJsonValue', () => {
|
||||
symbolDecorated,
|
||||
hiddenObject,
|
||||
symbolObject,
|
||||
customPrototypeObject,
|
||||
forgedArray,
|
||||
cyclic,
|
||||
[undefined],
|
||||
|
||||
Reference in New Issue
Block a user