mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-23 07:51:33 +00:00
test: trim plugin-sdk import-heavy startup
This commit is contained in:
@@ -5,10 +5,8 @@ export const pluginSdkEntrypoints = [...pluginSdkEntryList];
|
||||
export const pluginSdkSubpaths = pluginSdkEntrypoints.filter((entry) => entry !== "index");
|
||||
|
||||
/** Map every SDK entrypoint name to its source file path inside the repo. */
|
||||
export function buildPluginSdkEntrySources() {
|
||||
return Object.fromEntries(
|
||||
pluginSdkEntrypoints.map((entry) => [entry, `src/plugin-sdk/${entry}.ts`]),
|
||||
);
|
||||
export function buildPluginSdkEntrySources(entries: readonly string[] = pluginSdkEntrypoints) {
|
||||
return Object.fromEntries(entries.map((entry) => [entry, `src/plugin-sdk/${entry}.ts`]));
|
||||
}
|
||||
|
||||
/** List the public package specifiers that should resolve to plugin SDK entrypoints. */
|
||||
|
||||
@@ -12,58 +12,55 @@ const bundledRepresentativeEntrypoints = [
|
||||
"matrix-runtime-heavy",
|
||||
"windows-spawn",
|
||||
] as const;
|
||||
|
||||
function buildBundledCoverageEntrySources() {
|
||||
const allEntrySources = buildPluginSdkEntrySources();
|
||||
return Object.fromEntries(
|
||||
bundledRepresentativeEntrypoints.map((entry) => [entry, allEntrySources[entry]]),
|
||||
);
|
||||
}
|
||||
const bundledCoverageEntrySources = buildPluginSdkEntrySources(bundledRepresentativeEntrypoints);
|
||||
|
||||
describe("plugin-sdk bundled exports", () => {
|
||||
it("emits importable bundled subpath entries", { timeout: 120_000 }, async () => {
|
||||
const bundleTempRoot = path.join(process.cwd(), ".tmp");
|
||||
const bundleTempRoot = path.join(
|
||||
process.cwd(),
|
||||
"node_modules",
|
||||
".cache",
|
||||
"openclaw-plugin-sdk-build",
|
||||
);
|
||||
await fs.mkdir(bundleTempRoot, { recursive: true });
|
||||
const outDir = await fs.mkdtemp(path.join(bundleTempRoot, "openclaw-plugin-sdk-build-"));
|
||||
const outDir = path.join(bundleTempRoot, "bundle");
|
||||
await fs.rm(outDir, { recursive: true, force: true });
|
||||
await fs.mkdir(outDir, { recursive: true });
|
||||
|
||||
try {
|
||||
const { build } = await import(tsdownModuleUrl);
|
||||
await build({
|
||||
clean: false,
|
||||
config: false,
|
||||
dts: false,
|
||||
// Full plugin-sdk coverage belongs to `pnpm build`, package contract
|
||||
// guardrails, and `subpaths.test.ts`. This file only keeps the expensive
|
||||
// bundler path honest across representative entrypoint families.
|
||||
entry: buildBundledCoverageEntrySources(),
|
||||
env: { NODE_ENV: "production" },
|
||||
fixedExtension: false,
|
||||
logLevel: "error",
|
||||
outDir,
|
||||
platform: "node",
|
||||
});
|
||||
const { build } = await import(tsdownModuleUrl);
|
||||
await build({
|
||||
clean: false,
|
||||
config: false,
|
||||
dts: false,
|
||||
// Full plugin-sdk coverage belongs to `pnpm build`, package contract
|
||||
// guardrails, and `subpaths.test.ts`. This file only keeps the expensive
|
||||
// bundler path honest across representative entrypoint families.
|
||||
entry: bundledCoverageEntrySources,
|
||||
env: { NODE_ENV: "production" },
|
||||
fixedExtension: false,
|
||||
logLevel: "error",
|
||||
outDir,
|
||||
platform: "node",
|
||||
});
|
||||
|
||||
expect(pluginSdkEntrypoints.length).toBeGreaterThan(bundledRepresentativeEntrypoints.length);
|
||||
await Promise.all(
|
||||
bundledRepresentativeEntrypoints.map(async (entry) => {
|
||||
await expect(fs.stat(path.join(outDir, `${entry}.js`))).resolves.toBeTruthy();
|
||||
}),
|
||||
);
|
||||
expect(pluginSdkEntrypoints.length).toBeGreaterThan(bundledRepresentativeEntrypoints.length);
|
||||
await Promise.all(
|
||||
bundledRepresentativeEntrypoints.map(async (entry) => {
|
||||
await expect(fs.stat(path.join(outDir, `${entry}.js`))).resolves.toBeTruthy();
|
||||
}),
|
||||
);
|
||||
|
||||
// Export list and package-specifier coverage already live in
|
||||
// package-contract-guardrails.test.ts and subpaths.test.ts. Keep this file
|
||||
// focused on the expensive part: can tsdown emit working bundle artifacts?
|
||||
const importResults = await Promise.all(
|
||||
bundledRepresentativeEntrypoints.map(async (entry) => [
|
||||
entry,
|
||||
typeof (await import(pathToFileURL(path.join(outDir, `${entry}.js`)).href)),
|
||||
]),
|
||||
);
|
||||
expect(Object.fromEntries(importResults)).toEqual(
|
||||
Object.fromEntries(bundledRepresentativeEntrypoints.map((entry) => [entry, "object"])),
|
||||
);
|
||||
} finally {
|
||||
await fs.rm(outDir, { recursive: true, force: true });
|
||||
}
|
||||
// Export list and package-specifier coverage already live in
|
||||
// package-contract-guardrails.test.ts and subpaths.test.ts. Keep this file
|
||||
// focused on the expensive part: can tsdown emit working bundle artifacts?
|
||||
const importResults = await Promise.all(
|
||||
bundledRepresentativeEntrypoints.map(async (entry) => [
|
||||
entry,
|
||||
typeof (await import(pathToFileURL(path.join(outDir, `${entry}.js`)).href)),
|
||||
]),
|
||||
);
|
||||
expect(Object.fromEntries(importResults)).toEqual(
|
||||
Object.fromEntries(bundledRepresentativeEntrypoints.map((entry) => [entry, "object"])),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import type {
|
||||
BaseProbeResult as ContractBaseProbeResult,
|
||||
BaseTokenResolution as ContractBaseTokenResolution,
|
||||
@@ -46,11 +47,11 @@ import { pluginSdkSubpaths } from "./entrypoints.js";
|
||||
|
||||
const ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const PLUGIN_SDK_DIR = resolve(ROOT_DIR, "plugin-sdk");
|
||||
const requireFromHere = createRequire(import.meta.url);
|
||||
const sourceCache = new Map<string, string>();
|
||||
const representativeRuntimeSmokeSubpaths = [
|
||||
"channel-runtime",
|
||||
"conversation-runtime",
|
||||
"core",
|
||||
"discord",
|
||||
"provider-auth",
|
||||
"provider-setup",
|
||||
@@ -58,7 +59,8 @@ const representativeRuntimeSmokeSubpaths = [
|
||||
"webhook-ingress",
|
||||
] as const;
|
||||
|
||||
const importPluginSdkSubpath = (specifier: string) => import(/* @vite-ignore */ specifier);
|
||||
const importResolvedPluginSdkSubpath = async (specifier: string) =>
|
||||
import(pathToFileURL(requireFromHere.resolve(specifier)).href);
|
||||
|
||||
function readPluginSdkSource(subpath: string): string {
|
||||
const file = resolve(PLUGIN_SDK_DIR, `${subpath}.ts`);
|
||||
@@ -100,18 +102,25 @@ function sourceMentionsIdentifier(source: string, name: string): boolean {
|
||||
|
||||
function expectSourceMentions(subpath: string, names: readonly string[]) {
|
||||
const source = readPluginSdkSource(subpath);
|
||||
for (const name of names) {
|
||||
expect(sourceMentionsIdentifier(source, name), `${subpath} should mention ${name}`).toBe(true);
|
||||
}
|
||||
const missing = names.filter((name) => !sourceMentionsIdentifier(source, name));
|
||||
expect(missing, `${subpath} missing exports`).toEqual([]);
|
||||
}
|
||||
|
||||
function expectSourceOmits(subpath: string, names: readonly string[]) {
|
||||
const source = readPluginSdkSource(subpath);
|
||||
for (const name of names) {
|
||||
expect(sourceMentionsIdentifier(source, name), `${subpath} should not mention ${name}`).toBe(
|
||||
false,
|
||||
);
|
||||
}
|
||||
const present = names.filter((name) => sourceMentionsIdentifier(source, name));
|
||||
expect(present, `${subpath} leaked exports`).toEqual([]);
|
||||
}
|
||||
|
||||
function expectSourceContract(
|
||||
subpath: string,
|
||||
params: { mentions?: readonly string[]; omits?: readonly string[] },
|
||||
) {
|
||||
const source = readPluginSdkSource(subpath);
|
||||
const missing = (params.mentions ?? []).filter((name) => !sourceMentionsIdentifier(source, name));
|
||||
const present = (params.omits ?? []).filter((name) => sourceMentionsIdentifier(source, name));
|
||||
expect(missing, `${subpath} missing exports`).toEqual([]);
|
||||
expect(present, `${subpath} leaked exports`).toEqual([]);
|
||||
}
|
||||
|
||||
describe("plugin-sdk subpath exports", () => {
|
||||
@@ -154,16 +163,15 @@ describe("plugin-sdk subpath exports", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("re-exports the canonical plugin entry helper from core", async () => {
|
||||
const [coreSdk, pluginEntrySdk] = await Promise.all([
|
||||
importPluginSdkSubpath("openclaw/plugin-sdk/core"),
|
||||
importPluginSdkSubpath("openclaw/plugin-sdk/plugin-entry"),
|
||||
]);
|
||||
expect(coreSdk.definePluginEntry).toBe(pluginEntrySdk.definePluginEntry);
|
||||
});
|
||||
|
||||
it("keeps generic helper subpaths aligned", () => {
|
||||
expectSourceMentions("routing", ["buildAgentSessionKey", "resolveThreadSessionKeys"]);
|
||||
expectSourceContract("routing", {
|
||||
mentions: [
|
||||
"buildAgentSessionKey",
|
||||
"resolveThreadSessionKeys",
|
||||
"normalizeMessageChannel",
|
||||
"resolveGatewayMessageChannel",
|
||||
],
|
||||
});
|
||||
expectSourceMentions("reply-payload", [
|
||||
"buildMediaPayload",
|
||||
"deliverTextOrMediaReply",
|
||||
@@ -183,12 +191,14 @@ describe("plugin-sdk subpath exports", () => {
|
||||
"clearHistoryEntriesIfEnabled",
|
||||
"recordPendingHistoryEntryIfEnabled",
|
||||
]);
|
||||
expectSourceOmits("reply-runtime", [
|
||||
"buildPendingHistoryContextFromMap",
|
||||
"clearHistoryEntriesIfEnabled",
|
||||
"recordPendingHistoryEntryIfEnabled",
|
||||
"DEFAULT_GROUP_HISTORY_LIMIT",
|
||||
]);
|
||||
expectSourceContract("reply-runtime", {
|
||||
omits: [
|
||||
"buildPendingHistoryContextFromMap",
|
||||
"clearHistoryEntriesIfEnabled",
|
||||
"recordPendingHistoryEntryIfEnabled",
|
||||
"DEFAULT_GROUP_HISTORY_LIMIT",
|
||||
],
|
||||
});
|
||||
expectSourceMentions("account-helpers", ["createAccountListHelpers"]);
|
||||
expectSourceMentions("device-bootstrap", [
|
||||
"approveDevicePairing",
|
||||
@@ -199,23 +209,23 @@ describe("plugin-sdk subpath exports", () => {
|
||||
"buildDmGroupAccountAllowlistAdapter",
|
||||
"createNestedAllowlistOverrideResolver",
|
||||
]);
|
||||
expectSourceMentions("allow-from", [
|
||||
"addAllowlistUserEntriesFromConfigEntry",
|
||||
"buildAllowlistResolutionSummary",
|
||||
"canonicalizeAllowlistWithResolvedIds",
|
||||
"mapAllowlistResolutionInputs",
|
||||
"mergeAllowlist",
|
||||
"patchAllowlistUsersInConfigEntries",
|
||||
"summarizeMapping",
|
||||
]);
|
||||
expectSourceMentions("allow-from", [
|
||||
"compileAllowlist",
|
||||
"firstDefined",
|
||||
"formatAllowlistMatchMeta",
|
||||
"isSenderIdAllowed",
|
||||
"mergeDmAllowFromSources",
|
||||
"resolveAllowlistMatchSimple",
|
||||
]);
|
||||
expectSourceContract("allow-from", {
|
||||
mentions: [
|
||||
"addAllowlistUserEntriesFromConfigEntry",
|
||||
"buildAllowlistResolutionSummary",
|
||||
"canonicalizeAllowlistWithResolvedIds",
|
||||
"mapAllowlistResolutionInputs",
|
||||
"mergeAllowlist",
|
||||
"patchAllowlistUsersInConfigEntries",
|
||||
"summarizeMapping",
|
||||
"compileAllowlist",
|
||||
"firstDefined",
|
||||
"formatAllowlistMatchMeta",
|
||||
"isSenderIdAllowed",
|
||||
"mergeDmAllowFromSources",
|
||||
"resolveAllowlistMatchSimple",
|
||||
],
|
||||
});
|
||||
expectSourceMentions("runtime", ["createLoggerBackedRuntime"]);
|
||||
expectSourceMentions("discord", [
|
||||
"buildDiscordComponentMessage",
|
||||
@@ -223,7 +233,6 @@ describe("plugin-sdk subpath exports", () => {
|
||||
"registerBuiltDiscordComponentMessage",
|
||||
"resolveDiscordAccount",
|
||||
]);
|
||||
expectSourceMentions("routing", ["normalizeMessageChannel", "resolveGatewayMessageChannel"]);
|
||||
expectSourceMentions("conversation-runtime", [
|
||||
"recordInboundSession",
|
||||
"recordInboundSessionMetaSafe",
|
||||
@@ -237,12 +246,6 @@ describe("plugin-sdk subpath exports", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("exports infra runtime helpers from the dedicated subpath", async () => {
|
||||
const infraRuntimeSdk = await importPluginSdkSubpath("openclaw/plugin-sdk/infra-runtime");
|
||||
expect(typeof infraRuntimeSdk.createRuntimeOutboundDelegates).toBe("function");
|
||||
expect(typeof infraRuntimeSdk.resolveOutboundSendDep).toBe("function");
|
||||
});
|
||||
|
||||
it("exports channel runtime helpers from the dedicated subpath", () => {
|
||||
expectSourceOmits("channel-runtime", [
|
||||
"applyChannelMatchMeta",
|
||||
@@ -366,26 +369,43 @@ describe("plugin-sdk subpath exports", () => {
|
||||
"shouldDebounceTextInbound",
|
||||
"toLocationContext",
|
||||
]);
|
||||
expectSourceOmits("reply-runtime", [
|
||||
"buildMentionRegexes",
|
||||
"createInboundDebouncer",
|
||||
"formatInboundEnvelope",
|
||||
"formatInboundFromLabel",
|
||||
"matchesMentionPatterns",
|
||||
"matchesMentionWithExplicit",
|
||||
"normalizeMentionText",
|
||||
"resolveEnvelopeFormatOptions",
|
||||
"resolveInboundDebounceMs",
|
||||
]);
|
||||
expectSourceContract("reply-runtime", {
|
||||
omits: [
|
||||
"buildMentionRegexes",
|
||||
"createInboundDebouncer",
|
||||
"formatInboundEnvelope",
|
||||
"formatInboundFromLabel",
|
||||
"matchesMentionPatterns",
|
||||
"matchesMentionWithExplicit",
|
||||
"normalizeMentionText",
|
||||
"resolveEnvelopeFormatOptions",
|
||||
"resolveInboundDebounceMs",
|
||||
"hasControlCommand",
|
||||
"buildCommandTextFromArgs",
|
||||
"buildCommandsPaginationKeyboard",
|
||||
"buildModelsProviderData",
|
||||
"listNativeCommandSpecsForConfig",
|
||||
"listSkillCommandsForAgents",
|
||||
"normalizeCommandBody",
|
||||
"resolveCommandAuthorization",
|
||||
"resolveStoredModelOverride",
|
||||
"shouldComputeCommandAuthorized",
|
||||
"shouldHandleTextCommands",
|
||||
],
|
||||
});
|
||||
expectSourceMentions("channel-setup", [
|
||||
"createOptionalChannelSetupSurface",
|
||||
"createTopLevelChannelDmPolicy",
|
||||
]);
|
||||
expectSourceMentions("channel-actions", [
|
||||
"createUnionActionGate",
|
||||
"listTokenSourcedAccounts",
|
||||
"resolveReactionMessageId",
|
||||
]);
|
||||
expectSourceContract("channel-actions", {
|
||||
mentions: [
|
||||
"createUnionActionGate",
|
||||
"listTokenSourcedAccounts",
|
||||
"resolveReactionMessageId",
|
||||
"createMessageToolButtonsSchema",
|
||||
"createMessageToolCardSchema",
|
||||
],
|
||||
});
|
||||
expectSourceMentions("channel-targets", [
|
||||
"applyChannelMatchMeta",
|
||||
"buildChannelKeyCandidates",
|
||||
@@ -419,10 +439,6 @@ describe("plugin-sdk subpath exports", () => {
|
||||
"isRecord",
|
||||
"resolveEnabledConfiguredAccountId",
|
||||
]);
|
||||
expectSourceMentions("channel-actions", [
|
||||
"createMessageToolButtonsSchema",
|
||||
"createMessageToolCardSchema",
|
||||
]);
|
||||
expectSourceMentions("command-auth", [
|
||||
"buildCommandTextFromArgs",
|
||||
"buildCommandsPaginationKeyboard",
|
||||
@@ -440,19 +456,6 @@ describe("plugin-sdk subpath exports", () => {
|
||||
"shouldComputeCommandAuthorized",
|
||||
"shouldHandleTextCommands",
|
||||
]);
|
||||
expectSourceOmits("reply-runtime", [
|
||||
"hasControlCommand",
|
||||
"buildCommandTextFromArgs",
|
||||
"buildCommandsPaginationKeyboard",
|
||||
"buildModelsProviderData",
|
||||
"listNativeCommandSpecsForConfig",
|
||||
"listSkillCommandsForAgents",
|
||||
"normalizeCommandBody",
|
||||
"resolveCommandAuthorization",
|
||||
"resolveStoredModelOverride",
|
||||
"shouldComputeCommandAuthorized",
|
||||
"shouldHandleTextCommands",
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps channel contract types on the dedicated subpath", () => {
|
||||
@@ -470,39 +473,6 @@ describe("plugin-sdk subpath exports", () => {
|
||||
expectTypeOf<ContractChannelThreadingToolContext>().toMatchTypeOf<ChannelThreadingToolContext>();
|
||||
});
|
||||
|
||||
it("exports channel lifecycle helpers from the dedicated subpath", async () => {
|
||||
const channelLifecycleSdk = await importPluginSdkSubpath(
|
||||
"openclaw/plugin-sdk/channel-lifecycle",
|
||||
);
|
||||
expect(typeof channelLifecycleSdk.createDraftStreamLoop).toBe("function");
|
||||
expect(typeof channelLifecycleSdk.createFinalizableDraftLifecycle).toBe("function");
|
||||
expect(typeof channelLifecycleSdk.runPassiveAccountLifecycle).toBe("function");
|
||||
expect(typeof channelLifecycleSdk.createRunStateMachine).toBe("function");
|
||||
expect(typeof channelLifecycleSdk.createArmableStallWatchdog).toBe("function");
|
||||
});
|
||||
|
||||
it("exports channel pairing helpers from the dedicated subpath", async () => {
|
||||
const channelPairingSdk = await importPluginSdkSubpath("openclaw/plugin-sdk/channel-pairing");
|
||||
expectSourceMentions("channel-pairing", [
|
||||
"createChannelPairingController",
|
||||
"createChannelPairingChallengeIssuer",
|
||||
"createLoggedPairingApprovalNotifier",
|
||||
"createPairingPrefixStripper",
|
||||
"createTextPairingAdapter",
|
||||
]);
|
||||
expect("createScopedPairingAccess" in channelPairingSdk).toBe(false);
|
||||
});
|
||||
|
||||
it("exports channel reply pipeline helpers from the dedicated subpath", async () => {
|
||||
const channelReplyPipelineSdk = await importPluginSdkSubpath(
|
||||
"openclaw/plugin-sdk/channel-reply-pipeline",
|
||||
);
|
||||
expectSourceMentions("channel-reply-pipeline", ["createChannelReplyPipeline"]);
|
||||
expect("createTypingCallbacks" in channelReplyPipelineSdk).toBe(false);
|
||||
expect("createReplyPrefixContext" in channelReplyPipelineSdk).toBe(false);
|
||||
expect("createReplyPrefixOptions" in channelReplyPipelineSdk).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps source-only helper subpaths aligned", () => {
|
||||
expectSourceMentions("channel-send-result", [
|
||||
"attachChannelToResult",
|
||||
@@ -551,17 +521,15 @@ describe("plugin-sdk subpath exports", () => {
|
||||
"toFormUrlEncoded",
|
||||
]);
|
||||
expectSourceOmits("core", ["buildOauthProviderAuthResult"]);
|
||||
expectSourceMentions("provider-models", [
|
||||
"applyOpenAIConfig",
|
||||
"buildKilocodeModelDefinition",
|
||||
"discoverHuggingfaceModels",
|
||||
]);
|
||||
expectSourceOmits("provider-models", [
|
||||
"buildMinimaxModelDefinition",
|
||||
"buildMoonshotProvider",
|
||||
"QIANFAN_BASE_URL",
|
||||
"resolveZaiBaseUrl",
|
||||
]);
|
||||
expectSourceContract("provider-models", {
|
||||
mentions: ["applyOpenAIConfig", "buildKilocodeModelDefinition", "discoverHuggingfaceModels"],
|
||||
omits: [
|
||||
"buildMinimaxModelDefinition",
|
||||
"buildMoonshotProvider",
|
||||
"QIANFAN_BASE_URL",
|
||||
"resolveZaiBaseUrl",
|
||||
],
|
||||
});
|
||||
|
||||
expectSourceMentions("setup", [
|
||||
"DEFAULT_ACCOUNT_ID",
|
||||
@@ -589,7 +557,6 @@ describe("plugin-sdk subpath exports", () => {
|
||||
"normalizeResolvedSecretInputString",
|
||||
"normalizeSecretInputString",
|
||||
]);
|
||||
|
||||
expectSourceMentions("webhook-ingress", [
|
||||
"registerPluginHttpRoute",
|
||||
"resolveWebhookPath",
|
||||
@@ -613,10 +580,55 @@ describe("plugin-sdk subpath exports", () => {
|
||||
expectTypeOf<CoreChannelMessageActionContext>().toMatchTypeOf<SharedChannelMessageActionContext>();
|
||||
});
|
||||
|
||||
it("resolves representative curated public subpaths", async () => {
|
||||
it("keeps runtime entry subpaths importable", async () => {
|
||||
const [
|
||||
coreSdk,
|
||||
pluginEntrySdk,
|
||||
infraRuntimeSdk,
|
||||
channelLifecycleSdk,
|
||||
channelPairingSdk,
|
||||
channelReplyPipelineSdk,
|
||||
...representativeModules
|
||||
] = await Promise.all([
|
||||
importResolvedPluginSdkSubpath("openclaw/plugin-sdk/core"),
|
||||
importResolvedPluginSdkSubpath("openclaw/plugin-sdk/plugin-entry"),
|
||||
importResolvedPluginSdkSubpath("openclaw/plugin-sdk/infra-runtime"),
|
||||
importResolvedPluginSdkSubpath("openclaw/plugin-sdk/channel-lifecycle"),
|
||||
importResolvedPluginSdkSubpath("openclaw/plugin-sdk/channel-pairing"),
|
||||
importResolvedPluginSdkSubpath("openclaw/plugin-sdk/channel-reply-pipeline"),
|
||||
...representativeRuntimeSmokeSubpaths.map((id) =>
|
||||
importResolvedPluginSdkSubpath(`openclaw/plugin-sdk/${id}`),
|
||||
),
|
||||
]);
|
||||
|
||||
expect(coreSdk.definePluginEntry).toBe(pluginEntrySdk.definePluginEntry);
|
||||
|
||||
expect(typeof infraRuntimeSdk.createRuntimeOutboundDelegates).toBe("function");
|
||||
expect(typeof infraRuntimeSdk.resolveOutboundSendDep).toBe("function");
|
||||
|
||||
expect(typeof channelLifecycleSdk.createDraftStreamLoop).toBe("function");
|
||||
expect(typeof channelLifecycleSdk.createFinalizableDraftLifecycle).toBe("function");
|
||||
expect(typeof channelLifecycleSdk.runPassiveAccountLifecycle).toBe("function");
|
||||
expect(typeof channelLifecycleSdk.createRunStateMachine).toBe("function");
|
||||
expect(typeof channelLifecycleSdk.createArmableStallWatchdog).toBe("function");
|
||||
|
||||
expectSourceMentions("channel-pairing", [
|
||||
"createChannelPairingController",
|
||||
"createChannelPairingChallengeIssuer",
|
||||
"createLoggedPairingApprovalNotifier",
|
||||
"createPairingPrefixStripper",
|
||||
"createTextPairingAdapter",
|
||||
]);
|
||||
expect("createScopedPairingAccess" in channelPairingSdk).toBe(false);
|
||||
|
||||
expectSourceMentions("channel-reply-pipeline", ["createChannelReplyPipeline"]);
|
||||
expect("createTypingCallbacks" in channelReplyPipelineSdk).toBe(false);
|
||||
expect("createReplyPrefixContext" in channelReplyPipelineSdk).toBe(false);
|
||||
expect("createReplyPrefixOptions" in channelReplyPipelineSdk).toBe(false);
|
||||
|
||||
expect(pluginSdkSubpaths.length).toBeGreaterThan(representativeRuntimeSmokeSubpaths.length);
|
||||
for (const id of representativeRuntimeSmokeSubpaths) {
|
||||
const mod = await importPluginSdkSubpath(`openclaw/plugin-sdk/${id}`);
|
||||
for (const [index, id] of representativeRuntimeSmokeSubpaths.entries()) {
|
||||
const mod = representativeModules[index];
|
||||
expect(typeof mod).toBe("object");
|
||||
expect(mod, `subpath ${id} should resolve`).toBeTruthy();
|
||||
}
|
||||
|
||||
@@ -840,25 +840,13 @@ describe("installPluginFromDir", () => {
|
||||
expectInstalledWithPluginId(res, extensionsDir, "@openclaw/test-plugin");
|
||||
});
|
||||
|
||||
it("rejects bare @ as an invalid scoped id", () => {
|
||||
expect(() => resolvePluginInstallDir("@")).toThrow(
|
||||
"invalid plugin name: scoped ids must use @scope/name format",
|
||||
);
|
||||
});
|
||||
it("keeps scoped install-dir validation aligned", () => {
|
||||
for (const invalidId of ["@", "@/name", "team/name"]) {
|
||||
expect(() => resolvePluginInstallDir(invalidId)).toThrow(
|
||||
"invalid plugin name: scoped ids must use @scope/name format",
|
||||
);
|
||||
}
|
||||
|
||||
it("rejects empty scoped segments like @/name", () => {
|
||||
expect(() => resolvePluginInstallDir("@/name")).toThrow(
|
||||
"invalid plugin name: scoped ids must use @scope/name format",
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects two-segment ids without a scope prefix", () => {
|
||||
expect(() => resolvePluginInstallDir("team/name")).toThrow(
|
||||
"invalid plugin name: scoped ids must use @scope/name format",
|
||||
);
|
||||
});
|
||||
|
||||
it("uses a unique hashed install dir for scoped ids", () => {
|
||||
const extensionsDir = path.join(makeTempDir(), "extensions");
|
||||
const scopedTarget = resolvePluginInstallDir("@scope/name", extensionsDir);
|
||||
const hashedFlatId = safePathSegmentHashed("@scope/name");
|
||||
|
||||
@@ -11,6 +11,9 @@ const pluginSdkInternalInventoryPromise =
|
||||
const relativeOutsidePackageInventoryPromise = collectExtensionPluginSdkBoundaryInventory(
|
||||
"relative-outside-package",
|
||||
);
|
||||
const srcOutsideJsonOutputPromise = getJsonOutput("src-outside-plugin-sdk");
|
||||
const pluginSdkInternalJsonOutputPromise = getJsonOutput("plugin-sdk-internal");
|
||||
const relativeOutsidePackageJsonOutputPromise = getJsonOutput("relative-outside-package");
|
||||
|
||||
async function getJsonOutput(
|
||||
mode: Parameters<typeof collectExtensionPluginSdkBoundaryInventory>[0],
|
||||
@@ -71,7 +74,7 @@ describe("extension src outside plugin-sdk boundary inventory", () => {
|
||||
});
|
||||
|
||||
it("script json output is empty", async () => {
|
||||
const result = await getJsonOutput("src-outside-plugin-sdk");
|
||||
const result = await srcOutsideJsonOutputPromise;
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.stderr).toBe("");
|
||||
@@ -87,7 +90,7 @@ describe("extension plugin-sdk-internal boundary inventory", () => {
|
||||
});
|
||||
|
||||
it("script json output is empty", async () => {
|
||||
const result = await getJsonOutput("plugin-sdk-internal");
|
||||
const result = await pluginSdkInternalJsonOutputPromise;
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.stderr).toBe("");
|
||||
@@ -103,7 +106,7 @@ describe("extension relative-outside-package boundary inventory", () => {
|
||||
});
|
||||
|
||||
it("script json output is empty", async () => {
|
||||
const result = await getJsonOutput("relative-outside-package");
|
||||
const result = await relativeOutsidePackageJsonOutputPromise;
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.stderr).toBe("");
|
||||
|
||||
Reference in New Issue
Block a user