mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(e2e): keep text file tails UTF-8 safe (#109669)
* fix(e2e): keep text file tails UTF-8 safe * fix: preserve malformed UTF-8 tail bytes Co-authored-by: qingminlong <0668001063@xydigit.com> --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -1,11 +1,21 @@
|
||||
// Text file tail helpers for E2E assertions.
|
||||
import fs from "node:fs";
|
||||
|
||||
function decodeUtf8Tail(buffer, truncated) {
|
||||
let start = 0;
|
||||
if (truncated) {
|
||||
while (start < buffer.length && (buffer[start] & 0b1100_0000) === 0b1000_0000) {
|
||||
start += 1;
|
||||
}
|
||||
}
|
||||
return buffer.subarray(start).toString("utf8");
|
||||
}
|
||||
|
||||
export function tailText(text, maxBytes) {
|
||||
if (Buffer.byteLength(text, "utf8") <= maxBytes) {
|
||||
return text;
|
||||
}
|
||||
return Buffer.from(text, "utf8").subarray(-maxBytes).toString("utf8");
|
||||
return decodeUtf8Tail(Buffer.from(text, "utf8").subarray(-maxBytes), true);
|
||||
}
|
||||
|
||||
export function readTextFileTail(file, maxBytes) {
|
||||
@@ -26,7 +36,7 @@ export function readTextFileTail(file, maxBytes) {
|
||||
fd = fs.openSync(file, "r");
|
||||
const buffer = Buffer.alloc(length);
|
||||
const bytesRead = fs.readSync(fd, buffer, 0, length, start);
|
||||
return buffer.subarray(0, bytesRead).toString("utf8");
|
||||
return decodeUtf8Tail(buffer.subarray(0, bytesRead), start > 0);
|
||||
} catch {
|
||||
return "";
|
||||
} finally {
|
||||
|
||||
@@ -23,6 +23,8 @@ describe("e2e text file utilities", () => {
|
||||
it("keeps short diagnostic text intact and trims long text by byte count", () => {
|
||||
expect(tailText("short", 8)).toBe("short");
|
||||
expect(tailText("prefix-tail", 4)).toBe("tail");
|
||||
expect(tailText("prefix \u{1f600}tail", 7)).toBe("tail");
|
||||
expect(tailText("prefix \u{1f600}tail", 8)).toBe("\u{1f600}tail");
|
||||
});
|
||||
|
||||
it("reads only the requested file tail and treats missing or non-file paths as empty", () => {
|
||||
@@ -33,6 +35,11 @@ describe("e2e text file utilities", () => {
|
||||
writeFileSync(file, "line-one\nline-two\nline-three", "utf8");
|
||||
|
||||
expect(readTextFileTail(file, 10)).toBe("line-three");
|
||||
writeFileSync(file, "prefix \u{1f600}tail", "utf8");
|
||||
expect(readTextFileTail(file, 7)).toBe("tail");
|
||||
expect(readTextFileTail(file, 8)).toBe("\u{1f600}tail");
|
||||
writeFileSync(file, Buffer.concat([Buffer.from("prefix tail"), Buffer.from([0xf0])]));
|
||||
expect(readTextFileTail(file, 5)).toBe("tail\ufffd");
|
||||
expect(readTextFileTail(path.join(root, "missing.log"), 10)).toBe("");
|
||||
expect(readTextFileTail(directory, 10)).toBe("");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user