fix(microsoft-foundry): bound connection test error reads (#97812)

Co-authored-by: Pick-cat <266665499+Pick-cat@users.noreply.github.com>
This commit is contained in:
pick-cat
2026-06-30 01:50:38 +08:00
committed by GitHub
parent 2ec6708980
commit eb5fb2aa69
2 changed files with 84 additions and 2 deletions

View File

@@ -0,0 +1,73 @@
// Microsoft Foundry tests cover bounded connection-test error reads.
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import * as cli from "./cli.js";
import { testFoundryConnection } from "./onboard.js";
import { DEFAULT_API } from "./shared.js";
const hoisted = vi.hoisted(() => ({
fetchWithSsrFGuard: vi.fn(),
}));
vi.mock("openclaw/plugin-sdk/ssrf-runtime", () => ({
fetchWithSsrFGuard: hoisted.fetchWithSsrFGuard,
}));
function cancelTrackedResponse(
text: string,
init: ResponseInit,
): {
response: Response;
wasCanceled: () => boolean;
} {
let canceled = false;
const stream = new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(new TextEncoder().encode(text));
},
cancel() {
canceled = true;
},
});
return {
response: new Response(stream, init),
wasCanceled: () => canceled,
};
}
describe("testFoundryConnection", () => {
beforeEach(() => {
vi.spyOn(cli, "getAccessTokenResult").mockReturnValue({ accessToken: "token" });
});
afterEach(() => {
vi.restoreAllMocks();
hoisted.fetchWithSsrFGuard.mockReset();
});
it("bounds connection-test error bodies without using response.text()", async () => {
const note = vi.fn();
const tracked = cancelTrackedResponse(`${"foundry failure ".repeat(1024)}tail`, {
status: 503,
headers: { "content-type": "text/plain" },
});
const textSpy = vi.spyOn(tracked.response, "text").mockRejectedValue(new Error("unbounded"));
hoisted.fetchWithSsrFGuard.mockResolvedValue({
response: tracked.response,
release: async () => {},
});
await testFoundryConnection({
ctx: { prompter: { note } } as never,
endpoint: "https://example.openai.azure.com",
modelId: "gpt-4o",
api: DEFAULT_API,
});
expect(textSpy).not.toHaveBeenCalled();
expect(tracked.wasCanceled()).toBe(true);
expect(note).toHaveBeenCalledWith(
expect.stringContaining("Warning: test request returned 503"),
"Connection Test",
);
});
});

View File

@@ -1,6 +1,7 @@
// Microsoft Foundry setup module handles plugin onboarding behavior.
import type { ProviderAuthContext } from "openclaw/plugin-sdk/core";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
import {
normalizeOptionalString,
@@ -32,6 +33,8 @@ import {
usesFoundryResponsesByDefault,
} from "./shared.js";
const FOUNDRY_CONNECTION_TEST_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
export { listSubscriptions } from "./cli.js";
function listFoundryResources(subscriptionId?: string): FoundryResourceOption[] {
@@ -605,13 +608,19 @@ export async function testFoundryConnection(params: {
});
try {
if (res.status === 400) {
const body = await res.text().catch(() => "");
const body = await readResponseTextLimited(
res,
FOUNDRY_CONNECTION_TEST_ERROR_BODY_LIMIT_BYTES,
).catch(() => "");
await params.ctx.prompter.note(
`Endpoint is reachable but returned 400 Bad Request - check your deployment name and API version.\n${body.slice(0, 200)}`,
"Connection Test",
);
} else if (!res.ok) {
const body = await res.text().catch(() => "");
const body = await readResponseTextLimited(
res,
FOUNDRY_CONNECTION_TEST_ERROR_BODY_LIMIT_BYTES,
).catch(() => "");
await params.ctx.prompter.note(
`Warning: test request returned ${res.status}. ${body.slice(0, 200)}\nProceeding anyway - you can fix the endpoint later.`,
"Connection Test",