import { describe, expect, it } from "vitest"; import { MAX_ISSUE_REQUEST_DEPTH } from "../index.js"; import { addIssueCommentSchema, createIssueSchema, respondIssueThreadInteractionSchema, suggestedTaskDraftSchema, updateIssueSchema, upsertIssueDocumentSchema, } from "./issue.js"; import { createAgentSchema } from "./agent.js"; describe("issue validators", () => { it("passes real line breaks through unchanged", () => { const parsed = createIssueSchema.parse({ title: "Follow up PR", description: "Line 1\n\nLine 2", }); expect(parsed.description).toBe("Line 1\n\nLine 2"); }); it("accepts null and omitted optional multiline issue fields", () => { expect(createIssueSchema.parse({ title: "Follow up PR", description: null }).description) .toBeNull(); expect(createIssueSchema.parse({ title: "Follow up PR" }).description) .toBeUndefined(); expect(updateIssueSchema.parse({ comment: undefined }).comment) .toBeUndefined(); }); it("normalizes JSON-escaped line breaks in issue descriptions", () => { const parsed = createIssueSchema.parse({ title: "Follow up PR", description: "PR: https://example.com/pr/1\\n\\nShip the follow-up.", }); expect(parsed.description).toBe("PR: https://example.com/pr/1\n\nShip the follow-up."); }); it("normalizes escaped line breaks in issue update comments", () => { const parsed = updateIssueSchema.parse({ comment: "Done\\n\\n- Verified the route", }); expect(parsed.comment).toBe("Done\n\n- Verified the route"); }); it("normalizes escaped line breaks in issue comment bodies", () => { const parsed = addIssueCommentSchema.parse({ body: "Progress update\\r\\n\\r\\nNext action.", }); expect(parsed.body).toBe("Progress update\n\nNext action."); }); it("normalizes escaped line breaks in generated task drafts", () => { const parsed = suggestedTaskDraftSchema.parse({ clientKey: "task-1", title: "Follow up", description: "Line 1\\n\\nLine 2", }); expect(parsed.description).toBe("Line 1\n\nLine 2"); }); it("normalizes escaped line breaks in thread summaries and documents", () => { const response = respondIssueThreadInteractionSchema.parse({ answers: [], summaryMarkdown: "Summary\\n\\nNext action", }); const document = upsertIssueDocumentSchema.parse({ format: "markdown", body: "# Plan\\n\\nShip it", }); expect(response.summaryMarkdown).toBe("Summary\n\nNext action"); expect(document.body).toBe("# Plan\n\nShip it"); }); it("clamps oversized requestDepth values on create", () => { const parsed = createIssueSchema.parse({ title: "Clamp request depth", requestDepth: MAX_ISSUE_REQUEST_DEPTH + 500, }); expect(parsed.requestDepth).toBe(MAX_ISSUE_REQUEST_DEPTH); }); it("clamps oversized requestDepth values on update", () => { const parsed = updateIssueSchema.parse({ requestDepth: MAX_ISSUE_REQUEST_DEPTH + 1, }); expect(parsed.requestDepth).toBe(MAX_ISSUE_REQUEST_DEPTH); }); it("accepts the cheap model profile in issue assignee adapter overrides", () => { const parsed = createIssueSchema.parse({ title: "Run a cheap heartbeat", assigneeAdapterOverrides: { modelProfile: "cheap", }, }); expect(parsed.assigneeAdapterOverrides?.modelProfile).toBe("cheap"); }); it("rejects unknown issue model profile keys", () => { const parsed = updateIssueSchema.safeParse({ assigneeAdapterOverrides: { modelProfile: "fast", }, }); expect(parsed.success).toBe(false); }); it("validates agent runtime cheap model profile config without rejecting other runtime fields", () => { const parsed = createAgentSchema.parse({ name: "Coder", adapterType: "codex_local", runtimeConfig: { heartbeat: { enabled: true }, modelProfiles: { cheap: { enabled: true, label: "Cheap Codex", adapterConfig: { model: "gpt-5.3-codex-spark", }, }, }, }, }); expect(parsed.runtimeConfig.modelProfiles?.cheap?.adapterConfig).toEqual({ model: "gpt-5.3-codex-spark", }); expect(parsed.runtimeConfig.heartbeat).toEqual({ enabled: true }); }); it("validates cheap model profile env bindings like top-level adapter config", () => { const parsed = createAgentSchema.safeParse({ name: "Coder", adapterType: "codex_local", runtimeConfig: { modelProfiles: { cheap: { adapterConfig: { env: { API_TOKEN: 123, }, }, }, }, }, }); expect(parsed.success).toBe(false); }); it("rejects unknown agent runtime model profile keys", () => { const parsed = createAgentSchema.safeParse({ name: "Coder", adapterType: "codex_local", runtimeConfig: { modelProfiles: { fast: { adapterConfig: { model: "gpt-5-mini", }, }, }, }, }); expect(parsed.success).toBe(false); }); });