From c8bf75cbe4abfdef4290f8c6e4e7631c247f1bae Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Sun, 2 Aug 2026 21:36:12 +0800 Subject: [PATCH] test(code-runtime-python): cover the newline-free stray-flood ledger bound MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The line-aggregating stray capture added two branches — the post-truncation early return and the residual-overflow admit — that the aggregation and split tests did not exercise, so per-file coverage dropped below 100%. A 2 MB newline-free native write under a 4 KiB maxLogBytes drives the residual across the budget (admit-and-truncate) and then short-circuits later chunks, asserting the captured output ends at the truncation marker and stays under budget rather than buffering the whole flood. --- .../code-runtime-python/tests/runtime.spec.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/code-runtime/code-runtime-python/tests/runtime.spec.ts b/packages/code-runtime/code-runtime-python/tests/runtime.spec.ts index 2c9c0988e8..c884699707 100644 --- a/packages/code-runtime/code-runtime-python/tests/runtime.spec.ts +++ b/packages/code-runtime/code-runtime-python/tests/runtime.spec.ts @@ -711,6 +711,24 @@ describe('PythonCodeRuntime — programs and bindings', () => { expect(result.logs).toEqual(['one', 'two', 'three']) }) + it('bounds a newline-free native flood by the ledger instead of buffering it whole', async () => { + // A newline-free write far larger than maxLogBytes must not accumulate in + // the host-side residual: when the pending residual would cross the budget + // it is admitted (and truncated) immediately, and once the ledger has + // truncated, later chunks stop buffering entirely. The run still completes + // and the captured output ends at the truncation marker rather than + // retaining the whole flood. + const { runtime } = await setup({ maxLogBytes: 4096 }) + const result = await runtime.run({ + program: ['import os', 'os.write(1, b"A" * 2_000_000)', 'return None'].join('\n'), + bindings: [], + }) + expect(result.error).toBeUndefined() + expect(result.logs.at(-1)).toBe(logTruncationMarker(4096)) + // The retained output is bounded by the budget, not the 2 MB flood. + expect(result.logs.join('').length).toBeLessThan(4096) + }) + it('fails a completion dict with a non-string key as invalid-output (no key coercion)', async () => { // json.dumps would coerce {1: "a", "1": "b"} to a single "1" key, silently // dropping data. The shape validator rejects it before encoding.