mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 14:49:02 +00:00
refactor: dedupe core config and runtime helpers
This commit is contained in:
@@ -26,6 +26,22 @@ async function expectLoadRejectionPreservesField(params: {
|
||||
});
|
||||
}
|
||||
|
||||
type ConfigSnapshot = Awaited<ReturnType<typeof readConfigFileSnapshot>>;
|
||||
|
||||
async function withSnapshotForConfig(
|
||||
config: unknown,
|
||||
run: (params: { snapshot: ConfigSnapshot; parsed: unknown; configPath: string }) => Promise<void>,
|
||||
) {
|
||||
await withTempHome(async (home) => {
|
||||
const configPath = path.join(home, ".openclaw", "openclaw.json");
|
||||
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
||||
await fs.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
||||
const snapshot = await readConfigFileSnapshot();
|
||||
const parsed = JSON.parse(await fs.readFile(configPath, "utf-8")) as unknown;
|
||||
await run({ snapshot, parsed, configPath });
|
||||
});
|
||||
}
|
||||
|
||||
function expectValidConfigValue(params: {
|
||||
config: unknown;
|
||||
readValue: (config: unknown) => unknown;
|
||||
@@ -47,6 +63,20 @@ function expectInvalidIssuePath(config: unknown, expectedPath: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function expectRoutingAllowFromLegacySnapshot(
|
||||
ctx: { snapshot: ConfigSnapshot; parsed: unknown },
|
||||
expectedAllowFrom: string[],
|
||||
) {
|
||||
expect(ctx.snapshot.valid).toBe(false);
|
||||
expect(ctx.snapshot.legacyIssues.some((issue) => issue.path === "routing.allowFrom")).toBe(true);
|
||||
const parsed = ctx.parsed as {
|
||||
routing?: { allowFrom?: string[] };
|
||||
channels?: unknown;
|
||||
};
|
||||
expect(parsed.routing?.allowFrom).toEqual(expectedAllowFrom);
|
||||
expect(parsed.channels).toBeUndefined();
|
||||
}
|
||||
|
||||
describe("legacy config detection", () => {
|
||||
it('accepts imessage.dmPolicy="open" with allowFrom "*"', async () => {
|
||||
const res = validateConfigObject({
|
||||
@@ -224,43 +254,30 @@ describe("legacy config detection", () => {
|
||||
expect((res.config as { agent?: unknown } | undefined)?.agent).toBeUndefined();
|
||||
});
|
||||
it("flags legacy config in snapshot", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configPath = path.join(home, ".openclaw", "openclaw.json");
|
||||
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
JSON.stringify({ routing: { allowFrom: ["+15555550123"] } }),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const snap = await readConfigFileSnapshot();
|
||||
|
||||
expect(snap.valid).toBe(false);
|
||||
expect(snap.legacyIssues.some((issue) => issue.path === "routing.allowFrom")).toBe(true);
|
||||
|
||||
const raw = await fs.readFile(configPath, "utf-8");
|
||||
const parsed = JSON.parse(raw) as {
|
||||
routing?: { allowFrom?: string[] };
|
||||
channels?: unknown;
|
||||
};
|
||||
expect(parsed.routing?.allowFrom).toEqual(["+15555550123"]);
|
||||
expect(parsed.channels).toBeUndefined();
|
||||
await withSnapshotForConfig({ routing: { allowFrom: ["+15555550123"] } }, async (ctx) => {
|
||||
expectRoutingAllowFromLegacySnapshot(ctx, ["+15555550123"]);
|
||||
});
|
||||
});
|
||||
it("flags top-level memorySearch as legacy in snapshot", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configPath = path.join(home, ".openclaw", "openclaw.json");
|
||||
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
JSON.stringify({ memorySearch: { provider: "local", fallback: "none" } }),
|
||||
"utf-8",
|
||||
);
|
||||
await withSnapshotForConfig(
|
||||
{ memorySearch: { provider: "local", fallback: "none" } },
|
||||
async (ctx) => {
|
||||
expect(ctx.snapshot.valid).toBe(false);
|
||||
expect(ctx.snapshot.legacyIssues.some((issue) => issue.path === "memorySearch")).toBe(true);
|
||||
},
|
||||
);
|
||||
});
|
||||
it("flags legacy provider sections in snapshot", async () => {
|
||||
await withSnapshotForConfig({ whatsapp: { allowFrom: ["+1555"] } }, async (ctx) => {
|
||||
expect(ctx.snapshot.valid).toBe(false);
|
||||
expect(ctx.snapshot.legacyIssues.some((issue) => issue.path === "whatsapp")).toBe(true);
|
||||
|
||||
const snap = await readConfigFileSnapshot();
|
||||
|
||||
expect(snap.valid).toBe(false);
|
||||
expect(snap.legacyIssues.some((issue) => issue.path === "memorySearch")).toBe(true);
|
||||
const parsed = ctx.parsed as {
|
||||
channels?: unknown;
|
||||
whatsapp?: unknown;
|
||||
};
|
||||
expect(parsed.channels).toBeUndefined();
|
||||
expect(parsed.whatsapp).toBeTruthy();
|
||||
});
|
||||
});
|
||||
it("does not auto-migrate claude-cli auth profile mode on load", async () => {
|
||||
@@ -293,52 +310,9 @@ describe("legacy config detection", () => {
|
||||
expect(parsed.auth?.profiles?.["anthropic:claude-cli"]?.mode).toBe("token");
|
||||
});
|
||||
});
|
||||
it("flags legacy provider sections in snapshot", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configPath = path.join(home, ".openclaw", "openclaw.json");
|
||||
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
JSON.stringify({ whatsapp: { allowFrom: ["+1555"] } }, null, 2),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const snap = await readConfigFileSnapshot();
|
||||
|
||||
expect(snap.valid).toBe(false);
|
||||
expect(snap.legacyIssues.some((issue) => issue.path === "whatsapp")).toBe(true);
|
||||
|
||||
const raw = await fs.readFile(configPath, "utf-8");
|
||||
const parsed = JSON.parse(raw) as {
|
||||
channels?: unknown;
|
||||
whatsapp?: unknown;
|
||||
};
|
||||
expect(parsed.channels).toBeUndefined();
|
||||
expect(parsed.whatsapp).toBeTruthy();
|
||||
});
|
||||
});
|
||||
it("flags routing.allowFrom in snapshot", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configPath = path.join(home, ".openclaw", "openclaw.json");
|
||||
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
JSON.stringify({ routing: { allowFrom: ["+1666"] } }, null, 2),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const snap = await readConfigFileSnapshot();
|
||||
|
||||
expect(snap.valid).toBe(false);
|
||||
expect(snap.legacyIssues.some((issue) => issue.path === "routing.allowFrom")).toBe(true);
|
||||
|
||||
const raw = await fs.readFile(configPath, "utf-8");
|
||||
const parsed = JSON.parse(raw) as {
|
||||
channels?: unknown;
|
||||
routing?: { allowFrom?: string[] };
|
||||
};
|
||||
expect(parsed.channels).toBeUndefined();
|
||||
expect(parsed.routing?.allowFrom).toEqual(["+1666"]);
|
||||
await withSnapshotForConfig({ routing: { allowFrom: ["+1666"] } }, async (ctx) => {
|
||||
expectRoutingAllowFromLegacySnapshot(ctx, ["+1666"]);
|
||||
});
|
||||
});
|
||||
it("rejects bindings[].match.provider on load", async () => {
|
||||
@@ -374,61 +348,40 @@ describe("legacy config detection", () => {
|
||||
});
|
||||
});
|
||||
it("rejects session.sendPolicy.rules[].match.provider on load", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configPath = path.join(home, ".openclaw", "openclaw.json");
|
||||
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
session: {
|
||||
sendPolicy: {
|
||||
rules: [{ action: "deny", match: { provider: "telegram" } }],
|
||||
},
|
||||
},
|
||||
await withSnapshotForConfig(
|
||||
{
|
||||
session: {
|
||||
sendPolicy: {
|
||||
rules: [{ action: "deny", match: { provider: "telegram" } }],
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const snap = await readConfigFileSnapshot();
|
||||
|
||||
expect(snap.valid).toBe(false);
|
||||
expect(snap.issues.length).toBeGreaterThan(0);
|
||||
|
||||
const raw = await fs.readFile(configPath, "utf-8");
|
||||
const parsed = JSON.parse(raw) as {
|
||||
session?: { sendPolicy?: { rules?: Array<{ match?: { provider?: string } }> } };
|
||||
};
|
||||
expect(parsed.session?.sendPolicy?.rules?.[0]?.match?.provider).toBe("telegram");
|
||||
});
|
||||
},
|
||||
},
|
||||
async (ctx) => {
|
||||
expect(ctx.snapshot.valid).toBe(false);
|
||||
expect(ctx.snapshot.issues.length).toBeGreaterThan(0);
|
||||
const parsed = ctx.parsed as {
|
||||
session?: { sendPolicy?: { rules?: Array<{ match?: { provider?: string } }> } };
|
||||
};
|
||||
expect(parsed.session?.sendPolicy?.rules?.[0]?.match?.provider).toBe("telegram");
|
||||
},
|
||||
);
|
||||
});
|
||||
it("rejects messages.queue.byProvider on load", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configPath = path.join(home, ".openclaw", "openclaw.json");
|
||||
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
JSON.stringify({ messages: { queue: { byProvider: { whatsapp: "queue" } } } }, null, 2),
|
||||
"utf-8",
|
||||
);
|
||||
await withSnapshotForConfig(
|
||||
{ messages: { queue: { byProvider: { whatsapp: "queue" } } } },
|
||||
async (ctx) => {
|
||||
expect(ctx.snapshot.valid).toBe(false);
|
||||
expect(ctx.snapshot.issues.length).toBeGreaterThan(0);
|
||||
|
||||
const snap = await readConfigFileSnapshot();
|
||||
|
||||
expect(snap.valid).toBe(false);
|
||||
expect(snap.issues.length).toBeGreaterThan(0);
|
||||
|
||||
const raw = await fs.readFile(configPath, "utf-8");
|
||||
const parsed = JSON.parse(raw) as {
|
||||
messages?: {
|
||||
queue?: {
|
||||
byProvider?: Record<string, unknown>;
|
||||
const parsed = ctx.parsed as {
|
||||
messages?: {
|
||||
queue?: {
|
||||
byProvider?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
expect(parsed.messages?.queue?.byProvider?.whatsapp).toBe("queue");
|
||||
});
|
||||
expect(parsed.messages?.queue?.byProvider?.whatsapp).toBe("queue");
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user