Skip to content

Commit 7d7aba1

Browse files
committed
test(matrix): prove UTF-16 truncation boundaries
1 parent 166bb5b commit 7d7aba1

3 files changed

Lines changed: 31 additions & 29 deletions

File tree

extensions/matrix/src/matrix/monitor/handler.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3184,6 +3184,31 @@ describe("matrix monitor handler draft streaming", () => {
31843184
await finish();
31853185
});
31863186

3187+
it("keeps truncated Matrix tool progress UTF-16 safe", async () => {
3188+
const { dispatch } = createStreamingHarness({
3189+
streaming: "progress",
3190+
previewToolProgressEnabled: true,
3191+
accountConfig: {
3192+
streaming: {
3193+
mode: "progress",
3194+
progress: { label: false, maxLineChars: 500 },
3195+
},
3196+
} as never,
3197+
});
3198+
const { opts, finish } = await dispatch();
3199+
const progressPrefix = "x".repeat(298);
3200+
3201+
const progressText = `${progressPrefix}🎉tail`;
3202+
await opts.onItemEvent?.({ progressText });
3203+
await opts.onItemEvent?.({ progressText });
3204+
3205+
await vi.waitFor(() => {
3206+
expect(sendSingleTextMessageMatrixMock).toHaveBeenCalledTimes(1);
3207+
});
3208+
expect(singleTextMessageBody()).toBe(`- \`${progressPrefix}...\``);
3209+
await finish();
3210+
});
3211+
31873212
it("replaces recovered Matrix command progress instead of leaving stale failed text", async () => {
31883213
const { dispatch } = createStreamingHarness({
31893214
streaming: "progress",

extensions/matrix/src/matrix/sdk/event-helpers.test.ts

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -41,35 +41,12 @@ describe("event-helpers", () => {
4141
expect(fromText.statusCode).toBe(500);
4242
});
4343

44-
it("buildHttpError keeps the 500-char plain-text body truncation UTF-16 safe at emoji boundaries", () => {
45-
// 498 ASCII chars + emoji (😀 = U+1F600, UTF-16 surrogate pair) = 500 code units.
46-
// A naive .slice(0, 500) would land on the high surrogate and return a dangling
47-
// high surrogate (charCode 0xD83D), which downstream string consumers render
48-
// as U+FFFD / mojibake. truncateUtf16Safe trims the orphan high surrogate
49-
// and returns 499 code units ending cleanly before the emoji.
50-
const ascii = "a".repeat(498);
51-
const emoji = "😀"; // 2 UTF-16 code units
52-
const bodyText = ascii + emoji + "tail";
53-
const error = buildHttpError(500, bodyText);
54-
expect(error.message.length).toBe(499);
55-
expect(error.message).toBe(ascii);
56-
expect(error.message.charCodeAt(error.message.length - 1)).not.toBe(0xd83d);
57-
});
44+
it("keeps truncated HTTP error bodies UTF-16 safe", () => {
45+
const parsedPrefix = `{"detail":"${"a".repeat(488)}`;
46+
const invalidPrefix = `not-json ${"b".repeat(490)}`;
5847

59-
it("buildHttpError keeps a non-parseable JSON body truncation UTF-16 safe at emoji boundaries", () => {
60-
// 498 ASCII chars + emoji = 500 code units. The try/catch branch falls back
61-
// to truncateUtf16Safe(bodyText, 500); before this fix the same dangling
62-
// high surrogate would have leaked into the error message.
63-
const ascii = "b".repeat(498);
64-
const emoji = "🎉"; // 2 UTF-16 code units
65-
const bodyText = "not-json " + ascii + emoji + "more";
66-
const error = buildHttpError(502, bodyText);
67-
// The slice lands on the high surrogate of 🎉, which truncateUtf16Safe
68-
// trims — final length is 499 code units (one less than the cap).
69-
expect(error.message.length).toBe(499);
70-
// Final code unit is the 498th ASCII 'b' (0x62), not a dangling high surrogate.
71-
expect(error.message.charCodeAt(error.message.length - 1)).toBe(0x62);
72-
expect(error.message).not.toMatch(//);
48+
expect(buildHttpError(500, `${parsedPrefix}😀"}`).message).toBe(parsedPrefix);
49+
expect(buildHttpError(502, `${invalidPrefix}🎉tail`).message).toBe(invalidPrefix);
7350
});
7451

7552
it("serializes Matrix events and resolves state key from available sources", () => {

extensions/matrix/src/matrix/sdk/event-helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { MatrixEvent } from "matrix-js-sdk/lib/matrix.js";
21
// Matrix helper module supports event helpers behavior.
2+
import type { MatrixEvent } from "matrix-js-sdk/lib/matrix.js";
33
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
44
import type { MatrixRawEvent } from "./types.js";
55

0 commit comments

Comments
 (0)