























11import { describe, expect, it, vi } from "vitest";
22import { fetchProofComments } from "../../scripts/github/real-behavior-proof-check.mjs";
334+function stalledResponse() {
5+let keepAlive: ReturnType<typeof setTimeout> | undefined;
6+const reader = {
7+read: () =>
8+new Promise<ReadableStreamReadResult<Uint8Array>>(() => {
9+keepAlive = setTimeout(() => {}, 10_000);
10+}),
11+cancel: vi.fn(() => {
12+if (keepAlive) {
13+clearTimeout(keepAlive);
14+}
15+return Promise.resolve();
16+}),
17+releaseLock: vi.fn(),
18+};
19+return {
20+ok: true,
21+status: 200,
22+headers: new Headers(),
23+body: {
24+getReader: () => reader,
25+},
26+};
27+}
28+29+function contentLengthResponse(contentLength: number) {
30+const cancel = vi.fn(() => Promise.resolve());
31+return {
32+ok: true,
33+status: 200,
34+headers: new Headers({ "content-length": String(contentLength) }),
35+body: { cancel },
36+ cancel,
37+};
38+}
39+40+function chunkedResponse(chunks: Uint8Array[]) {
41+const cancel = vi.fn(() => Promise.resolve());
42+const read = vi.fn();
43+for (const chunk of chunks) {
44+read.mockResolvedValueOnce({ done: false, value: chunk });
45+}
46+read.mockResolvedValueOnce({ done: true, value: undefined });
47+return {
48+ok: true,
49+status: 200,
50+headers: new Headers(),
51+body: {
52+getReader: () => ({
53+ read,
54+ cancel,
55+releaseLock: vi.fn(),
56+}),
57+},
58+};
59+}
60+461describe("real-behavior-proof-check GitHub lookups", () => {
562it("aborts stalled proof comment fetches", async () => {
663const fetch = vi.fn((_url: URL, init: RequestInit) => {
@@ -22,11 +79,7 @@ describe("real-behavior-proof-check GitHub lookups", () => {
2279});
23802481it("times out stalled proof comment response bodies", async () => {
25-const fetch = vi.fn().mockResolvedValue({
26-ok: true,
27-status: 200,
28-json: () => new Promise(() => {}),
29-});
82+const fetch = vi.fn().mockResolvedValue(stalledResponse());
30833184await expect(
3285fetchProofComments({
@@ -39,4 +92,88 @@ describe("real-behavior-proof-check GitHub lookups", () => {
3992}),
4093).rejects.toThrow(/proof comment response page 1 timed out after 5ms/);
4194});
95+96+it("skips oversized proof comment bodies by content length after narrow fallback", async () => {
97+const response = contentLengthResponse(1024 * 1024 + 1);
98+const fetch = vi.fn((url: URL) => {
99+const perPage = url.searchParams.get("per_page");
100+const page = url.searchParams.get("page");
101+if ((perPage === "100" && page === "1") || (perPage === "1" && page === "1")) {
102+return Promise.resolve(response);
103+}
104+return Promise.resolve(new Response("[]", { status: 200 }));
105+});
106+107+await expect(
108+fetchProofComments({
109+fetchImpl: fetch as typeof globalThis.fetch,
110+issueNumber: 123,
111+owner: "openclaw",
112+repo: "openclaw",
113+tokens: ["tok"],
114+}),
115+).resolves.toEqual([]);
116+expect(response.cancel).toHaveBeenCalled();
117+});
118+119+it("skips oversized streamed proof comment bodies after narrow fallback", async () => {
120+const encoder = new TextEncoder();
121+const oversizedResponse = () =>
122+chunkedResponse([
123+encoder.encode("["),
124+encoder.encode(" ".repeat(1024 * 1024)),
125+encoder.encode("]"),
126+]);
127+const fetch = vi.fn((url: URL) => {
128+const perPage = url.searchParams.get("per_page");
129+const page = url.searchParams.get("page");
130+if ((perPage === "100" && page === "1") || (perPage === "1" && page === "1")) {
131+return Promise.resolve(oversizedResponse());
132+}
133+return Promise.resolve(new Response("[]", { status: 200 }));
134+});
135+136+await expect(
137+fetchProofComments({
138+fetchImpl: fetch as typeof globalThis.fetch,
139+issueNumber: 123,
140+owner: "openclaw",
141+repo: "openclaw",
142+tokens: ["tok"],
143+}),
144+).resolves.toEqual([]);
145+});
146+147+it("falls back to one-comment pages when a bulk comment page is oversized", async () => {
148+const fetch = vi.fn((url: URL) => {
149+const perPage = url.searchParams.get("per_page");
150+const page = url.searchParams.get("page");
151+if (perPage === "100" && page === "1") {
152+return Promise.resolve(contentLengthResponse(1024 * 1024 + 1));
153+}
154+if (perPage === "1" && page === "1") {
155+return Promise.resolve(contentLengthResponse(1024 * 1024 + 1));
156+}
157+if (perPage === "1" && page === "2") {
158+return Promise.resolve(new Response('[{"id":2,"body":"trusted proof"}]', { status: 200 }));
159+}
160+return Promise.resolve(new Response("[]", { status: 200 }));
161+});
162+163+await expect(
164+fetchProofComments({
165+fetchImpl: fetch as typeof globalThis.fetch,
166+issueNumber: 123,
167+owner: "openclaw",
168+repo: "openclaw",
169+tokens: ["tok"],
170+}),
171+).resolves.toEqual([{ id: 2, body: "trusted proof" }]);
172+173+expect(
174+fetch.mock.calls.map(
175+([url]) => `${url.searchParams.get("per_page")}:${url.searchParams.get("page")}`,
176+),
177+).toEqual(["100:1", "1:1", "1:2", "1:3"]);
178+});
42179});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。