





























@@ -268,6 +268,115 @@ describe("runtime tool fixture", () => {
268268expect(details).toContain("read live provider failure planned args");
269269});
270270271+it("allows async live runtime tool fixtures to prove the happy path with the planned call", async () => {
272+const env = await makeEnv();
273+await writeQaSessionTranscript(env, "agent:qa:runtime-tool:image_generate:happy", [
274+{
275+role: "assistant",
276+content: [
277+{
278+type: "tool_use",
279+id: "call-image-happy",
280+name: "image_generate",
281+input: { prompt: "QA lighthouse runtime parity fixture" },
282+},
283+],
284+},
285+]);
286+await writeQaSessionTranscript(env, "agent:qa:runtime-tool:image_generate:failure", [
287+{
288+role: "assistant",
289+content: [
290+{
291+type: "tool_use",
292+id: "call-image-failure",
293+name: "image_generate",
294+input: { __qaFailureMode: "denied-input" },
295+},
296+],
297+},
298+{
299+role: "tool",
300+toolName: "image_generate",
301+tool_call_id: "call-image-failure",
302+isError: true,
303+content: "denied-input",
304+},
305+]);
306+307+const details = await runRuntimeToolFixture(
308+env,
309+{
310+toolName: "image_generate",
311+toolCoverage: {
312+bucket: "openclaw-dynamic-integration",
313+expectedLayer: "openclaw-dynamic",
314+},
315+happyPathOutputRequired: false,
316+},
317+{
318+createSession: vi.fn(async (_env, _label, key) => key!),
319+readEffectiveTools: vi.fn(async () => new Set(["image_generate"])),
320+runAgentPrompt: vi.fn(async () => ({})),
321+fetchJson: vi.fn(),
322+ensureImageGenerationConfigured: vi.fn(),
323+},
324+);
325+326+expect(details).toContain(
327+"image_generate live provider happy direct output not required for this async fixture",
328+);
329+expect(details).toContain("image_generate live provider failure planned args");
330+});
331+332+it("still requires async live runtime tool fixtures to call the happy-path tool", async () => {
333+const env = await makeEnv();
334+await writeQaSessionTranscript(env, "agent:qa:runtime-tool:image_generate:happy", [
335+{ role: "assistant", content: "I can start image generation later." },
336+]);
337+await writeQaSessionTranscript(env, "agent:qa:runtime-tool:image_generate:failure", [
338+{
339+role: "assistant",
340+content: [
341+{
342+type: "tool_use",
343+id: "call-image-failure",
344+name: "image_generate",
345+input: { __qaFailureMode: "denied-input" },
346+},
347+],
348+},
349+{
350+role: "tool",
351+toolName: "image_generate",
352+tool_call_id: "call-image-failure",
353+isError: true,
354+content: "denied-input",
355+},
356+]);
357+358+await expect(
359+runRuntimeToolFixture(
360+env,
361+{
362+toolName: "image_generate",
363+toolCoverage: {
364+bucket: "openclaw-dynamic-integration",
365+expectedLayer: "openclaw-dynamic",
366+},
367+happyPathOutputRequired: false,
368+},
369+{
370+createSession: vi.fn(async (_env, _label, key) => key!),
371+readEffectiveTools: vi.fn(async () => new Set(["image_generate"])),
372+runAgentPrompt: vi.fn(async () => ({})),
373+fetchJson: vi.fn(),
374+ensureImageGenerationConfigured: vi.fn(),
375+},
376+),
377+).rejects.toThrow("expected live happy-path tool call for image_generate");
378+});
379+271380it("requires live failure fixtures to produce failure-shaped tool output", async () => {
272381const env = await makeEnv();
273382await writeQaSessionTranscript(env, "agent:qa:runtime-tool:read:happy", [
@@ -445,6 +554,70 @@ describe("runtime tool fixture", () => {
445554expect(details).toContain("mock provider happy planned args (diagnostic only)");
446555});
447556557+it("reports Codex-native async planned-only happy fixtures without dereferencing missing output", async () => {
558+const env = await makeEnv({
559+mock: { baseUrl: "http://127.0.0.1:9999" },
560+gateway: {
561+baseUrl: "http://127.0.0.1:1",
562+tempRoot: "",
563+workspaceDir: "",
564+runtimeEnv: { OPENCLAW_QA_FORCE_RUNTIME: "codex" },
565+call: vi.fn(),
566+},
567+});
568+env.gateway.tempRoot = env.repoRoot;
569+env.gateway.workspaceDir = env.repoRoot;
570+571+const fetchJson = vi
572+.fn()
573+.mockResolvedValueOnce([])
574+.mockResolvedValueOnce([
575+{
576+allInputText: "target=image_generate",
577+plannedToolCallId: "call-image-happy",
578+plannedToolName: "image_generate",
579+plannedToolArgs: { prompt: "QA lighthouse runtime parity fixture" },
580+},
581+{
582+allInputText: "failure target=image_generate",
583+plannedToolCallId: "call-image-failure",
584+plannedToolName: "image_generate",
585+plannedToolArgs: { __qaFailureMode: "denied-input" },
586+},
587+{
588+allInputText: "failure target=image_generate",
589+toolOutputCallId: "call-image-failure",
590+toolOutput: "Error: denied-input",
591+},
592+]);
593+594+const details = await runRuntimeToolFixture(
595+env,
596+{
597+toolName: "image_generate",
598+toolCoverage: {
599+bucket: "codex-native-workspace",
600+expectedLayer: "codex-native-workspace",
601+reason: "Codex owns image generation natively in this fixture.",
602+},
603+promptSnippet: "target=image_generate",
604+failurePromptSnippet: "failure target=image_generate",
605+happyPathOutputRequired: false,
606+},
607+{
608+createSession: vi.fn(async (_env, _label, key) => key!),
609+readEffectiveTools: vi.fn(async () => new Set<string>()),
610+runAgentPrompt: vi.fn(async () => ({})),
611+ fetchJson,
612+ensureImageGenerationConfigured: vi.fn(),
613+},
614+);
615+616+expect(details).toContain("codex-native-workspace image_generate");
617+expect(details).toContain('"prompt":"QA lighthouse runtime parity fixture"');
618+expect(details).toContain('"__qaFailureMode":"denied-input"');
619+});
620+448621it("requires mock runtime tool fixtures to produce tool output", async () => {
449622const env = await makeEnv({
450623mock: { baseUrl: "http://127.0.0.1:9999" },
@@ -492,6 +665,61 @@ describe("runtime tool fixture", () => {
492665).rejects.toThrow("expected mock happy-path tool output for read");
493666});
494667668+it("allows async mock runtime tool fixtures to prove the happy path with the planned call", async () => {
669+const env = await makeEnv({
670+mock: { baseUrl: "http://127.0.0.1:9999" },
671+});
672+const fetchJson = vi
673+.fn()
674+.mockResolvedValueOnce([])
675+.mockResolvedValueOnce([
676+{
677+allInputText: "target=image_generate",
678+plannedToolCallId: "call-image-happy",
679+plannedToolName: "image_generate",
680+plannedToolArgs: { prompt: "QA lighthouse runtime parity fixture" },
681+},
682+{
683+allInputText: "failure target=image_generate",
684+plannedToolCallId: "call-image-failure",
685+plannedToolName: "image_generate",
686+plannedToolArgs: { __qaFailureMode: "denied-input" },
687+},
688+{
689+allInputText: "failure target=image_generate",
690+toolOutputCallId: "call-image-failure",
691+toolOutput: "Error: denied-input",
692+},
693+]);
694+695+const details = await runRuntimeToolFixture(
696+env,
697+{
698+toolName: "image_generate",
699+toolCoverage: {
700+bucket: "openclaw-dynamic-integration",
701+expectedLayer: "openclaw-dynamic",
702+},
703+promptSnippet: "target=image_generate",
704+failurePromptSnippet: "failure target=image_generate",
705+happyPathOutputRequired: false,
706+},
707+{
708+createSession: vi.fn(async (_env, _label, key) => key!),
709+readEffectiveTools: vi.fn(async () => new Set(["image_generate"])),
710+runAgentPrompt: vi.fn(async () => ({})),
711+ fetchJson,
712+ensureImageGenerationConfigured: vi.fn(),
713+},
714+);
715+716+expect(details).toContain(
717+"image_generate mock provider happy direct output not required for this async fixture",
718+);
719+expect(details).toContain('"prompt":"QA lighthouse runtime parity fixture"');
720+expect(details).toContain('"__qaFailureMode":"denied-input"');
721+});
722+495723it("accepts mock runtime tool fixtures only after planned calls return output", async () => {
496724const env = await makeEnv({
497725mock: { baseUrl: "http://127.0.0.1:9999" },
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。