File size: 7,196 Bytes
5cb63c1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | // Plugins core loader contract tests cover channel plugin loader setup and teardown behavior.
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { setActivePluginRegistry } from "../../../plugins/runtime.js";
import { withPluginRuntimeRegistryScope } from "../../../plugins/runtime/gateway-request-scope.js";
import {
createChannelTestPluginBase,
createOutboundTestPlugin,
createTestRegistry,
} from "../../../test-utils/channel-plugins.js";
import { loadChannelOutboundAdapter } from "../outbound/load.js";
import { createChannelRegistryLoader } from "../registry-loader.js";
import type { ChannelOutboundAdapter, ChannelPlugin } from "../types.public.js";
const loadChannelPlugin = createChannelRegistryLoader<ChannelPlugin>((entry) => entry.plugin);
const emptyRegistry = createTestRegistry([]);
const demoOutbound: ChannelOutboundAdapter = {
deliveryMode: "direct",
sendText: async () => ({ channel: "demo-loader", messageId: "m1" }),
sendMedia: async () => ({ channel: "demo-loader", messageId: "m2" }),
};
const demoLoaderPlugin: ChannelPlugin = {
...createChannelTestPluginBase({
id: "demo-loader",
label: "Demo Loader",
config: { listAccountIds: () => [], resolveAccount: () => ({}) },
}),
outbound: demoOutbound,
};
const registryWithDemoLoader = createTestRegistry([
{ pluginId: "demo-loader", plugin: demoLoaderPlugin, source: "test" },
]);
const demoOutboundV2: ChannelOutboundAdapter = {
deliveryMode: "direct",
sendText: async () => ({ channel: "demo-loader", messageId: "m3" }),
sendMedia: async () => ({ channel: "demo-loader", messageId: "m4" }),
};
const demoLoaderPluginV2 = createOutboundTestPlugin({
id: "demo-loader",
label: "Demo Loader",
outbound: demoOutboundV2,
});
const registryWithDemoLoaderV2 = createTestRegistry([
{ pluginId: "demo-loader", plugin: demoLoaderPluginV2, source: "test-v2" },
]);
const demoNoOutboundPlugin = createChannelTestPluginBase({
id: "demo-loader",
label: "Demo Loader",
});
const registryWithDemoLoaderNoOutbound = createTestRegistry([
{ pluginId: "demo-loader", plugin: demoNoOutboundPlugin, source: "test-no-outbound" },
]);
describe("channel plugin loader", () => {
async function expectLoadedPluginCase(params: {
registry: Parameters<typeof setActivePluginRegistry>[0];
expectedPlugin: ChannelPlugin;
}) {
setActivePluginRegistry(params.registry);
expect(await loadChannelPlugin("demo-loader")).toBe(params.expectedPlugin);
}
async function expectLoadedOutboundCase(params: {
registry: Parameters<typeof setActivePluginRegistry>[0];
expectedOutbound: ChannelOutboundAdapter | undefined;
}) {
setActivePluginRegistry(params.registry);
expect(await loadChannelOutboundAdapter("demo-loader")).toBe(params.expectedOutbound);
}
async function expectReloadedLoaderCase(params: {
load: typeof loadChannelPlugin | typeof loadChannelOutboundAdapter;
firstRegistry: Parameters<typeof setActivePluginRegistry>[0];
secondRegistry: Parameters<typeof setActivePluginRegistry>[0];
firstExpected: ChannelPlugin | ChannelOutboundAdapter | undefined;
secondExpected: ChannelPlugin | ChannelOutboundAdapter | undefined;
}) {
setActivePluginRegistry(params.firstRegistry);
expect(await params.load("demo-loader")).toBe(params.firstExpected);
setActivePluginRegistry(params.secondRegistry);
expect(await params.load("demo-loader")).toBe(params.secondExpected);
}
async function expectOutboundAdapterMissingCase(
registry: Parameters<typeof setActivePluginRegistry>[0],
) {
setActivePluginRegistry(registry);
expect(await loadChannelOutboundAdapter("demo-loader")).toBeUndefined();
}
beforeEach(() => {
setActivePluginRegistry(emptyRegistry);
});
afterEach(() => {
setActivePluginRegistry(emptyRegistry);
});
it("prefers the registry scoped to a bootstrapped channel handler", async () => {
setActivePluginRegistry(emptyRegistry);
const loaded = await withPluginRuntimeRegistryScope(registryWithDemoLoader, () =>
loadChannelOutboundAdapter("demo-loader"),
);
expect(loaded).toBe(demoOutbound);
});
it("does not escape the scoped registry when the channel is omitted", async () => {
setActivePluginRegistry(registryWithDemoLoader);
const loaded = await withPluginRuntimeRegistryScope(emptyRegistry, () =>
loadChannelOutboundAdapter("demo-loader"),
);
expect(loaded).toBeUndefined();
});
it("preserves a missing adapter from the scoped channel registration", async () => {
setActivePluginRegistry(registryWithDemoLoader);
const loaded = await withPluginRuntimeRegistryScope(registryWithDemoLoaderNoOutbound, () =>
loadChannelOutboundAdapter("demo-loader"),
);
expect(loaded).toBeUndefined();
});
it.each([
{
name: "loads channel plugins from the active registry",
kind: "plugin" as const,
registry: registryWithDemoLoader,
expectedPlugin: demoLoaderPlugin,
},
{
name: "loads outbound adapters from registered plugins",
kind: "outbound" as const,
registry: registryWithDemoLoader,
expectedOutbound: demoOutbound,
},
{
name: "reads updated plugin values when registry changes",
kind: "reload-plugin" as const,
firstRegistry: registryWithDemoLoader,
secondRegistry: registryWithDemoLoaderV2,
firstExpected: demoLoaderPlugin,
secondExpected: demoLoaderPluginV2,
},
{
name: "reads updated outbound values when registry changes",
kind: "reload-outbound" as const,
firstRegistry: registryWithDemoLoader,
secondRegistry: registryWithDemoLoaderV2,
firstExpected: demoOutbound,
secondExpected: demoOutboundV2,
},
{
name: "returns undefined when plugin has no outbound adapter",
kind: "missing-outbound" as const,
registry: registryWithDemoLoaderNoOutbound,
},
] as const)("$name", async (testCase) => {
switch (testCase.kind) {
case "plugin":
await expectLoadedPluginCase({
registry: testCase.registry,
expectedPlugin: testCase.expectedPlugin,
});
return;
case "outbound":
await expectLoadedOutboundCase({
registry: testCase.registry,
expectedOutbound: testCase.expectedOutbound,
});
return;
case "reload-plugin":
await expectReloadedLoaderCase({
load: loadChannelPlugin,
firstRegistry: testCase.firstRegistry,
secondRegistry: testCase.secondRegistry,
firstExpected: testCase.firstExpected,
secondExpected: testCase.secondExpected,
});
return;
case "reload-outbound":
await expectReloadedLoaderCase({
load: loadChannelOutboundAdapter,
firstRegistry: testCase.firstRegistry,
secondRegistry: testCase.secondRegistry,
firstExpected: testCase.firstExpected,
secondExpected: testCase.secondExpected,
});
return;
case "missing-outbound":
await expectOutboundAdapterMissingCase(testCase.registry);
}
});
});
|