refactor: split plugin testing seam from bundled extension helpers

This commit is contained in:
Peter Steinberger
2026-03-17 00:59:16 -07:00
parent 527a1919ea
commit f9588da3e0
101 changed files with 144 additions and 105 deletions

View File

@@ -34,6 +34,7 @@ Docs: https://docs.openclaw.ai
- Skills/prompt budget: preserve all registered skills via a compact catalog fallback before dropping entries when the full prompt format exceeds `maxSkillsPromptChars`. (#47553) Thanks @snese.
- Plugins/bundles: make enabled bundle MCP servers expose runnable tools in embedded Pi, and default relative bundle MCP launches to the bundle root so marketplace bundles like Context7 work through Pi instead of stopping at config import.
- Scope message SecretRef resolution and harden doctor/status paths. (#48728) Thanks @joshavant.
- Plugins/testing: add a public `openclaw/plugin-sdk/testing` seam for plugin-author test helpers, and move bundled-extension-only test bridges out of `extensions/` into private repo test helpers.
### Breaking

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { registerSingleProviderPlugin } from "../test-utils/plugin-registration.js";
import { registerSingleProviderPlugin } from "../../test/helpers/extensions/plugin-registration.js";
import amazonBedrockPlugin from "./index.js";
describe("amazon-bedrock provider plugin", () => {

View File

@@ -2,7 +2,7 @@ import { EventEmitter } from "node:events";
import type { IncomingMessage, ServerResponse } from "node:http";
import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/bluebubbles";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import type { ResolvedBlueBubblesAccount } from "./accounts.js";
import { fetchBlueBubblesHistory } from "./history.js";
import { resetBlueBubblesSelfChatCache } from "./monitor-self-chat-cache.js";

View File

@@ -2,7 +2,7 @@ import { EventEmitter } from "node:events";
import type { IncomingMessage, ServerResponse } from "node:http";
import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/bluebubbles";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import type { ResolvedBlueBubblesAccount } from "./accounts.js";
import { fetchBlueBubblesHistory } from "./history.js";
import {

View File

@@ -1,8 +1,8 @@
import type { IncomingMessage } from "node:http";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/diffs";
import { describe, expect, it, vi } from "vitest";
import { createMockServerResponse } from "../test-utils/mock-http-response.js";
import { createTestPluginApi } from "../test-utils/plugin-api.js";
import { createMockServerResponse } from "../../test/helpers/extensions/mock-http-response.js";
import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js";
import plugin from "./index.js";
describe("diffs plugin registration", () => {

View File

@@ -1,6 +1,6 @@
import type { IncomingMessage } from "node:http";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { createMockServerResponse } from "../../test-utils/mock-http-response.js";
import { createMockServerResponse } from "../../../test/helpers/extensions/mock-http-response.js";
import { createDiffsHttpHandler } from "./http.js";
import { DiffArtifactStore } from "./store.js";
import { createDiffStoreHarness } from "./test-helpers.js";

View File

@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
import path from "node:path";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/diffs";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createTestPluginApi } from "../../test-utils/plugin-api.js";
import { createTestPluginApi } from "../../../test/helpers/extensions/plugin-api.js";
import type { DiffScreenshotter } from "./browser.js";
import { DEFAULT_DIFFS_TOOL_DEFAULTS } from "./config.js";
import { DiffArtifactStore } from "./store.js";

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js";
import { fetchDiscord } from "./api.js";
import { jsonResponse } from "./test-http-helpers.js";

View File

@@ -1,5 +1,8 @@
import { describe, expect, it } from "vitest";
import { countLines, hasBalancedFences } from "../../test-utils/chunk-test-helpers.js";
import {
countLines,
hasBalancedFences,
} from "../../../test/helpers/extensions/chunk-test-helpers.js";
import { chunkDiscordText, chunkDiscordTextWithMode } from "./chunk.js";
describe("chunkDiscordText", () => {

View File

@@ -1,6 +1,6 @@
import { ChannelType, type Guild } from "@buape/carbon";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { typedCases } from "../../test-utils/typed-cases.js";
import { typedCases } from "../../../test/helpers/extensions/typed-cases.js";
import {
allowListMatches,
buildDiscordMediaPayload,

View File

@@ -1,4 +1,4 @@
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { vi } from "vitest";
export const sendMock: MockFn = vi.fn();

View File

@@ -1,4 +1,4 @@
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { vi } from "vitest";
export const preflightDiscordMessageMock: MockFn = vi.fn();

View File

@@ -5,7 +5,7 @@ import {
baseRuntime,
getProviderMonitorTestMocks,
resetDiscordProviderMonitorMocks,
} from "../../../test-utils/discord-provider.test-support.js";
} from "../../../../test/helpers/extensions/discord-provider.test-support.js";
const { createDiscordNativeCommandMock, clientHandleDeployRequestMock, monitorLifecycleMock } =
getProviderMonitorTestMocks();

View File

@@ -1 +1 @@
export * from "../../../test-utils/discord-provider.test-support.js";
export * from "../../../../test/helpers/extensions/discord-provider.test-support.js";

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js";
import { resolveDiscordChannelAllowlist } from "./resolve-channels.js";
import { jsonResponse, urlToString } from "./test-http-helpers.js";

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js";
import { resolveDiscordUserAllowlist } from "./resolve-users.js";
import { jsonResponse, urlToString } from "./test-http-helpers.js";

View File

@@ -1,4 +1,4 @@
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { vi } from "vitest";
type DiscordWebMediaMockFactoryResult = {

View File

@@ -1,6 +1,6 @@
import type { ClawdbotConfig, PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/feishu";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import type { FeishuMessageEvent } from "./bot.js";
import {
buildBroadcastSessionKey,

View File

@@ -5,7 +5,7 @@ import {
createInboundDebouncer,
resolveInboundDebounceMs,
} from "../../../src/auto-reply/inbound-debounce.js";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import { monitorSingleAccount } from "./monitor.account.js";
import { setFeishuRuntime } from "./runtime.js";
import type { ResolvedFeishuAccount } from "./types.js";

View File

@@ -5,7 +5,7 @@ import {
createInboundDebouncer,
resolveInboundDebounceMs,
} from "../../../src/auto-reply/inbound-debounce.js";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import { parseFeishuMessageEvent, type FeishuMessageEvent } from "./bot.js";
import * as dedup from "./dedup.js";
import { monitorSingleAccount } from "./monitor.account.js";

View File

@@ -1,5 +1,8 @@
import { describe, expect, it } from "vitest";
import { createProviderUsageFetch, makeResponse } from "../test-utils/provider-usage-fetch.js";
import {
createProviderUsageFetch,
makeResponse,
} from "../../test/helpers/extensions/provider-usage-fetch.js";
import { fetchCopilotUsage } from "./usage.js";
describe("fetchCopilotUsage", () => {

View File

@@ -4,7 +4,7 @@ import {
abortStartedAccount,
expectPendingUntilAbort,
startAccountAndTrackLifecycle,
} from "../../test-utils/start-account-lifecycle.js";
} from "../../../test/helpers/extensions/start-account-lifecycle.js";
import type { ResolvedGoogleChatAccount } from "./accounts.js";
const hoisted = vi.hoisted(() => ({

View File

@@ -4,7 +4,7 @@ import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/googlech
import { afterEach, describe, expect, it, vi } from "vitest";
import { createEmptyPluginRegistry } from "../../../src/plugins/registry.js";
import { setActivePluginRegistry } from "../../../src/plugins/runtime.js";
import { createMockServerResponse } from "../../test-utils/mock-http-response.js";
import { createMockServerResponse } from "../../../test/helpers/extensions/mock-http-response.js";
import type { ResolvedGoogleChatAccount } from "./accounts.js";
import { verifyGoogleChatRequest } from "./auth.js";
import { handleGoogleChatWebhookRequest, registerGoogleChatWebhookTarget } from "./monitor.js";

View File

@@ -1,8 +1,8 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/googlechat";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../../test/helpers/extensions/setup-wizard.js";
import { googlechatPlugin } from "./channel.js";
const googlechatConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({

View File

@@ -2,7 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import {
expectStopPendingUntilAbort,
startAccountAndTrackLifecycle,
} from "../../test-utils/start-account-lifecycle.js";
} from "../../../test/helpers/extensions/start-account-lifecycle.js";
import type { ResolvedIrcAccount } from "./accounts.js";
const hoisted = vi.hoisted(() => ({

View File

@@ -3,7 +3,7 @@ import {
createSendCfgThreadingRuntime,
expectProvidedCfgSkipsRuntimeLoad,
expectRuntimeCfgFallback,
} from "../../test-utils/send-config.js";
} from "../../../test/helpers/extensions/send-config.js";
import type { IrcClient } from "./client.js";
import type { CoreConfig } from "./types.js";

View File

@@ -1,8 +1,8 @@
import type { RuntimeEnv } from "openclaw/plugin-sdk/irc";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../../test/helpers/extensions/setup-wizard.js";
import { ircPlugin } from "./channel.js";
import type { CoreConfig } from "./types.js";

View File

@@ -1,6 +1,6 @@
import type { OpenClawConfig, PluginRuntime, ResolvedLineAccount } from "openclaw/plugin-sdk/line";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { linePlugin } from "./channel.js";
import { setLineRuntime } from "./runtime.js";

View File

@@ -6,7 +6,7 @@ import type {
ResolvedLineAccount,
} from "openclaw/plugin-sdk/line";
import { describe, expect, it, vi } from "vitest";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { linePlugin } from "./channel.js";
import { setLineRuntime } from "./runtime.js";

View File

@@ -6,8 +6,8 @@ import {
resolveDefaultLineAccountId,
resolveLineAccount,
} from "../../../src/line/accounts.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../../test/helpers/extensions/setup-wizard.js";
import { lineSetupAdapter, lineSetupWizard } from "./setup-surface.js";
const lineConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({

View File

@@ -1,6 +1,6 @@
import type { PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/matrix";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { matrixPlugin } from "./channel.js";
import { setMatrixRuntime } from "./runtime.js";
import { createMatrixBotSdkMock } from "./test-mocks.js";

View File

@@ -1,6 +1,6 @@
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/mattermost";
import { describe, expect, it, vi } from "vitest";
import { createTestPluginApi } from "../test-utils/plugin-api.js";
import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js";
import plugin from "./index.js";
function createApi(

View File

@@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import {
expectProvidedCfgSkipsRuntimeLoad,
expectRuntimeCfgFallback,
} from "../../../test-utils/send-config.js";
} from "../../../../test/helpers/extensions/send-config.js";
import { parseMattermostTarget, sendMessageMattermost } from "./send.js";
import { resetMattermostOpaqueTargetCacheForTests } from "./target-resolution.js";

View File

@@ -1,6 +1,6 @@
import type { PluginRuntime, SsrFPolicy } from "openclaw/plugin-sdk/msteams";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import {
buildMSTeamsAttachmentPlaceholder,
buildMSTeamsGraphMessageUrls,

View File

@@ -1,6 +1,9 @@
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk/msteams";
import { describe, expect, it } from "vitest";
import { createDirectoryTestRuntime, expectDirectorySurface } from "../../test-utils/directory.js";
import {
createDirectoryTestRuntime,
expectDirectorySurface,
} from "../../../test/helpers/extensions/directory.js";
import { msteamsPlugin } from "./channel.js";
describe("msteams directory", () => {

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, vi } from "vitest";
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js";
import { uploadToOneDrive, uploadToSharePoint } from "./graph-upload.js";
describe("graph upload helpers", () => {

View File

@@ -3,7 +3,7 @@ import os from "node:os";
import path from "node:path";
import { SILENT_REPLY_TOKEN, type PluginRuntime } from "openclaw/plugin-sdk/msteams";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import type { StoredConversationReference } from "./conversation-store.js";
const graphUploadMockState = vi.hoisted(() => ({
uploadAndShareOneDrive: vi.fn(),

View File

@@ -1,9 +1,9 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { createStartAccountContext } from "../../test-utils/start-account-context.js";
import { createStartAccountContext } from "../../../test/helpers/extensions/start-account-context.js";
import {
expectStopPendingUntilAbort,
startAccountAndTrackLifecycle,
} from "../../test-utils/start-account-lifecycle.js";
} from "../../../test/helpers/extensions/start-account-lifecycle.js";
import type { ResolvedNextcloudTalkAccount } from "./accounts.js";
const hoisted = vi.hoisted(() => ({

View File

@@ -3,7 +3,7 @@ import {
createSendCfgThreadingRuntime,
expectProvidedCfgSkipsRuntimeLoad,
expectRuntimeCfgFallback,
} from "../../test-utils/send-config.js";
} from "../../../test/helpers/extensions/send-config.js";
const hoisted = vi.hoisted(() => ({
loadConfig: vi.fn(),

View File

@@ -1,6 +1,6 @@
import type { PluginRuntime } from "openclaw/plugin-sdk/nostr";
import { afterEach, describe, expect, it, vi } from "vitest";
import { createStartAccountContext } from "../../test-utils/start-account-context.js";
import { createStartAccountContext } from "../../../test/helpers/extensions/start-account-context.js";
import { nostrPlugin } from "./channel.js";
import { setNostrRuntime } from "./runtime.js";

View File

@@ -1,8 +1,8 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/nostr";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../../test/helpers/extensions/setup-wizard.js";
import { nostrPlugin } from "./channel.js";
const nostrConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({

View File

@@ -7,7 +7,7 @@ import type {
PluginCommandContext,
} from "openclaw/plugin-sdk/phone-control";
import { describe, expect, it, vi } from "vitest";
import { createTestPluginApi } from "../test-utils/plugin-api.js";
import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js";
import registerPhoneControl from "./index.js";
function createApi(params: {

View File

@@ -1,6 +1,6 @@
import { resetSystemEventsForTest } from "openclaw/plugin-sdk/infra-runtime";
import { resetInboundDedupe } from "openclaw/plugin-sdk/reply-runtime";
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { beforeEach, vi } from "vitest";
import type { SignalDaemonExitEvent, SignalDaemonHandle } from "./daemon.js";

View File

@@ -4,7 +4,10 @@ import * as mediaFetch from "../../../../src/media/fetch.js";
import type { SavedMedia } from "../../../../src/media/store.js";
import * as mediaStore from "../../../../src/media/store.js";
import { mockPinnedHostnameResolution } from "../../../../src/test-helpers/ssrf.js";
import { type FetchMock, withFetchPreconnect } from "../../../test-utils/fetch-mock.js";
import {
type FetchMock,
withFetchPreconnect,
} from "../../../../test/helpers/extensions/fetch-mock.js";
import {
fetchWithSlackAuth,
resolveSlackAttachmentContent,

View File

@@ -1,8 +1,8 @@
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../../test/helpers/extensions/setup-wizard.js";
import { synologyChatPlugin } from "./channel.js";
import { synologyChatSetupWizard } from "./setup-surface.js";

View File

@@ -1,6 +1,6 @@
import { describe, expect, it, vi } from "vitest";
import type { OpenClawPluginCommandDefinition } from "../test-utils/plugin-command.js";
import { createPluginRuntimeMock } from "../test-utils/plugin-runtime-mock.js";
import type { OpenClawPluginCommandDefinition } from "../../test/helpers/extensions/plugin-command.js";
import { createPluginRuntimeMock } from "../../test/helpers/extensions/plugin-runtime-mock.js";
import register from "./index.js";
function createHarness(config: Record<string, unknown>) {

View File

@@ -3,7 +3,7 @@ import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { withEnv } from "../../test-utils/env.js";
import { withEnv } from "../../../test/helpers/extensions/env.js";
import { inspectTelegramAccount } from "./account-inspect.js";
describe("inspectTelegramAccount SecretRef resolution", () => {

View File

@@ -1,7 +1,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../../src/config/config.js";
import * as subsystemModule from "../../../src/logging/subsystem.js";
import { withEnv } from "../../test-utils/env.js";
import { withEnv } from "../../../test/helpers/extensions/env.js";
import {
listTelegramAccountIds,
resetMissingDefaultWarnFlag,

View File

@@ -2,7 +2,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import type { ChannelGroupPolicy } from "openclaw/plugin-sdk/config-runtime";
import type { TelegramAccountConfig } from "openclaw/plugin-sdk/config-runtime";
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { vi } from "vitest";
import {
createNativeCommandTestParams,

View File

@@ -2,7 +2,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { resetInboundDedupe } from "openclaw/plugin-sdk/reply-runtime";
import type { MsgContext } from "openclaw/plugin-sdk/reply-runtime";
import type { GetReplyOptions, ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { beforeEach, vi } from "vitest";
type AnyMock = MockFn<(...args: unknown[]) => unknown>;

View File

@@ -3,8 +3,8 @@ import os from "node:os";
import path from "node:path";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
import { escapeRegExp, formatEnvelopeTimestamp } from "../../../test/helpers/envelope-timestamp.js";
import { withEnvAsync } from "../../test-utils/env.js";
import { useFrozenTime, useRealTime } from "../../test-utils/frozen-time.js";
import { withEnvAsync } from "../../../test/helpers/extensions/env.js";
import { useFrozenTime, useRealTime } from "../../../test/helpers/extensions/frozen-time.js";
import {
answerCallbackQuerySpy,
botCtorSpy,

View File

@@ -5,7 +5,7 @@ import type {
PluginRuntime,
} from "openclaw/plugin-sdk/telegram";
import { afterEach, describe, expect, it, vi } from "vitest";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import type { ResolvedTelegramAccount } from "./accounts.js";
import * as auditModule from "./audit.js";
import { telegramPlugin } from "./channel.js";

View File

@@ -1,5 +1,5 @@
import { afterEach, type Mock, describe, expect, it, vi } from "vitest";
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js";
import { probeTelegram, resetTelegramProbeFetcherCacheForTests } from "./probe.js";
const resolveTelegramFetch = vi.hoisted(() => vi.fn());

View File

@@ -1,4 +1,4 @@
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { beforeEach, vi } from "vitest";
const { botApi, botCtorSpy } = vi.hoisted(() => ({

View File

@@ -1 +0,0 @@
export { countLines, hasBalancedFences } from "../../src/test-utils/chunk-test-helpers.js";

View File

@@ -1 +0,0 @@
export { captureEnv, withEnv, withEnvAsync } from "../../src/test-utils/env.js";

View File

@@ -1 +0,0 @@
export { withFetchPreconnect, type FetchMock } from "../../src/test-utils/fetch-mock.js";

View File

@@ -1 +0,0 @@
export { useFrozenTime, useRealTime } from "../../src/test-utils/frozen-time.js";

View File

@@ -1 +0,0 @@
export { createMockServerResponse } from "../../src/test-utils/mock-http-response.js";

View File

@@ -1 +0,0 @@
export { registerSingleProviderPlugin } from "../../src/test-utils/plugin-registration.js";

View File

@@ -1,4 +0,0 @@
export {
createProviderUsageFetch,
makeResponse,
} from "../../src/test-utils/provider-usage-fetch.js";

View File

@@ -1 +0,0 @@
export { withTempDir } from "../../src/test-utils/temp-dir.js";

View File

@@ -1 +0,0 @@
export { typedCases } from "../../src/test-utils/typed-cases.js";

View File

@@ -1,8 +1,8 @@
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk/tlon";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../../test/helpers/extensions/setup-wizard.js";
import { tlonPlugin } from "./channel.js";
const tlonConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({

View File

@@ -2,7 +2,7 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { captureEnv } from "../../test-utils/env.js";
import { captureEnv } from "../../../test/helpers/extensions/env.js";
import { hasAnyWhatsAppAuth, listWhatsAppAuthDirs } from "./accounts.js";
describe("hasAnyWhatsAppAuth", () => {

View File

@@ -5,7 +5,7 @@ import { beforeAll, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { setLoggerOverride } from "../../../src/logging.js";
import { escapeRegExp, formatEnvelopeTimestamp } from "../../../test/helpers/envelope-timestamp.js";
import { withEnvAsync } from "../../test-utils/env.js";
import { withEnvAsync } from "../../../test/helpers/extensions/env.js";
import {
createMockWebListener,
createWebListenerFactoryCapture,

View File

@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import { saveSessionStore } from "../../../../src/config/sessions.js";
import { withTempDir } from "../../../test-utils/temp-dir.js";
import { withTempDir } from "../../../../test/helpers/extensions/temp-dir.js";
import {
debugMention,
isBotMentionedFromTargets,

View File

@@ -7,8 +7,8 @@ import { resolveStateDir } from "../../../src/config/paths.js";
import { resolvePreferredOpenClawTmpDir } from "../../../src/infra/tmp-openclaw-dir.js";
import { optimizeImageToPng } from "../../../src/media/image-ops.js";
import { mockPinnedHostnameResolution } from "../../../src/test-helpers/ssrf.js";
import { captureEnv } from "../../../test/helpers/extensions/env.js";
import { sendVoiceMessageDiscord } from "../../discord/src/send.js";
import { captureEnv } from "../../test-utils/env.js";
import {
LocalMediaAccessError,
loadWebMedia,

View File

@@ -1,6 +1,9 @@
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk/zalo";
import { describe, expect, it } from "vitest";
import { createDirectoryTestRuntime, expectDirectorySurface } from "../../test-utils/directory.js";
import {
createDirectoryTestRuntime,
expectDirectorySurface,
} from "../../../test/helpers/extensions/directory.js";
import { zaloPlugin } from "./channel.js";
describe("zalo directory", () => {

View File

@@ -3,7 +3,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import {
expectPendingUntilAbort,
startAccountAndTrackLifecycle,
} from "../../test-utils/start-account-lifecycle.js";
} from "../../../test/helpers/extensions/start-account-lifecycle.js";
import type { ResolvedZaloAccount } from "./accounts.js";
const hoisted = vi.hoisted(() => ({

View File

@@ -1,8 +1,8 @@
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk/zalo";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../../test/helpers/extensions/setup-wizard.js";
import { zaloPlugin } from "./channel.js";
const zaloConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { expectOpenDmPolicyConfigIssue } from "../../test-utils/status-issues.js";
import { expectOpenDmPolicyConfigIssue } from "../../../test/helpers/extensions/status-issues.js";
import { collectZaloStatusIssues } from "./status-issues.js";
describe("collectZaloStatusIssues", () => {

View File

@@ -1,8 +1,8 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/zalouser";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { createTestWizardPrompter } from "../../../test/helpers/extensions/setup-wizard.js";
vi.mock("./zalo-js.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./zalo-js.js")>();

View File

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

View File

@@ -278,6 +278,10 @@
"types": "./dist/plugin-sdk/talk-voice.d.ts",
"default": "./dist/plugin-sdk/talk-voice.js"
},
"./plugin-sdk/testing": {
"types": "./dist/plugin-sdk/testing.d.ts",
"default": "./dist/plugin-sdk/testing.js"
},
"./plugin-sdk/test-utils": {
"types": "./dist/plugin-sdk/test-utils.d.ts",
"default": "./dist/plugin-sdk/test-utils.js"

View File

@@ -6,17 +6,25 @@ const FORBIDDEN_PATTERNS: Array<{ pattern: RegExp; hint: string }> = [
pattern: /["']openclaw\/plugin-sdk["']/,
hint: "Use openclaw/plugin-sdk/<subpath> instead of the monolithic root entry.",
},
{
pattern: /["']openclaw\/plugin-sdk\/test-utils["']/,
hint: "Use openclaw/plugin-sdk/testing for the public extension test seam.",
},
{
pattern: /["']openclaw\/plugin-sdk\/compat["']/,
hint: "Use a focused public plugin-sdk subpath instead of compat.",
},
{
pattern: /["'](?:\.\.\/)+(?:test-utils\/)[^"']+["']/,
hint: "Use test/helpers/extensions/* for repo-only bundled extension test helpers.",
},
{
pattern: /["'](?:\.\.\/)+(?:src\/test-utils\/)[^"']+["']/,
hint: "Use extensions/test-utils/* bridges for shared extension test helpers.",
hint: "Use test/helpers/extensions/* for repo-only helpers, or openclaw/plugin-sdk/testing for public seams.",
},
{
pattern: /["'](?:\.\.\/)+(?:src\/plugins\/types\.js)["']/,
hint: "Use public plugin-sdk/core types or extensions/test-utils bridges instead.",
hint: "Use public plugin-sdk/core types or test/helpers/extensions/* instead.",
},
];

View File

@@ -59,6 +59,7 @@
"qwen-portal-auth",
"synology-chat",
"talk-voice",
"testing",
"test-utils",
"thread-ownership",
"tlon",

View File

@@ -20,6 +20,7 @@ import * as setupSdk from "openclaw/plugin-sdk/setup";
import * as signalSdk from "openclaw/plugin-sdk/signal";
import * as slackSdk from "openclaw/plugin-sdk/slack";
import * as telegramSdk from "openclaw/plugin-sdk/telegram";
import * as testingSdk from "openclaw/plugin-sdk/testing";
import * as whatsappSdk from "openclaw/plugin-sdk/whatsapp";
import { describe, expect, expectTypeOf, it } from "vitest";
import type { ChannelMessageActionContext } from "../channels/plugins/types.js";
@@ -114,6 +115,11 @@ describe("plugin-sdk subpath exports", () => {
expectTypeOf<CoreChannelMessageActionContext>().toMatchTypeOf<ChannelMessageActionContext>();
});
it("exports the public testing seam", () => {
expect(typeof testingSdk.removeAckReactionAfterReply).toBe("function");
expect(typeof testingSdk.shouldAckReaction).toBe("function");
});
it("keeps core shared types aligned with the channel prelude", () => {
expectTypeOf<CoreOpenClawPluginApi>().toMatchTypeOf<SharedOpenClawPluginApi>();
expectTypeOf<CorePluginRuntime>().toMatchTypeOf<SharedPluginRuntime>();

View File

@@ -1,9 +1,4 @@
// Narrow plugin-sdk surface for the bundled test-utils plugin.
// Keep this list additive and scoped to symbols used under extensions/test-utils.
// Deprecated compatibility alias.
// Prefer openclaw/plugin-sdk/testing for public test helpers.
export { removeAckReactionAfterReply, shouldAckReaction } from "../channels/ack-reactions.js";
export type { ChannelAccountSnapshot, ChannelGatewayContext } from "../channels/plugins/types.js";
export type { OpenClawConfig } from "../config/config.js";
export type { PluginRuntime } from "../plugins/runtime/types.js";
export type { RuntimeEnv } from "../runtime.js";
export type { MockFn } from "../test-utils/vitest-mock-fn.js";
export * from "./testing.js";

View File

@@ -0,0 +1,9 @@
// Narrow public testing surface for plugin authors.
// Keep this list additive and limited to helpers we are willing to support.
export { removeAckReactionAfterReply, shouldAckReaction } from "../channels/ack-reactions.js";
export type { ChannelAccountSnapshot, ChannelGatewayContext } from "../channels/plugins/types.js";
export type { OpenClawConfig } from "../config/config.js";
export type { PluginRuntime } from "../plugins/runtime/types.js";
export type { RuntimeEnv } from "../runtime.js";
export type { MockFn } from "../test-utils/vitest-mock-fn.js";

View File

@@ -0,0 +1 @@
export { countLines, hasBalancedFences } from "../../../src/test-utils/chunk-test-helpers.js";

View File

@@ -0,0 +1 @@
export { captureEnv, withEnv, withEnvAsync } from "../../../src/test-utils/env.js";

View File

@@ -0,0 +1 @@
export { withFetchPreconnect, type FetchMock } from "../../../src/test-utils/fetch-mock.js";

View File

@@ -0,0 +1 @@
export { useFrozenTime, useRealTime } from "../../../src/test-utils/frozen-time.js";

View File

@@ -0,0 +1 @@
export { createMockServerResponse } from "../../../src/test-utils/mock-http-response.js";

View File

@@ -0,0 +1 @@
export { registerSingleProviderPlugin } from "../../../src/test-utils/plugin-registration.js";

View File

@@ -1,6 +1,6 @@
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "openclaw/plugin-sdk/agent-runtime";
import type { PluginRuntime } from "openclaw/plugin-sdk/test-utils";
import { removeAckReactionAfterReply, shouldAckReaction } from "openclaw/plugin-sdk/test-utils";
import type { PluginRuntime } from "openclaw/plugin-sdk/testing";
import { removeAckReactionAfterReply, shouldAckReaction } from "openclaw/plugin-sdk/testing";
import { vi } from "vitest";
type DeepPartial<T> = {

View File

@@ -0,0 +1,4 @@
export {
createProviderUsageFetch,
makeResponse,
} from "../../../src/test-utils/provider-usage-fetch.js";

View File

@@ -1,4 +1,4 @@
import type { RuntimeEnv } from "openclaw/plugin-sdk/test-utils";
import type { RuntimeEnv } from "openclaw/plugin-sdk/testing";
import { vi } from "vitest";
export function createRuntimeEnv(): RuntimeEnv {

View File

@@ -1,7 +1,7 @@
import { vi } from "vitest";
import type { WizardPrompter } from "../../src/wizard/prompts.js";
import type { WizardPrompter } from "../../../src/wizard/prompts.js";
export type { WizardPrompter } from "../../src/wizard/prompts.js";
export type { WizardPrompter } from "../../../src/wizard/prompts.js";
export async function selectFirstWizardOption<T>(params: {
options: Array<{ value: T }>;

View File

@@ -2,7 +2,7 @@ import type {
ChannelAccountSnapshot,
ChannelGatewayContext,
OpenClawConfig,
} from "openclaw/plugin-sdk/test-utils";
} from "openclaw/plugin-sdk/testing";
import { vi } from "vitest";
import { createRuntimeEnv } from "./runtime-env.js";

View File

@@ -1,4 +1,4 @@
import type { ChannelAccountSnapshot, ChannelGatewayContext } from "openclaw/plugin-sdk/test-utils";
import type { ChannelAccountSnapshot, ChannelGatewayContext } from "openclaw/plugin-sdk/testing";
import { expect, vi } from "vitest";
import { createStartAccountContext } from "./start-account-context.js";

View File

@@ -0,0 +1 @@
export { withTempDir } from "../../../src/test-utils/temp-dir.js";

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