refactor: promote plugin test helpers to sdk

This commit is contained in:
Peter Steinberger 2026-04-28 00:53:58 +01:00
parent 49d069cd94
commit 8057561cee
No known key found for this signature in database
153 changed files with 496 additions and 291 deletions

View file

@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai
- Channels/QQBot: add full group chat support (history tracking, @-mention gating, activation modes, per-group config, FIFO message queue with deliver debounce), C2C `stream_messages` streaming with a `StreamingController` lifecycle manager, unified `sendMedia` with chunked upload for large files, and refactor the engine into pipeline stages, focused outbound submodules, builtin slash-command modules, and explicit DI ports via `createEngineAdapters()`. (#70624) Thanks @cxyhhhhh.
- Gateway/runtime: reuse the current plugin metadata snapshot for provider discovery so repeated model-provider discovery avoids rebuilding plugin manifest metadata. Thanks @shakkernerd.
- Gateway/startup: pass the plugin metadata snapshot from config validation into plugin bootstrap so startup reuses one manifest product instead of rebuilding plugin metadata. Thanks @shakkernerd.
- Plugin SDK/testing: promote bundled plugin/provider/channel contract helpers to focused SDK test subpaths and retire the repo-only `test/helpers/plugins` TypeScript bridge. Thanks @vincentkoc.
- Plugin SDK/testing: add a focused `plugin-sdk/plugin-test-api` helper subpath and move bundled plugin registration tests off the repo-only plugin API bridge. Thanks @vincentkoc.
- Plugin SDK/testing: expose provider catalog, wizard, registry, manifest, public-artifact, outbound, and TTS contract helpers through documented SDK testing seams so bundled plugin tests no longer import repo `src/**` internals. Thanks @vincentkoc.
- Matrix: attach versioned structured approval metadata to pending approval messages so capable Matrix clients can render richer approval UI while body text and reaction fallback keep working. (#72432) Thanks @kakahu2015.

View file

@ -1,2 +1,2 @@
0736a1666860383e3e5f8ada181c016455d8304a2852ac6966355765f799add4 plugin-sdk-api-baseline.json
761cdb609547f5912513e5714d8b0ec8fff2b29905690af376cc5bdd74f2c279 plugin-sdk-api-baseline.jsonl
8ddaaac1ae57b1b5d89d906653b5441bd69b1a296582422541de99944fdb382e plugin-sdk-api-baseline.json
73c8f58d5ce7056c67ed6abb12e90285f2101c6af94a4c8f4ecae02a6c494b79 plugin-sdk-api-baseline.jsonl

View file

@ -16,16 +16,19 @@ For the plugin authoring guide, see [Plugin SDK overview](/plugins/sdk-overview)
## Plugin entry
| Subpath | Key exports |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `plugin-sdk/plugin-entry` | `definePluginEntry` |
| `plugin-sdk/core` | `defineChannelPluginEntry`, `createChatChannelPlugin`, `createChannelPluginBase`, `defineSetupPluginEntry`, `buildChannelConfigSchema` |
| `plugin-sdk/config-schema` | `OpenClawSchema` |
| `plugin-sdk/provider-entry` | `defineSingleProviderPluginEntry` |
| `plugin-sdk/testing` | Public plugin test fixtures, provider registration/catalog helpers, wizard contract hooks, and bundled-plugin contract maintenance helpers |
| `plugin-sdk/plugin-test-api` | Minimal `OpenClawPluginApi` mock builder for direct plugin registration unit tests |
| `plugin-sdk/migration` | Migration provider item helpers such as `createMigrationItem`, reason constants, item status markers, redaction helpers, and `summarizeMigrationItems` |
| `plugin-sdk/migration-runtime` | Runtime migration helpers such as `copyMigrationFileItem` and `writeMigrationReport` |
| Subpath | Key exports |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `plugin-sdk/plugin-entry` | `definePluginEntry` |
| `plugin-sdk/core` | `defineChannelPluginEntry`, `createChatChannelPlugin`, `createChannelPluginBase`, `defineSetupPluginEntry`, `buildChannelConfigSchema` |
| `plugin-sdk/config-schema` | `OpenClawSchema` |
| `plugin-sdk/provider-entry` | `defineSingleProviderPluginEntry` |
| `plugin-sdk/testing` | Public plugin test fixtures, provider registration/catalog helpers, wizard contract hooks, and bundled-plugin contract maintenance helpers |
| `plugin-sdk/plugin-test-api` | Minimal `OpenClawPluginApi` mock builder for direct plugin registration unit tests |
| `plugin-sdk/channel-test-helpers` | Channel account lifecycle, directory, send-config, runtime mock, and hook test helpers |
| `plugin-sdk/plugin-test-contracts` | Plugin registration, package manifest, public artifact, runtime API, import side-effect, and direct import contract helpers |
| `plugin-sdk/provider-test-contracts` | Provider runtime, auth, discovery, onboard, catalog, web-search/fetch, and wizard contract helpers |
| `plugin-sdk/migration` | Migration provider item helpers such as `createMigrationItem`, reason constants, item status markers, redaction helpers, and `summarizeMigrationItems` |
| `plugin-sdk/migration-runtime` | Runtime migration helpers such as `copyMigrationFileItem` and `writeMigrationReport` |
<AccordionGroup>
<Accordion title="Channel subpaths">
@ -262,6 +265,9 @@ For the plugin authoring guide, see [Plugin SDK overview](/plugins/sdk-overview)
| `plugin-sdk/zod` | Re-exported `zod` for plugin SDK consumers |
| `plugin-sdk/testing` | Public extension test helpers including plugin registry/runtime mocks, provider registration capture, setup-wizard helpers, fetch/env/temp/time fixtures, schema/media/live-test helpers, `installCommonResolveTargetErrorCases`, `writeSkill`, `createTestRegistry`, and live generation env loading. Extension `*.test-support.ts` helpers stay on this or focused SDK subpaths, not core internals |
| `plugin-sdk/plugin-test-api` | Minimal `createTestPluginApi` helper for direct plugin registration unit tests without importing repo test helper bridges |
| `plugin-sdk/channel-test-helpers` | Channel-oriented test helpers for account startup lifecycle, directory assertions, send-config threading, runtime mocks, status issues, outbound delivery, and hook registration |
| `plugin-sdk/plugin-test-contracts` | Plugin package, registration, public artifact, direct import, runtime API, and import side-effect contract helpers |
| `plugin-sdk/provider-test-contracts` | Provider runtime, auth, discovery, onboard, catalog, wizard, web-search/fetch, and stream contract helpers |
</Accordion>
<Accordion title="Memory subpaths">

View file

@ -25,6 +25,12 @@ plugins.
**Channel contract import:** `openclaw/plugin-sdk/channel-contract-testing`
**Channel test helper import:** `openclaw/plugin-sdk/channel-test-helpers`
**Plugin contract import:** `openclaw/plugin-sdk/plugin-test-contracts`
**Provider contract import:** `openclaw/plugin-sdk/provider-test-contracts`
The testing subpath exports a narrow set of helpers for plugin authors:
```typescript
@ -35,6 +41,9 @@ import {
} from "openclaw/plugin-sdk/testing";
import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api";
import { expectChannelInboundContextContract } from "openclaw/plugin-sdk/channel-contract-testing";
import { createStartAccountContext } from "openclaw/plugin-sdk/channel-test-helpers";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
import { describeOpenAIProviderRuntimeContract } from "openclaw/plugin-sdk/provider-test-contracts";
```
### Available exports
@ -44,6 +53,9 @@ import { expectChannelInboundContextContract } from "openclaw/plugin-sdk/channel
| `createTestPluginApi` | Build a minimal plugin API mock for direct registration unit tests. Import from `plugin-sdk/plugin-test-api` |
| `expectChannelInboundContextContract` | Assert channel inbound context shape. Import from `plugin-sdk/channel-contract-testing` |
| `installChannelOutboundPayloadContractSuite` | Install channel outbound payload contract cases. Import from `plugin-sdk/channel-contract-testing` |
| `createStartAccountContext` | Build channel account lifecycle contexts. Import from `plugin-sdk/channel-test-helpers` |
| `describePluginRegistrationContract` | Install plugin registration contract checks. Import from `plugin-sdk/plugin-test-contracts` |
| `describeOpenAIProviderRuntimeContract` | Install provider-family runtime contract checks. Import from `plugin-sdk/provider-test-contracts` |
| `installCommonResolveTargetErrorCases` | Shared test cases for target resolution error handling |
| `shouldAckReaction` | Check whether a channel should add an ack reaction |
| `removeAckReactionAfterReply` | Remove ack reaction after reply delivery |
@ -73,11 +85,14 @@ import { expectChannelInboundContextContract } from "openclaw/plugin-sdk/channel
| `typedCases` | Preserve literal types for table-driven tests |
Bundled-plugin contract suites also use SDK testing subpaths for test-only
registry, manifest, public-artifact, and runtime fixture helpers. Keep new
extension tests on `openclaw/plugin-sdk/testing` or a narrower documented SDK
subpath such as `plugin-sdk/plugin-test-api` or
`plugin-sdk/channel-contract-testing` rather than importing repo `src/**` files
directly.
registry, manifest, public-artifact, and runtime fixture helpers. Core-only
suites that depend on bundled OpenClaw inventory stay under `src/plugins/contracts`.
Keep new extension tests on `openclaw/plugin-sdk/testing` or a narrower
documented SDK subpath such as `plugin-sdk/plugin-test-api` or
`plugin-sdk/channel-contract-testing`, `plugin-sdk/channel-test-helpers`,
`plugin-sdk/plugin-test-contracts`, or `plugin-sdk/provider-test-contracts`
rather than importing repo `src/**` files or repo `test/helpers/plugins/*`
bridges directly.
### Types
@ -136,8 +151,8 @@ entry to declare `kind: "memory"`.
### Testing runtime config access
Prefer the shared plugin runtime mock from the repo test helpers when testing
bundled plugins. Its deprecated `runtime.config.loadConfig()` and
Prefer the shared plugin runtime mock from `openclaw/plugin-sdk/channel-test-helpers`
when testing bundled channel plugins. Its deprecated `runtime.config.loadConfig()` and
`runtime.config.writeConfigFile(...)` mocks throw by default so tests catch new
usage of compatibility APIs. Override those mocks only when the test is
explicitly covering legacy compatibility behavior.

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "alibaba",

View file

@ -1,3 +1,3 @@
import { describeAnthropicProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
import { describeAnthropicProviderRuntimeContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeAnthropicProviderRuntimeContract(() => import("./index.js"));

View file

@ -1,7 +1,7 @@
import { createPluginRuntimeMock } from "openclaw/plugin-sdk/channel-test-helpers";
import type { PluginRuntime } from "openclaw/plugin-sdk/core";
import type { HistoryEntry } from "openclaw/plugin-sdk/reply-history";
import { vi } from "vitest";
import { createPluginRuntimeMock } from "../../../../test/helpers/plugins/plugin-runtime-mock.js";
import { _resetBlueBubblesInboundDedupForTest } from "../inbound-dedupe.js";
import {
_resetBlueBubblesShortIdState,

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "byteplus",

View file

@ -1,3 +1,3 @@
import { describeCloudflareAiGatewayProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js";
import { describeCloudflareAiGatewayProviderDiscoveryContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeCloudflareAiGatewayProviderDiscoveryContract(() => import("./index.js"));

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "comfy",

View file

@ -1,8 +1,8 @@
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { createStartAccountContext } from "openclaw/plugin-sdk/channel-test-helpers";
import type { PluginRuntime } from "openclaw/plugin-sdk/core";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js";
import type { ResolvedDiscordAccount } from "./accounts.js";
import type { OpenClawConfig } from "./runtime-api.js";
import * as sendModule from "./send.js";

View file

@ -1,9 +1,9 @@
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import {
getRequiredHookHandler,
registerHookHandlersForTest,
} from "../../../test/helpers/plugins/subagent-hooks.js";
} from "openclaw/plugin-sdk/channel-test-helpers";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
type ThreadBindingRecord = {
accountId: string;

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "fal",

View file

@ -1,8 +1,8 @@
import { beforeEach, describe, expect, it } from "vitest";
import {
getRequiredHookHandler,
registerHookHandlersForTest,
} from "../../../test/helpers/plugins/subagent-hooks.js";
} from "openclaw/plugin-sdk/channel-test-helpers";
import { beforeEach, describe, expect, it } from "vitest";
import type { ClawdbotConfig, OpenClawPluginApi } from "../runtime-api.js";
import { registerFeishuSubagentHooks } from "../subagent-hooks-api.js";
import {

View file

@ -1,6 +1,6 @@
import { randomUUID } from "node:crypto";
import { createPluginRuntimeMock } from "openclaw/plugin-sdk/channel-test-helpers";
import { expect, vi, type Mock } from "vitest";
import { createPluginRuntimeMock } from "../../../../test/helpers/plugins/plugin-runtime-mock.js";
import type { ClawdbotConfig, PluginRuntime, RuntimeEnv } from "../../runtime-api.js";
import { createFeishuMessageReceiveHandler } from "../monitor.message-handler.js";
import { setFeishuRuntime } from "../runtime.js";

View file

@ -1,3 +1,3 @@
import { describeGithubCopilotProviderAuthContract } from "../../test/helpers/plugins/provider-auth-contract.js";
import { describeGithubCopilotProviderAuthContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeGithubCopilotProviderAuthContract(() => import("./index.js"));

View file

@ -1,5 +1,5 @@
import { fileURLToPath } from "node:url";
import { describeGithubCopilotProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js";
import { describeGithubCopilotProviderDiscoveryContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeGithubCopilotProviderDiscoveryContract({
load: () => import("./index.js"),

View file

@ -1,3 +1,3 @@
import { describeGithubCopilotProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
import { describeGithubCopilotProviderRuntimeContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeGithubCopilotProviderRuntimeContract(() => import("./index.js"));

View file

@ -3,9 +3,9 @@ import type {
ProviderReplaySessionEntry,
ProviderSanitizeReplayHistoryContext,
} from "openclaw/plugin-sdk/plugin-entry";
import { createCapturedThinkingConfigStream } from "openclaw/plugin-sdk/provider-test-contracts";
import { registerProviderPlugin, requireRegisteredProvider } from "openclaw/plugin-sdk/testing";
import { describe, expect, it } from "vitest";
import { createCapturedThinkingConfigStream } from "../../test/helpers/plugins/stream-hooks.js";
import { registerGoogleGeminiCliProvider } from "./gemini-cli-provider.js";
import { registerGoogleProvider } from "./provider-registration.js";

View file

@ -1,5 +1,5 @@
import { pluginRegistrationContractCases } from "../../test/helpers/plugins/plugin-registration-contract-cases.js";
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { pluginRegistrationContractCases } from "openclaw/plugin-sdk/plugin-test-contracts";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
...pluginRegistrationContractCases.google,

View file

@ -1,3 +1,3 @@
import { describeGoogleProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
import { describeGoogleProviderRuntimeContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeGoogleProviderRuntimeContract(() => import("./index.js"));

View file

@ -1,8 +1,8 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import {
createDirectoryTestRuntime,
expectDirectorySurface,
} from "../../../test/helpers/plugins/directory.ts";
} from "openclaw/plugin-sdk/channel-test-helpers";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../runtime-api.js";
import {
googlechatDirectoryAdapter,

View file

@ -1,3 +1,8 @@
import {
expectLifecyclePatch,
expectPendingUntilAbort,
startAccountAndTrackLifecycle,
} from "openclaw/plugin-sdk/channel-test-helpers";
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
import {
createPluginSetupWizardConfigure,
@ -7,11 +12,6 @@ import {
type WizardPrompter,
} from "openclaw/plugin-sdk/testing";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
expectLifecyclePatch,
expectPendingUntilAbort,
startAccountAndTrackLifecycle,
} from "../../../test/helpers/plugins/start-account-lifecycle.js";
import type { OpenClawConfig } from "../runtime-api.js";
import {
listGoogleChatAccountIds,

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "gradium",

View file

@ -1,5 +1,5 @@
import { runDirectImportSmoke } from "openclaw/plugin-sdk/plugin-test-contracts";
import { describe, expect, it } from "vitest";
import { runDirectImportSmoke } from "../../test/helpers/plugins/direct-smoke.js";
describe("irc bundled api seams", () => {
it("loads narrow public api modules in direct smoke", async () => {

View file

@ -1,5 +1,5 @@
import { createSendCfgThreadingRuntime } from "openclaw/plugin-sdk/channel-test-helpers";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createSendCfgThreadingRuntime } from "../../../test/helpers/plugins/send-config.js";
import type { IrcClient } from "./client.js";
import { setIrcRuntime } from "./runtime.js";
import type { CoreConfig } from "./types.js";

View file

@ -1,3 +1,8 @@
import {
expectStopPendingUntilAbort,
startAccountAndTrackLifecycle,
waitForStartedMocks,
} from "openclaw/plugin-sdk/channel-test-helpers";
import {
createPluginSetupWizardAdapter,
createPluginSetupWizardStatus,
@ -7,11 +12,6 @@ import {
type WizardPrompter,
} from "openclaw/plugin-sdk/testing";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
expectStopPendingUntilAbort,
startAccountAndTrackLifecycle,
waitForStartedMocks,
} from "../../../test/helpers/plugins/start-account-lifecycle.js";
import {
listIrcAccountIds,
resolveDefaultIrcAccountId,

View file

@ -1,5 +1,6 @@
import { readFileSync } from "node:fs";
import path from "node:path";
import { createStartAccountContext } from "openclaw/plugin-sdk/channel-test-helpers";
import {
createPluginSetupWizardConfigure,
createTestWizardPrompter,
@ -9,7 +10,6 @@ import {
import ts from "typescript";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { bundledPluginRoot } from "../../../test/helpers/bundled-plugin-paths.js";
import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js";
import type { OpenClawConfig, PluginRuntime, ResolvedLineAccount } from "../api.js";
import { linePlugin } from "./channel.js";
import { lineGatewayAdapter } from "./gateway.js";

View file

@ -1,5 +1,5 @@
import { expectProviderOnboardMergedLegacyConfig } from "openclaw/plugin-sdk/provider-test-contracts";
import { describe, expect, it } from "vitest";
import { expectProviderOnboardMergedLegacyConfig } from "../../test/helpers/plugins/provider-onboard.js";
import { applyLitellmProviderConfig } from "./onboard.js";
describe("litellm onboard", () => {

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "lmstudio",

View file

@ -1,9 +1,9 @@
import type { OpenClawPluginApi as MatrixEntryPluginApi } from "openclaw/plugin-sdk/channel-entry-contract";
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
getRequiredHookHandler,
registerHookHandlersForTest,
} from "../../../../test/helpers/plugins/subagent-hooks.js";
} from "openclaw/plugin-sdk/channel-test-helpers";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { registerMatrixSubagentHooks } from "../../subagent-hooks-api.js";
// Hoisted stubs referenced in vi.mock factories below

View file

@ -1,5 +1,5 @@
import { expectProvidedCfgSkipsRuntimeLoad } from "openclaw/plugin-sdk/channel-test-helpers";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { expectProvidedCfgSkipsRuntimeLoad } from "../../../../test/helpers/plugins/send-config.js";
let parseMattermostTarget: typeof import("./send.js").parseMattermostTarget;
let sendMessageMattermost: typeof import("./send.js").sendMessageMattermost;

View file

@ -1,9 +1,9 @@
import { resolveAgentModelPrimaryValue } from "openclaw/plugin-sdk/provider-onboard";
import { describe, expect, it } from "vitest";
import {
expectProviderOnboardMergedLegacyConfig,
expectProviderOnboardPreservesPrimary,
} from "../../test/helpers/plugins/provider-onboard.js";
} from "openclaw/plugin-sdk/provider-test-contracts";
import { describe, expect, it } from "vitest";
import { applyMinimaxApiConfig, applyMinimaxApiProviderConfig } from "./onboard.js";
describe("minimax onboard", () => {

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "minimax",

View file

@ -1,3 +1,3 @@
import { describeMinimaxProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js";
import { describeMinimaxProviderDiscoveryContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeMinimaxProviderDiscoveryContract(() => import("./index.js"));

View file

@ -1,8 +1,8 @@
import { describe, expect, it } from "vitest";
import {
expectProviderOnboardMergedLegacyConfig,
expectProviderOnboardPrimaryAndFallbacks,
} from "../../test/helpers/plugins/provider-onboard.js";
} from "openclaw/plugin-sdk/provider-test-contracts";
import { describe, expect, it } from "vitest";
import { buildMistralModelDefinition as buildBundledMistralModelDefinition } from "./model-definitions.js";
import {
applyMistralConfig,

View file

@ -1,8 +1,8 @@
import fs from "node:fs";
import type { Context, Model } from "@mariozechner/pi-ai";
import { createCapturedThinkingConfigStream } from "openclaw/plugin-sdk/provider-test-contracts";
import { registerSingleProviderPlugin } from "openclaw/plugin-sdk/testing";
import { describe, expect, it } from "vitest";
import { createCapturedThinkingConfigStream } from "../../test/helpers/plugins/stream-hooks.js";
import plugin from "./index.js";
import { createKimiWebSearchProvider } from "./src/kimi-web-search-provider.js";

View file

@ -1,8 +1,8 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import {
createDirectoryTestRuntime,
expectDirectorySurface,
} from "../../../test/helpers/plugins/directory.js";
} from "openclaw/plugin-sdk/channel-test-helpers";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig, RuntimeEnv } from "../runtime-api.js";
import { msteamsDirectoryAdapter } from "./directory.js";
import { resolveMSTeamsOutboundSessionRoute } from "./session-route.js";

View file

@ -1,10 +1,10 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js";
import { createStartAccountContext } from "openclaw/plugin-sdk/channel-test-helpers";
import {
expectStopPendingUntilAbort,
startAccountAndTrackLifecycle,
waitForStartedMocks,
} from "../../../test/helpers/plugins/start-account-lifecycle.js";
} from "openclaw/plugin-sdk/channel-test-helpers";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { ResolvedNextcloudTalkAccount } from "./accounts.js";
const hoisted = vi.hoisted(() => ({

View file

@ -1,8 +1,8 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
createSendCfgThreadingRuntime,
expectProvidedCfgSkipsRuntimeLoad,
} from "../../../test/helpers/plugins/send-config.js";
} from "openclaw/plugin-sdk/channel-test-helpers";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const hoisted = vi.hoisted(() => ({
loadConfig: vi.fn(),

View file

@ -1,5 +1,5 @@
import { createStartAccountContext } from "openclaw/plugin-sdk/channel-test-helpers";
import { afterEach, describe, expect, it, vi } from "vitest";
import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js";
import type { PluginRuntime } from "../runtime-api.js";
import { startNostrGatewayAccount } from "./gateway.js";
import { setNostrRuntime } from "./runtime.js";

View file

@ -1,6 +1,6 @@
import { createStartAccountContext } from "openclaw/plugin-sdk/channel-test-helpers";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
import { afterEach, describe, expect, it, vi } from "vitest";
import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js";
import type { PluginRuntime } from "../runtime-api.js";
import { nostrOutboundAdapter, startNostrGatewayAccount } from "./gateway.js";
import { setNostrRuntime } from "./runtime.js";

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "ollama",

View file

@ -1,5 +1,5 @@
import { pluginRegistrationContractCases } from "../../test/helpers/plugins/plugin-registration-contract-cases.js";
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { pluginRegistrationContractCases } from "openclaw/plugin-sdk/plugin-test-contracts";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
...pluginRegistrationContractCases.openai,

View file

@ -1,3 +1,3 @@
import { describeOpenAICodexProviderAuthContract } from "../../test/helpers/plugins/provider-auth-contract.js";
import { describeOpenAICodexProviderAuthContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeOpenAICodexProviderAuthContract(() => import("./index.js"));

View file

@ -1,3 +1,3 @@
import { describeOpenAIProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
import { describeOpenAIProviderRuntimeContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeOpenAIProviderRuntimeContract(() => import("./index.js"));

View file

@ -1,5 +1,3 @@
import { registerProviderPlugin, requireRegisteredProvider } from "openclaw/plugin-sdk/testing";
import { beforeEach, describe, it, vi } from "vitest";
import {
expectAugmentedCodexCatalog,
expectedAugmentedOpenaiCodexCatalogEntriesWithGpt55,
@ -7,8 +5,10 @@ import {
expectCodexMissingAuthHint,
importProviderRuntimeCatalogModule,
loadBundledPluginPublicSurface,
} from "../../../test/helpers/plugins/provider-catalog.js";
import type { ProviderPlugin } from "../../../test/helpers/plugins/provider-catalog.js";
} from "openclaw/plugin-sdk/provider-test-contracts";
import type { ProviderPlugin } from "openclaw/plugin-sdk/provider-test-contracts";
import { registerProviderPlugin, requireRegisteredProvider } from "openclaw/plugin-sdk/testing";
import { beforeEach, describe, it, vi } from "vitest";
const PROVIDER_CATALOG_CONTRACT_TIMEOUT_MS = 300_000;

View file

@ -1,5 +1,5 @@
import { expectProviderOnboardPrimaryAndFallbacks } from "openclaw/plugin-sdk/provider-test-contracts";
import { describe, expect, it } from "vitest";
import { expectProviderOnboardPrimaryAndFallbacks } from "../../test/helpers/plugins/provider-onboard.js";
import { applyOpencodeGoConfig, applyOpencodeGoProviderConfig } from "./onboard.js";
const MODEL_REF = "opencode-go/kimi-k2.6";

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "opencode-go",

View file

@ -1,8 +1,8 @@
import { describe, it } from "vitest";
import {
expectProviderOnboardAllowlistAlias,
expectProviderOnboardPrimaryAndFallbacks,
} from "../../test/helpers/plugins/provider-onboard.js";
} from "openclaw/plugin-sdk/provider-test-contracts";
import { describe, it } from "vitest";
import { applyOpencodeZenConfig, applyOpencodeZenProviderConfig } from "./onboard.js";
const MODEL_REF = "opencode/claude-opus-4-6";

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "opencode",

View file

@ -1,8 +1,8 @@
import { describe, it } from "vitest";
import {
expectProviderOnboardAllowlistAlias,
expectProviderOnboardPrimaryAndFallbacks,
} from "../../test/helpers/plugins/provider-onboard.js";
} from "openclaw/plugin-sdk/provider-test-contracts";
import { describe, it } from "vitest";
import {
applyOpenrouterConfig,
applyOpenrouterProviderConfig,

View file

@ -1,3 +1,3 @@
import { describeOpenRouterProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
import { describeOpenRouterProviderRuntimeContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeOpenRouterProviderRuntimeContract(() => import("./index.js"));

View file

@ -1,3 +1,4 @@
import { createStartAccountContext } from "openclaw/plugin-sdk/channel-test-helpers";
import type { PluginRuntime } from "openclaw/plugin-sdk/core";
import {
createTestRegistry,
@ -6,7 +7,6 @@ import {
} from "openclaw/plugin-sdk/testing";
import { extractToolPayload } from "openclaw/plugin-sdk/tool-payload";
import { afterEach, describe, expect, it } from "vitest";
import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js";
import { createQaBusState, startQaBusServer } from "../../qa-lab/bus-api.js";
import { qaChannelPlugin, setQaChannelRuntime } from "../api.js";

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "qwen",

View file

@ -1,3 +1,3 @@
import { describeModelStudioProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js";
import { describeModelStudioProviderDiscoveryContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeModelStudioProviderDiscoveryContract(() => import("./index.js"));

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "runway",

View file

@ -1,5 +1,5 @@
import { fileURLToPath } from "node:url";
import { describeSglangProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js";
import { describeSglangProviderDiscoveryContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeSglangProviderDiscoveryContract({
load: () => import("./index.js"),

View file

@ -1,4 +1,3 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
addTestHook,
createEmptyPluginRegistry,
@ -10,7 +9,8 @@ import {
resetGlobalHookRunner,
setActivePluginRegistry,
type PluginHookRegistration,
} from "../../../test/helpers/plugins/outbound-delivery.js";
} from "openclaw/plugin-sdk/channel-test-helpers";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { slackOutbound } from "./outbound-adapter.js";
import type { OpenClawConfig } from "./runtime-api.js";

View file

@ -1,8 +1,8 @@
import { describe, expect, it } from "vitest";
import {
expectProviderOnboardMergedLegacyConfig,
expectProviderOnboardPrimaryModel,
} from "../../test/helpers/plugins/provider-onboard.js";
} from "openclaw/plugin-sdk/provider-test-contracts";
import { describe, expect, it } from "vitest";
import { SYNTHETIC_DEFAULT_MODEL_REF as SYNTHETIC_DEFAULT_MODEL_REF_PUBLIC } from "./api.js";
import {
applySyntheticConfig,

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "together",

View file

@ -1,3 +1,3 @@
import { describeVeniceProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
import { describeVeniceProviderRuntimeContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeVeniceProviderRuntimeContract(() => import("./index.js"));

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "vydra",

View file

@ -1,5 +1,5 @@
import { createDirectoryTestRuntime } from "openclaw/plugin-sdk/channel-test-helpers";
import { describe, expect, it } from "vitest";
import { createDirectoryTestRuntime } from "../../../test/helpers/plugins/directory.ts";
import {
listWhatsAppDirectoryGroupsFromConfig,
listWhatsAppDirectoryPeersFromConfig,

View file

@ -2,12 +2,12 @@ import {
resolveAgentModelFallbackValues,
resolveAgentModelPrimaryValue,
} from "openclaw/plugin-sdk/provider-onboard";
import { describe, expect, it } from "vitest";
import {
createConfigWithFallbacks,
createLegacyProviderConfig,
EXPECTED_FALLBACKS,
} from "../../test/helpers/plugins/onboard-config.js";
} from "openclaw/plugin-sdk/provider-test-contracts";
import { describe, expect, it } from "vitest";
import { applyXaiConfig, applyXaiProviderConfig, XAI_DEFAULT_MODEL_REF } from "./onboard.js";
describe("xai onboard", () => {

View file

@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "openclaw/plugin-sdk/plugin-test-contracts";
describePluginRegistrationContract({
pluginId: "xai",

View file

@ -1,8 +1,8 @@
import { describe, expect, it } from "vitest";
import {
expectProviderOnboardMergedLegacyConfig,
expectProviderOnboardPrimaryModel,
} from "../../test/helpers/plugins/provider-onboard.js";
} from "openclaw/plugin-sdk/provider-test-contracts";
import { describe, expect, it } from "vitest";
import { applyXiaomiConfig, applyXiaomiProviderConfig } from "./onboard.js";
describe("xiaomi onboard", () => {

View file

@ -1,6 +1,6 @@
import { resolveAgentModelPrimaryValue } from "openclaw/plugin-sdk/provider-onboard";
import { expectProviderOnboardPreservesPrimary } from "openclaw/plugin-sdk/provider-test-contracts";
import { describe, expect, it } from "vitest";
import { expectProviderOnboardPreservesPrimary } from "../../test/helpers/plugins/provider-onboard.js";
import { ZAI_CODING_CN_BASE_URL, ZAI_GLOBAL_BASE_URL } from "./model-definitions.js";
import { applyZaiConfig, applyZaiProviderConfig } from "./onboard.js";

View file

@ -1,3 +1,3 @@
import { describeZAIProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
import { describeZAIProviderRuntimeContract } from "openclaw/plugin-sdk/provider-test-contracts";
describeZAIProviderRuntimeContract(() => import("./index.js"));

View file

@ -1,6 +1,6 @@
import path from "node:path";
import { loadRuntimeApiExportTypesViaJiti } from "openclaw/plugin-sdk/plugin-test-contracts";
import { describe, expect, it } from "vitest";
import { loadRuntimeApiExportTypesViaJiti } from "../../test/helpers/plugins/jiti-runtime-api.js";
describe("zalo runtime api", () => {
it("loads the narrow runtime api without reentering setup surfaces", () => {

View file

@ -1,8 +1,8 @@
import { describe, expect, it } from "vitest";
import {
createDirectoryTestRuntime,
expectDirectorySurface,
} from "../../../test/helpers/plugins/directory.js";
} from "openclaw/plugin-sdk/channel-test-helpers";
import { describe, expect, it } from "vitest";
import type { OpenClawConfig, RuntimeEnv } from "../runtime-api.js";
import { zaloPlugin } from "./channel.js";

View file

@ -1,10 +1,10 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import {
expectLifecyclePatch,
expectPendingUntilAbort,
startAccountAndTrackLifecycle,
waitForStartedMocks,
} from "../../../test/helpers/plugins/start-account-lifecycle.js";
} from "openclaw/plugin-sdk/channel-test-helpers";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { ResolvedZaloAccount } from "./accounts.js";
const hoisted = vi.hoisted(() => ({

View file

@ -1,5 +1,5 @@
import { expectOpenDmPolicyConfigIssue } from "openclaw/plugin-sdk/channel-test-helpers";
import { describe, it } from "vitest";
import { expectOpenDmPolicyConfigIssue } from "../../../test/helpers/plugins/status-issues.js";
import { collectZaloStatusIssues } from "./status-issues.js";
describe("collectZaloStatusIssues", () => {

View file

@ -1,10 +1,10 @@
import { createPluginRuntimeMock } from "openclaw/plugin-sdk/channel-test-helpers";
import {
createEmptyPluginRegistry,
createRuntimeEnv,
setActivePluginRegistry,
} from "openclaw/plugin-sdk/testing";
import { vi, type Mock } from "vitest";
import { createPluginRuntimeMock } from "../../../test/helpers/plugins/plugin-runtime-mock.js";
import type { OpenClawConfig } from "../runtime-api.js";
import type { ResolvedZaloAccount } from "../src/types.js";

View file

@ -1,5 +1,5 @@
import { expectOpenDmPolicyConfigIssue } from "openclaw/plugin-sdk/channel-test-helpers";
import { describe, expect, it } from "vitest";
import { expectOpenDmPolicyConfigIssue } from "../../../test/helpers/plugins/status-issues.js";
import { collectZalouserStatusIssues } from "./status-issues.js";
describe("collectZalouserStatusIssues", () => {

View file

@ -502,10 +502,22 @@
"types": "./dist/plugin-sdk/lazy-runtime.d.ts",
"default": "./dist/plugin-sdk/lazy-runtime.js"
},
"./plugin-sdk/channel-test-helpers": {
"types": "./dist/plugin-sdk/channel-test-helpers.d.ts",
"default": "./dist/plugin-sdk/channel-test-helpers.js"
},
"./plugin-sdk/plugin-test-api": {
"types": "./dist/plugin-sdk/plugin-test-api.d.ts",
"default": "./dist/plugin-sdk/plugin-test-api.js"
},
"./plugin-sdk/plugin-test-contracts": {
"types": "./dist/plugin-sdk/plugin-test-contracts.d.ts",
"default": "./dist/plugin-sdk/plugin-test-contracts.js"
},
"./plugin-sdk/provider-test-contracts": {
"types": "./dist/plugin-sdk/provider-test-contracts.d.ts",
"default": "./dist/plugin-sdk/provider-test-contracts.js"
},
"./plugin-sdk/testing": {
"types": "./dist/plugin-sdk/testing.d.ts",
"default": "./dist/plugin-sdk/testing.js"

View file

@ -19,15 +19,19 @@ const FORBIDDEN_PATTERNS: Array<{ pattern: RegExp; hint: string }> = [
},
{
pattern: /["'](?:\.\.\/)+(?:test-utils\/)[^"']+["']/,
hint: "Use test/helpers/plugins/* for repo-only bundled extension test helpers.",
hint: "Use a documented openclaw/plugin-sdk test subpath for bundled extension test helpers.",
},
{
pattern: /["'](?:\.\.\/)+(?:test\/helpers\/plugins\/)[^"']+["']/,
hint: "Use a documented openclaw/plugin-sdk test subpath instead of repo-only plugin helper bridges.",
},
{
pattern: /["'](?:\.\.\/)+(?:src\/test-utils\/)[^"']+["']/,
hint: "Use test/helpers/plugins/* for repo-only helpers, or openclaw/plugin-sdk/testing for public surfaces.",
hint: "Use a documented openclaw/plugin-sdk test subpath for public surfaces.",
},
{
pattern: /["'](?:\.\.\/)+(?:src\/plugins\/types\.js)["']/,
hint: "Use public plugin-sdk/core types or test/helpers/plugins/* instead.",
hint: "Use public plugin-sdk/core types or documented plugin-sdk test helpers instead.",
},
{
pattern: /["'](?:\.\.\/)+(?:src\/channels\/plugins\/contracts\/test-helpers\.js)["']/,
@ -49,17 +53,46 @@ const RETIRED_EXTENSION_TEST_HELPER_BRIDGE_FILES = [
"test/helpers/plugins/frozen-time.ts",
"test/helpers/plugins/media-understanding.ts",
"test/helpers/plugins/mock-http-response.ts",
"test/helpers/plugins/contracts-testkit.ts",
"test/helpers/plugins/direct-smoke.ts",
"test/helpers/plugins/directory.ts",
"test/helpers/plugins/jiti-runtime-api.ts",
"test/helpers/plugins/onboard-config.ts",
"test/helpers/plugins/outbound-delivery.ts",
"test/helpers/plugins/package-manifest-contract.ts",
"test/helpers/plugins/plugin-api.ts",
"test/helpers/plugins/plugin-registration-contract-cases.ts",
"test/helpers/plugins/plugin-registration-contract.ts",
"test/helpers/plugins/plugin-registration.ts",
"test/helpers/plugins/plugin-runtime-mock.ts",
"test/helpers/plugins/plugin-registry.ts",
"test/helpers/plugins/provider-auth-contract.ts",
"test/helpers/plugins/provider-catalog.ts",
"test/helpers/plugins/provider-contract-suites.ts",
"test/helpers/plugins/provider-contract.ts",
"test/helpers/plugins/provider-discovery-contract.ts",
"test/helpers/plugins/provider-onboard.ts",
"test/helpers/plugins/provider-registration.ts",
"test/helpers/plugins/provider-runtime-contract.ts",
"test/helpers/plugins/provider-usage-fetch.ts",
"test/helpers/plugins/provider-wizard-contract-suites.ts",
"test/helpers/plugins/public-artifacts.ts",
"test/helpers/plugins/public-surface-loader.ts",
"test/helpers/plugins/runtime-taskflow.ts",
"test/helpers/plugins/runtime-env.ts",
"test/helpers/plugins/send-config.ts",
"test/helpers/plugins/setup-wizard.ts",
"test/helpers/plugins/start-account-context.ts",
"test/helpers/plugins/start-account-lifecycle.ts",
"test/helpers/plugins/status-issues.ts",
"test/helpers/plugins/stream-hooks.ts",
"test/helpers/plugins/subagent-hooks.ts",
"test/helpers/plugins/temp-dir.ts",
"test/helpers/plugins/temp-home.ts",
"test/helpers/plugins/tts-contract-suites.ts",
"test/helpers/plugins/typed-cases.ts",
"test/helpers/plugins/web-fetch-provider-contract.ts",
"test/helpers/plugins/web-search-provider-contract.ts",
];
function isExtensionTestFile(filePath: string): boolean {

View file

@ -51,7 +51,7 @@ const FULL_INSTALL_SMOKE_SCOPE_RE =
/^(Dockerfile$|\.npmrc$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|scripts\/ci-changed-scope\.mjs$|scripts\/install\.sh$|scripts\/test-install-sh-docker\.sh$|scripts\/docker\/|scripts\/e2e\/(?:Dockerfile(?:\.qr-import)?|qr-import-docker\.sh|bun-global-install-smoke\.sh)$|\.github\/workflows\/install-smoke\.yml$|\.github\/actions\/setup-node-env\/action\.yml$)/;
const FAST_INSTALL_SMOKE_RUNTIME_SCOPE_RE = /^src\/(?:channels|gateway|plugin-sdk|plugins)\//;
const NODE_FAST_PLUGIN_CONTRACT_SCOPE_RE =
/^(src\/plugins\/contracts\/(?:inventory\/bundled-capability-metadata|registry)\.ts$|test\/helpers\/plugins\/tts-contract-suites\.ts$|scripts\/test-projects(?:\.test-support)?\.mjs$|test\/scripts\/test-projects\.test\.ts$)/;
/^(src\/plugins\/contracts\/(?:inventory\/bundled-capability-metadata|registry|tts-contract-suites)\.ts$|scripts\/test-projects(?:\.test-support)?\.mjs$|test\/scripts\/test-projects\.test\.ts$)/;
const NODE_FAST_CI_ROUTING_SCOPE_RE =
/^(scripts\/ci-changed-scope\.mjs$|src\/commands\/status\.scan-result\.test\.ts$|src\/scripts\/ci-changed-scope\.test\.ts$|\.github\/workflows\/ci\.yml$)/;
const NODE_FAST_SCOPE_RE = new RegExp(

View file

@ -41,6 +41,12 @@ export const pluginSdkDocMetadata = {
"plugin-entry": {
category: "core",
},
"plugin-test-api": {
category: "utilities",
},
"plugin-test-contracts": {
category: "utilities",
},
"channel-actions": {
category: "channel",
},
@ -53,6 +59,9 @@ export const pluginSdkDocMetadata = {
"channel-contract": {
category: "channel",
},
"channel-contract-testing": {
category: "channel",
},
"channel-pairing": {
category: "channel",
},
@ -98,6 +107,12 @@ export const pluginSdkDocMetadata = {
testing: {
category: "utilities",
},
"channel-test-helpers": {
category: "utilities",
},
"provider-test-contracts": {
category: "utilities",
},
} as const satisfies Record<string, PluginSdkDocMetadata>;
export type PluginSdkDocEntrypoint = keyof typeof pluginSdkDocMetadata;

View file

@ -109,7 +109,10 @@
"acp-binding-runtime",
"acp-binding-resolve-runtime",
"lazy-runtime",
"channel-test-helpers",
"plugin-test-api",
"plugin-test-contracts",
"provider-test-contracts",
"testing",
"temp-path",
"logging-core",

View file

@ -219,7 +219,7 @@ const BROAD_CHANGED_FALLBACK_PATTERNS = [
];
const PRECISE_SOURCE_TEST_TARGETS = new Map([
[
"test/helpers/plugins/tts-contract-suites.ts",
"src/plugins/contracts/tts-contract-suites.ts",
[
"src/plugins/contracts/core-extension-facade-boundary.test.ts",
"src/plugins/contracts/tts.contract.test.ts",

View file

@ -1,9 +1,9 @@
import { readdirSync, readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES } from "openclaw/plugin-sdk/plugin-test-contracts";
import { describe, expect, it } from "vitest";
import { classifyBundledExtensionSourcePath } from "../../../../scripts/lib/extension-source-classifier.mjs";
import { GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES } from "../../../../test/helpers/plugins/public-artifacts.js";
import { loadPluginManifestRegistry } from "../../../plugins/manifest-registry.js";
const ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");

View file

@ -0,0 +1,33 @@
export { createDirectoryTestRuntime, expectDirectorySurface } from "./test-helpers/directory.js";
export {
addTestHook,
createEmptyPluginRegistry,
createOutboundTestPlugin,
createTestRegistry,
deliverOutboundPayloads,
initializeGlobalHookRunner,
releasePinnedPluginChannelRegistry,
resetGlobalHookRunner,
setActivePluginRegistry,
type PluginHookRegistration,
} from "./test-helpers/outbound-delivery.js";
export { createPluginRuntimeMock } from "./test-helpers/plugin-runtime-mock.js";
export {
createSendCfgThreadingRuntime,
expectProvidedCfgSkipsRuntimeLoad,
expectRuntimeCfgFallback,
} from "./test-helpers/send-config.js";
export { createStartAccountContext } from "./test-helpers/start-account-context.js";
export {
abortStartedAccount,
expectLifecyclePatch,
expectPendingUntilAbort,
expectStopPendingUntilAbort,
startAccountAndTrackLifecycle,
waitForStartedMocks,
} from "./test-helpers/start-account-lifecycle.js";
export { expectOpenDmPolicyConfigIssue } from "./test-helpers/status-issues.js";
export {
getRequiredHookHandler,
registerHookHandlersForTest,
} from "./test-helpers/subagent-hooks.js";

View file

@ -0,0 +1,24 @@
export {
assertNoImportTimeSideEffects,
createPluginRegistryFixture,
registerProviders,
registerTestPlugin,
registerVirtualTestPlugin,
requireProvider,
uniqueSortedStrings,
} from "./test-helpers/contracts-testkit.js";
export { runDirectImportSmoke } from "./test-helpers/direct-smoke.js";
export { loadRuntimeApiExportTypesViaJiti } from "./test-helpers/jiti-runtime-api.js";
export { describePackageManifestContract } from "./test-helpers/package-manifest-contract.js";
export { pluginRegistrationContractCases } from "./test-helpers/plugin-registration-contract-cases.js";
export { describePluginRegistrationContract } from "./test-helpers/plugin-registration-contract.js";
export {
GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES,
BUNDLED_RUNTIME_SIDECAR_BASENAMES,
getPublicArtifactBasename,
} from "./test-helpers/public-artifacts.js";
export {
loadBundledPluginPublicSurface,
loadBundledPluginPublicSurfaceSync,
resolveWorkspacePackagePublicModuleUrl,
} from "./test-helpers/public-surface-loader.js";

View file

@ -0,0 +1,60 @@
export {
describeGithubCopilotProviderAuthContract,
describeOpenAICodexProviderAuthContract,
type ProviderAuthContractPluginLoader,
} from "./test-helpers/provider-auth-contract.js";
export {
expectAugmentedCodexCatalog,
expectedAugmentedOpenaiCodexCatalogEntriesWithGpt55,
expectCodexBuiltInSuppression,
expectCodexMissingAuthHint,
importProviderRuntimeCatalogModule,
loadBundledPluginPublicSurface,
loadBundledPluginPublicSurfaceSync,
type ProviderPlugin,
} from "./test-helpers/provider-catalog.js";
export { describeProviderContracts } from "./test-helpers/provider-contract.js";
export {
installProviderPluginContractSuite,
installWebFetchProviderContractSuite,
installWebSearchProviderContractSuite,
} from "./test-helpers/provider-contract-suites.js";
export {
describeCloudflareAiGatewayProviderDiscoveryContract,
describeGithubCopilotProviderDiscoveryContract,
describeMinimaxProviderDiscoveryContract,
describeModelStudioProviderDiscoveryContract,
describeSglangProviderDiscoveryContract,
describeVllmProviderDiscoveryContract,
type ProviderDiscoveryContractPluginLoader,
} from "./test-helpers/provider-discovery-contract.js";
export {
EXPECTED_FALLBACKS,
createConfigWithFallbacks,
createLegacyProviderConfig,
} from "./test-helpers/onboard-config.js";
export {
expectProviderOnboardAllowlistAlias,
expectProviderOnboardMergedLegacyConfig,
expectProviderOnboardPreservesPrimary,
expectProviderOnboardPrimaryAndFallbacks,
expectProviderOnboardPrimaryModel,
} from "./test-helpers/provider-onboard.js";
export {
describeAnthropicProviderRuntimeContract,
describeGithubCopilotProviderRuntimeContract,
describeGoogleProviderRuntimeContract,
describeOpenAIProviderRuntimeContract,
describeOpenRouterProviderRuntimeContract,
describeVeniceProviderRuntimeContract,
describeZAIProviderRuntimeContract,
type ProviderRuntimeContractPluginLoader,
} from "./test-helpers/provider-runtime-contract.js";
export {
describeProviderWizardChoiceResolutionContract,
describeProviderWizardModelPickerContract,
describeProviderWizardSetupOptionsContract,
} from "./test-helpers/provider-wizard-contract-suites.js";
export { createCapturedThinkingConfigStream } from "./test-helpers/stream-hooks.js";
export { describeWebFetchProviderContracts } from "./test-helpers/web-fetch-provider-contract.js";
export { describeWebSearchProviderContracts } from "./test-helpers/web-search-provider-contract.js";

View file

@ -1,4 +1,4 @@
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
import type { OpenClawPluginApi } from "../plugin-entry.js";
import {
createPluginRecord,
createPluginRegistry,
@ -7,7 +7,7 @@ import {
type OpenClawConfig,
type PluginRecord,
type PluginRuntime,
} from "openclaw/plugin-sdk/testing";
} from "../testing.js";
export { registerProviders, requireProvider };

View file

@ -1,4 +1,4 @@
import type { ChannelDirectoryAdapter } from "openclaw/plugin-sdk/channel-contract";
import type { ChannelDirectoryAdapter } from "../channel-contract.js";
type DirectorySurface = {
listPeers: NonNullable<ChannelDirectoryAdapter["listPeers"]>;

View file

@ -1,5 +1,5 @@
import type { ModelApi } from "openclaw/plugin-sdk/provider-model-shared";
import type { OpenClawConfig } from "openclaw/plugin-sdk/testing";
import type { ModelApi } from "../provider-model-shared.js";
import type { OpenClawConfig } from "../testing.js";
export const EXPECTED_FALLBACKS = ["anthropic/claude-opus-4-5"] as const;

View file

@ -9,4 +9,4 @@ export {
resetGlobalHookRunner,
setActivePluginRegistry,
type PluginHookRegistration,
} from "openclaw/plugin-sdk/testing";
} from "../testing.js";

View file

@ -1,12 +1,7 @@
import fs from "node:fs";
import path from "node:path";
import {
isAtLeast,
parseMinHostVersionRequirement,
parseSemver,
} from "openclaw/plugin-sdk/testing";
import { describe, expect, it } from "vitest";
import { bundledPluginFile } from "../bundled-plugin-paths.js";
import { isAtLeast, parseMinHostVersionRequirement, parseSemver } from "../testing.js";
type PackageManifest = {
dependencies?: Record<string, string>;
@ -31,6 +26,10 @@ function readJson<T>(relativePath: string): T {
return JSON.parse(fs.readFileSync(absolutePath, "utf8")) as T;
}
function bundledPluginFile(pluginId: string, relativePath: string): string {
return `extensions/${pluginId}/${relativePath}`;
}
export function describePackageManifestContract(params: PackageManifestContractParams) {
const packagePath = bundledPluginFile(params.pluginId, "package.json");

View file

@ -1,8 +1,5 @@
import {
loadPluginManifestRegistry,
pluginRegistrationContractRegistry,
} from "openclaw/plugin-sdk/testing";
import { describe, expect, it } from "vitest";
import { loadPluginManifestRegistry, pluginRegistrationContractRegistry } from "../testing.js";
type PluginRegistrationContractParams = {
pluginId: string;

View file

@ -1,15 +1,15 @@
import { vi } from "vitest";
import {
implicitMentionKindWhen,
resolveInboundMentionDecision,
} from "openclaw/plugin-sdk/channel-mention-gating";
} from "../channel-mention-gating.js";
import {
createAckReactionHandle,
removeAckReactionAfterReply,
removeAckReactionHandleAfterReply,
shouldAckReaction,
} from "openclaw/plugin-sdk/testing";
import type { PluginRuntime } from "openclaw/plugin-sdk/testing";
import { vi } from "vitest";
} from "../testing.js";
import type { PluginRuntime } from "../testing.js";
const DEFAULT_PROVIDER = "openai";
const DEFAULT_MODEL = "gpt-5.5";

View file

@ -1,15 +1,12 @@
import {
clearRuntimeAuthProfileStoreSnapshots,
type AuthProfileStore,
} from "openclaw/plugin-sdk/agent-runtime";
import { createNonExitingRuntime } from "openclaw/plugin-sdk/runtime";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { clearRuntimeAuthProfileStoreSnapshots, type AuthProfileStore } from "../agent-runtime.js";
import { createNonExitingRuntime } from "../runtime.js";
import type {
WizardMultiSelectParams,
WizardPrompter,
WizardProgress,
WizardSelectParams,
} from "openclaw/plugin-sdk/setup";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
} from "../setup.js";
import { registerProviders, requireProvider } from "./contracts-testkit.js";
type LoginOpenAICodexOAuth =
@ -30,28 +27,6 @@ export type ProviderAuthContractPluginLoader = () => Promise<{
default: Parameters<typeof registerProviders>[0];
}>;
vi.mock("openclaw/plugin-sdk/provider-auth-login", async () => {
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/provider-auth-login")>(
"openclaw/plugin-sdk/provider-auth-login",
);
return {
...actual,
loginOpenAICodexOAuth: loginOpenAICodexOAuthMock,
githubCopilotLoginCommand: githubCopilotLoginCommandMock,
};
});
vi.mock("openclaw/plugin-sdk/provider-auth", async () => {
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/provider-auth")>(
"openclaw/plugin-sdk/provider-auth",
);
return {
...actual,
ensureAuthProfileStore: ensureAuthProfileStoreMock,
listProfilesForProvider: listProfilesForProviderMock,
};
});
function buildPrompter(): WizardPrompter {
const progress: WizardProgress = {
update() {},
@ -131,6 +106,26 @@ function buildOpenAICodexOAuthResult(params: {
function installSharedAuthProfileStoreHooks(state: { authStore: AuthProfileStore }) {
beforeEach(() => {
vi.doMock("openclaw/plugin-sdk/provider-auth-login", async () => {
const actual = await vi.importActual<
typeof import("openclaw/plugin-sdk/provider-auth-login")
>("openclaw/plugin-sdk/provider-auth-login");
return {
...actual,
loginOpenAICodexOAuth: loginOpenAICodexOAuthMock,
githubCopilotLoginCommand: githubCopilotLoginCommandMock,
};
});
vi.doMock("openclaw/plugin-sdk/provider-auth", async () => {
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/provider-auth")>(
"openclaw/plugin-sdk/provider-auth",
);
return {
...actual,
ensureAuthProfileStore: ensureAuthProfileStoreMock,
listProfilesForProvider: listProfilesForProviderMock,
};
});
state.authStore = { version: 1, profiles: {} };
ensureAuthProfileStoreMock.mockReset();
ensureAuthProfileStoreMock.mockImplementation(() => state.authStore);

View file

@ -3,8 +3,8 @@ export {
expectedAugmentedOpenaiCodexCatalogEntriesWithGpt55,
expectCodexBuiltInSuppression,
expectCodexMissingAuthHint,
} from "openclaw/plugin-sdk/testing";
export type { ProviderPlugin } from "openclaw/plugin-sdk/provider-model-shared";
} from "../testing.js";
export type { ProviderPlugin } from "../provider-model-shared.js";
export {
loadBundledPluginPublicSurface,
loadBundledPluginPublicSurfaceSync,

View file

@ -1,8 +1,8 @@
import type { ProviderPlugin } from "openclaw/plugin-sdk/provider-model-shared";
import type { WebFetchProviderPlugin } from "openclaw/plugin-sdk/provider-web-fetch-contract";
import type { WebSearchProviderPlugin } from "openclaw/plugin-sdk/provider-web-search-contract";
import type { OpenClawConfig } from "openclaw/plugin-sdk/testing";
import { expect, it } from "vitest";
import type { ProviderPlugin } from "../provider-model-shared.js";
import type { WebFetchProviderPlugin } from "../provider-web-fetch-contract.js";
import type { WebSearchProviderPlugin } from "../provider-web-search-contract.js";
import type { OpenClawConfig } from "../testing.js";
type Lazy<T> = T | (() => T);

View file

@ -1,10 +1,10 @@
import type { ProviderPlugin } from "openclaw/plugin-sdk/provider-model-shared";
import { describe, expect, it } from "vitest";
import type { ProviderPlugin } from "../provider-model-shared.js";
import {
providerContractLoadError,
resolveBundledExplicitProviderContractsFromPublicArtifacts,
resolveProviderContractProvidersForPluginIds,
} from "openclaw/plugin-sdk/testing";
import { describe, expect, it } from "vitest";
} from "../testing.js";
import { installProviderPluginContractSuite } from "./provider-contract-suites.js";
type ProviderContractEntry = {

View file

@ -1,10 +1,10 @@
import type { AuthProfileStore, OpenClawConfig } from "openclaw/plugin-sdk/provider-auth";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { AuthProfileStore, OpenClawConfig } from "../provider-auth.js";
import {
registerProviderPlugins as registerProviders,
requireRegisteredProvider as requireProvider,
runProviderCatalog,
} from "openclaw/plugin-sdk/testing";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
} from "../testing.js";
const resolveCopilotApiTokenMock = vi.hoisted(() => vi.fn());
const buildVllmProviderMock = vi.hoisted(() => vi.fn());

Some files were not shown because too many files have changed in this diff Show more