Skip to content

Commit e4490be

Browse files
committed
test(doctor): cover partial legacy migration writes
1 parent 9cefc67 commit e4490be

2 files changed

Lines changed: 86 additions & 36 deletions

File tree

src/commands/doctor/shared/legacy-config-migrate.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, expect, it } from "vitest";
22
import type { OpenClawConfig } from "../../../config/types.js";
3+
import { migrateLegacyConfig } from "./legacy-config-migrate.js";
34
import { LEGACY_CONFIG_MIGRATIONS } from "./legacy-config-migrations.js";
45

56
function migrateLegacyConfigForTest(raw: unknown): {
@@ -210,6 +211,38 @@ describe("legacy migrate mention routing", () => {
210211
});
211212

212213
describe("legacy migrate sandbox scope aliases", () => {
214+
it("returns migrated config when unrelated plugin validation issues remain (#76798)", () => {
215+
const res = migrateLegacyConfig({
216+
agents: {
217+
defaults: {
218+
model: { primary: "openai/gpt-5.5" },
219+
llm: { idleTimeoutSeconds: 120 },
220+
},
221+
},
222+
plugins: {
223+
entries: {
224+
brave: {
225+
enabled: true,
226+
config: { webSearch: { mode: "definitely-invalid" } },
227+
},
228+
},
229+
},
230+
tools: { web: { search: { provider: "brave" } } },
231+
});
232+
233+
expect(res.partiallyValid).toBe(true);
234+
expect(res.changes).toContain(
235+
"Removed agents.defaults.llm; model idle timeout now follows models.providers.<id>.timeoutSeconds.",
236+
);
237+
expect(res.changes).toContain(
238+
"Migration applied; other validation issues remain — run doctor to review.",
239+
);
240+
expect(res.config?.agents?.defaults).toEqual({
241+
model: { primary: "openai/gpt-5.5" },
242+
});
243+
expect(res.config?.tools?.web?.search?.provider).toBe("brave");
244+
});
245+
213246
it("removes legacy agents.defaults.llm timeout config", () => {
214247
const res = migrateLegacyConfigForTest({
215248
agents: {

src/config/io.write-config.test.ts

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,49 +1220,66 @@ describe("config io write", () => {
12201220
});
12211221

12221222
it("skipPluginValidation bypasses plugin schema rejection on writeConfigFile (#76800)", async () => {
1223-
mockLoadPluginManifestRegistry.mockReturnValue({
1224-
diagnostics: [],
1225-
plugins: [
1226-
{
1227-
id: "strict-plugin",
1228-
origin: "bundled",
1229-
channels: [],
1230-
providers: [],
1231-
cliBackends: [],
1232-
skills: [],
1233-
hooks: [],
1234-
rootDir: "/tmp/openclaw-test-strict-plugin",
1235-
source: "/tmp/openclaw-test-strict-plugin/index.ts",
1236-
manifestPath: "/tmp/openclaw-test-strict-plugin/openclaw.plugin.json",
1237-
configSchema: {
1238-
type: "object",
1239-
properties: { token: { type: "string" } },
1240-
required: ["token"],
1241-
additionalProperties: false,
1242-
},
1243-
},
1244-
],
1245-
} satisfies PluginManifestRegistry);
1246-
12471223
await withSuiteHome(async (home) => {
12481224
const configPath = path.join(home, ".openclaw", "openclaw.json");
1225+
const previousConfigPath = process.env.OPENCLAW_CONFIG_PATH;
1226+
process.env.OPENCLAW_CONFIG_PATH = configPath;
12491227
await fs.mkdir(path.dirname(configPath), { recursive: true });
12501228
await fs.writeFile(configPath, "{}\n", "utf-8");
1251-
// Plugin is enabled but missing required "token" — validation fails without skip
1252-
const cfg: OpenClawConfig = {
1253-
agents: { list: [{ id: "main", default: true }] },
1254-
plugins: { entries: { "strict-plugin": { enabled: true } } },
1255-
};
1229+
mockLoadPluginManifestRegistry.mockReturnValue({
1230+
diagnostics: [],
1231+
plugins: [
1232+
{
1233+
id: "strict-plugin",
1234+
origin: "bundled",
1235+
channels: [],
1236+
providers: [],
1237+
cliBackends: [],
1238+
skills: [],
1239+
hooks: [],
1240+
rootDir: "/tmp/openclaw-test-strict-plugin",
1241+
source: "/tmp/openclaw-test-strict-plugin/index.ts",
1242+
manifestPath: "/tmp/openclaw-test-strict-plugin/openclaw.plugin.json",
1243+
configSchema: {
1244+
type: "object",
1245+
properties: { token: { type: "string" } },
1246+
required: ["token"],
1247+
additionalProperties: false,
1248+
},
1249+
},
1250+
],
1251+
} satisfies PluginManifestRegistry);
12561252

1257-
await expect(writeConfigFile(cfg, { skipPluginValidation: true })).resolves.not.toThrow();
1253+
try {
1254+
// Plugin is enabled but missing required "token" — validation fails without skip.
1255+
const cfg: OpenClawConfig = {
1256+
agents: { list: [{ id: "main", default: true }] },
1257+
plugins: { entries: { "strict-plugin": { enabled: true } } },
1258+
};
12581259

1259-
await expect(writeConfigFile(cfg, { skipPluginValidation: false })).rejects.toThrow();
1260-
});
1260+
await expect(writeConfigFile(cfg, { skipPluginValidation: true })).resolves.not.toThrow();
1261+
await expect(fs.readFile(configPath, "utf-8")).resolves.toContain('"strict-plugin"');
12611262

1262-
mockLoadPluginManifestRegistry.mockReturnValue({
1263-
diagnostics: [],
1264-
plugins: [],
1265-
} satisfies PluginManifestRegistry);
1263+
await expect(writeConfigFile(cfg, { skipPluginValidation: false })).rejects.toThrow(
1264+
/Config validation failed/,
1265+
);
1266+
await expect(
1267+
writeConfigFile({ agents: { list: "not-array" } } as unknown as OpenClawConfig, {
1268+
skipPluginValidation: true,
1269+
}),
1270+
).rejects.toThrow(/Config validation failed/);
1271+
} finally {
1272+
mockLoadPluginManifestRegistry.mockReturnValue({
1273+
diagnostics: [],
1274+
plugins: [],
1275+
} satisfies PluginManifestRegistry);
1276+
if (previousConfigPath === undefined) {
1277+
delete process.env.OPENCLAW_CONFIG_PATH;
1278+
} else {
1279+
process.env.OPENCLAW_CONFIG_PATH = previousConfigPath;
1280+
}
1281+
}
1282+
});
12661283
});
12671284

12681285
it("preserves authored tilde paths when runtime-shaped writes hand back absolute paths", async () => {

0 commit comments

Comments
 (0)