test(config): cover thread binding legacy doctor paths

This commit is contained in:
Vincent Koc 2026-04-04 00:25:53 +09:00
parent f1f6b98639
commit 7c6eba4634
2 changed files with 191 additions and 0 deletions

View File

@ -1430,6 +1430,136 @@ describe("doctor config flow", () => {
}
});
it("warns clearly about legacy thread binding ttlHours config and points to doctor --fix", async () => {
const noteSpy = vi.spyOn(noteModule, "note").mockImplementation(() => {});
try {
await runDoctorConfigWithInput({
config: {
session: {
threadBindings: {
ttlHours: 24,
},
},
channels: {
discord: {
threadBindings: {
ttlHours: 12,
},
accounts: {
alpha: {
threadBindings: {
ttlHours: 6,
},
},
},
},
},
},
run: loadAndMaybeMigrateDoctorConfig,
});
expect(
noteSpy.mock.calls.some(
([message, title]) =>
title === "Legacy config keys detected" &&
String(message).includes("session.threadBindings:") &&
String(message).includes("session.threadBindings.idleHours"),
),
).toBe(true);
expect(
noteSpy.mock.calls.some(
([message, title]) =>
title === "Legacy config keys detected" &&
String(message).includes("channels.discord.threadBindings:") &&
String(message).includes("channels.discord.threadBindings.idleHours"),
),
).toBe(true);
expect(
noteSpy.mock.calls.some(
([message, title]) =>
title === "Legacy config keys detected" &&
String(message).includes("channels.discord.accounts:") &&
String(message).includes("channels.discord.accounts.<id>.threadBindings.idleHours"),
),
).toBe(true);
expect(
noteSpy.mock.calls.some(
([message, title]) =>
title === "Doctor" &&
String(message).includes('Run "openclaw doctor --fix" to migrate legacy config keys.'),
),
).toBe(true);
} finally {
noteSpy.mockRestore();
}
});
it("repairs legacy thread binding ttlHours config on repair", async () => {
const result = await runDoctorConfigWithInput({
repair: true,
config: {
session: {
threadBindings: {
ttlHours: 24,
},
},
channels: {
discord: {
threadBindings: {
ttlHours: 12,
},
accounts: {
alpha: {
threadBindings: {
ttlHours: 6,
},
},
},
},
},
},
run: loadAndMaybeMigrateDoctorConfig,
});
const cfg = result.cfg as {
session?: {
threadBindings?: {
idleHours?: number;
ttlHours?: number;
};
};
channels?: {
discord?: {
threadBindings?: {
idleHours?: number;
ttlHours?: number;
};
accounts?: Record<
string,
{
threadBindings?: {
idleHours?: number;
ttlHours?: number;
};
}
>;
};
};
};
expect(cfg.session?.threadBindings).toMatchObject({
idleHours: 24,
});
expect(cfg.channels?.discord?.threadBindings).toMatchObject({
idleHours: 12,
});
expect(cfg.channels?.discord?.accounts?.alpha?.threadBindings).toMatchObject({
idleHours: 6,
});
expect(cfg.session?.threadBindings?.ttlHours).toBeUndefined();
expect(cfg.channels?.discord?.threadBindings?.ttlHours).toBeUndefined();
expect(cfg.channels?.discord?.accounts?.alpha?.threadBindings?.ttlHours).toBeUndefined();
});
it("warns clearly about legacy tts provider config and points to doctor --fix", async () => {
const noteSpy = vi.spyOn(noteModule, "note").mockImplementation(() => {});
try {

View File

@ -672,6 +672,67 @@ describe("config strict validation", () => {
});
});
it("accepts legacy thread binding ttlHours via auto-migration and reports legacyIssues", async () => {
await withTempHome(async (home) => {
await writeOpenClawConfig(home, {
session: {
threadBindings: {
ttlHours: 24,
},
},
channels: {
discord: {
threadBindings: {
ttlHours: 12,
},
accounts: {
alpha: {
threadBindings: {
ttlHours: 6,
},
},
},
},
},
});
const snap = await readConfigFileSnapshot();
expect(snap.valid).toBe(true);
expect(snap.legacyIssues.some((issue) => issue.path === "session.threadBindings")).toBe(true);
expect(
snap.legacyIssues.some((issue) => issue.path === "channels.discord.threadBindings"),
).toBe(true);
expect(snap.legacyIssues.some((issue) => issue.path === "channels.discord.accounts")).toBe(
true,
);
expect(snap.sourceConfig.session?.threadBindings).toMatchObject({
idleHours: 24,
});
expect(snap.sourceConfig.channels?.discord?.threadBindings).toMatchObject({
idleHours: 12,
});
expect(snap.sourceConfig.channels?.discord?.accounts?.alpha?.threadBindings).toMatchObject({
idleHours: 6,
});
expect(
(snap.sourceConfig.session?.threadBindings as Record<string, unknown> | undefined)
?.ttlHours,
).toBeUndefined();
expect(
(snap.sourceConfig.channels?.discord?.threadBindings as Record<string, unknown> | undefined)
?.ttlHours,
).toBeUndefined();
expect(
(
snap.sourceConfig.channels?.discord?.accounts?.alpha?.threadBindings as
| Record<string, unknown>
| undefined
)?.ttlHours,
).toBeUndefined();
});
});
it("accepts legacy channel streaming aliases via auto-migration and reports legacyIssues", async () => {
await withTempHome(async (home) => {
await writeOpenClawConfig(home, {