[codex] refresh plugin regression fixtures

Summary:
- Refresh plugin regression fixtures and test-support mocks for guarded network resolution, progress streaming windows, staged TTS output, QQBot STT, and CLI runner assertions.
- Resolve current-main conflicts in Discord, Google video, QQBot STT, and CLI runner tests without changing runtime code.

Verification:
- pnpm check:test-types
- pnpm vitest run $(git diff --name-only origin/main...HEAD)
- git diff --check
- GitHub CI passed, including Real behavior proof, auto-response, ClawSweeper dispatch, CodeQL, and full CI checks.

Co-authored-by: Jason Zhou <22532527+JayZeeDesign@users.noreply.github.com>
This commit is contained in:
Jason Zhou
2026-05-11 12:44:50 +08:00
committed by GitHub
parent b627dd168d
commit bfd540bcdf
18 changed files with 149 additions and 33 deletions

View File

@@ -1,6 +1,7 @@
import { afterEach, describe, expect, it } from "vitest";
import { type WebSocket, WebSocketServer } from "ws";
import { rawDataToString } from "../infra/ws.js";
import "../test-support/browser-security.mock.js";
import {
type AriaSnapshotNode,
captureScreenshot,

View File

@@ -1,5 +1,6 @@
import { describe, expect, it, vi } from "vitest";
import type { BrowserRouteContext, ProfileContext } from "../server-context.js";
import "../../test-support/browser-security.mock.js";
import {
readBody,
resolveSafeRouteTabUrl,

View File

@@ -1,6 +1,7 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import "../../../test-support.js";
import "../server-context.chrome-test-harness.js";
import "../../test-support/browser-security.mock.js";
import * as chromeModule from "../chrome.js";
import { createBrowserRouteContext } from "../server-context.js";
import { makeBrowserServerState } from "../server-context.test-harness.js";

View File

@@ -1,5 +1,6 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { withBrowserFetchPreconnect } from "../../test-fetch.js";
import "../test-support/browser-security.mock.js";
vi.hoisted(() => {
vi.resetModules();

View File

@@ -1,6 +1,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { getFreePort } from "./test-port.js";
import { getBrowserTestFetch } from "./test-support/fetch.js";
import "../test-support/browser-security.mock.js";
let testPort = 0;
let prevGatewayPort: string | undefined;

View File

@@ -1,13 +1,24 @@
import { vi } from "vitest";
vi.mock("../sdk-security-runtime.js", async () => {
const actual = await vi.importActual<typeof import("../sdk-security-runtime.js")>(
"../sdk-security-runtime.js",
);
const lookupFn = async (_hostname: string, options?: { all?: boolean }) => {
const result = { address: "93.184.216.34", family: 4 };
return options?.all === true ? [result] : result;
};
const lookupFn = vi.hoisted(() => async (_hostname: string, options?: { all?: boolean }) => {
const result = { address: "93.184.216.34", family: 4 };
return options?.all === true ? [result] : result;
});
vi.mock("../infra/net/ssrf.js", async () => {
const actual =
await vi.importActual<typeof import("../infra/net/ssrf.js")>("../infra/net/ssrf.js");
return {
...actual,
resolvePinnedHostnameWithPolicy: (hostname: string, params: object = {}) =>
actual.resolvePinnedHostnameWithPolicy(hostname, { ...params, lookupFn: lookupFn as never }),
};
});
vi.mock("../sdk-security-runtime.js", async () => {
const actual = await vi.importActual<typeof import("../sdk-security-runtime.js")>(
"../sdk-security-runtime.js",
);
return {
...actual,
resolvePinnedHostnameWithPolicy: (hostname: string, params: object = {}) =>

View File

@@ -1728,7 +1728,7 @@ describe("processDiscordMessage draft streaming", () => {
mode: "progress",
progress: {
label: "Clawing...",
maxLines: 3,
maxLines: 4,
},
},
},
@@ -1736,8 +1736,7 @@ describe("processDiscordMessage draft streaming", () => {
await runProcessDiscordMessage(ctx);
expect(draftStream.update).toHaveBeenNthCalledWith(1, "Clawing...\n🧩 First\n🧩 Second");
expect(draftStream.update).toHaveBeenNthCalledWith(2, "🧩 First\n🧩 Second\n🧩 Third");
expect(draftStream.update).toHaveBeenCalledWith("Clawing...\n🧩 First\n🧩 Second\n🧩 Third");
});
it("skips empty apply_patch starts and renders the patch summary", async () => {

View File

@@ -1,10 +1,22 @@
import { describe, expect, it, vi } from "vitest";
import { mockPinnedHostnameResolution } from "openclaw/plugin-sdk/test-env";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
elevenLabsMediaUnderstandingProvider,
transcribeElevenLabsAudio,
} from "./media-understanding-provider.js";
describe("elevenLabsMediaUnderstandingProvider", () => {
let ssrfMock: { mockRestore: () => void } | undefined;
beforeEach(() => {
ssrfMock = mockPinnedHostnameResolution();
});
afterEach(() => {
ssrfMock?.mockRestore();
ssrfMock = undefined;
});
it("has expected provider metadata", () => {
expect(elevenLabsMediaUnderstandingProvider.id).toBe("elevenlabs");
expect(elevenLabsMediaUnderstandingProvider.capabilities).toEqual(["audio"]);

View File

@@ -1,9 +1,12 @@
import * as providerAuthRuntime from "openclaw/plugin-sdk/provider-auth-runtime";
import * as providerHttp from "openclaw/plugin-sdk/provider-http";
import { afterEach, describe, expect, it, vi } from "vitest";
import { mockPinnedHostnameResolution } from "openclaw/plugin-sdk/test-env";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { buildGoogleImageGenerationProvider } from "./image-generation-provider.js";
import { __testing as geminiWebSearchTesting } from "./src/gemini-web-search-provider.js";
let ssrfMock: { mockRestore: () => void } | undefined;
function mockGoogleApiKeyAuth() {
vi.spyOn(providerAuthRuntime, "resolveApiKeyForProvider").mockResolvedValue({
apiKey: "google-test-key",
@@ -75,7 +78,13 @@ function postJsonRequestOptions(spy: unknown): {
}
describe("Google image-generation provider", () => {
beforeEach(() => {
ssrfMock = mockPinnedHostnameResolution();
});
afterEach(() => {
ssrfMock?.mockRestore();
ssrfMock = undefined;
vi.restoreAllMocks();
vi.unstubAllGlobals();
});

View File

@@ -1,6 +1,7 @@
import { writeFile } from "node:fs/promises";
import path from "node:path";
import { afterAll, afterEach, describe, expect, it, vi } from "vitest";
import { mockPinnedHostnameResolution } from "openclaw/plugin-sdk/test-env";
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const { createGoogleGenAIMock, downloadMock, generateVideosMock, getVideosOperationMock } =
vi.hoisted(() => {
@@ -54,8 +55,16 @@ function firstGoogleClientHttpOptions(): Record<string, unknown> {
return recordField(firstObjectArg(createGoogleGenAIMock).httpOptions, "httpOptions");
}
let ssrfMock: { mockRestore: () => void } | undefined;
describe("google video generation provider", () => {
beforeEach(() => {
ssrfMock = mockPinnedHostnameResolution();
});
afterEach(() => {
ssrfMock?.mockRestore();
ssrfMock = undefined;
vi.restoreAllMocks();
vi.unstubAllGlobals();
downloadMock.mockReset();

View File

@@ -2,7 +2,7 @@ import {
verifyChannelMessageAdapterCapabilityProofs,
verifyChannelMessageReceiveAckPolicyAdapterProofs,
} from "openclaw/plugin-sdk/channel-message";
import { describe, expect, it, vi } from "vitest";
import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig, PluginRuntime } from "../api.js";
import { linePlugin } from "./channel.js";
import { lineConfigAdapter } from "./config-adapter.js";
@@ -11,6 +11,19 @@ import { lineOutboundAdapter } from "./outbound.js";
import { setLineRuntime } from "./runtime.js";
import { createLineSendReceipt } from "./send-receipt.js";
const ssrfMocks = vi.hoisted(() => ({
resolvePinnedHostnameWithPolicy: vi.fn(),
}));
vi.mock("openclaw/plugin-sdk/ssrf-runtime", () => ({
resolvePinnedHostnameWithPolicy: ssrfMocks.resolvePinnedHostnameWithPolicy,
}));
afterAll(() => {
vi.doUnmock("openclaw/plugin-sdk/ssrf-runtime");
vi.resetModules();
});
type LineRuntimeMocks = {
pushMessageLine: ReturnType<typeof vi.fn>;
pushMessagesLine: ReturnType<typeof vi.fn>;
@@ -26,6 +39,14 @@ type LineRuntimeMocks = {
resolveTextChunkLimit: ReturnType<typeof vi.fn>;
};
beforeEach(() => {
ssrfMocks.resolvePinnedHostnameWithPolicy.mockReset();
ssrfMocks.resolvePinnedHostnameWithPolicy.mockResolvedValue({
hostname: "example.com",
addresses: ["93.184.216.34"],
});
});
function lineResult(messageId: string, chatId = "c1") {
return {
messageId,

View File

@@ -83,6 +83,7 @@ const createTokenProvider = (
typeof tokenOrResolver === "function" ? await tokenOrResolver(scope) : tokenOrResolver,
),
});
const resolvePublicHost = async (): Promise<{ address: string }> => ({ address: "93.184.216.34" });
const createBufferResponse = (payload: Buffer | string, contentType: string, status = 200) => {
const raw = Buffer.isBuffer(payload) ? payload : Buffer.from(payload);
return new Response(new Uint8Array(raw), {
@@ -197,6 +198,7 @@ const downloadGraphMediaWithMockOptions = async (
tokenProvider: createTokenProvider(),
maxBytes: DEFAULT_MAX_BYTES,
fetchFn: asFetchFn(fetchMock),
resolveFn: resolvePublicHost,
...overrides,
});
return { fetchMock, media };
@@ -282,6 +284,7 @@ describe("msteams graph attachments", () => {
allowHosts: [...DEFAULT_SHAREPOINT_ALLOW_HOSTS, "example.com"],
authAllowHosts: DEFAULT_SHAREPOINT_ALLOW_HOSTS,
fetchFn: asFetchFn(fetchMock),
resolveFn: resolvePublicHost,
});
expectAttachmentMediaLength(media.media, 1);

View File

@@ -80,6 +80,10 @@ function buildTokenProvider(): MSTeamsAccessTokenProvider {
};
}
async function resolvePublicHost(): Promise<{ address: string }> {
return { address: "93.184.216.34" };
}
describe("isBotFrameworkPersonalChatId", () => {
it("detects a: prefix personal chat IDs", () => {
expect(isBotFrameworkPersonalChatId("a:1dRsHCobZ1AxURzY05Dc")).toBe(true);
@@ -140,6 +144,7 @@ describe("downloadMSTeamsBotFrameworkAttachment", () => {
tokenProvider: buildTokenProvider(),
maxBytes: 10_000_000,
fetchFn,
resolveFn: resolvePublicHost,
});
expect(media?.path).toBe(runtime.savePath);
@@ -162,6 +167,7 @@ describe("downloadMSTeamsBotFrameworkAttachment", () => {
tokenProvider: buildTokenProvider(),
maxBytes: 10_000_000,
fetchFn,
resolveFn: resolvePublicHost,
});
expect(media).toBeUndefined();
@@ -187,6 +193,7 @@ describe("downloadMSTeamsBotFrameworkAttachment", () => {
tokenProvider: buildTokenProvider(),
maxBytes: 10_000_000,
fetchFn,
resolveFn: resolvePublicHost,
});
expect(media).toBeUndefined();
@@ -208,6 +215,7 @@ describe("downloadMSTeamsBotFrameworkAttachment", () => {
tokenProvider: buildTokenProvider(),
maxBytes: 10_000_000,
fetchFn,
resolveFn: resolvePublicHost,
});
expect(media).toBeUndefined();
@@ -266,6 +274,7 @@ describe("downloadMSTeamsBotFrameworkAttachment", () => {
tokenProvider: buildTokenProvider(),
maxBytes: 10_000_000,
fetchFn,
resolveFn: resolvePublicHost,
});
expect(media?.path).toBe(runtime.savePath);
@@ -296,6 +305,7 @@ describe("downloadMSTeamsBotFrameworkAttachment", () => {
tokenProvider: buildTokenProvider(),
maxBytes: 10_000_000,
fetchFn,
resolveFn: resolvePublicHost,
logger,
});
@@ -332,6 +342,7 @@ describe("downloadMSTeamsBotFrameworkAttachment", () => {
tokenProvider: buildTokenProvider(),
maxBytes: 10_000_000,
fetchFn,
resolveFn: resolvePublicHost,
logger,
});
@@ -358,6 +369,7 @@ describe("downloadMSTeamsBotFrameworkAttachment", () => {
tokenProvider: buildTokenProvider(),
maxBytes: 10_000_000,
fetchFn,
resolveFn: resolvePublicHost,
logger: { warn },
});
@@ -407,6 +419,7 @@ describe("downloadMSTeamsBotFrameworkAttachments", () => {
tokenProvider: buildTokenProvider(),
maxBytes: 10_000,
fetchFn,
resolveFn: resolvePublicHost,
});
expect(result.media).toHaveLength(2);
@@ -453,6 +466,7 @@ describe("downloadMSTeamsBotFrameworkAttachments", () => {
tokenProvider: buildTokenProvider(),
maxBytes: 10_000,
fetchFn,
resolveFn: resolvePublicHost,
});
expect(result.media).toHaveLength(1);

View File

@@ -309,7 +309,7 @@ describe("createTeamsReplyStreamController", () => {
mode: "progress",
progress: {
label: "Working",
maxLines: 1,
maxLines: 3,
},
},
} as never,
@@ -320,7 +320,9 @@ describe("createTeamsReplyStreamController", () => {
expect(ctrl.shouldSuppressDefaultToolProgressMessages()).toBe(true);
expect(ctrl.shouldStreamPreviewToolProgress()).toBe(true);
expect(streamInstances[0]?.sendInformativeUpdate).toHaveBeenLastCalledWith("- tool: exec");
expect(streamInstances[0]?.sendInformativeUpdate).toHaveBeenLastCalledWith(
"Working\n- tool: search\n- tool: exec",
);
});
it("suppresses Teams default progress messages without stream lines when tool progress is disabled", async () => {

View File

@@ -1,19 +1,36 @@
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const fetchWithSsrFGuardMock = vi.hoisted(() => vi.fn());
const ssrfRuntimeMocks = vi.hoisted(() => ({
fetchWithSsrFGuard: vi.fn(),
}));
vi.mock("openclaw/plugin-sdk/ssrf-runtime", () => ({
fetchWithSsrFGuard: fetchWithSsrFGuardMock,
fetchWithSsrFGuard: ssrfRuntimeMocks.fetchWithSsrFGuard,
}));
afterAll(() => {
vi.doUnmock("openclaw/plugin-sdk/ssrf-runtime");
vi.resetModules();
});
import { resolveSTTConfig, transcribeAudio } from "./stt.js";
describe("engine/utils/stt", () => {
beforeEach(() => {
ssrfRuntimeMocks.fetchWithSsrFGuard.mockReset();
ssrfRuntimeMocks.fetchWithSsrFGuard.mockImplementation(
async ({ url, init }: { url: string; init?: RequestInit }) => ({
response: await fetch(url, init),
release: vi.fn(async () => {}),
}),
);
});
afterEach(() => {
fetchWithSsrFGuardMock.mockReset();
ssrfRuntimeMocks.fetchWithSsrFGuard.mockReset();
vi.unstubAllGlobals();
});
@@ -81,7 +98,7 @@ describe("engine/utils/stt", () => {
fs.writeFileSync(audioPath, Buffer.from([1, 2, 3, 4]));
const release = vi.fn(async () => {});
fetchWithSsrFGuardMock.mockResolvedValueOnce({
ssrfRuntimeMocks.fetchWithSsrFGuard.mockResolvedValueOnce({
response: Response.json({
text: "hello from audio",
}),
@@ -101,14 +118,17 @@ describe("engine/utils/stt", () => {
});
expect(transcript).toBe("hello from audio");
const request = fetchWithSsrFGuardMock.mock.calls[0]?.[0] as
| { url?: string; auditContext?: string; init?: RequestInit }
| undefined;
expect(request?.url).toBe("https://api.example.test/v1/audio/transcriptions");
expect(request?.auditContext).toBe("qqbot-stt");
expect(request?.init?.method).toBe("POST");
expect(request?.init?.headers).toEqual({ Authorization: "Bearer secret" });
expect(request?.init?.body).toBeInstanceOf(FormData);
expect(ssrfRuntimeMocks.fetchWithSsrFGuard).toHaveBeenCalledWith(
expect.objectContaining({
url: "https://api.example.test/v1/audio/transcriptions",
auditContext: "qqbot-stt",
init: expect.objectContaining({
method: "POST",
headers: { Authorization: "Bearer secret" },
body: expect.any(FormData),
}),
}),
);
expect(release).toHaveBeenCalledTimes(1);
});
});

View File

@@ -102,6 +102,9 @@ describe("buildCliSpeechProvider", () => {
if (typeof outputPath !== "string") {
throw new Error("missing ffmpeg output path");
}
const stagedTarget = outputPath.endsWith(".part")
? outputPath.slice(0, -".part".length)
: outputPath;
const forcedFormatIndex = args.lastIndexOf("-f");
const forcedFormat =
forcedFormatIndex >= 0 && typeof args[forcedFormatIndex + 1] === "string"
@@ -112,7 +115,7 @@ describe("buildCliSpeechProvider", () => {
? ".pcm"
: forcedFormat
? `.${forcedFormat}`
: path.extname(outputPath.replace(/\.part$/, ""));
: path.extname(stagedTarget);
writeFileSync(outputPath, Buffer.from(`converted:${extension}`));
});
});

View File

@@ -1,10 +1,18 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { mockPinnedHostnameResolution } from "openclaw/plugin-sdk/test-env";
import { beforeEach, afterEach, describe, expect, it, vi } from "vitest";
import { isValidXaiTtsVoice, XAI_BASE_URL, XAI_TTS_VOICES, xaiTTS } from "./tts.js";
describe("xai tts", () => {
const originalFetch = globalThis.fetch;
let ssrfMock: { mockRestore: () => void } | undefined;
beforeEach(() => {
ssrfMock = mockPinnedHostnameResolution();
});
afterEach(() => {
ssrfMock?.mockRestore();
ssrfMock = undefined;
globalThis.fetch = originalFetch;
vi.restoreAllMocks();
});

View File

@@ -491,7 +491,7 @@ describe("runCliAgent spawn path", () => {
},
}),
);
await expectPathMissing(pluginDir);
await expect(fs.access(pluginDir)).rejects.toMatchObject({ code: "ENOENT" });
} finally {
await fs.rm(workspaceDir, { recursive: true, force: true });
}