mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-23 07:01:40 +00:00
166 lines
5.0 KiB
TypeScript
166 lines
5.0 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
|
|
const mockLoadPluginManifestRegistry = vi.hoisted(() => vi.fn());
|
|
|
|
let validateConfigObjectWithPlugins: typeof import("./validation.js").validateConfigObjectWithPlugins;
|
|
let validateConfigObjectRawWithPlugins: typeof import("./validation.js").validateConfigObjectRawWithPlugins;
|
|
|
|
vi.mock("../plugins/manifest-registry.js", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("../plugins/manifest-registry.js")>();
|
|
return {
|
|
...actual,
|
|
loadPluginManifestRegistry: (...args: unknown[]) => mockLoadPluginManifestRegistry(...args),
|
|
resolveManifestContractPluginIds: () => [],
|
|
};
|
|
});
|
|
|
|
vi.mock("../plugins/doctor-contract-registry.js", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("../plugins/doctor-contract-registry.js")>();
|
|
return {
|
|
...actual,
|
|
listPluginDoctorLegacyConfigRules: () => [],
|
|
applyPluginDoctorCompatibilityMigrations: () => ({ next: null, changes: [] }),
|
|
};
|
|
});
|
|
|
|
async function loadValidationModule() {
|
|
vi.resetModules();
|
|
({ validateConfigObjectWithPlugins, validateConfigObjectRawWithPlugins } =
|
|
await import("./validation.js"));
|
|
}
|
|
|
|
function setupTelegramSchemaWithDefault() {
|
|
mockLoadPluginManifestRegistry.mockReturnValue({
|
|
diagnostics: [],
|
|
plugins: [
|
|
{
|
|
id: "telegram",
|
|
origin: "bundled",
|
|
channels: ["telegram"],
|
|
channelCatalogMeta: {
|
|
id: "telegram",
|
|
label: "Telegram",
|
|
blurb: "Telegram channel",
|
|
},
|
|
channelConfigs: {
|
|
telegram: {
|
|
schema: {
|
|
type: "object",
|
|
properties: {
|
|
dmPolicy: {
|
|
type: "string",
|
|
enum: ["pairing", "allowlist"],
|
|
default: "pairing",
|
|
},
|
|
},
|
|
// validateConfigObjectWithPlugins starts from the core validated
|
|
// config, which can already include bundled runtime defaults for
|
|
// the channel. Keep this mock schema focused on the plugin-owned
|
|
// default under test instead of rejecting unrelated core fields.
|
|
additionalProperties: true,
|
|
},
|
|
uiHints: {},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
function setupPluginSchemaWithRequiredDefault() {
|
|
mockLoadPluginManifestRegistry.mockReturnValue({
|
|
diagnostics: [],
|
|
plugins: [
|
|
{
|
|
id: "opik",
|
|
origin: "bundled",
|
|
channels: [],
|
|
providers: [],
|
|
kind: ["tool"],
|
|
configSchema: {
|
|
type: "object",
|
|
properties: {
|
|
workspace: {
|
|
type: "string",
|
|
default: "default-workspace",
|
|
},
|
|
},
|
|
required: ["workspace"],
|
|
additionalProperties: true,
|
|
},
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
describe("validateConfigObjectWithPlugins channel metadata (applyDefaults: true)", () => {
|
|
it("applies bundled channel defaults from plugin-owned schema metadata", async () => {
|
|
setupTelegramSchemaWithDefault();
|
|
await loadValidationModule();
|
|
|
|
const result = validateConfigObjectWithPlugins({
|
|
channels: {
|
|
telegram: {},
|
|
},
|
|
});
|
|
|
|
expect(result.ok).toBe(true);
|
|
if (result.ok) {
|
|
expect(result.config.channels?.telegram).toEqual(
|
|
expect.objectContaining({ dmPolicy: "pairing" }),
|
|
);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("validateConfigObjectRawWithPlugins channel metadata", () => {
|
|
it("still injects channel AJV defaults even in raw mode — persistence safety is handled by io.ts", async () => {
|
|
// Channel and plugin AJV validation always runs with applyDefaults: true
|
|
// (hardcoded) to avoid breaking schemas that mark defaulted fields as
|
|
// required (e.g., BlueBubbles enrichGroupParticipantsFromContacts).
|
|
//
|
|
// The actual protection against leaking these defaults to disk lives in
|
|
// writeConfigFile (io.ts), which uses persistCandidate (the pre-validation
|
|
// merge-patched value) instead of validated.config.
|
|
setupTelegramSchemaWithDefault();
|
|
await loadValidationModule();
|
|
|
|
const result = validateConfigObjectRawWithPlugins({
|
|
channels: {
|
|
telegram: {},
|
|
},
|
|
});
|
|
|
|
expect(result.ok).toBe(true);
|
|
if (result.ok) {
|
|
// AJV defaults ARE injected into validated.config even in raw mode.
|
|
// This is intentional — see comment above.
|
|
expect(result.config.channels?.telegram).toEqual(
|
|
expect.objectContaining({ dmPolicy: "pairing" }),
|
|
);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("validateConfigObjectRawWithPlugins plugin config defaults", () => {
|
|
it("does not inject plugin AJV defaults in raw mode for plugin-owned config", async () => {
|
|
setupPluginSchemaWithRequiredDefault();
|
|
await loadValidationModule();
|
|
|
|
const result = validateConfigObjectRawWithPlugins({
|
|
plugins: {
|
|
entries: {
|
|
opik: {
|
|
enabled: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.ok).toBe(true);
|
|
if (result.ok) {
|
|
expect(result.config.plugins?.entries?.opik?.config).toBeUndefined();
|
|
}
|
|
});
|
|
});
|