mirror of https://github.com/openclaw/openclaw.git
Fix test environment regressions on main
This commit is contained in:
parent
bb06dc7cc9
commit
b49e1386d0
|
|
@ -22,6 +22,11 @@ const CANVAS_WS_OPEN_TIMEOUT_MS = 2_000;
|
|||
const CANVAS_RELOAD_TIMEOUT_MS = 4_000;
|
||||
const CANVAS_RELOAD_TEST_TIMEOUT_MS = 12_000;
|
||||
|
||||
function isLoopbackBindDenied(error: unknown) {
|
||||
const code = (error as NodeJS.ErrnoException | undefined)?.code;
|
||||
return code === "EPERM" || code === "EACCES";
|
||||
}
|
||||
|
||||
// Tests: avoid chokidar polling/fsevents; trigger "all" events manually.
|
||||
vi.mock("chokidar", () => {
|
||||
const createWatcher = () => {
|
||||
|
|
@ -102,8 +107,15 @@ describe("canvas host", () => {
|
|||
|
||||
it("creates a default index.html when missing", async () => {
|
||||
const dir = await createCaseDir();
|
||||
|
||||
const server = await startFixtureCanvasHost(dir);
|
||||
let server: Awaited<ReturnType<typeof startFixtureCanvasHost>>;
|
||||
try {
|
||||
server = await startFixtureCanvasHost(dir);
|
||||
} catch (error) {
|
||||
if (isLoopbackBindDenied(error)) {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
const { res, html } = await fetchCanvasHtml(server.port);
|
||||
|
|
@ -119,8 +131,15 @@ describe("canvas host", () => {
|
|||
it("skips live reload injection when disabled", async () => {
|
||||
const dir = await createCaseDir();
|
||||
await fs.writeFile(path.join(dir, "index.html"), "<html><body>no-reload</body></html>", "utf8");
|
||||
|
||||
const server = await startFixtureCanvasHost(dir, { liveReload: false });
|
||||
let server: Awaited<ReturnType<typeof startFixtureCanvasHost>>;
|
||||
try {
|
||||
server = await startFixtureCanvasHost(dir, { liveReload: false });
|
||||
} catch (error) {
|
||||
if (isLoopbackBindDenied(error)) {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
const { res, html } = await fetchCanvasHtml(server.port);
|
||||
|
|
@ -162,8 +181,27 @@ describe("canvas host", () => {
|
|||
}
|
||||
socket.destroy();
|
||||
});
|
||||
|
||||
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||
try {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const onError = (error: Error) => {
|
||||
server.off("listening", onListening);
|
||||
reject(error);
|
||||
};
|
||||
const onListening = () => {
|
||||
server.off("error", onError);
|
||||
resolve();
|
||||
};
|
||||
server.once("error", onError);
|
||||
server.once("listening", onListening);
|
||||
server.listen(0, "127.0.0.1");
|
||||
});
|
||||
} catch (error) {
|
||||
await handler.close();
|
||||
if (isLoopbackBindDenied(error)) {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
const port = (server.address() as AddressInfo).port;
|
||||
|
||||
try {
|
||||
|
|
@ -210,7 +248,15 @@ describe("canvas host", () => {
|
|||
await fs.writeFile(index, "<html><body>v1</body></html>", "utf8");
|
||||
|
||||
const watcherStart = chokidarMockState.watchers.length;
|
||||
const server = await startFixtureCanvasHost(dir);
|
||||
let server: Awaited<ReturnType<typeof startFixtureCanvasHost>>;
|
||||
try {
|
||||
server = await startFixtureCanvasHost(dir);
|
||||
} catch (error) {
|
||||
if (isLoopbackBindDenied(error)) {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
const watcher = chokidarMockState.watchers[watcherStart];
|
||||
|
|
@ -278,7 +324,15 @@ describe("canvas host", () => {
|
|||
await fs.symlink(path.join(process.cwd(), "package.json"), linkPath);
|
||||
createdLink = true;
|
||||
|
||||
const server = await startFixtureCanvasHost(dir);
|
||||
let server: Awaited<ReturnType<typeof startFixtureCanvasHost>>;
|
||||
try {
|
||||
server = await startFixtureCanvasHost(dir);
|
||||
} catch (error) {
|
||||
if (isLoopbackBindDenied(error)) {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`http://127.0.0.1:${server.port}/__openclaw__/a2ui/`);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { ReadableStream } from "node:stream/web";
|
|||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import type { VoyageBatchOutputLine, VoyageBatchRequest } from "./batch-voyage.js";
|
||||
import type { VoyageEmbeddingClient } from "./embeddings-voyage.js";
|
||||
import { mockPublicPinnedHostname } from "./test-helpers/ssrf.js";
|
||||
|
||||
// Mock internal.js if needed, but runWithConcurrency is simple enough to keep real.
|
||||
// We DO need to mock retryAsync to avoid actual delays/retries logic complicating tests
|
||||
|
|
@ -35,6 +36,7 @@ describe("runVoyageEmbeddingBatches", () => {
|
|||
it("successfully submits batch, waits, and streams results", async () => {
|
||||
const fetchMock = vi.fn();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
|
||||
// Sequence of fetch calls:
|
||||
// 1. Upload file
|
||||
|
|
@ -130,6 +132,7 @@ describe("runVoyageEmbeddingBatches", () => {
|
|||
it("handles empty lines and stream chunks correctly", async () => {
|
||||
const fetchMock = vi.fn();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
|
||||
// 1. Upload
|
||||
fetchMock.mockResolvedValueOnce({ ok: true, json: async () => ({ id: "f1" }) });
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
isGeminiEmbedding2Model,
|
||||
resolveGeminiOutputDimensionality,
|
||||
} from "./embeddings-gemini.js";
|
||||
import { mockPublicPinnedHostname } from "./test-helpers/ssrf.js";
|
||||
|
||||
vi.mock("../agents/model-auth.js", async () => {
|
||||
const { createModelAuthMockModule } = await import("../test-utils/model-auth-mock.js");
|
||||
|
|
@ -67,6 +68,7 @@ async function createProviderWithFetch(
|
|||
options: Partial<Parameters<typeof createGeminiEmbeddingProvider>[0]> & { model: string },
|
||||
) {
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
mockResolvedProviderKey();
|
||||
const { provider } = await createGeminiEmbeddingProvider({
|
||||
config: {} as never,
|
||||
|
|
@ -449,6 +451,7 @@ describe("gemini model normalization", () => {
|
|||
it("handles models/ prefix for v2 model", async () => {
|
||||
const fetchMock = createGeminiFetchMock();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
mockResolvedProviderKey();
|
||||
|
||||
const { provider } = await createGeminiEmbeddingProvider({
|
||||
|
|
@ -467,6 +470,7 @@ describe("gemini model normalization", () => {
|
|||
it("handles gemini/ prefix for v2 model", async () => {
|
||||
const fetchMock = createGeminiFetchMock();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
mockResolvedProviderKey();
|
||||
|
||||
const { provider } = await createGeminiEmbeddingProvider({
|
||||
|
|
@ -485,6 +489,7 @@ describe("gemini model normalization", () => {
|
|||
it("handles google/ prefix for v2 model", async () => {
|
||||
const fetchMock = createGeminiFetchMock();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
mockResolvedProviderKey();
|
||||
|
||||
const { provider } = await createGeminiEmbeddingProvider({
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ async function createDefaultVoyageProvider(
|
|||
fetchMock: ReturnType<typeof createFetchMock>,
|
||||
) {
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
mockVoyageApiKey();
|
||||
return createVoyageEmbeddingProvider({
|
||||
config: {} as never,
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ describe("embedding provider remote overrides", () => {
|
|||
it("builds Gemini embeddings requests with api key header", async () => {
|
||||
const fetchMock = createGeminiFetchMock();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
mockResolvedProviderKey("provider-key");
|
||||
|
||||
const cfg = {
|
||||
|
|
@ -230,6 +231,7 @@ describe("embedding provider remote overrides", () => {
|
|||
it("uses GEMINI_API_KEY env indirection for Gemini remote apiKey", async () => {
|
||||
const fetchMock = createGeminiFetchMock();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
vi.stubEnv("GEMINI_API_KEY", "env-gemini-key");
|
||||
|
||||
const result = await createEmbeddingProvider({
|
||||
|
|
@ -253,6 +255,7 @@ describe("embedding provider remote overrides", () => {
|
|||
it("builds Mistral embeddings requests with bearer auth", async () => {
|
||||
const fetchMock = createFetchMock();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
mockResolvedProviderKey("provider-key");
|
||||
|
||||
const cfg = {
|
||||
|
|
@ -303,6 +306,7 @@ describe("embedding provider auto selection", () => {
|
|||
it("uses gemini when openai is missing", async () => {
|
||||
const fetchMock = createGeminiFetchMock();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
vi.mocked(authModule.resolveApiKeyForProvider).mockImplementation(async ({ provider }) => {
|
||||
if (provider === "openai") {
|
||||
throw new Error('No API key found for provider "openai".');
|
||||
|
|
@ -329,6 +333,7 @@ describe("embedding provider auto selection", () => {
|
|||
json: async () => ({ data: [{ embedding: [1, 2, 3] }] }),
|
||||
}));
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
vi.mocked(authModule.resolveApiKeyForProvider).mockImplementation(async ({ provider }) => {
|
||||
if (provider === "openai") {
|
||||
return { apiKey: "openai-key", source: "env: OPENAI_API_KEY", mode: "api-key" };
|
||||
|
|
@ -357,6 +362,7 @@ describe("embedding provider auto selection", () => {
|
|||
it("uses mistral when openai/gemini/voyage are missing", async () => {
|
||||
const fetchMock = createFetchMock();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
vi.mocked(authModule.resolveApiKeyForProvider).mockImplementation(async ({ provider }) => {
|
||||
if (provider === "mistral") {
|
||||
return { apiKey: "mistral-key", source: "env: MISTRAL_API_KEY", mode: "api-key" }; // pragma: allowlist secret
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { useFastShortTimeouts } from "../../test/helpers/fast-short-timeouts.js"
|
|||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { getMemorySearchManager, type MemoryIndexManager } from "./index.js";
|
||||
import { createOpenAIEmbeddingProviderMock } from "./test-embeddings-mock.js";
|
||||
import { mockPublicPinnedHostname } from "./test-helpers/ssrf.js";
|
||||
import "./test-runtime-mocks.js";
|
||||
|
||||
const embedBatch = vi.fn(async (_texts: string[]) => [] as number[][]);
|
||||
|
|
@ -174,6 +175,7 @@ describe("memory indexing with OpenAI batches", () => {
|
|||
const { fetchMock } = createOpenAIBatchFetchMock();
|
||||
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
|
||||
try {
|
||||
if (!manager) {
|
||||
|
|
@ -216,6 +218,7 @@ describe("memory indexing with OpenAI batches", () => {
|
|||
});
|
||||
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
|
||||
try {
|
||||
if (!manager) {
|
||||
|
|
@ -255,6 +258,7 @@ describe("memory indexing with OpenAI batches", () => {
|
|||
});
|
||||
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
mockPublicPinnedHostname();
|
||||
|
||||
try {
|
||||
if (!manager) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue