From 1f5c8239d71cbd19c51499e893fc54ad60cb771d Mon Sep 17 00:00:00 2001 From: Leon-SK668 <0668001470@xydigit.com> Date: Fri, 17 Jul 2026 09:49:36 +0800 Subject: [PATCH] fix(scripts): preserve emoji in request log previews (#109481) * fix(scripts): preserve emoji in request log previews * fix(scripts): keep mock preview standalone * refactor(scripts): reuse UTF-16 truncation helper Co-authored-by: Leon-SK668 <0668001470@xydigit.com> * test(scripts): cover raw Node mock loading Co-authored-by: Leon-SK668 <0668001470@xydigit.com> * docs(scripts): note raw Node type stripping contract Co-authored-by: Leon-SK668 <0668001470@xydigit.com> --------- Co-authored-by: Peter Steinberger --- scripts/e2e/lib/mock-openai-http.mjs | 4 +++- test/scripts/mock-openai-http.test.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/scripts/e2e/lib/mock-openai-http.mjs b/scripts/e2e/lib/mock-openai-http.mjs index be1c64394cb1..e814c4073c75 100644 --- a/scripts/e2e/lib/mock-openai-http.mjs +++ b/scripts/e2e/lib/mock-openai-http.mjs @@ -1,5 +1,7 @@ // Mock OpenAI-compatible HTTP server helpers for E2E scenarios. import fs from "node:fs"; +// Raw launchers meet the repo's Node 22.22.3 minimum, where native TS stripping is enabled. +import { truncateUtf16Safe } from "../../../packages/normalization-core/src/utf16-slice.ts"; import { readPositiveIntEnv } from "./env-limits.mjs"; const DEFAULT_REQUEST_MAX_BYTES = 4 * 1024 * 1024; @@ -76,7 +78,7 @@ export function boundedRequestLogBody(value, bodyText, limits = readMockOpenAiHt return { truncated: true, byteLength, - preview: bodyText.slice(0, REQUEST_LOG_PREVIEW_CHARS), + preview: truncateUtf16Safe(bodyText, REQUEST_LOG_PREVIEW_CHARS), }; } diff --git a/test/scripts/mock-openai-http.test.ts b/test/scripts/mock-openai-http.test.ts index abe1adc55bd4..e4f15df392dc 100644 --- a/test/scripts/mock-openai-http.test.ts +++ b/test/scripts/mock-openai-http.test.ts @@ -1,4 +1,5 @@ // Mock Openai Http tests cover mock openai http script behavior. +import { spawnSync } from "node:child_process"; import { PassThrough } from "node:stream"; import { describe, expect, it } from "vitest"; import { @@ -15,6 +16,17 @@ function bodyStream(text: string) { } describe("mock OpenAI HTTP helpers", () => { + it("loads under raw Node without a TypeScript loader", () => { + const result = spawnSync( + process.execPath, + ["--input-type=module", "--eval", "await import('./scripts/e2e/lib/mock-openai-http.mjs')"], + { cwd: process.cwd(), encoding: "utf8" }, + ); + + expect(result.status, result.stderr).toBe(0); + expect(result.signal).toBeNull(); + }); + it("reads request bodies within the configured ceiling", async () => { await expect(readBody(bodyStream("small"), { requestMaxBytes: 8 })).resolves.toBe("small"); }); @@ -43,6 +55,20 @@ describe("mock OpenAI HTTP helpers", () => { }); }); + it("does not split emoji in request-log previews", () => { + const bodyText = `${"a".repeat(4095)}😀tail`; + + expect( + boundedRequestLogBody({ full: bodyText }, bodyText, { + requestLogBodyMaxBytes: 8, + }), + ).toEqual({ + truncated: true, + byteLength: Buffer.byteLength(bodyText, "utf8"), + preview: "a".repeat(4095), + }); + }); + it("keeps small request-log bodies intact", () => { const body = { ok: true }; expect(boundedRequestLogBody(body, JSON.stringify(body), { requestLogBodyMaxBytes: 64 })).toBe(