205 lines · 6.8 KB · TypeScript
RawDownload
import { describe, expect, it } from "vite-plus/test"; import {  buildBranchNamePrompt,  buildCommitMessagePrompt,  buildPrContentPrompt,  buildThreadTitlePrompt,} from "./TextGenerationPrompts.ts";import { normalizeCliError, sanitizeThreadTitle } from "./TextGenerationUtils.ts";import { TextGenerationError } from "@t3tools/contracts"; describe("buildCommitMessagePrompt", () => {  it("includes staged patch and summary in the prompt", () => {    const result = buildCommitMessagePrompt({      branch: "main",      stagedSummary: "M README.md",      stagedPatch: "diff --git a/README.md b/README.md\n+hello",      includeBranch: false,    });     expect(result.prompt).toContain("Staged files:");    expect(result.prompt).toContain("M README.md");    expect(result.prompt).toContain("Staged patch:");    expect(result.prompt).toContain("diff --git a/README.md b/README.md");    expect(result.prompt).toContain("Branch: main");    // Should NOT include the branch generation instruction    expect(result.prompt).not.toContain("branch must be a short semantic git branch fragment");  });   it("includes branch generation instruction when includeBranch is true", () => {    const result = buildCommitMessagePrompt({      branch: "feature/foo",      stagedSummary: "M README.md",      stagedPatch: "diff",      includeBranch: true,    });     expect(result.prompt).toContain("branch must be a short semantic git branch fragment");    expect(result.prompt).toContain("Return a JSON object with keys: subject, body, branch.");  });   it("shows (detached) when branch is null", () => {    const result = buildCommitMessagePrompt({      branch: null,      stagedSummary: "M a.ts",      stagedPatch: "diff",      includeBranch: false,    });     expect(result.prompt).toContain("Branch: (detached)");  });}); describe("buildPrContentPrompt", () => {  it("includes branch names, commits, and diff in the prompt", () => {    const result = buildPrContentPrompt({      baseBranch: "main",      headBranch: "feature/auth",      commitSummary: "feat: add login page",      diffSummary: "3 files changed",      diffPatch: "diff --git a/auth.ts b/auth.ts\n+export function login()",    });     expect(result.prompt).toContain("Base branch: main");    expect(result.prompt).toContain("Head branch: feature/auth");    expect(result.prompt).toContain("Commits:");    expect(result.prompt).toContain("feat: add login page");    expect(result.prompt).toContain("Diff stat:");    expect(result.prompt).toContain("3 files changed");    expect(result.prompt).toContain("Diff patch:");    expect(result.prompt).toContain("export function login()");  });}); describe("buildBranchNamePrompt", () => {  it("includes the user message in the prompt", () => {    const result = buildBranchNamePrompt({      message: "Fix the login timeout bug",    });     expect(result.prompt).toContain("User message:");    expect(result.prompt).toContain("Fix the login timeout bug");    expect(result.prompt).not.toContain("Attachment metadata:");  });   it("includes attachment metadata when attachments are provided", () => {    const result = buildBranchNamePrompt({      message: "Fix the layout from screenshot",      attachments: [        {          type: "image" as const,          id: "att-123",          name: "screenshot.png",          mimeType: "image/png",          sizeBytes: 12345,        },      ],    });     expect(result.prompt).toContain("Attachment metadata:");    expect(result.prompt).toContain("screenshot.png");    expect(result.prompt).toContain("image/png");    expect(result.prompt).toContain("12345 bytes");  });}); describe("buildThreadTitlePrompt", () => {  it("includes the user message in the prompt", () => {    const result = buildThreadTitlePrompt({      message: "Investigate reconnect regressions after session restore",    });     expect(result.prompt).toContain("User message:");    expect(result.prompt).toContain("Investigate reconnect regressions after session restore");    expect(result.prompt).not.toContain("Attachment metadata:");  });   it("includes attachment metadata when attachments are provided", () => {    const result = buildThreadTitlePrompt({      message: "Name this thread from the screenshot",      attachments: [        {          type: "image" as const,          id: "att-456",          name: "thread.png",          mimeType: "image/png",          sizeBytes: 67890,        },      ],    });     expect(result.prompt).toContain("Attachment metadata:");    expect(result.prompt).toContain("thread.png");    expect(result.prompt).toContain("image/png");    expect(result.prompt).toContain("67890 bytes");  });}); describe("sanitizeThreadTitle", () => {  it("truncates long titles with the shared sidebar-safe limit", () => {    expect(      sanitizeThreadTitle(        '  "Reconnect failures after restart because the session state does not recover"  ',      ),    ).toBe("Reconnect failures after restart because the se...");  });}); describe("normalizeCliError", () => {  it("detects 'Command not found' and includes CLI name in the message", () => {    const error = normalizeCliError(      "claude",      "generateCommitMessage",      new Error("Command not found: claude"),      "Something went wrong",    );     expect(error).toBeInstanceOf(TextGenerationError);    expect(error.detail).toContain("Claude CLI");    expect(error.detail).toContain("not available on PATH");  });   it("uses the CLI name from the first argument for codex", () => {    const error = normalizeCliError(      "codex",      "generateBranchName",      new Error("Command not found: codex"),      "Something went wrong",    );     expect(error).toBeInstanceOf(TextGenerationError);    expect(error.detail).toContain("Codex CLI");    expect(error.detail).toContain("not available on PATH");  });   it("returns the error as-is if it is already a TextGenerationError", () => {    const existing = new TextGenerationError({      operation: "generatePrContent",      detail: "Already wrapped",    });     const result = normalizeCliError("claude", "generatePrContent", existing, "fallback");     expect(result).toBe(existing);  });   it("wraps unknown non-Error values with the fallback message", () => {    const result = normalizeCliError("codex", "generateCommitMessage", "string error", "fallback");     expect(result).toBeInstanceOf(TextGenerationError);    expect(result.detail).toBe("fallback");  });   it("does not expose CLI failure details in the public error message", () => {    const result = normalizeCliError(      "codex",      "generateCommitMessage",      new Error("request failed with access_token=secret-token"),      "Failed to generate a commit message",    );     expect(result.detail).toBe("Failed to generate a commit message");    expect(result.message).not.toContain("secret-token");  });});