mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-29 02:41:07 +00:00
test: dedupe config compatibility fixtures
This commit is contained in:
@@ -74,6 +74,25 @@ async function writeManifestlessClaudeBundleFixture(params: { dir: string }) {
|
||||
await fs.writeFile(path.join(params.dir, "settings.json"), '{"hideThinkingBlock":true}', "utf-8");
|
||||
}
|
||||
|
||||
function expectRemovedPluginWarnings(
|
||||
result: { ok: boolean; warnings?: Array<{ path: string; message: string }> },
|
||||
removedId: string,
|
||||
removedLabel: string,
|
||||
) {
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
const message = `plugin removed: ${removedLabel} (stale config entry ignored; remove it from plugins config)`;
|
||||
expect(result.warnings).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ path: `plugins.entries.${removedId}`, message },
|
||||
{ path: "plugins.allow", message },
|
||||
{ path: "plugins.deny", message },
|
||||
{ path: "plugins.slots.memory", message },
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
describe("config plugin validation", () => {
|
||||
let fixtureRoot = "";
|
||||
let suiteHome = "";
|
||||
@@ -99,6 +118,18 @@ describe("config plugin validation", () => {
|
||||
const validateInSuite = (raw: unknown) =>
|
||||
validateConfigObjectWithPlugins(raw, { env: suiteEnv() });
|
||||
|
||||
const validateRemovedPluginConfig = (removedId: string) =>
|
||||
validateInSuite({
|
||||
agents: { list: [{ id: "pi" }] },
|
||||
plugins: {
|
||||
enabled: false,
|
||||
entries: { [removedId]: { enabled: true } },
|
||||
allow: [removedId],
|
||||
deny: [removedId],
|
||||
slots: { memory: removedId },
|
||||
},
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-config-plugin-validation-"));
|
||||
await chmodSafeDir(fixtureRoot);
|
||||
@@ -267,84 +298,14 @@ describe("config plugin validation", () => {
|
||||
|
||||
it("warns for removed legacy plugin ids instead of failing validation", async () => {
|
||||
const removedId = "google-antigravity-auth";
|
||||
const res = validateInSuite({
|
||||
agents: { list: [{ id: "pi" }] },
|
||||
plugins: {
|
||||
enabled: false,
|
||||
entries: { [removedId]: { enabled: true } },
|
||||
allow: [removedId],
|
||||
deny: [removedId],
|
||||
slots: { memory: removedId },
|
||||
},
|
||||
});
|
||||
expect(res.ok).toBe(true);
|
||||
if (res.ok) {
|
||||
expect(res.warnings).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
path: `plugins.entries.${removedId}`,
|
||||
message:
|
||||
"plugin removed: google-antigravity-auth (stale config entry ignored; remove it from plugins config)",
|
||||
},
|
||||
{
|
||||
path: "plugins.allow",
|
||||
message:
|
||||
"plugin removed: google-antigravity-auth (stale config entry ignored; remove it from plugins config)",
|
||||
},
|
||||
{
|
||||
path: "plugins.deny",
|
||||
message:
|
||||
"plugin removed: google-antigravity-auth (stale config entry ignored; remove it from plugins config)",
|
||||
},
|
||||
{
|
||||
path: "plugins.slots.memory",
|
||||
message:
|
||||
"plugin removed: google-antigravity-auth (stale config entry ignored; remove it from plugins config)",
|
||||
},
|
||||
]),
|
||||
);
|
||||
}
|
||||
const res = validateRemovedPluginConfig(removedId);
|
||||
expectRemovedPluginWarnings(res, removedId, removedId);
|
||||
});
|
||||
|
||||
it("warns for removed google gemini auth plugin ids instead of failing validation", async () => {
|
||||
const removedId = "google-gemini-cli-auth";
|
||||
const res = validateInSuite({
|
||||
agents: { list: [{ id: "pi" }] },
|
||||
plugins: {
|
||||
enabled: false,
|
||||
entries: { [removedId]: { enabled: true } },
|
||||
allow: [removedId],
|
||||
deny: [removedId],
|
||||
slots: { memory: removedId },
|
||||
},
|
||||
});
|
||||
expect(res.ok).toBe(true);
|
||||
if (res.ok) {
|
||||
expect(res.warnings).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
path: `plugins.entries.${removedId}`,
|
||||
message:
|
||||
"plugin removed: google-gemini-cli-auth (stale config entry ignored; remove it from plugins config)",
|
||||
},
|
||||
{
|
||||
path: "plugins.allow",
|
||||
message:
|
||||
"plugin removed: google-gemini-cli-auth (stale config entry ignored; remove it from plugins config)",
|
||||
},
|
||||
{
|
||||
path: "plugins.deny",
|
||||
message:
|
||||
"plugin removed: google-gemini-cli-auth (stale config entry ignored; remove it from plugins config)",
|
||||
},
|
||||
{
|
||||
path: "plugins.slots.memory",
|
||||
message:
|
||||
"plugin removed: google-gemini-cli-auth (stale config entry ignored; remove it from plugins config)",
|
||||
},
|
||||
]),
|
||||
);
|
||||
}
|
||||
const res = validateRemovedPluginConfig(removedId);
|
||||
expectRemovedPluginWarnings(res, removedId, removedId);
|
||||
});
|
||||
|
||||
it("does not auto-allow config-loaded overrides of bundled web search plugin ids", async () => {
|
||||
|
||||
@@ -35,6 +35,36 @@ function createIoForHome(home: string, env: NodeJS.ProcessEnv = {} as NodeJS.Pro
|
||||
});
|
||||
}
|
||||
|
||||
async function expectNoNewerVersionWarning(touchedVersion: string) {
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".openclaw");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
const configPath = path.join(configDir, "openclaw.json");
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
JSON.stringify({ meta: { lastTouchedVersion: touchedVersion } }, null, 2),
|
||||
);
|
||||
|
||||
const logger = {
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
const io = createConfigIO({
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
homedir: () => home,
|
||||
logger,
|
||||
});
|
||||
|
||||
io.loadConfig();
|
||||
|
||||
expect(logger.warn).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining("Config was last written by a newer OpenClaw"),
|
||||
);
|
||||
expect(io.configPath).toBe(configPath);
|
||||
});
|
||||
}
|
||||
|
||||
describe("config io paths", () => {
|
||||
it("uses ~/.openclaw/openclaw.json when config exists", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
@@ -166,34 +196,7 @@ describe("config io paths", () => {
|
||||
throw new Error(`Unable to parse VERSION: ${VERSION}`);
|
||||
}
|
||||
const touchedVersion = `${parsedVersion.major}.${parsedVersion.minor}.${parsedVersion.patch}-${(parsedVersion.revision ?? 0) + 1}`;
|
||||
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".openclaw");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
const configPath = path.join(configDir, "openclaw.json");
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
JSON.stringify({ meta: { lastTouchedVersion: touchedVersion } }, null, 2),
|
||||
);
|
||||
|
||||
const logger = {
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
const io = createConfigIO({
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
homedir: () => home,
|
||||
logger,
|
||||
});
|
||||
|
||||
io.loadConfig();
|
||||
|
||||
expect(logger.warn).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining("Config was last written by a newer OpenClaw"),
|
||||
);
|
||||
expect(io.configPath).toBe(configPath);
|
||||
});
|
||||
await expectNoNewerVersionWarning(touchedVersion);
|
||||
});
|
||||
|
||||
it("does not warn for same-base prerelease configs when current version is newer", async () => {
|
||||
@@ -202,33 +205,6 @@ describe("config io paths", () => {
|
||||
throw new Error(`Unable to parse VERSION: ${VERSION}`);
|
||||
}
|
||||
const touchedVersion = `${parsedVersion.major}.${parsedVersion.minor}.${parsedVersion.patch}-beta.1`;
|
||||
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".openclaw");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
const configPath = path.join(configDir, "openclaw.json");
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
JSON.stringify({ meta: { lastTouchedVersion: touchedVersion } }, null, 2),
|
||||
);
|
||||
|
||||
const logger = {
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
const io = createConfigIO({
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
homedir: () => home,
|
||||
logger,
|
||||
});
|
||||
|
||||
io.loadConfig();
|
||||
|
||||
expect(logger.warn).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining("Config was last written by a newer OpenClaw"),
|
||||
);
|
||||
expect(io.configPath).toBe(configPath);
|
||||
});
|
||||
await expectNoNewerVersionWarning(touchedVersion);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,6 +38,19 @@ function makeSnapshot(params: { valid: boolean; config?: OpenClawConfig }): Conf
|
||||
};
|
||||
}
|
||||
|
||||
async function readSchemaNodes() {
|
||||
const { readBestEffortRuntimeConfigSchema } = await import("./runtime-schema.js");
|
||||
const result = await readBestEffortRuntimeConfigSchema();
|
||||
const schema = result.schema as { properties?: Record<string, unknown> };
|
||||
const channelsNode = schema.properties?.channels as Record<string, unknown> | undefined;
|
||||
const channelProps = channelsNode?.properties as Record<string, unknown> | undefined;
|
||||
const pluginsNode = schema.properties?.plugins as Record<string, unknown> | undefined;
|
||||
const pluginProps = pluginsNode?.properties as Record<string, unknown> | undefined;
|
||||
const entriesNode = pluginProps?.entries as Record<string, unknown> | undefined;
|
||||
const entryProps = entriesNode?.properties as Record<string, unknown> | undefined;
|
||||
return { channelProps, entryProps };
|
||||
}
|
||||
|
||||
describe("readBestEffortRuntimeConfigSchema", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
@@ -90,15 +103,7 @@ describe("readBestEffortRuntimeConfigSchema", () => {
|
||||
channelSetups: [],
|
||||
});
|
||||
|
||||
const { readBestEffortRuntimeConfigSchema } = await import("./runtime-schema.js");
|
||||
const result = await readBestEffortRuntimeConfigSchema();
|
||||
const schema = result.schema as { properties?: Record<string, unknown> };
|
||||
const channelsNode = schema.properties?.channels as Record<string, unknown> | undefined;
|
||||
const channelProps = channelsNode?.properties as Record<string, unknown> | undefined;
|
||||
const pluginsNode = schema.properties?.plugins as Record<string, unknown> | undefined;
|
||||
const pluginProps = pluginsNode?.properties as Record<string, unknown> | undefined;
|
||||
const entriesNode = pluginProps?.entries as Record<string, unknown> | undefined;
|
||||
const entryProps = entriesNode?.properties as Record<string, unknown> | undefined;
|
||||
const { channelProps, entryProps } = await readSchemaNodes();
|
||||
|
||||
expect(mockLoadOpenClawPlugins).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@@ -157,15 +162,7 @@ describe("readBestEffortRuntimeConfigSchema", () => {
|
||||
],
|
||||
});
|
||||
|
||||
const { readBestEffortRuntimeConfigSchema } = await import("./runtime-schema.js");
|
||||
const result = await readBestEffortRuntimeConfigSchema();
|
||||
const schema = result.schema as { properties?: Record<string, unknown> };
|
||||
const channelsNode = schema.properties?.channels as Record<string, unknown> | undefined;
|
||||
const channelProps = channelsNode?.properties as Record<string, unknown> | undefined;
|
||||
const pluginsNode = schema.properties?.plugins as Record<string, unknown> | undefined;
|
||||
const pluginProps = pluginsNode?.properties as Record<string, unknown> | undefined;
|
||||
const entriesNode = pluginProps?.entries as Record<string, unknown> | undefined;
|
||||
const entryProps = entriesNode?.properties as Record<string, unknown> | undefined;
|
||||
const { channelProps, entryProps } = await readSchemaNodes();
|
||||
|
||||
expect(mockLoadOpenClawPlugins).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
|
||||
Reference in New Issue
Block a user