fix(test): keep bounded child output UTF-8 safe (#109168)

* fix(test): keep bounded child output UTF-8 safe

* test: preserve incomplete child output tails

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
qingminlong
2026-07-17 13:53:11 +08:00
committed by GitHub
parent dabeae8c90
commit e4a48157d1
2 changed files with 36 additions and 1 deletions
+25
View File
@@ -22,4 +22,29 @@ describe("bounded child output", () => {
expect(output.text()).toBe("x-recent");
});
it("drops split UTF-8 prefixes after buffered output overflow", () => {
const output = createBoundedChildOutput(7);
output.append(Buffer.from("prefix 😀"));
output.append(Buffer.from("tail"));
expect(output.text()).toBe("tail");
});
it("drops split UTF-8 prefixes from single oversized chunks", () => {
const output = createBoundedChildOutput(7);
output.append(Buffer.from("prefix 😀tail"));
expect(output.text()).toBe("tail");
});
it("preserves replacement output for incomplete trailing bytes", () => {
const output = createBoundedChildOutput(7);
output.append(Buffer.from([0x61, 0xe2]));
expect(output.text()).toBe("a");
});
});
+11 -1
View File
@@ -1,5 +1,15 @@
import { StringDecoder } from "node:string_decoder";
export const DEFAULT_CHILD_OUTPUT_TAIL_BYTES = 128 * 1024;
function decodeUtf8Tail(buffer: Buffer): string {
let start = 0;
while (start < buffer.length && (buffer[start]! & 0b1100_0000) === 0b1000_0000) {
start += 1;
}
return new StringDecoder("utf8").end(buffer.subarray(start));
}
export function createBoundedChildOutput(maxBytes = DEFAULT_CHILD_OUTPUT_TAIL_BYTES) {
const limit =
Number.isInteger(maxBytes) && maxBytes > 0 ? maxBytes : DEFAULT_CHILD_OUTPUT_TAIL_BYTES;
@@ -37,7 +47,7 @@ export function createBoundedChildOutput(maxBytes = DEFAULT_CHILD_OUTPUT_TAIL_BY
trim();
},
text(): string {
return Buffer.concat(chunks, totalBytes).toString("utf8");
return decodeUtf8Tail(Buffer.concat(chunks, totalBytes));
},
};
}