test: dedupe plugin utility config suites

This commit is contained in:
Peter Steinberger
2026-03-28 04:43:20 +00:00
parent c9d5d12183
commit 25fea00bc7
6 changed files with 123 additions and 68 deletions

View File

@@ -70,9 +70,6 @@ function expectStartupPluginIds(config: OpenClawConfig, expected: readonly strin
env: process.env,
}),
).toEqual(expected);
}
function expectManifestRegistryFixture() {
expect(loadPluginManifestRegistry).toHaveBeenCalled();
}
@@ -123,6 +120,5 @@ describe("resolveGatewayStartupPluginIds", () => {
],
] as const)("%s", (_name, config, expected) => {
expectStartupPluginIds(config, expected);
expectManifestRegistryFixture();
});
});

View File

@@ -54,6 +54,33 @@ function expectPluginLoaderConfig(config: OpenClawConfig) {
);
}
function createAutoEnabledCliFixture() {
const rawConfig = {
plugins: {},
channels: { demo: { enabled: true } },
} as OpenClawConfig;
const autoEnabledConfig = {
...rawConfig,
plugins: {
entries: {
demo: { enabled: true },
},
},
} as OpenClawConfig;
return { rawConfig, autoEnabledConfig };
}
function expectAutoEnabledCliLoad(params: {
rawConfig: OpenClawConfig;
autoEnabledConfig: OpenClawConfig;
}) {
expect(mocks.applyPluginAutoEnable).toHaveBeenCalledWith({
config: params.rawConfig,
env: process.env,
});
expectPluginLoaderConfig(params.autoEnabledConfig);
}
describe("registerPluginCliCommands", () => {
beforeEach(() => {
mocks.memoryRegister.mockClear();
@@ -89,27 +116,12 @@ describe("registerPluginCliCommands", () => {
it("loads plugin CLI commands from the auto-enabled config snapshot", () => {
const program = createProgram();
const rawConfig = {
plugins: {},
channels: { demo: { enabled: true } },
} as OpenClawConfig;
const autoEnabledConfig = {
...rawConfig,
plugins: {
entries: {
demo: { enabled: true },
},
},
} as OpenClawConfig;
const { rawConfig, autoEnabledConfig } = createAutoEnabledCliFixture();
mocks.applyPluginAutoEnable.mockReturnValue({ config: autoEnabledConfig, changes: [] });
registerPluginCliCommands(program, rawConfig);
expect(mocks.applyPluginAutoEnable).toHaveBeenCalledWith({
config: rawConfig,
env: process.env,
});
expectPluginLoaderConfig(autoEnabledConfig);
expectAutoEnabledCliLoad({ rawConfig, autoEnabledConfig });
expect(mocks.memoryRegister).toHaveBeenCalledWith(
expect.objectContaining({
config: autoEnabledConfig,

View File

@@ -23,6 +23,13 @@ function createExpectedResolutionFields(
};
}
function expectResolutionFields(
input: Parameters<typeof buildNpmResolutionInstallFields>[0],
overrides: Partial<ReturnType<typeof buildNpmResolutionInstallFields>>,
) {
expect(buildNpmResolutionInstallFields(input)).toEqual(createExpectedResolutionFields(overrides));
}
describe("buildNpmResolutionInstallFields", () => {
it.each([
{
@@ -52,6 +59,17 @@ describe("buildNpmResolutionInstallFields", () => {
] as const)("$name", ({ input, expected }) => {
expect(buildNpmResolutionInstallFields(input)).toEqual(expected);
});
it("keeps missing partial resolution fields undefined", () => {
expectResolutionFields(
{
name: "@openclaw/demo",
},
{
resolvedName: "@openclaw/demo",
},
);
});
});
describe("recordPluginInstall", () => {

View File

@@ -62,6 +62,22 @@ function createSingleCatalogProvider(overrides: Partial<ModelProviderConfig> & {
};
}
function createPairedCatalogProviders(
apiKey: string,
overrides: Partial<ModelProviderConfig> = {},
) {
return {
alpha: {
...createProviderConfig(overrides),
apiKey,
},
beta: {
...createProviderConfig(overrides),
apiKey,
},
};
}
async function expectSingleCatalogResult(params: {
ctx: ProviderCatalogContext;
allowExplicitBaseUrl?: boolean;
@@ -199,20 +215,7 @@ describe("buildSingleProviderApiKeyCatalog", () => {
ctx: createCatalogContext({
apiKeys: { "test-provider": "secret-key" },
}),
expected: {
alpha: {
api: "openai-completions",
baseUrl: "https://default.example/v1",
models: [],
apiKey: "secret-key",
},
beta: {
api: "openai-completions",
baseUrl: "https://default.example/v1",
models: [],
apiKey: "secret-key",
},
},
expected: createPairedCatalogProviders("secret-key"),
});
});
});

View File

@@ -68,6 +68,23 @@ describe("applyExclusiveSlotSelection", () => {
expect(result.warnings).toHaveLength(0);
}
function expectUnchangedSelectionCase(params: {
config: OpenClawConfig;
selectedId: string;
selectedKind?: string;
registry?: { plugins: Array<{ id: string; kind: string }> };
}) {
const result = applyExclusiveSlotSelection({
config: params.config,
selectedId: params.selectedId,
...(params.selectedKind ? { selectedKind: params.selectedKind } : {}),
...(params.registry ? { registry: params.registry } : {}),
});
expectUnchangedSelection(result);
expect(result.config).toBe(params.config);
}
it("selects the slot and disables other entries for the same kind", () => {
const config = createMemoryConfig({
slots: { memory: "memory-core" },
@@ -88,19 +105,28 @@ describe("applyExclusiveSlotSelection", () => {
});
});
it("does nothing when the slot already matches", () => {
const config = createMemoryConfig({
slots: { memory: "memory" },
});
const result = applyExclusiveSlotSelection({
config,
it.each([
{
name: "does nothing when the slot already matches",
config: createMemoryConfig({
slots: { memory: "memory" },
}),
selectedId: "memory",
selectedKind: "memory",
registry: { plugins: [{ id: "memory", kind: "memory" }] },
},
{
name: "skips changes when no exclusive slot applies",
config: {} as OpenClawConfig,
selectedId: "custom",
},
] as const)("$name", ({ config, selectedId, selectedKind, registry }) => {
expectUnchangedSelectionCase({
config,
selectedId,
...(selectedKind ? { selectedKind } : {}),
...(registry ? { registry } : {}),
});
expectUnchangedSelection(result);
expect(result.config).toBe(config);
});
it.each([
@@ -136,15 +162,4 @@ describe("applyExclusiveSlotSelection", () => {
});
expectSelectionWarnings(result.warnings, warningChecks);
});
it("skips changes when no exclusive slot applies", () => {
const config: OpenClawConfig = {};
const result = applyExclusiveSlotSelection({
config,
selectedId: "custom",
});
expectUnchangedSelection(result);
expect(result.config).toBe(config);
});
});

View File

@@ -28,6 +28,22 @@ function expectFormattedSource(params: {
expect(out.rootKey).toBe(params.expectedRootKey);
}
function expectResolvedSourceRoots(params: {
homeDir: string;
env: NodeJS.ProcessEnv;
workspaceDir: string;
expected: Record<"stock" | "global" | "workspace", string>;
}) {
const roots = withPathResolutionEnv(params.homeDir, params.env, (env) =>
resolvePluginSourceRoots({
env,
workspaceDir: params.workspaceDir,
}),
);
expect(roots).toEqual(params.expected);
}
describe("formatPluginSourceForTable", () => {
it.each([
{
@@ -73,23 +89,18 @@ describe("formatPluginSourceForTable", () => {
it("resolves source roots from an explicit env override", () => {
const homeDir = path.resolve(path.sep, "tmp", "openclaw-home");
const roots = withPathResolutionEnv(
expectResolvedSourceRoots({
homeDir,
{
env: {
OPENCLAW_BUNDLED_PLUGINS_DIR: "~/bundled",
OPENCLAW_STATE_DIR: "~/state",
} as NodeJS.ProcessEnv,
workspaceDir: "~/ws",
expected: {
stock: path.join(homeDir, "bundled"),
global: path.join(homeDir, "state", "extensions"),
workspace: path.join(homeDir, "ws", ".openclaw", "extensions"),
},
(env) =>
resolvePluginSourceRoots({
env,
workspaceDir: "~/ws",
}),
);
expect(roots).toEqual({
stock: path.join(homeDir, "bundled"),
global: path.join(homeDir, "state", "extensions"),
workspace: path.join(homeDir, "ws", ".openclaw", "extensions"),
});
});
});