diff --git a/src/install-sh-version.test.ts b/src/install-sh-version.test.ts index 391c2b00cd6..8e4469f154e 100644 --- a/src/install-sh-version.test.ts +++ b/src/install-sh-version.test.ts @@ -21,27 +21,30 @@ printf '%s\n' '${escapedOutput}' return { root, cliPath }; } -function resolveVersionFromInstaller(cliPath: string): string { +function resolveVersionsFromInstaller(cliPaths: string[]): string[] { const installerPath = path.join(process.cwd(), "scripts", "install.sh"); const output = execFileSync( "bash", [ "-lc", `source "${installerPath}" >/dev/null 2>&1 -OPENCLAW_BIN="$FAKE_OPENCLAW_BIN" -resolve_openclaw_version`, +for openclaw_bin in "$@"; do + OPENCLAW_BIN="$openclaw_bin" + resolve_openclaw_version +done`, + "openclaw-version-test", + ...cliPaths, ], { cwd: process.cwd(), encoding: "utf-8", env: { ...process.env, - FAKE_OPENCLAW_BIN: cliPath, OPENCLAW_INSTALL_SH_NO_RUN: "1", }, }, ); - return output.trim(); + return output.trimEnd().split("\n"); } function resolveVersionFromInstallerViaStdin(cliPath: string, cwd: string): string { @@ -68,23 +71,15 @@ describe("install.sh version resolution", () => { cleanupTempDirs(tempRoots); }); - it.runIf(process.platform !== "win32")( - "extracts the semantic version from decorated CLI output", - () => { - const fixture = withFakeCli("OpenClaw 2026.3.10 (abcdef0)"); + it.runIf(process.platform !== "win32")("parses decorated and raw CLI versions", () => { + const decorated = withFakeCli("OpenClaw 2026.3.10 (abcdef0)"); + const raw = withFakeCli("OpenClaw dev's build"); - expect(resolveVersionFromInstaller(fixture.cliPath)).toBe("2026.3.10"); - }, - ); - - it.runIf(process.platform !== "win32")( - "falls back to raw output when no semantic version is present", - () => { - const fixture = withFakeCli("OpenClaw dev's build"); - - expect(resolveVersionFromInstaller(fixture.cliPath)).toBe("OpenClaw dev's build"); - }, - ); + expect(resolveVersionsFromInstaller([decorated.cliPath, raw.cliPath])).toEqual([ + "2026.3.10", + "OpenClaw dev's build", + ]); + }); it.runIf(process.platform !== "win32")( "does not source version helpers from cwd when installer runs via stdin", diff --git a/src/memory-host-sdk/host/batch-openai.test.ts b/src/memory-host-sdk/host/batch-openai.test.ts index 70ec73d0942..ae48b8d7d93 100644 --- a/src/memory-host-sdk/host/batch-openai.test.ts +++ b/src/memory-host-sdk/host/batch-openai.test.ts @@ -38,18 +38,23 @@ const mocks = vi.hoisted(() => ({ ), })); -vi.mock("./batch-embedding-common.js", async () => { - const actual = await vi.importActual( - "./batch-embedding-common.js", - ); - return { - ...actual, - uploadBatchJsonlFile: mocks.uploadBatchJsonlFile, - postJsonWithRetry: mocks.postJsonWithRetry, - resolveCompletedBatchResult: mocks.resolveCompletedBatchResult, - withRemoteHttpResponse: mocks.withRemoteHttpResponse, - }; -}); +vi.mock("./batch-upload.js", () => ({ + uploadBatchJsonlFile: mocks.uploadBatchJsonlFile, +})); + +vi.mock("./batch-http.js", () => ({ + postJsonWithRetry: mocks.postJsonWithRetry, +})); + +vi.mock("./batch-status.js", () => ({ + resolveBatchCompletionFromStatus: vi.fn(), + resolveCompletedBatchResult: mocks.resolveCompletedBatchResult, + throwIfBatchTerminalFailure: vi.fn(), +})); + +vi.mock("./remote-http.js", () => ({ + withRemoteHttpResponse: mocks.withRemoteHttpResponse, +})); describe("runOpenAiEmbeddingBatches", () => { beforeEach(() => { diff --git a/src/memory-host-sdk/host/embeddings-gemini.test.ts b/src/memory-host-sdk/host/embeddings-gemini.test.ts index 84cfbaae917..a9c4826a1c9 100644 --- a/src/memory-host-sdk/host/embeddings-gemini.test.ts +++ b/src/memory-host-sdk/host/embeddings-gemini.test.ts @@ -35,6 +35,21 @@ vi.mock("../../agents/model-auth.js", () => { }; }); +vi.mock("../../agents/api-key-rotation.js", () => ({ + collectProviderApiKeysForExecution: (params: { primaryApiKey?: string }) => + params.primaryApiKey ? [params.primaryApiKey] : [], + executeWithApiKeyRotation: async (params: { + apiKeys: string[]; + execute: (apiKey: string) => Promise; + }) => { + const apiKey = params.apiKeys[0]; + if (!apiKey) { + throw new Error('No API keys configured for provider "google".'); + } + return await params.execute(apiKey); + }, +})); + beforeEach(() => { vi.useRealTimers(); vi.doUnmock("undici"); diff --git a/test/helpers/media-generation/runtime-module-mocks.ts b/test/helpers/media-generation/runtime-module-mocks.ts index 145f428c31c..68cc58c4986 100644 --- a/test/helpers/media-generation/runtime-module-mocks.ts +++ b/test/helpers/media-generation/runtime-module-mocks.ts @@ -53,6 +53,9 @@ const mediaRuntimeMocks = vi.hoisted(() => { vi.fn<(raw?: string) => ModelRef | undefined>(parseGenerationModelRef), parseVideoGenerationModelRef: vi.fn<(raw?: string) => ModelRef | undefined>(parseGenerationModelRef), + ensureAuthProfileStore: vi.fn(() => ({ version: 1, profiles: {} })), + listProfilesForProvider: vi.fn(() => []), + resolveEnvApiKey: vi.fn(() => undefined), resolveAgentModelFallbackValues: vi.fn<(value: unknown) => string[]>(() => []), resolveAgentModelPrimaryValue: vi.fn<(value: unknown) => string | undefined>(() => undefined), resolveProviderAuthEnvVarCandidates: vi.fn(() => ({})), @@ -61,10 +64,20 @@ const mediaRuntimeMocks = vi.hoisted(() => { }; }); +vi.mock("../../../src/agents/auth-profiles.js", () => ({ + ensureAuthProfileStore: mediaRuntimeMocks.ensureAuthProfileStore, + listProfilesForProvider: mediaRuntimeMocks.listProfilesForProvider, +})); +vi.mock("../../../src/agents/defaults.js", () => ({ + DEFAULT_PROVIDER: "openai", +})); vi.mock("../../../src/agents/failover-error.js", () => ({ describeFailoverError: mediaRuntimeMocks.describeFailoverError, isFailoverError: mediaRuntimeMocks.isFailoverError, })); +vi.mock("../../../src/agents/model-auth-env.js", () => ({ + resolveEnvApiKey: mediaRuntimeMocks.resolveEnvApiKey, +})); vi.mock("../../../src/config/model-input.js", () => ({ resolveAgentModelFallbackValues: mediaRuntimeMocks.resolveAgentModelFallbackValues, resolveAgentModelPrimaryValue: mediaRuntimeMocks.resolveAgentModelPrimaryValue, @@ -104,6 +117,7 @@ export function getMediaGenerationRuntimeMocks() { } export function resetImageGenerationRuntimeMocks(): void { + resetSharedRuntimeImportMocks(); resetGenerationRuntimeMocks({ ...mediaRuntimeMocks, getProvider: mediaRuntimeMocks.getImageGenerationProvider, @@ -113,6 +127,7 @@ export function resetImageGenerationRuntimeMocks(): void { } export function resetMusicGenerationRuntimeMocks(): void { + resetSharedRuntimeImportMocks(); resetGenerationRuntimeMocks({ ...mediaRuntimeMocks, getProvider: mediaRuntimeMocks.getMusicGenerationProvider, @@ -122,6 +137,7 @@ export function resetMusicGenerationRuntimeMocks(): void { } export function resetVideoGenerationRuntimeMocks(): void { + resetSharedRuntimeImportMocks(); resetGenerationRuntimeMocks({ ...mediaRuntimeMocks, getProvider: mediaRuntimeMocks.getVideoGenerationProvider, @@ -129,3 +145,12 @@ export function resetVideoGenerationRuntimeMocks(): void { parseModelRef: mediaRuntimeMocks.parseVideoGenerationModelRef, }); } + +function resetSharedRuntimeImportMocks(): void { + mediaRuntimeMocks.ensureAuthProfileStore.mockReset(); + mediaRuntimeMocks.ensureAuthProfileStore.mockReturnValue({ version: 1, profiles: {} }); + mediaRuntimeMocks.listProfilesForProvider.mockReset(); + mediaRuntimeMocks.listProfilesForProvider.mockReturnValue([]); + mediaRuntimeMocks.resolveEnvApiKey.mockReset(); + mediaRuntimeMocks.resolveEnvApiKey.mockReturnValue(undefined); +}