






















1-// Shared web helper tests cover timeout normalization and process-local cache
2-// expiry guards.
1+// Shared web helper tests cover timeout normalization, process-local cache
2+// expiry guards, and bounded response body cleanup.
33import {
44MAX_TIMER_TIMEOUT_MS,
55MAX_TIMER_TIMEOUT_SECONDS,
66} from "@openclaw/normalization-core/number-coercion";
77import { afterEach, describe, expect, it, vi } from "vitest";
88import {
99readCache,
10+readResponseText,
1011resolvePositiveTimeoutSeconds,
1112resolveTimeoutSeconds,
1213withTimeout,
@@ -18,6 +19,28 @@ afterEach(() => {
1819vi.restoreAllMocks();
1920});
202122+function responseFromReader(params: {
23+chunks: string[];
24+cancel: () => Promise<void>;
25+releaseLock: () => void;
26+}): Response {
27+const chunks: Array<ReadableStreamReadResult<Uint8Array>> = params.chunks.map((chunk) => ({
28+done: false,
29+value: new TextEncoder().encode(chunk),
30+}));
31+chunks.push({ done: true, value: undefined });
32+const reader = {
33+read: async () => chunks.shift() ?? { done: true, value: undefined },
34+cancel: params.cancel,
35+releaseLock: params.releaseLock,
36+} as ReadableStreamDefaultReader<Uint8Array>;
37+38+return {
39+body: { getReader: () => reader },
40+headers: new Headers({ "content-type": "text/plain; charset=utf-8" }),
41+} as Response;
42+}
43+2144describe("web shared timeout seconds", () => {
2245it("caps timeoutSeconds at the shared timer-safe ceiling", () => {
2346expect(resolveTimeoutSeconds(Number.MAX_SAFE_INTEGER, 30)).toBe(MAX_TIMER_TIMEOUT_SECONDS);
@@ -86,3 +109,41 @@ describe("web shared withTimeout", () => {
86109expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), MAX_TIMER_TIMEOUT_MS);
87110});
88111});
112+113+describe("readResponseText", () => {
114+it("releases bounded response readers after complete reads", async () => {
115+const cancel = vi.fn(async () => undefined);
116+const releaseLock = vi.fn();
117+const response = responseFromReader({
118+chunks: ["hello", " world"],
119+ cancel,
120+ releaseLock,
121+});
122+123+await expect(readResponseText(response, { maxBytes: 64 })).resolves.toEqual({
124+text: "hello world",
125+truncated: false,
126+bytesRead: 11,
127+});
128+expect(cancel).not.toHaveBeenCalled();
129+expect(releaseLock).toHaveBeenCalledTimes(1);
130+});
131+132+it("cancels and releases bounded response readers after truncation", async () => {
133+const cancel = vi.fn(async () => undefined);
134+const releaseLock = vi.fn();
135+const response = responseFromReader({
136+chunks: ["hello world"],
137+ cancel,
138+ releaseLock,
139+});
140+141+await expect(readResponseText(response, { maxBytes: 5 })).resolves.toEqual({
142+text: "hello",
143+truncated: true,
144+bytesRead: 5,
145+});
146+expect(cancel).toHaveBeenCalledTimes(1);
147+expect(releaseLock).toHaveBeenCalledTimes(1);
148+});
149+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。