test(FN-4993): add AI parity and pr router coverage

Fusion-Task-Id: FN-4993
Fusion-Task-Lineage: 20ce6d5f-ef33-49e6-8519-648d769cc473
This commit is contained in:
Fusion (runfusion.ai)
2026-05-18 05:46:06 -07:00
committed by gsxdsm
parent 60d3da7394
commit e0b1e6ac37
3 changed files with 69 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
describe("bin pr router wiring", () => {
const source = readFileSync(resolve(__dirname, "../bin.ts"), "utf8");
it("includes top-level pr create router", () => {
expect(source).toContain('case "pr":');
expect(source).toContain('case "create":');
expect(source).toContain("runTaskPrCreate(id, parsePrCreateOptions(args.slice(3)), projectName)");
});
it("parses draft/no-ai/reviewer flags for pr-create aliases", () => {
expect(source).toContain('const draft = args.includes("--draft")');
expect(source).toContain('const ai = !args.includes("--no-ai")');
expect(source).toContain('args[i] === "--reviewer"');
expect(source).toContain('case "pr-create":');
});
});

View File

@@ -2924,6 +2924,50 @@ describe("runTaskPrCreate", () => {
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("Using AI-generated title/body"));
});
it("uses user title + AI body when only title is provided", async () => {
const task = makeInReviewTask();
mockGetTask.mockResolvedValueOnce(task);
vi.mocked(generatePrMetadata).mockResolvedValueOnce({ title: "AI Title", body: "AI Body", templateUsed: false });
mockCreatePr.mockResolvedValueOnce(makePrInfo());
await runTaskPrCreate("FN-001", { title: "User Title" });
expect(mockCreatePr).toHaveBeenCalledWith(expect.objectContaining({ title: "User Title", body: "AI Body" }));
});
it("uses AI title + user body when only body is provided", async () => {
const task = makeInReviewTask();
mockGetTask.mockResolvedValueOnce(task);
vi.mocked(generatePrMetadata).mockResolvedValueOnce({ title: "AI Title", body: "AI Body", templateUsed: false });
mockCreatePr.mockResolvedValueOnce(makePrInfo());
await runTaskPrCreate("FN-001", { body: "User Body" });
expect(mockCreatePr).toHaveBeenCalledWith(expect.objectContaining({ title: "AI Title", body: "User Body" }));
});
it("does not call AI when both title and body are provided", async () => {
const task = makeInReviewTask();
mockGetTask.mockResolvedValueOnce(task);
mockCreatePr.mockResolvedValueOnce(makePrInfo());
await runTaskPrCreate("FN-001", { title: "User Title", body: "User Body" });
expect(generatePrMetadata).not.toHaveBeenCalled();
});
it("falls back when AI metadata generation fails", async () => {
const task = makeInReviewTask({ title: undefined, description: "fallback description value for title" });
mockGetTask.mockResolvedValueOnce(task);
vi.mocked(generatePrMetadata).mockRejectedValueOnce(new Error("ai failed"));
mockCreatePr.mockResolvedValueOnce(makePrInfo());
await runTaskPrCreate("FN-001", {});
expect(mockCreatePr).toHaveBeenCalledWith(expect.objectContaining({ title: "Fallback description value for title".slice(0, 50) }));
expect(stderrWriteSpy).toHaveBeenCalledWith(expect.stringContaining("AI metadata generation failed"));
});
it("skips AI metadata when --no-ai is set", async () => {
const task = makeInReviewTask();
mockGetTask.mockResolvedValueOnce(task);