


























@@ -9,6 +9,22 @@ function createHarness(config: Record<string, unknown>) {
99config: {
1010current: vi.fn(() => config),
1111loadConfig: vi.fn(() => config),
12+mutateConfigFile: vi.fn(
13+async ({ mutate }: { mutate: (draft: Record<string, unknown>) => void }) => {
14+const draft = structuredClone(config);
15+mutate(draft);
16+config = draft;
17+return {
18+path: "/tmp/openclaw.json",
19+previousHash: null,
20+snapshot: {},
21+nextConfig: config,
22+afterWrite: { mode: "auto" },
23+followUp: { mode: "auto", requiresRestart: false },
24+result: undefined,
25+};
26+},
27+),
1228replaceConfigFile: vi.fn(async ({ nextConfig }: { nextConfig: Record<string, unknown> }) => {
1329config = nextConfig;
1430}),
@@ -187,19 +203,20 @@ describe("talk-voice plugin", () => {
187203createCommandContext("set Claudia", "webchat", ["operator.admin"]),
188204);
189205190-expect(runtime.config.replaceConfigFile).toHaveBeenCalledWith({
206+expect(runtime.config.mutateConfigFile).toHaveBeenCalledWith({
191207afterWrite: { mode: "auto" },
192-nextConfig: {
193-talk: {
194-provider: "elevenlabs",
195-providers: {
196-elevenlabs: {
197-apiKey: "sk-eleven",
198-voiceId: "voice-a",
199-},
208+mutate: expect.any(Function),
209+});
210+expect(runtime.config.current()).toEqual({
211+talk: {
212+provider: "elevenlabs",
213+providers: {
214+elevenlabs: {
215+apiKey: "sk-eleven",
216+voiceId: "voice-a",
200217},
201-voiceId: "voice-a",
202218},
219+voiceId: "voice-a",
203220},
204221});
205222expect(result).toEqual({
@@ -220,15 +237,16 @@ describe("talk-voice plugin", () => {
220237221238await command.handler(createCommandContext("set Ava", "webchat", ["operator.admin"]));
222239223-expect(runtime.config.replaceConfigFile).toHaveBeenCalledWith({
240+expect(runtime.config.mutateConfigFile).toHaveBeenCalledWith({
224241afterWrite: { mode: "auto" },
225-nextConfig: {
226-talk: {
227-provider: "microsoft",
228-providers: {
229-microsoft: {
230-voiceId: "en-US-AvaNeural",
231-},
242+mutate: expect.any(Function),
243+});
244+expect(runtime.config.current()).toEqual({
245+talk: {
246+provider: "microsoft",
247+providers: {
248+microsoft: {
249+voiceId: "en-US-AvaNeural",
232250},
233251},
234252},
@@ -240,22 +258,22 @@ describe("talk-voice plugin", () => {
240258const result = await run();
241259242260expect(result.text).toContain("requires operator.admin");
243-expect(runtime.config.replaceConfigFile).not.toHaveBeenCalled();
261+expect(runtime.config.mutateConfigFile).not.toHaveBeenCalled();
244262});
245263246264it("rejects /voice set from non-webchat gateway callers missing operator.admin", async () => {
247265const { runtime, run } = createElevenlabsVoiceSetHarness("telegram", ["operator.write"]);
248266const result = await run();
249267250268expect(result.text).toContain("requires operator.admin");
251-expect(runtime.config.replaceConfigFile).not.toHaveBeenCalled();
269+expect(runtime.config.mutateConfigFile).not.toHaveBeenCalled();
252270});
253271254272it("allows /voice set from gateway client with operator.admin scope", async () => {
255273const { runtime, run } = createElevenlabsVoiceSetHarness("webchat", ["operator.admin"]);
256274const result = await run();
257275258-expect(runtime.config.replaceConfigFile).toHaveBeenCalled();
276+expect(runtime.config.mutateConfigFile).toHaveBeenCalled();
259277expect(result.text).toContain("voice-a");
260278});
261279@@ -264,22 +282,22 @@ describe("talk-voice plugin", () => {
264282const result = await run();
265283266284expect(result.text).toContain("requires operator.admin");
267-expect(runtime.config.replaceConfigFile).not.toHaveBeenCalled();
285+expect(runtime.config.mutateConfigFile).not.toHaveBeenCalled();
268286});
269287270288it("allows /voice set from non-gateway channels without operator.admin", async () => {
271289const { runtime, run } = createElevenlabsVoiceSetHarness("telegram");
272290const result = await run();
273291274-expect(runtime.config.replaceConfigFile).toHaveBeenCalled();
292+expect(runtime.config.mutateConfigFile).toHaveBeenCalled();
275293expect(result.text).toContain("voice-a");
276294});
277295278296it("allows /voice set when operator.admin is present on a non-webchat channel", async () => {
279297const { runtime, run } = createElevenlabsVoiceSetHarness("telegram", ["operator.admin"]);
280298const result = await run();
281299282-expect(runtime.config.replaceConfigFile).toHaveBeenCalled();
300+expect(runtime.config.mutateConfigFile).toHaveBeenCalled();
283301expect(result.text).toContain("voice-a");
284302});
285303此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。