mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(qa-channel): add timeout to qa-bus state guarded fetch (#106538)
* fix(qa-channel): add timeout to qa-bus state guarded fetch * test(qa-channel): add executable negative control for bus-state hang * test(qa-channel): oxfmt negative-control bus-client test * fix(qa-channel): sync SDK facade type with getQaBusState options parameter Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn> * test(qa-channel): add standalone production-path proof for bus-state timeout Proof exercises the real getQaBusState code path against loopback TCP peers that accept but never return HTTP headers: - Negative control: fetchWithSsrFGuard without timeoutMs stays pending - Positive control: getQaBusState with timeout rejects with TimeoutError - Valid response: normal server still resolves within the timeout floor Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn> * test(qa-channel): log node version and head SHA in bus-state timeout proof Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn> * fix(qa-channel): drop test timeout from public Plugin SDK facade type Keep the `timeoutMs` option internal to `getQaBusState` for test-only short floors. The public facade callers do not need it and the SDK surface should not expose test-only parameters. Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn> * chore(qa-channel): remove standalone proof script per review The negative, timeout, and responsive cases are already durably covered in bus-client.test.ts. The executed live output remains in the PR body as evidence. Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn> * chore: trigger CI after rebase onto main * chore: trigger CI after rebase onto main * test(qa-channel): keep state timeout internal --------- Co-authored-by: nebulacoder-v8.0 <noreply@zte.com.cn> Co-authored-by: Peter Steinberger <steipete@gmail.com> Co-authored-by: Peter Steinberger <peter@steipete.me>
This commit is contained in:
co-authored by
nebulacoder-v8.0
Peter Steinberger
Peter Steinberger
parent
f41c143345
commit
d11f304519
@@ -1,8 +1,26 @@
|
||||
// Qa Channel tests cover bus client plugin behavior.
|
||||
import { createServer, type Server } from "node:http";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { buildQaTarget, getQaBusState, parseQaTarget, pollQaBus } from "./bus-client.js";
|
||||
|
||||
const guardedFetchCalls = vi.hoisted(
|
||||
() =>
|
||||
[] as Array<
|
||||
Parameters<typeof import("openclaw/plugin-sdk/ssrf-runtime").fetchWithSsrFGuard>[0]
|
||||
>,
|
||||
);
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/ssrf-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/ssrf-runtime")>();
|
||||
return {
|
||||
...actual,
|
||||
fetchWithSsrFGuard: (params: Parameters<typeof actual.fetchWithSsrFGuard>[0]) => {
|
||||
guardedFetchCalls.push(params);
|
||||
return actual.fetchWithSsrFGuard(params);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const OVERSIZED_RESPONSE_BYTES = 18 * 1024 * 1024;
|
||||
|
||||
async function startJsonServer(
|
||||
@@ -134,6 +152,7 @@ describe("qa-bus client", () => {
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(stops.splice(0).map((stop) => stop()));
|
||||
guardedFetchCalls.length = 0;
|
||||
});
|
||||
|
||||
it("roundtrips explicit group targets", () => {
|
||||
@@ -251,6 +270,10 @@ describe("qa-bus client", () => {
|
||||
messages: [],
|
||||
events: [],
|
||||
});
|
||||
expect(guardedFetchCalls.at(-1)).toMatchObject({
|
||||
auditContext: "qa-channel.bus-state",
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
});
|
||||
|
||||
it("bounds oversized qa-bus state responses", async () => {
|
||||
|
||||
@@ -38,6 +38,8 @@ export type {
|
||||
|
||||
type JsonResult<T> = Promise<T>;
|
||||
const QA_BUS_JSON_RESPONSE_MAX_BYTES = 16 * 1024 * 1024;
|
||||
/** Total deadline for local qa-bus state requests. */
|
||||
const QA_BUS_STATE_TIMEOUT_MS = 10_000;
|
||||
|
||||
function buildQaBusUrl(baseUrl: string, path: string): URL {
|
||||
const normalizedBaseUrl = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
||||
@@ -304,6 +306,7 @@ export async function getQaBusState(baseUrl: string): Promise<QaBusStateSnapshot
|
||||
url: buildQaBusUrl(baseUrl, "/v1/state").toString(),
|
||||
policy: { allowPrivateNetwork: true },
|
||||
auditContext: "qa-channel.bus-state",
|
||||
timeoutMs: QA_BUS_STATE_TIMEOUT_MS,
|
||||
});
|
||||
try {
|
||||
if (!response.ok) {
|
||||
|
||||
Reference in New Issue
Block a user