From 0631252f4b66b0b958c44638fec2bf1afeab2672 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 16 Jul 2026 13:55:23 -0700 Subject: [PATCH] refactor: reduce plugin SDK test import overhead (#109333) * refactor(test): split plugin SDK test helpers * fix(test): align plugin SDK test subpaths --- docs/plugins/sdk-subpaths.md | 3 +- docs/plugins/sdk-testing.md | 7 ++- .../azure-speech/azure-speech.live.test.ts | 2 +- extensions/azure-speech/tts.test.ts | 2 +- extensions/baseten/baseten.live.test.ts | 2 +- extensions/browser/test-support.ts | 3 +- extensions/byteplus/live.test.ts | 2 +- extensions/comfy/comfy.live.test.ts | 2 +- extensions/copilot/src/attempt.live.test.ts | 2 +- extensions/deepgram/audio.live.test.ts | 2 +- extensions/deepgram/audio.test.ts | 2 +- extensions/deepseek/deepseek.live.test.ts | 2 +- .../src/internal/live-smoke.live.test.ts | 2 +- extensions/elevenlabs/elevenlabs.live.test.ts | 2 +- .../featherless/featherless.live.test.ts | 2 +- .../google-meet/google-meet.live.test.ts | 2 +- extensions/google/google.live.test.ts | 2 +- ...media-understanding-provider.video.test.ts | 4 +- extensions/gradium/gradium.live.test.ts | 2 +- extensions/gradium/speech-provider.test.ts | 2 +- extensions/gradium/tts.test.ts | 2 +- extensions/inworld/inworld.live.test.ts | 2 +- extensions/meta/meta.live.test.ts | 2 +- extensions/microsoft/microsoft.live.test.ts | 2 +- .../minimax/image-generation-provider.test.ts | 2 +- extensions/minimax/minimax.live.test.ts | 2 +- .../media-understanding-provider.test.ts | 2 +- extensions/mistral/mistral.live.test.ts | 2 +- .../media-understanding-provider.test.ts | 2 +- extensions/moonshot/moonshot.live.test.ts | 2 +- .../music-generation-providers.live.test.ts | 13 +++-- .../media-understanding-provider.test.ts | 2 +- extensions/openai/openai-tts.live.test.ts | 2 +- extensions/openai/openai.live.test.ts | 2 +- extensions/opencode/opencode.live.test.ts | 2 +- extensions/parallel/parallel.live.test.ts | 2 +- .../qwen/media-understanding-provider.test.ts | 2 +- .../media-understanding-provider.test.ts | 2 +- .../generation-live-test-helpers.ts | 2 +- .../tsconfig.package-boundary.paths.json | 12 ++++ .../video-generation-providers.live.test.ts | 19 ++++--- .../vydra/image-generation-provider.test.ts | 2 +- extensions/vydra/speech-provider.test.ts | 2 +- .../vydra/video-generation-provider.test.ts | 2 +- extensions/vydra/vydra.live.test.ts | 2 +- extensions/xai/tsconfig.json | 12 ++++ extensions/xai/x-search.live.test.ts | 2 +- extensions/xai/xai.live.test.ts | 2 +- extensions/xiaomi/xiaomi.live.test.ts | 2 +- package.json | 8 +++ .../check-no-extension-test-core-imports.ts | 2 +- scripts/lib/plugin-sdk-entrypoints.json | 4 ++ ...lugin-sdk-private-local-only-subpaths.json | 4 ++ src/agents/live-test-config.ts | 43 ++++++++++++++ src/agents/live-test-helpers.ts | 56 +++---------------- .../tools/image-tool.providers.live.test.ts | 2 +- src/infra/package-dist-inventory.ts | 8 +++ src/plugin-sdk/test-env.ts | 54 +----------------- src/plugin-sdk/test-live-auth.ts | 5 ++ src/plugin-sdk/test-live.ts | 17 ++++++ src/plugin-sdk/test-media-generation.ts | 30 ++++++++++ src/plugin-sdk/test-media-understanding.ts | 7 +++ .../contracts/plugin-sdk-subpaths.test.ts | 30 +++++++++- 63 files changed, 261 insertions(+), 164 deletions(-) create mode 100644 src/agents/live-test-config.ts create mode 100644 src/plugin-sdk/test-live-auth.ts create mode 100644 src/plugin-sdk/test-live.ts create mode 100644 src/plugin-sdk/test-media-generation.ts create mode 100644 src/plugin-sdk/test-media-understanding.ts diff --git a/docs/plugins/sdk-subpaths.md b/docs/plugins/sdk-subpaths.md index 76986978921..47cb725b375 100644 --- a/docs/plugins/sdk-subpaths.md +++ b/docs/plugins/sdk-subpaths.md @@ -59,7 +59,8 @@ longer package exports: `agent-runtime-test-contracts`, `plugin-state-test-runtime`, `plugin-test-api`, `plugin-test-contracts`, `plugin-test-runtime`, `provider-http-test-mocks`, `provider-test-contracts`, `reply-payload-testing`, `sqlite-runtime-testing`, `test-env`, `test-fixtures`, -`test-node-mocks`, and `testing`. The private bundled helper surfaces +`test-live`, `test-live-auth`, `test-media-generation`, +`test-media-understanding`, `test-node-mocks`, and `testing`. The private bundled helper surfaces `ssrf-runtime-internal` and `codex-native-task-runtime` are also repo-local only. diff --git a/docs/plugins/sdk-testing.md b/docs/plugins/sdk-testing.md index 291f5dcceca..bb5e54b3b3a 100644 --- a/docs/plugins/sdk-testing.md +++ b/docs/plugins/sdk-testing.md @@ -38,6 +38,8 @@ import { registerSingleProviderPlugin } from "openclaw/plugin-sdk/plugin-test-ru import { describeOpenAIProviderRuntimeContract } from "openclaw/plugin-sdk/provider-test-contracts"; import { getProviderHttpMocks } from "openclaw/plugin-sdk/provider-http-test-mocks"; import { withEnv, withFetchPreconnect, withServer } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; +import { createRequestCaptureJsonFetch } from "openclaw/plugin-sdk/test-media-understanding"; import { bundledPluginRoot, createCliRuntimeCapture, @@ -102,7 +104,10 @@ imports of that alias. | `createTestRegistry` | Build a channel plugin registry fixture. Import from `plugin-sdk/plugin-test-runtime` or `plugin-sdk/channel-test-helpers` | | `createEmptyPluginRegistry` | Build an empty plugin registry fixture. Import from `plugin-sdk/plugin-test-runtime` or `plugin-sdk/channel-test-helpers` | | `setActivePluginRegistry` | Install a registry fixture for plugin runtime tests. Import from `plugin-sdk/plugin-test-runtime` or `plugin-sdk/channel-test-helpers` | -| `createRequestCaptureJsonFetch` | Capture JSON fetch requests in media helper tests. Import from `plugin-sdk/test-env` | +| `createRequestCaptureJsonFetch` | Capture JSON fetch requests in media helper tests. Import from `plugin-sdk/test-media-understanding` | +| `isLiveTestEnabled` | Gate opt-in live provider tests. Import from `plugin-sdk/test-live` | +| `collectProviderApiKeys` | Discover credentials for live provider tests. Import from `plugin-sdk/test-live-auth` | +| `parseProviderModelMap` | Parse music/video live-test model overrides. Import from `plugin-sdk/test-media-generation` | | `withServer` | Run tests against a disposable local HTTP server. Import from `plugin-sdk/test-env` | | `createMockIncomingRequest` | Build a minimal incoming HTTP request object. Import from `plugin-sdk/test-env` | | `withFetchPreconnect` | Run fetch tests with preconnect hooks installed. Import from `plugin-sdk/test-env` | diff --git a/extensions/azure-speech/azure-speech.live.test.ts b/extensions/azure-speech/azure-speech.live.test.ts index 23fa06cbded..208cd75f08b 100644 --- a/extensions/azure-speech/azure-speech.live.test.ts +++ b/extensions/azure-speech/azure-speech.live.test.ts @@ -3,7 +3,7 @@ import { registerProviderPlugin, requireRegisteredProvider, } from "openclaw/plugin-sdk/plugin-test-runtime"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import plugin from "./index.js"; diff --git a/extensions/azure-speech/tts.test.ts b/extensions/azure-speech/tts.test.ts index b4f5f945e9a..e54cdd0bea6 100644 --- a/extensions/azure-speech/tts.test.ts +++ b/extensions/azure-speech/tts.test.ts @@ -1,5 +1,5 @@ // Azure Speech tests cover tts plugin behavior. -import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-env"; +import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-media-understanding"; import { afterEach, describe, expect, it, vi } from "vitest"; import { azureSpeechTTS, diff --git a/extensions/baseten/baseten.live.test.ts b/extensions/baseten/baseten.live.test.ts index 2f359a6e52a..62fa0997b8d 100644 --- a/extensions/baseten/baseten.live.test.ts +++ b/extensions/baseten/baseten.live.test.ts @@ -7,7 +7,7 @@ import { } from "openclaw/plugin-sdk/llm"; import { registerSingleProviderPlugin } from "openclaw/plugin-sdk/plugin-test-runtime"; import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { Type } from "typebox"; import { describe, expect, it } from "vitest"; import { runSingleProviderCatalog } from "../test-support/provider-model-test-helpers.js"; diff --git a/extensions/browser/test-support.ts b/extensions/browser/test-support.ts index a0bc474cd19..e7afbf3694c 100644 --- a/extensions/browser/test-support.ts +++ b/extensions/browser/test-support.ts @@ -10,8 +10,9 @@ export { expectGeneratedTokenPersistedToGatewayAuth, type CliRuntimeCapture, } from "openclaw/plugin-sdk/test-fixtures"; -export { createTempHomeEnv, isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +export { createTempHomeEnv } from "openclaw/plugin-sdk/test-env"; export type { TempHomeEnv } from "openclaw/plugin-sdk/test-env"; +export { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; export type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; export function useAutoCleanupTempDirTracker(registerCleanup: (cleanup: () => void) => unknown) { diff --git a/extensions/byteplus/live.test.ts b/extensions/byteplus/live.test.ts index a0b0bd6a44a..299a1a31be8 100644 --- a/extensions/byteplus/live.test.ts +++ b/extensions/byteplus/live.test.ts @@ -4,7 +4,7 @@ import { createSingleUserPromptMessage, extractNonEmptyAssistantText, isLiveTestEnabled, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import { BYTEPLUS_CODING_BASE_URL } from "./models.js"; diff --git a/extensions/comfy/comfy.live.test.ts b/extensions/comfy/comfy.live.test.ts index d40f9466d45..42ab85c8567 100644 --- a/extensions/comfy/comfy.live.test.ts +++ b/extensions/comfy/comfy.live.test.ts @@ -3,7 +3,7 @@ import { resolveDefaultAgentDir } from "openclaw/plugin-sdk/agent-runtime"; import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api"; import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { beforeAll, describe, expect, it } from "vitest"; import plugin from "./index.js"; import { getComfyConfigForTesting } from "./test-support.js"; diff --git a/extensions/copilot/src/attempt.live.test.ts b/extensions/copilot/src/attempt.live.test.ts index 8caaa2e349c..1131572e83a 100644 --- a/extensions/copilot/src/attempt.live.test.ts +++ b/extensions/copilot/src/attempt.live.test.ts @@ -4,7 +4,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { CopilotClient, approveAll } from "@github/copilot-sdk"; import type { AgentHarnessAttemptParams } from "openclaw/plugin-sdk/agent-harness-runtime"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it, vi } from "vitest"; import { createCopilotAgentHarness } from "../harness.js"; import type { CopilotClientPool } from "./runtime.js"; diff --git a/extensions/deepgram/audio.live.test.ts b/extensions/deepgram/audio.live.test.ts index 92b0a5c0c73..66a209f2af7 100644 --- a/extensions/deepgram/audio.live.test.ts +++ b/extensions/deepgram/audio.live.test.ts @@ -3,7 +3,7 @@ import { runRealtimeSttLiveTest, synthesizeElevenLabsLiveSpeech, } from "openclaw/plugin-sdk/provider-test-contracts"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import { transcribeDeepgramAudio } from "./audio.js"; import { buildDeepgramRealtimeTranscriptionProvider } from "./realtime-transcription-provider.js"; diff --git a/extensions/deepgram/audio.test.ts b/extensions/deepgram/audio.test.ts index edad94ba5d1..b315a5a6e53 100644 --- a/extensions/deepgram/audio.test.ts +++ b/extensions/deepgram/audio.test.ts @@ -3,7 +3,7 @@ import { createAuthCaptureJsonFetch, createRequestCaptureJsonFetch, installPinnedHostnameTestHooks, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-media-understanding"; import { describe, expect, it, vi } from "vitest"; import { transcribeDeepgramAudio } from "./audio.js"; diff --git a/extensions/deepseek/deepseek.live.test.ts b/extensions/deepseek/deepseek.live.test.ts index 925c3812efe..43773cbd5de 100644 --- a/extensions/deepseek/deepseek.live.test.ts +++ b/extensions/deepseek/deepseek.live.test.ts @@ -10,7 +10,7 @@ import { createSingleUserPromptMessage, extractNonEmptyAssistantText, isLiveTestEnabled, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-live"; import { Type } from "typebox"; import { describe, expect, it } from "vitest"; import { buildDeepSeekProvider } from "./provider-catalog.js"; diff --git a/extensions/discord/src/internal/live-smoke.live.test.ts b/extensions/discord/src/internal/live-smoke.live.test.ts index 60567a753d6..dcc511bb910 100644 --- a/extensions/discord/src/internal/live-smoke.live.test.ts +++ b/extensions/discord/src/internal/live-smoke.live.test.ts @@ -1,6 +1,6 @@ // Discord tests cover live smoke plugin behavior. import { Routes } from "discord-api-types/v10"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import { parseApplicationIdFromToken } from "../probe.js"; import { RequestClient } from "./rest.js"; diff --git a/extensions/elevenlabs/elevenlabs.live.test.ts b/extensions/elevenlabs/elevenlabs.live.test.ts index 5f3515356fd..1fc574b4190 100644 --- a/extensions/elevenlabs/elevenlabs.live.test.ts +++ b/extensions/elevenlabs/elevenlabs.live.test.ts @@ -8,7 +8,7 @@ import { runRealtimeSttLiveTest, synthesizeElevenLabsLiveSpeech, } from "openclaw/plugin-sdk/provider-test-contracts"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import plugin from "./index.js"; import { elevenLabsMediaUnderstandingProvider } from "./media-understanding-provider.js"; diff --git a/extensions/featherless/featherless.live.test.ts b/extensions/featherless/featherless.live.test.ts index 0993963892f..e763c4a4689 100644 --- a/extensions/featherless/featherless.live.test.ts +++ b/extensions/featherless/featherless.live.test.ts @@ -5,7 +5,7 @@ import { type Model, type Tool, } from "openclaw/plugin-sdk/llm"; -import { extractNonEmptyAssistantText, isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { extractNonEmptyAssistantText, isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { Type } from "typebox"; import { describe, expect, it } from "vitest"; import { FEATHERLESS_DEFAULT_MODEL_ID } from "./models.js"; diff --git a/extensions/google-meet/google-meet.live.test.ts b/extensions/google-meet/google-meet.live.test.ts index e8d8f128166..d7727680c10 100644 --- a/extensions/google-meet/google-meet.live.test.ts +++ b/extensions/google-meet/google-meet.live.test.ts @@ -1,5 +1,5 @@ // Google Meet tests cover google meet plugin behavior. -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import { buildGoogleMeetExportManifest, googleMeetExportFileNames } from "./src/cli.js"; import { diff --git a/extensions/google/google.live.test.ts b/extensions/google/google.live.test.ts index 780119147c4..6b35daec706 100644 --- a/extensions/google/google.live.test.ts +++ b/extensions/google/google.live.test.ts @@ -5,7 +5,7 @@ import { requireRegisteredProvider, } from "openclaw/plugin-sdk/plugin-test-runtime"; import { normalizeTranscriptForMatch } from "openclaw/plugin-sdk/provider-test-contracts"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import plugin from "./index.js"; import { createGeminiWebSearchProvider } from "./src/gemini-web-search-provider.js"; diff --git a/extensions/google/media-understanding-provider.video.test.ts b/extensions/google/media-understanding-provider.video.test.ts index 844084d4a0d..550043d12e9 100644 --- a/extensions/google/media-understanding-provider.video.test.ts +++ b/extensions/google/media-understanding-provider.video.test.ts @@ -1,10 +1,10 @@ // Google tests cover media understanding provider.video plugin behavior. import { createServer, type Server } from "node:http"; +import { withFetchPreconnect } from "openclaw/plugin-sdk/test-env"; import { createRequestCaptureJsonFetch, installPinnedHostnameTestHooks, - withFetchPreconnect, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-media-understanding"; import { describe, expect, it } from "vitest"; import { describeGeminiVideo, transcribeGeminiAudio } from "./media-understanding-provider.js"; import { resolveGoogleGenerativeAiHttpRequestConfig } from "./runtime-api.js"; diff --git a/extensions/gradium/gradium.live.test.ts b/extensions/gradium/gradium.live.test.ts index 8fe39d9693f..12d02da87cd 100644 --- a/extensions/gradium/gradium.live.test.ts +++ b/extensions/gradium/gradium.live.test.ts @@ -6,7 +6,7 @@ import { registerProviderPlugin, requireRegisteredProvider, } from "openclaw/plugin-sdk/plugin-test-runtime"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import plugin from "./index.js"; diff --git a/extensions/gradium/speech-provider.test.ts b/extensions/gradium/speech-provider.test.ts index 8cf9cd38f16..84dfb369c61 100644 --- a/extensions/gradium/speech-provider.test.ts +++ b/extensions/gradium/speech-provider.test.ts @@ -1,5 +1,5 @@ // Gradium tests cover speech provider plugin behavior. -import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-env"; +import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-media-understanding"; import { afterEach, describe, expect, it, vi } from "vitest"; import { buildGradiumSpeechProvider } from "./speech-provider.js"; diff --git a/extensions/gradium/tts.test.ts b/extensions/gradium/tts.test.ts index c0fc6b61166..84963bc3a64 100644 --- a/extensions/gradium/tts.test.ts +++ b/extensions/gradium/tts.test.ts @@ -1,5 +1,5 @@ // Gradium tests cover tts plugin behavior. -import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-env"; +import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-media-understanding"; import { afterEach, describe, expect, it, vi } from "vitest"; import { gradiumTTS } from "./tts.js"; diff --git a/extensions/inworld/inworld.live.test.ts b/extensions/inworld/inworld.live.test.ts index f313cfb3ea7..567b6ba3cae 100644 --- a/extensions/inworld/inworld.live.test.ts +++ b/extensions/inworld/inworld.live.test.ts @@ -3,7 +3,7 @@ import { registerProviderPlugin, requireRegisteredProvider, } from "openclaw/plugin-sdk/plugin-test-runtime"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import plugin from "./index.js"; diff --git a/extensions/meta/meta.live.test.ts b/extensions/meta/meta.live.test.ts index 69be65f14bc..08dff9a293c 100644 --- a/extensions/meta/meta.live.test.ts +++ b/extensions/meta/meta.live.test.ts @@ -1,6 +1,6 @@ // Meta live tests prove muse-spark-1.1 auth and Responses API completion. import { streamSimple, type Model } from "openclaw/plugin-sdk/llm"; -import { extractNonEmptyAssistantText, isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { extractNonEmptyAssistantText, isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import { buildMetaProvider } from "./provider-catalog.js"; import { wrapMetaProviderStream } from "./stream.js"; diff --git a/extensions/microsoft/microsoft.live.test.ts b/extensions/microsoft/microsoft.live.test.ts index 7b869a524d6..6ebd5ce14e2 100644 --- a/extensions/microsoft/microsoft.live.test.ts +++ b/extensions/microsoft/microsoft.live.test.ts @@ -1,5 +1,5 @@ // Microsoft tests cover microsoft plugin behavior. -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import { buildMicrosoftSpeechProvider } from "./speech-provider.js"; diff --git a/extensions/minimax/image-generation-provider.test.ts b/extensions/minimax/image-generation-provider.test.ts index cb2294bfd8a..d12eddcab60 100644 --- a/extensions/minimax/image-generation-provider.test.ts +++ b/extensions/minimax/image-generation-provider.test.ts @@ -1,7 +1,7 @@ // Minimax tests cover image generation provider plugin behavior. import * as providerAuth from "openclaw/plugin-sdk/provider-auth-runtime"; import * as providerHttp from "openclaw/plugin-sdk/provider-http"; -import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-env"; +import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-media-understanding"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { buildMinimaxImageGenerationProvider, diff --git a/extensions/minimax/minimax.live.test.ts b/extensions/minimax/minimax.live.test.ts index 93d9c72a9d0..e5d27fd2992 100644 --- a/extensions/minimax/minimax.live.test.ts +++ b/extensions/minimax/minimax.live.test.ts @@ -4,7 +4,7 @@ import { registerProviderPlugin, requireRegisteredProvider, } from "openclaw/plugin-sdk/plugin-test-runtime"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import plugin from "./index.js"; import { buildMinimaxSpeechProvider } from "./speech-provider.js"; diff --git a/extensions/mistral/media-understanding-provider.test.ts b/extensions/mistral/media-understanding-provider.test.ts index 338c30ed8d2..89e72c74e32 100644 --- a/extensions/mistral/media-understanding-provider.test.ts +++ b/extensions/mistral/media-understanding-provider.test.ts @@ -2,7 +2,7 @@ import { createRequestCaptureJsonFetch, installPinnedHostnameTestHooks, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-media-understanding"; import { describe, expect, it } from "vitest"; import { mistralMediaUnderstandingProvider } from "./media-understanding-provider.js"; diff --git a/extensions/mistral/mistral.live.test.ts b/extensions/mistral/mistral.live.test.ts index edb358b145b..ce75e4f126e 100644 --- a/extensions/mistral/mistral.live.test.ts +++ b/extensions/mistral/mistral.live.test.ts @@ -4,7 +4,7 @@ import { runRealtimeSttLiveTest, synthesizeElevenLabsLiveSpeech, } from "openclaw/plugin-sdk/provider-test-contracts"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import { mistralMediaUnderstandingProvider } from "./media-understanding-provider.js"; import { buildMistralRealtimeTranscriptionProvider } from "./realtime-transcription-provider.js"; diff --git a/extensions/moonshot/media-understanding-provider.test.ts b/extensions/moonshot/media-understanding-provider.test.ts index 92a4a2e117d..6a66987b41c 100644 --- a/extensions/moonshot/media-understanding-provider.test.ts +++ b/extensions/moonshot/media-understanding-provider.test.ts @@ -2,7 +2,7 @@ import { createRequestCaptureJsonFetch, installPinnedHostnameTestHooks, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-media-understanding"; import { describe, expect, it } from "vitest"; import { moonshotMediaUnderstandingProvider } from "./media-understanding-provider.js"; diff --git a/extensions/moonshot/moonshot.live.test.ts b/extensions/moonshot/moonshot.live.test.ts index 5240572f18d..1da2cb4cfae 100644 --- a/extensions/moonshot/moonshot.live.test.ts +++ b/extensions/moonshot/moonshot.live.test.ts @@ -7,7 +7,7 @@ import { type Tool, } from "openclaw/plugin-sdk/llm"; import { registerSingleProviderPlugin } from "openclaw/plugin-sdk/plugin-test-runtime"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { Type } from "typebox"; import { describe, expect, it } from "vitest"; import plugin from "./index.js"; diff --git a/extensions/music-generation-providers.live.test.ts b/extensions/music-generation-providers.live.test.ts index b7844f255e6..40b1b2ddd4b 100644 --- a/extensions/music-generation-providers.live.test.ts +++ b/extensions/music-generation-providers.live.test.ts @@ -10,11 +10,6 @@ import { } from "openclaw/plugin-sdk/plugin-test-runtime"; import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot"; import { - DEFAULT_LIVE_MUSIC_MODELS, - collectProviderApiKeys, - encodePngRgba, - fillPixel, - getShellEnvAppliedKeys, isAuthErrorMessage, isBillingErrorMessage, isLiveProfileKeyModeEnabled, @@ -24,12 +19,18 @@ import { isServerErrorMessage, isTimeoutErrorMessage, isTruthyEnvValue, +} from "openclaw/plugin-sdk/test-live"; +import { collectProviderApiKeys, getShellEnvAppliedKeys } from "openclaw/plugin-sdk/test-live-auth"; +import { + DEFAULT_LIVE_MUSIC_MODELS, + encodePngRgba, + fillPixel, parseCsvFilter, parseProviderModelMap, redactLiveApiKey, resolveConfiguredLiveMusicModels, resolveLiveMusicAuthStore, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-media-generation"; import { describe, expect, it } from "vitest"; import falPlugin from "./fal/index.js"; import googlePlugin from "./google/index.js"; diff --git a/extensions/openai/media-understanding-provider.test.ts b/extensions/openai/media-understanding-provider.test.ts index 880bc56a374..245635700d6 100644 --- a/extensions/openai/media-understanding-provider.test.ts +++ b/extensions/openai/media-understanding-provider.test.ts @@ -3,7 +3,7 @@ import { createAuthCaptureJsonFetch, createRequestCaptureJsonFetch, installPinnedHostnameTestHooks, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-media-understanding"; import { describe, expect, it } from "vitest"; import { openaiMediaUnderstandingProvider } from "./media-understanding-provider.js"; diff --git a/extensions/openai/openai-tts.live.test.ts b/extensions/openai/openai-tts.live.test.ts index 25dca5d9637..0dc9b5472d1 100644 --- a/extensions/openai/openai-tts.live.test.ts +++ b/extensions/openai/openai-tts.live.test.ts @@ -1,5 +1,5 @@ // Openai tests cover openai tts plugin behavior. -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import { buildOpenAISpeechProvider } from "./speech-provider.js"; diff --git a/extensions/openai/openai.live.test.ts b/extensions/openai/openai.live.test.ts index cd1f7fe87a3..80896f07d27 100644 --- a/extensions/openai/openai.live.test.ts +++ b/extensions/openai/openai.live.test.ts @@ -17,7 +17,7 @@ import { isOverloadedErrorMessage, isServerErrorMessage, isTimeoutErrorMessage, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import plugin from "./index.js"; diff --git a/extensions/opencode/opencode.live.test.ts b/extensions/opencode/opencode.live.test.ts index c431841b6d3..08f95872779 100644 --- a/extensions/opencode/opencode.live.test.ts +++ b/extensions/opencode/opencode.live.test.ts @@ -5,7 +5,7 @@ import { type Model, type Tool, } from "openclaw/plugin-sdk/llm"; -import { extractNonEmptyAssistantText, isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { extractNonEmptyAssistantText, isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { Type } from "typebox"; import { describe, expect, it } from "vitest"; import { buildStaticOpencodeZenProviderConfig } from "./provider-catalog.js"; diff --git a/extensions/parallel/parallel.live.test.ts b/extensions/parallel/parallel.live.test.ts index c7aef98132c..8fa643b8869 100644 --- a/extensions/parallel/parallel.live.test.ts +++ b/extensions/parallel/parallel.live.test.ts @@ -1,4 +1,4 @@ -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import { createParallelWebSearchProvider } from "./src/parallel-web-search-provider.js"; diff --git a/extensions/qwen/media-understanding-provider.test.ts b/extensions/qwen/media-understanding-provider.test.ts index d9d9de85656..8338fcf0ed3 100644 --- a/extensions/qwen/media-understanding-provider.test.ts +++ b/extensions/qwen/media-understanding-provider.test.ts @@ -2,7 +2,7 @@ import { createRequestCaptureJsonFetch, installPinnedHostnameTestHooks, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-media-understanding"; import { describe, expect, it } from "vitest"; import { buildQwenMediaUnderstandingProvider } from "./media-understanding-provider.js"; diff --git a/extensions/senseaudio/media-understanding-provider.test.ts b/extensions/senseaudio/media-understanding-provider.test.ts index 2c2358647fb..8ea2252ef5c 100644 --- a/extensions/senseaudio/media-understanding-provider.test.ts +++ b/extensions/senseaudio/media-understanding-provider.test.ts @@ -8,7 +8,7 @@ import { createAuthCaptureJsonFetch, createRequestCaptureJsonFetch, installPinnedHostnameTestHooks, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-media-understanding"; import { describe, expect, it } from "vitest"; import { senseaudioMediaUnderstandingProvider } from "./media-understanding-provider.js"; diff --git a/extensions/test-support/generation-live-test-helpers.ts b/extensions/test-support/generation-live-test-helpers.ts index 99f604f4175..27b8a826f4f 100644 --- a/extensions/test-support/generation-live-test-helpers.ts +++ b/extensions/test-support/generation-live-test-helpers.ts @@ -1,2 +1,2 @@ // Test Support helper module supports generation live test helpers behavior. -export { maybeLoadShellEnvForGenerationProviders } from "openclaw/plugin-sdk/test-env"; +export { maybeLoadShellEnvForGenerationProviders } from "openclaw/plugin-sdk/test-live-auth"; diff --git a/extensions/tsconfig.package-boundary.paths.json b/extensions/tsconfig.package-boundary.paths.json index 72b07b3730d..94ef0d8da9e 100644 --- a/extensions/tsconfig.package-boundary.paths.json +++ b/extensions/tsconfig.package-boundary.paths.json @@ -53,6 +53,18 @@ "openclaw/plugin-sdk/test-fixtures": [ "../packages/plugin-sdk/dist/src/plugin-sdk/test-fixtures.d.ts" ], + "openclaw/plugin-sdk/test-live": [ + "../packages/plugin-sdk/dist/src/plugin-sdk/test-live.d.ts" + ], + "openclaw/plugin-sdk/test-live-auth": [ + "../packages/plugin-sdk/dist/src/plugin-sdk/test-live-auth.d.ts" + ], + "openclaw/plugin-sdk/test-media-generation": [ + "../packages/plugin-sdk/dist/src/plugin-sdk/test-media-generation.d.ts" + ], + "openclaw/plugin-sdk/test-media-understanding": [ + "../packages/plugin-sdk/dist/src/plugin-sdk/test-media-understanding.d.ts" + ], "openclaw/plugin-sdk/test-node-mocks": [ "../packages/plugin-sdk/dist/src/plugin-sdk/test-node-mocks.d.ts" ], diff --git a/extensions/video-generation-providers.live.test.ts b/extensions/video-generation-providers.live.test.ts index c28b7a0b30e..1e2bbd80f15 100644 --- a/extensions/video-generation-providers.live.test.ts +++ b/extensions/video-generation-providers.live.test.ts @@ -10,13 +10,6 @@ import { } from "openclaw/plugin-sdk/plugin-test-runtime"; import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot"; import { - DEFAULT_LIVE_VIDEO_MODELS, - canRunBufferBackedImageToVideoLiveLane, - canRunBufferBackedVideoToVideoLiveLane, - collectProviderApiKeys, - encodePngRgba, - fillPixel, - getShellEnvAppliedKeys, isAuthErrorMessage, isBillingErrorMessage, isLiveProfileKeyModeEnabled, @@ -26,6 +19,14 @@ import { isServerErrorMessage, isTimeoutErrorMessage, isTruthyEnvValue, +} from "openclaw/plugin-sdk/test-live"; +import { collectProviderApiKeys, getShellEnvAppliedKeys } from "openclaw/plugin-sdk/test-live-auth"; +import { + DEFAULT_LIVE_VIDEO_MODELS, + canRunBufferBackedImageToVideoLiveLane, + canRunBufferBackedVideoToVideoLiveLane, + encodePngRgba, + fillPixel, normalizeVideoGenerationDuration, parseCsvFilter, parseProviderModelMap, @@ -34,14 +35,14 @@ import { resolveConfiguredLiveVideoModels, resolveLiveVideoAuthStore, resolveLiveVideoResolution, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-media-generation"; import type { GeneratedVideoAsset, VideoGenerationMode, VideoGenerationModeCapabilities, VideoGenerationProvider, VideoGenerationRequest, -} from "openclaw/plugin-sdk/test-env"; +} from "openclaw/plugin-sdk/test-media-generation"; import { describe, expect, it } from "vitest"; import alibabaPlugin from "./alibaba/index.js"; import byteplusPlugin from "./byteplus/index.js"; diff --git a/extensions/vydra/image-generation-provider.test.ts b/extensions/vydra/image-generation-provider.test.ts index 514af26ab33..67bba7c9d42 100644 --- a/extensions/vydra/image-generation-provider.test.ts +++ b/extensions/vydra/image-generation-provider.test.ts @@ -1,5 +1,5 @@ // Vydra tests cover image generation provider plugin behavior. -import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-env"; +import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-media-understanding"; import { afterEach, describe, expect, it, vi } from "vitest"; import { buildVydraImageGenerationProvider } from "./image-generation-provider.js"; import { diff --git a/extensions/vydra/speech-provider.test.ts b/extensions/vydra/speech-provider.test.ts index 926ab1cb422..f6422843cd4 100644 --- a/extensions/vydra/speech-provider.test.ts +++ b/extensions/vydra/speech-provider.test.ts @@ -1,5 +1,5 @@ // Vydra tests cover speech provider plugin behavior. -import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-env"; +import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-media-understanding"; import { afterEach, describe, expect, it, vi } from "vitest"; import { buildVydraSpeechProvider } from "./speech-provider.js"; diff --git a/extensions/vydra/video-generation-provider.test.ts b/extensions/vydra/video-generation-provider.test.ts index c66f1156a6a..beebda92a9b 100644 --- a/extensions/vydra/video-generation-provider.test.ts +++ b/extensions/vydra/video-generation-provider.test.ts @@ -1,6 +1,6 @@ // Vydra tests cover video generation provider plugin behavior. import { expectExplicitVideoGenerationCapabilities } from "openclaw/plugin-sdk/provider-test-contracts"; -import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-env"; +import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-media-understanding"; import { afterEach, describe, expect, it, vi } from "vitest"; import { binaryResponse, diff --git a/extensions/vydra/vydra.live.test.ts b/extensions/vydra/vydra.live.test.ts index db65f580662..42caf1fc7d2 100644 --- a/extensions/vydra/vydra.live.test.ts +++ b/extensions/vydra/vydra.live.test.ts @@ -3,7 +3,7 @@ import { registerProviderPlugin, requireRegisteredProvider, } from "openclaw/plugin-sdk/plugin-test-runtime"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import plugin from "./index.js"; diff --git a/extensions/xai/tsconfig.json b/extensions/xai/tsconfig.json index fa31cc23c5a..a28730d3b50 100644 --- a/extensions/xai/tsconfig.json +++ b/extensions/xai/tsconfig.json @@ -54,6 +54,18 @@ "openclaw/plugin-sdk/test-fixtures": [ "../../packages/plugin-sdk/dist/src/plugin-sdk/test-fixtures.d.ts" ], + "openclaw/plugin-sdk/test-live": [ + "../../packages/plugin-sdk/dist/src/plugin-sdk/test-live.d.ts" + ], + "openclaw/plugin-sdk/test-live-auth": [ + "../../packages/plugin-sdk/dist/src/plugin-sdk/test-live-auth.d.ts" + ], + "openclaw/plugin-sdk/test-media-generation": [ + "../../packages/plugin-sdk/dist/src/plugin-sdk/test-media-generation.d.ts" + ], + "openclaw/plugin-sdk/test-media-understanding": [ + "../../packages/plugin-sdk/dist/src/plugin-sdk/test-media-understanding.d.ts" + ], "openclaw/plugin-sdk/test-node-mocks": [ "../../packages/plugin-sdk/dist/src/plugin-sdk/test-node-mocks.d.ts" ], diff --git a/extensions/xai/x-search.live.test.ts b/extensions/xai/x-search.live.test.ts index 8c25eb7478b..8005d4fd71a 100644 --- a/extensions/xai/x-search.live.test.ts +++ b/extensions/xai/x-search.live.test.ts @@ -1,5 +1,5 @@ // Xai tests cover x search plugin behavior. -import { isBillingErrorMessage } from "openclaw/plugin-sdk/test-env"; +import { isBillingErrorMessage } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import { createXSearchTool } from "./x-search.js"; diff --git a/extensions/xai/xai.live.test.ts b/extensions/xai/xai.live.test.ts index 1acd04413b1..d0c904a5aff 100644 --- a/extensions/xai/xai.live.test.ts +++ b/extensions/xai/xai.live.test.ts @@ -21,7 +21,7 @@ import { type RealtimeVoiceBridgeEvent, } from "openclaw/plugin-sdk/realtime-voice"; import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot"; -import { isBillingErrorMessage } from "openclaw/plugin-sdk/test-env"; +import { isBillingErrorMessage } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import { createCodeExecutionTool } from "./code-execution.js"; import plugin from "./index.js"; diff --git a/extensions/xiaomi/xiaomi.live.test.ts b/extensions/xiaomi/xiaomi.live.test.ts index 9966f24e0df..01d574d0a9f 100644 --- a/extensions/xiaomi/xiaomi.live.test.ts +++ b/extensions/xiaomi/xiaomi.live.test.ts @@ -3,7 +3,7 @@ import { registerProviderPlugin, requireRegisteredProvider, } from "openclaw/plugin-sdk/plugin-test-runtime"; -import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env"; +import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live"; import { describe, expect, it } from "vitest"; import plugin from "./index.js"; diff --git a/package.json b/package.json index 979a1dc87e9..23d27e20f0c 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,14 @@ "!dist/plugin-sdk/test-env.d.ts", "!dist/plugin-sdk/test-fixtures.js", "!dist/plugin-sdk/test-fixtures.d.ts", + "!dist/plugin-sdk/test-live.js", + "!dist/plugin-sdk/test-live.d.ts", + "!dist/plugin-sdk/test-live-auth.js", + "!dist/plugin-sdk/test-live-auth.d.ts", + "!dist/plugin-sdk/test-media-generation.js", + "!dist/plugin-sdk/test-media-generation.d.ts", + "!dist/plugin-sdk/test-media-understanding.js", + "!dist/plugin-sdk/test-media-understanding.d.ts", "!dist/plugin-sdk/test-node-mocks.js", "!dist/plugin-sdk/test-node-mocks.d.ts", "!dist/plugin-sdk/test-utils.js", diff --git a/scripts/check-no-extension-test-core-imports.ts b/scripts/check-no-extension-test-core-imports.ts index 60b6468edc8..34ff0372619 100644 --- a/scripts/check-no-extension-test-core-imports.ts +++ b/scripts/check-no-extension-test-core-imports.ts @@ -32,7 +32,7 @@ const FORBIDDEN_PATTERNS: Array<{ pattern: RegExp; hint: string }> = [ }, { pattern: /["'](?:\.\.\/)+(?:test\/helpers\/media-generation\/)[^"']+["']/, - hint: "Use openclaw/plugin-sdk/provider-test-contracts or openclaw/plugin-sdk/provider-http-test-mocks instead of repo-only media provider helper bridges.", + hint: "Use openclaw/plugin-sdk/test-media-generation, provider-test-contracts, or provider-http-test-mocks instead of repo-only media provider helper bridges.", }, { pattern: diff --git a/scripts/lib/plugin-sdk-entrypoints.json b/scripts/lib/plugin-sdk-entrypoints.json index e484c35ccfc..2254c082896 100644 --- a/scripts/lib/plugin-sdk-entrypoints.json +++ b/scripts/lib/plugin-sdk-entrypoints.json @@ -137,6 +137,10 @@ "provider-test-contracts", "test-env", "test-fixtures", + "test-live", + "test-live-auth", + "test-media-generation", + "test-media-understanding", "test-node-mocks", "temp-path", "time-runtime", diff --git a/scripts/lib/plugin-sdk-private-local-only-subpaths.json b/scripts/lib/plugin-sdk-private-local-only-subpaths.json index 532595327c0..91c425c2c13 100644 --- a/scripts/lib/plugin-sdk-private-local-only-subpaths.json +++ b/scripts/lib/plugin-sdk-private-local-only-subpaths.json @@ -19,6 +19,10 @@ "ssrf-runtime-internal", "test-env", "test-fixtures", + "test-live", + "test-live-auth", + "test-media-generation", + "test-media-understanding", "test-node-mocks", "test-utils" ] diff --git a/src/agents/live-test-config.ts b/src/agents/live-test-config.ts new file mode 100644 index 00000000000..0c9178c9229 --- /dev/null +++ b/src/agents/live-test-config.ts @@ -0,0 +1,43 @@ +import { isTruthyEnvValue } from "../infra/env.js"; + +const LIVE_OK_PROMPT = "Reply with the word ok."; + +/** Return whether live tests are enabled by standard or caller-specific env flags. */ +export function isLiveTestEnabled( + extraEnvVars: readonly string[] = [], + env: NodeJS.ProcessEnv = process.env, +): boolean { + return [...extraEnvVars, "LIVE", "OPENCLAW_LIVE_TEST"].some((name) => + isTruthyEnvValue(env[name]), + ); +} + +/** Return whether live tests must prefer profile credentials over env keys. */ +export function isLiveProfileKeyModeEnabled(env: NodeJS.ProcessEnv = process.env): boolean { + return isTruthyEnvValue(env.OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS); +} + +/** Build a single user-message prompt for simple live model probes. */ +export function createSingleUserPromptMessage(content = LIVE_OK_PROMPT) { + return [ + { + role: "user" as const, + content, + timestamp: Date.now(), + }, + ]; +} + +/** Extract non-empty assistant text from content blocks. */ +export function extractNonEmptyAssistantText( + content: Array<{ + type?: string; + text?: string; + }>, +) { + return content + .filter((block) => block.type === "text") + .map((block) => block.text?.trim() ?? "") + .filter(Boolean) + .join(" "); +} diff --git a/src/agents/live-test-helpers.ts b/src/agents/live-test-helpers.ts index 0dcfe7c2c98..b7335f035b5 100644 --- a/src/agents/live-test-helpers.ts +++ b/src/agents/live-test-helpers.ts @@ -1,28 +1,19 @@ /** * Shared helpers for live provider tests and timeout-wrapped completions. */ -import { isTruthyEnvValue } from "../infra/env.js"; import { completeSimple } from "../llm/stream.js"; import type { Api, Model } from "../llm/types.js"; -// Shared live-test helpers. Live lanes opt in via env flags and use guarded -// model calls so missing credentials skip cleanly instead of hanging tests. -const LIVE_OK_PROMPT = "Reply with the word ok."; +export { + createSingleUserPromptMessage, + extractNonEmptyAssistantText, + isLiveProfileKeyModeEnabled, + isLiveTestEnabled, +} from "./live-test-config.js"; -/** Return whether live tests are enabled by standard or caller-specific env flags. */ -export function isLiveTestEnabled( - extraEnvVars: readonly string[] = [], - env: NodeJS.ProcessEnv = process.env, -): boolean { - return [...extraEnvVars, "LIVE", "OPENCLAW_LIVE_TEST"].some((name) => - isTruthyEnvValue(env[name]), - ); -} - -/** Return whether live tests must prefer profile credentials over env keys. */ -export function isLiveProfileKeyModeEnabled(env: NodeJS.ProcessEnv = process.env): boolean { - return isTruthyEnvValue(env.OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS); -} +export type CompleteSimpleContent = Awaited< + ReturnType> +>["content"]; /** Return whether a provider requires profile credentials in the current live mode. */ export function requiresLiveProfileCredential( @@ -42,35 +33,6 @@ export function resolveLiveCredentialPrecedence( : "env-first"; } -/** Build a single user-message prompt for simple live model probes. */ -export function createSingleUserPromptMessage(content = LIVE_OK_PROMPT) { - return [ - { - role: "user" as const, - content, - timestamp: Date.now(), - }, - ]; -} - -/** Extract non-empty assistant text from content blocks. */ -export function extractNonEmptyAssistantText( - content: Array<{ - type?: string; - text?: string; - }>, -) { - return content - .filter((block) => block.type === "text") - .map((block) => block.text?.trim() ?? "") - .filter(Boolean) - .join(" "); -} - -export type CompleteSimpleContent = Awaited< - ReturnType> ->["content"]; - /** Write a namespaced live-test progress line to stderr. */ export function logLiveProgress(message: string): void { process.stderr.write(`[live] ${message}\n`); diff --git a/src/agents/tools/image-tool.providers.live.test.ts b/src/agents/tools/image-tool.providers.live.test.ts index e680b2e55a9..6f864a5efda 100644 --- a/src/agents/tools/image-tool.providers.live.test.ts +++ b/src/agents/tools/image-tool.providers.live.test.ts @@ -18,7 +18,7 @@ import { isBillingErrorMessage, isOverloadedErrorMessage, isServerErrorMessage, -} from "../../plugin-sdk/test-env.js"; +} from "../embedded-agent-helpers/failover-matches.js"; import { isLiveTestEnabled } from "../live-test-helpers.js"; import { isLiveAuthDrift } from "../live-test-provider-drift.test-support.js"; import { createImageTool } from "./image-tool.js"; diff --git a/src/infra/package-dist-inventory.ts b/src/infra/package-dist-inventory.ts index 86c5d54ced0..a7b4a89664c 100644 --- a/src/infra/package-dist-inventory.ts +++ b/src/infra/package-dist-inventory.ts @@ -59,6 +59,14 @@ const OMITTED_PLUGIN_SDK_TEST_FILES = new Set([ "dist/plugin-sdk/test-env.js", "dist/plugin-sdk/test-fixtures.d.ts", "dist/plugin-sdk/test-fixtures.js", + "dist/plugin-sdk/test-live.d.ts", + "dist/plugin-sdk/test-live.js", + "dist/plugin-sdk/test-live-auth.d.ts", + "dist/plugin-sdk/test-live-auth.js", + "dist/plugin-sdk/test-media-generation.d.ts", + "dist/plugin-sdk/test-media-generation.js", + "dist/plugin-sdk/test-media-understanding.d.ts", + "dist/plugin-sdk/test-media-understanding.js", "dist/plugin-sdk/test-node-mocks.d.ts", "dist/plugin-sdk/test-node-mocks.js", ]); diff --git a/src/plugin-sdk/test-env.ts b/src/plugin-sdk/test-env.ts index 4610e93484c..2fae7c8fcbb 100644 --- a/src/plugin-sdk/test-env.ts +++ b/src/plugin-sdk/test-env.ts @@ -1,56 +1,4 @@ -// Focused public test helpers for environment, network, and time fixtures. - -export { - createAuthCaptureJsonFetch, - createRequestCaptureJsonFetch, - installPinnedHostnameTestHooks, -} from "../media-understanding/audio.test-helpers.ts"; -export { - createSingleUserPromptMessage, - extractNonEmptyAssistantText, - isLiveProfileKeyModeEnabled, - isLiveTestEnabled, -} from "../agents/live-test-helpers.js"; -export { collectProviderApiKeys } from "../agents/live-auth-keys.js"; -export { isModelNotFoundErrorMessage } from "../agents/live-model-errors.js"; -export { - isAuthErrorMessage, - isBillingErrorMessage, - isOverloadedErrorMessage, - isServerErrorMessage, - isTimeoutErrorMessage, -} from "../agents/embedded-agent-helpers/failover-matches.js"; -export { maybeLoadShellEnvForGenerationProviders } from "../test-utils/generation-live-test-helpers.js"; -export { isTruthyEnvValue } from "../infra/env.js"; -export { getShellEnvAppliedKeys } from "../infra/shell-env.js"; -export { encodePngRgba, fillPixel } from "../media/png-encode.js"; -export { - parseLiveCsvFilter as parseCsvFilter, - parseProviderModelMap, - redactLiveApiKey, -} from "../media-generation/live-test-helpers.js"; -export { - DEFAULT_LIVE_MUSIC_MODELS, - resolveConfiguredLiveMusicModels, - resolveLiveMusicAuthStore, -} from "../music-generation/live-test-helpers.js"; -export { - canRunBufferBackedImageToVideoLiveLane, - canRunBufferBackedVideoToVideoLiveLane, - DEFAULT_LIVE_VIDEO_MODELS, - resolveConfiguredLiveVideoModels, - resolveLiveVideoAuthStore, - resolveLiveVideoResolution, -} from "../video-generation/live-test-helpers.js"; -export { normalizeVideoGenerationDuration } from "../video-generation/duration-support.js"; -export { parseVideoGenerationModelRef } from "../video-generation/model-ref.js"; -export type { - GeneratedVideoAsset, - VideoGenerationMode, - VideoGenerationModeCapabilities, - VideoGenerationProvider, - VideoGenerationRequest, -} from "../video-generation/types.js"; +// Repo-local helpers for environment, network, filesystem, and time fixtures. export { jsonResponse, requestBodyText, requestUrl } from "../test-helpers/http.js"; export { mockPinnedHostnameResolution } from "../test-helpers/ssrf.js"; export { createWindowsCmdShimFixture } from "../test-helpers/windows-cmd-shim.js"; diff --git a/src/plugin-sdk/test-live-auth.ts b/src/plugin-sdk/test-live-auth.ts new file mode 100644 index 00000000000..1351c58b9f6 --- /dev/null +++ b/src/plugin-sdk/test-live-auth.ts @@ -0,0 +1,5 @@ +// Repo-local helpers for live-test credential discovery and shell environment loading. + +export { collectProviderApiKeys } from "../agents/live-auth-keys.js"; +export { getShellEnvAppliedKeys } from "../infra/shell-env.js"; +export { maybeLoadShellEnvForGenerationProviders } from "../test-utils/generation-live-test-helpers.js"; diff --git a/src/plugin-sdk/test-live.ts b/src/plugin-sdk/test-live.ts new file mode 100644 index 00000000000..bdc509a4115 --- /dev/null +++ b/src/plugin-sdk/test-live.ts @@ -0,0 +1,17 @@ +// Repo-local helpers for live-test gating, prompts, and provider error classification. + +export { + createSingleUserPromptMessage, + extractNonEmptyAssistantText, + isLiveProfileKeyModeEnabled, + isLiveTestEnabled, +} from "../agents/live-test-config.js"; +export { isModelNotFoundErrorMessage } from "../agents/live-model-errors.js"; +export { + isAuthErrorMessage, + isBillingErrorMessage, + isOverloadedErrorMessage, + isServerErrorMessage, + isTimeoutErrorMessage, +} from "../agents/embedded-agent-helpers/failover-matches.js"; +export { isTruthyEnvValue } from "../infra/env.js"; diff --git a/src/plugin-sdk/test-media-generation.ts b/src/plugin-sdk/test-media-generation.ts new file mode 100644 index 00000000000..9ad41ec2160 --- /dev/null +++ b/src/plugin-sdk/test-media-generation.ts @@ -0,0 +1,30 @@ +// Repo-local helpers for music, image, and video generation tests. + +export { encodePngRgba, fillPixel } from "../media/png-encode.js"; +export { + parseLiveCsvFilter as parseCsvFilter, + parseProviderModelMap, + redactLiveApiKey, +} from "../media-generation/live-test-helpers.js"; +export { + DEFAULT_LIVE_MUSIC_MODELS, + resolveConfiguredLiveMusicModels, + resolveLiveMusicAuthStore, +} from "../music-generation/live-test-helpers.js"; +export { + canRunBufferBackedImageToVideoLiveLane, + canRunBufferBackedVideoToVideoLiveLane, + DEFAULT_LIVE_VIDEO_MODELS, + resolveConfiguredLiveVideoModels, + resolveLiveVideoAuthStore, + resolveLiveVideoResolution, +} from "../video-generation/live-test-helpers.js"; +export { normalizeVideoGenerationDuration } from "../video-generation/duration-support.js"; +export { parseVideoGenerationModelRef } from "../video-generation/model-ref.js"; +export type { + GeneratedVideoAsset, + VideoGenerationMode, + VideoGenerationModeCapabilities, + VideoGenerationProvider, + VideoGenerationRequest, +} from "../video-generation/types.js"; diff --git a/src/plugin-sdk/test-media-understanding.ts b/src/plugin-sdk/test-media-understanding.ts new file mode 100644 index 00000000000..6ce22278e8c --- /dev/null +++ b/src/plugin-sdk/test-media-understanding.ts @@ -0,0 +1,7 @@ +// Repo-local request-capture helpers for media-understanding provider tests. + +export { + createAuthCaptureJsonFetch, + createRequestCaptureJsonFetch, + installPinnedHostnameTestHooks, +} from "../media-understanding/audio.test-helpers.ts"; diff --git a/src/plugins/contracts/plugin-sdk-subpaths.test.ts b/src/plugins/contracts/plugin-sdk-subpaths.test.ts index 2f6ca75dc79..5fd845ef4fa 100644 --- a/src/plugins/contracts/plugin-sdk-subpaths.test.ts +++ b/src/plugins/contracts/plugin-sdk-subpaths.test.ts @@ -80,6 +80,10 @@ const PUBLIC_SDK_TEST_HELPER_SUBPATHS = [ "provider-test-contracts", "test-env", "test-fixtures", + "test-live", + "test-live-auth", + "test-media-generation", + "test-media-understanding", "test-node-mocks", ] as const; const PUBLIC_SDK_TEST_HELPER_SUBPATHS_WITH_TOP_LEVEL_MOCKS = ["provider-http-test-mocks"] as const; @@ -817,9 +821,33 @@ describe("plugin-sdk subpath exports", () => { "withTempHome", "createMockIncomingRequest", "withFetchPreconnect", + ]); + expectSourceOmits("test-env", [ + "collectProviderApiKeys", + "createRequestCaptureJsonFetch", + "isLiveTestEnabled", + "resolveConfiguredLiveVideoModels", + ]); + expectSourceMentions("test-live", [ + "createSingleUserPromptMessage", + "isLiveTestEnabled", + "isBillingErrorMessage", + "isModelNotFoundErrorMessage", + ]); + expectSourceMentions("test-live-auth", [ + "collectProviderApiKeys", + "getShellEnvAppliedKeys", + "maybeLoadShellEnvForGenerationProviders", + ]); + expectSourceMentions("test-media-understanding", [ "createRequestCaptureJsonFetch", "installPinnedHostnameTestHooks", - "isLiveTestEnabled", + ]); + expectSourceMentions("test-media-generation", [ + "encodePngRgba", + "parseProviderModelMap", + "resolveConfiguredLiveMusicModels", + "resolveConfiguredLiveVideoModels", ]); expectSourceMentions("test-fixtures", [ "createCliRuntimeCapture",