























@@ -154,8 +154,45 @@ function requireRegexMatch(value: string, pattern: RegExp): RegExpExecArray {
154154return match;
155155}
156156157+function requireRecord(value: unknown, label: string): Record<string, unknown> {
158+if (!value || typeof value !== "object") {
159+throw new Error(`expected ${label} to be an object`);
160+}
161+return value as Record<string, unknown>;
162+}
163+164+function mockCallArg(mock: ReturnType<typeof vi.fn>, callIndex = 0, argIndex = 0): unknown {
165+const call = mock.mock.calls[callIndex] as unknown[] | undefined;
166+if (!call) {
167+throw new Error(`expected mock call ${callIndex}`);
168+}
169+return call[argIndex];
170+}
171+172+async function expectRejectsWithFields(
173+promise: Promise<unknown>,
174+expected: Record<string, unknown>,
175+): Promise<Record<string, unknown>> {
176+try {
177+await promise;
178+} catch (error) {
179+const actual = requireRecord(error, "rejection");
180+for (const [key, value] of Object.entries(expected)) {
181+expect(actual[key]).toBe(value);
182+}
183+return actual;
184+}
185+throw new Error("expected promise to reject");
186+}
187+157188async function expectPathMissing(targetPath: string): Promise<void> {
158-await expect(fs.access(targetPath)).rejects.toMatchObject({ code: "ENOENT" });
189+try {
190+await fs.access(targetPath);
191+} catch (error) {
192+expect(requireRecord(error, "filesystem error").code).toBe("ENOENT");
193+return;
194+}
195+throw new Error(`expected ${targetPath} to be missing`);
159196}
160197161198describe("runCliAgent spawn path", () => {
@@ -329,7 +366,7 @@ describe("runCliAgent spawn path", () => {
329366}),
330367);
331368332-await expect(fs.access(systemPromptPath)).rejects.toMatchObject({ code: "ENOENT" });
369+await expectPathMissing(systemPromptPath);
333370});
334371335372it("passes --session-id for new Claude sessions", async () => {
@@ -369,15 +406,12 @@ describe("runCliAgent spawn path", () => {
369406}),
370407);
371408372-expect(resolveExecutionArgs).toHaveBeenCalledWith(
373-expect.objectContaining({
374-provider: "claude-cli",
375-modelId: "sonnet",
376-thinkingLevel: "high",
377-useResume: false,
378-baseArgs: ["-p", "--output-format", "stream-json"],
379-}),
380-);
409+const resolveArgsInput = requireRecord(mockCallArg(resolveExecutionArgs), "resolved args");
410+expect(resolveArgsInput.provider).toBe("claude-cli");
411+expect(resolveArgsInput.modelId).toBe("sonnet");
412+expect(resolveArgsInput.thinkingLevel).toBe("high");
413+expect(resolveArgsInput.useResume).toBe(false);
414+expect(resolveArgsInput.baseArgs).toEqual(["-p", "--output-format", "stream-json"]);
381415const input = supervisorSpawnMock.mock.calls[0]?.[0] as { argv?: string[] };
382416const effortArgIndex = input.argv?.indexOf("--effort") ?? -1;
383417expect(effortArgIndex).toBeGreaterThanOrEqual(0);
@@ -410,10 +444,8 @@ describe("runCliAgent spawn path", () => {
410444const manifest = JSON.parse(
411445await fs.readFile(path.join(pluginDir, ".claude-plugin", "plugin.json"), "utf-8"),
412446) as { name?: string; skills?: string };
413-expect(manifest).toMatchObject({
414-name: "openclaw-skills",
415-skills: "./skills",
416-});
447+expect(manifest.name).toBe("openclaw-skills");
448+expect(manifest.skills).toBe("./skills");
417449await expect(
418450fs.readFile(path.join(pluginDir, "skills", "weather", "SKILL.md"), "utf-8"),
419451).resolves.toContain("Read forecast data before replying.");
@@ -738,7 +770,7 @@ describe("runCliAgent spawn path", () => {
738770});
739771abortController.abort();
740772741-await expect(runPromise).rejects.toMatchObject({ name: "AbortError" });
773+await expectRejectsWithFields(runPromise, { name: "AbortError" });
742774expect(cancel).toHaveBeenCalledWith("manual-cancel");
743775});
744776@@ -1037,7 +1069,7 @@ describe("runCliAgent spawn path", () => {
10371069};
10381070});
103910711040-await expect(
1072+await expectRejectsWithFields(
10411073executePreparedCliRun(
10421074buildPreparedCliRunContext({
10431075provider: "claude-cli",
@@ -1053,10 +1085,11 @@ describe("runCliAgent spawn path", () => {
10531085},
10541086}),
10551087),
1056-).rejects.toMatchObject({
1057-name: "FailoverError",
1058-message: "Claude CLI JSONL line exceeded output limit.",
1059-});
1088+{
1089+name: "FailoverError",
1090+message: "Claude CLI JSONL line exceeded output limit.",
1091+},
1092+);
10601093});
1061109410621095it("accepts operator-raised Claude live stream-json raw turn limits", async () => {
@@ -1173,7 +1206,8 @@ describe("runCliAgent spawn path", () => {
11731206].join("\n") + "\n",
11741207);
117512081176-await expect(run).resolves.toMatchObject({ text: "done" });
1209+const result = await run;
1210+expect(result.text).toBe("done");
11771211expect(replyRunRegistry.isStreaming("agent:main:main")).toBe(false);
11781212operation.complete();
11791213});
@@ -1448,16 +1482,9 @@ describe("runCliAgent spawn path", () => {
14481482useResume: false,
14491483});
145014841451-expect(args).toEqual(
1452-expect.arrayContaining([
1453-"--input-format",
1454-"stream-json",
1455-"--output-format",
1456-"stream-json",
1457-"--permission-prompt-tool",
1458-"stdio",
1459-]),
1460-);
1485+expect(requireArgAfter(args, "--input-format")).toBe("stream-json");
1486+expect(requireArgAfter(args, "--output-format")).toBe("stream-json");
1487+expect(requireArgAfter(args, "--permission-prompt-tool")).toBe("stdio");
14611488});
1462148914631490it("restarts Claude live sessions for env changes and fresh retries", async () => {
@@ -1632,7 +1659,7 @@ describe("runCliAgent spawn path", () => {
16321659};
16331660});
163416611635-await expect(
1662+await expectRejectsWithFields(
16361663executePreparedCliRun(
16371664buildPreparedCliRunContext({
16381665provider: "claude-cli",
@@ -1643,10 +1670,11 @@ describe("runCliAgent spawn path", () => {
16431670},
16441671}),
16451672),
1646-).rejects.toMatchObject({
1647-name: "FailoverError",
1648-message: "Credit balance is too low",
1649-});
1673+{
1674+name: "FailoverError",
1675+message: "Credit balance is too low",
1676+},
1677+);
16501678});
1651167916521680it("fails when Claude exits before a live turn starts", async () => {
@@ -1757,7 +1785,7 @@ describe("runCliAgent spawn path", () => {
17571785});
17581786abortController.abort();
175917871760-await expect(first).rejects.toMatchObject({ name: "AbortError" });
1788+await expectRejectsWithFields(first, { name: "AbortError" });
17611789expect(cancels[0]).toHaveBeenCalledWith("manual-cancel");
17621790stdoutListener?.(
17631791[
@@ -2073,7 +2101,7 @@ describe("runCliAgent spawn path", () => {
20732101);
2074210220752103expect(first.text).toBe("first-ok");
2076-await expect(second).rejects.toMatchObject({
2104+await expectRejectsWithFields(second, {
20772105name: "FailoverError",
20782106message: "Claude CLI failed.",
20792107});
@@ -2103,7 +2131,7 @@ describe("runCliAgent spawn path", () => {
21032131}),
21042132);
210521332106-await expect(run).rejects.toMatchObject({
2134+await expectRejectsWithFields(run, {
21072135name: "FailoverError",
21082136 message,
21092137reason: "billing",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。