





















@@ -23,6 +23,7 @@ const getConfigOverridesMock = vi.hoisted(() => vi.fn(() => ({})));
2323const getConfigValueAtPathMock = vi.hoisted(() => vi.fn());
2424const parseConfigPathMock = vi.hoisted(() => vi.fn());
2525const setConfigValueAtPathMock = vi.hoisted(() => vi.fn());
26+const unsetConfigValueAtPathMock = vi.hoisted(() => vi.fn(() => true));
2627const resolveConfigWriteDeniedTextMock = vi.hoisted(() =>
2728vi.fn<(...args: never[]) => string | null>(() => null),
2829);
@@ -124,7 +125,7 @@ vi.mock("../../config/config-paths.js", () => ({
124125getConfigValueAtPath: getConfigValueAtPathMock,
125126parseConfigPath: parseConfigPathMock,
126127setConfigValueAtPath: setConfigValueAtPathMock,
127-unsetConfigValueAtPath: vi.fn(() => true),
128+unsetConfigValueAtPath: unsetConfigValueAtPathMock,
128129}));
129130130131vi.mock("../../config/config.js", () => ({
@@ -270,6 +271,7 @@ describe("command gating", () => {
270271}
271272},
272273);
274+unsetConfigValueAtPathMock.mockReturnValue(true);
273275});
274276275277it("blocks disabled bash", async () => {
@@ -631,4 +633,23 @@ describe("command gating", () => {
631633afterWrite: { mode: "auto" },
632634});
633635});
636+637+it("does not write config when /config unset misses", async () => {
638+unsetConfigValueAtPathMock.mockReturnValue(false);
639+readConfigFileSnapshotMock.mockResolvedValue({
640+valid: true,
641+parsed: { messages: {} },
642+});
643+const params = buildParams("/config unset messages.missing", {
644+commands: { config: true, text: true },
645+} as OpenClawConfig);
646+params.ctx.GatewayClientScopes = ["operator.admin"];
647+params.command.senderIsOwner = true;
648+649+const result = await handleConfigCommand(params, true);
650+651+expect(result?.shouldContinue).toBe(false);
652+expect(result?.reply?.text).toContain("No config value found");
653+expect(replaceConfigFileMock).not.toHaveBeenCalled();
654+});
634655});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。