From 4ba2c1b484e2e52bc8b055dc66f4dad944fac8be Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 2 Aug 2026 04:38:09 +0800 Subject: [PATCH] test(ci): stabilize plugin prerelease validation --- .../bundled-capability-runtime.test.ts | 76 ++++++++++++++++--- .../manifest-model-id-normalization.test.ts | 4 +- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/src/plugins/bundled-capability-runtime.test.ts b/src/plugins/bundled-capability-runtime.test.ts index 412b275afee5..df44fdc4b76f 100644 --- a/src/plugins/bundled-capability-runtime.test.ts +++ b/src/plugins/bundled-capability-runtime.test.ts @@ -60,6 +60,56 @@ function writeArtifactPreferencePlugin(id: string): TempPlugin { return plugin; } +function writeDistShimCapabilityPlugin(id: string): TempPlugin { + return writePlugin({ + id, + filename: "index.ts", + body: `import { isVoiceCompatibleAudio } from "openclaw/plugin-sdk/media-runtime"; + +void isVoiceCompatibleAudio; + +export default { + id: ${JSON.stringify(id)}, + register(api) { + api.registerMediaUnderstandingProvider({ id: ${JSON.stringify(id)} }); + api.registerWebSearchProvider({ id: ${JSON.stringify(id)} }); + api.registerMigrationProvider({ id: ${JSON.stringify(id)} }); + }, +};`, + }); +} + +function writeChannelCapabilityPlugin(id: string): TempPlugin { + const plugin = writePlugin({ + id, + body: `module.exports = { + id: ${JSON.stringify(id)}, + register(api) { + if (api.registrationMode === "discovery") { + api.registerTranscriptSourceProvider({ + id: ${JSON.stringify(`${id}-voice`)}, + name: "Voice transcripts", + sourceKinds: ["meeting"], + }); + } + }, + };`, + }); + fs.writeFileSync( + path.join(plugin.dir, "openclaw.plugin.json"), + JSON.stringify( + { + id, + channels: [id], + configSchema: { type: "object", additionalProperties: false, properties: {} }, + }, + null, + 2, + ), + ); + return plugin; +} + function loadCanonicalFixture(plugin: TempPlugin, discovery: PluginDiscoveryResult) { return loadOpenClawPlugins({ config: { @@ -147,39 +197,43 @@ describe("loadBundledCapabilityRuntimeRegistry", () => { ); it("loads runtime-backed bundled capabilities through the dist SDK shims", () => { + const target = writeDistShimCapabilityPlugin("capability-dist-shims"); const registry = loadBundledCapabilityRuntimeRegistry({ - pluginIds: ["codex"], + pluginIds: [target.id], pluginSdkResolution: "dist", + discovery: discoveryFor(target), }); - const plugin = registry.plugins.find((entry) => entry.id === "codex"); + const plugin = registry.plugins.find((entry) => entry.id === target.id); expect( plugin?.status, JSON.stringify({ plugin, diagnostics: registry.diagnostics }, null, 2), ).toBe("loaded"); expect(registry.mediaUnderstandingProviders.map((entry) => entry.provider.id)).toEqual([ - "codex", + target.id, ]); - expect(registry.webSearchProviders.map((entry) => entry.provider.id)).toEqual(["codex"]); - expect(registry.migrationProviders.map((entry) => entry.provider.id)).toEqual(["codex"]); + expect(registry.webSearchProviders.map((entry) => entry.provider.id)).toEqual([target.id]); + expect(registry.migrationProviders.map((entry) => entry.provider.id)).toEqual([target.id]); }); - it("registers Discord voice transcript capabilities without full channel activation", () => { + it("registers channel capabilities during discovery without replacing the active registry", () => { + const target = writeChannelCapabilityPlugin("capability-channel"); const active = createEmptyPluginRegistry(); - setActivePluginRegistry(active, "existing-discord-registry"); + setActivePluginRegistry(active, "existing-channel-registry"); const registry = loadBundledCapabilityRuntimeRegistry({ - pluginIds: ["discord"], + pluginIds: [target.id], pluginSdkResolution: "dist", + discovery: discoveryFor(target), }); - const plugin = registry.plugins.find((entry) => entry.id === "discord"); + const plugin = registry.plugins.find((entry) => entry.id === target.id); expect( plugin?.status, JSON.stringify({ plugin, diagnostics: registry.diagnostics }, null, 2), ).toBe("loaded"); - expect(plugin?.transcriptSourceProviderIds).toEqual(["discord-voice"]); + expect(plugin?.transcriptSourceProviderIds).toEqual([`${target.id}-voice`]); expect(registry.transcriptSourceProviders.map((entry) => entry.provider.id)).toEqual([ - "discord-voice", + `${target.id}-voice`, ]); expect(registry.typedHooks).toEqual([]); expect(getActivePluginRegistry()).toBe(active); diff --git a/src/plugins/manifest-model-id-normalization.test.ts b/src/plugins/manifest-model-id-normalization.test.ts index 9d32ef8d74c4..28062b220abd 100644 --- a/src/plugins/manifest-model-id-normalization.test.ts +++ b/src/plugins/manifest-model-id-normalization.test.ts @@ -108,7 +108,7 @@ describe("manifest model id normalization", () => { } }); - it("keeps process metadata stable across manifest edits and reflects lifecycle resets", () => { + it("reflects manifest edits and state directory changes without a prepared snapshot", () => { const stateDirA = makeTempDir(); const pluginDirA = path.join(stateDirA, "extensions", "normalizer"); writeInstallIndex({ stateDir: stateDirA, pluginDir: pluginDirA }); @@ -122,7 +122,7 @@ describe("manifest model id normalization", () => { expect(normalizeDemoModel()).toBe("alpha/demo-model"); writeNormalizerManifest({ pluginDir: pluginDirA, prefix: "bravo-local" }); - expect(normalizeDemoModel()).toBe("alpha/demo-model"); + expect(normalizeDemoModel()).toBe("bravo-local/demo-model"); const stateDirB = makeTempDir(); const pluginDirB = path.join(stateDirB, "extensions", "normalizer");