codeflash-internal/js/cf-api/endpoints/tests/create-pr.unit.test.ts
HeshamHM28 6f5c2d7ad8
Implement Tests for CF-API Flow (#1634)
Co-authored-by: Sarthak Agarwal <sarthak.saga@gmail.com>
2025-06-25 03:36:26 +05:30

1150 lines
36 KiB
TypeScript

import { jest, describe, it, expect, beforeEach, afterEach, beforeAll } from "@jest/globals"
let createPr: typeof import("../create-pr").createPr
let triggerCreatePr: typeof import("../create-pr").triggerCreatePr
let setCreatePrDependencies: typeof import("../create-pr").setCreatePrDependencies
let resetCreatePrDependencies: typeof import("../create-pr").resetCreatePrDependencies
let setTriggerCreatePrDependencies: typeof import("../create-pr").setTriggerCreatePrDependencies
let resetTriggerCreatePrDependencies: typeof import("../create-pr").resetTriggerCreatePrDependencies
let createStandalonePRTitleAndBody: typeof import("../create-pr").createStandalonePRTitleAndBody
type PrContentBuilder = import("../create-pr").PrContentBuilder
// Note: Global mocks are set up in jest.setup.ts
describe("createPr", () => {
beforeAll(async () => {
process.env.KEY_VAULT_NAME = "mocked-keyvault-name"
const mod = await import("../create-pr")
createPr = mod.createPr
triggerCreatePr = mod.triggerCreatePr
setCreatePrDependencies = mod.setCreatePrDependencies
resetCreatePrDependencies = mod.resetCreatePrDependencies
setTriggerCreatePrDependencies = mod.setTriggerCreatePrDependencies
resetTriggerCreatePrDependencies = mod.resetTriggerCreatePrDependencies
createStandalonePRTitleAndBody = mod.createStandalonePRTitleAndBody
})
let mockReq: any
let mockRes: any
let mockDependencies: any
beforeEach(() => {
// Setup request mock
mockReq = {
body: {
owner: "test-owner",
repo: "test-repo",
baseBranch: "main",
diffContents: [{ file: "test.js", content: "test" }],
prCommentFields: {
function_name: "testFunction",
speedup_pct: 50,
speedup_x: 2,
optimization_explanation: "Test optimization",
},
existingTests: [],
generatedTests: [],
coverage_message: "coverage message", // Changed from coverage to coverage_message
traceId: "test-trace-id",
},
userId: "test-user-id",
}
// Setup response mock
mockRes = {
status: jest.fn().mockReturnThis(),
send: jest.fn(),
json: jest.fn(),
}
// Setup mock dependencies with proper typing - cast to any to avoid complex Prisma types
mockDependencies = {
prisma: {
optimization_features: {
findUnique: jest.fn(),
update: jest.fn(),
},
} as any, // Cast to any to avoid Prisma type complexity
userNickname: jest.fn(),
getInstallationOctokitByOwner: jest.fn(),
isUserCollaborator: jest.fn(),
requiresApproval: jest.fn(),
requestApproval: jest.fn(),
triggerCreatePr: jest.fn(),
posthog: {
capture: jest.fn(),
},
isDiffContentsWellFormed: jest.fn().mockReturnValue(true),
githubApp: {},
}
setCreatePrDependencies(mockDependencies)
})
afterEach(() => {
resetCreatePrDependencies()
jest.clearAllMocks()
})
describe("input validation", () => {
it("should return 400 when owner is missing", async () => {
mockReq.body.owner = undefined
await createPr(mockReq, mockRes)
expect(mockRes.status).toHaveBeenCalledWith(400)
expect(mockRes.send).toHaveBeenCalledWith("Missing or malformed fields")
})
it("should return 400 when repo is missing", async () => {
mockReq.body.repo = undefined
await createPr(mockReq, mockRes)
expect(mockRes.status).toHaveBeenCalledWith(400)
expect(mockRes.send).toHaveBeenCalledWith("Missing or malformed fields")
})
it("should return 400 when baseBranch is missing", async () => {
mockReq.body.baseBranch = undefined
await createPr(mockReq, mockRes)
expect(mockRes.status).toHaveBeenCalledWith(400)
expect(mockRes.send).toHaveBeenCalledWith("Missing or malformed fields")
})
it("should return 400 for malformed diff contents", async () => {
mockDependencies.isDiffContentsWellFormed.mockReturnValue(false)
await createPr(mockReq, mockRes)
expect(mockRes.status).toHaveBeenCalledWith(400)
expect(mockRes.send).toHaveBeenCalledWith("Missing or malformed fields")
expect(mockDependencies.isDiffContentsWellFormed).toHaveBeenCalledWith(
mockReq.body.diffContents,
)
})
it("should handle missing traceId by defaulting to empty string", async () => {
delete mockReq.body.traceId
mockDependencies.userNickname.mockResolvedValue("test-user")
mockDependencies.getInstallationOctokitByOwner.mockResolvedValue({})
mockDependencies.isUserCollaborator.mockResolvedValue(true)
mockDependencies.requiresApproval.mockReturnValue(false)
mockDependencies.triggerCreatePr.mockResolvedValue(456)
await createPr(mockReq, mockRes)
expect(mockDependencies.triggerCreatePr).toHaveBeenCalledWith(
"test-owner",
"test-repo",
"main",
mockReq.body.diffContents,
mockReq.body.prCommentFields,
mockReq.body.existingTests,
mockReq.body.generatedTests,
mockReq.body.coverage_message,
"test-user-id",
"test-user",
{},
"", // traceId should be empty string
)
})
})
describe("authentication and authorization", () => {
it("should return 401 when user nickname is null", async () => {
mockDependencies.userNickname.mockResolvedValue(null)
await createPr(mockReq, mockRes)
expect(mockDependencies.userNickname).toHaveBeenCalledWith("test-user-id")
expect(mockRes.status).toHaveBeenCalledWith(401)
expect(mockRes.json).toHaveBeenCalledWith({ error: "Unauthorized" })
})
it("should return 401 when installation octokit returns Error", async () => {
mockDependencies.userNickname.mockResolvedValue("test-user")
mockDependencies.getInstallationOctokitByOwner.mockResolvedValue(
new Error("Installation not found"),
)
await createPr(mockReq, mockRes)
expect(mockDependencies.getInstallationOctokitByOwner).toHaveBeenCalledWith(
mockDependencies.githubApp,
"test-owner",
"test-repo",
)
expect(mockRes.status).toHaveBeenCalledWith(401)
expect(mockRes.send).toHaveBeenCalledWith("Installation not found")
})
it("should return 401 when user is not a collaborator", async () => {
mockDependencies.userNickname.mockResolvedValue("test-user")
mockDependencies.getInstallationOctokitByOwner.mockResolvedValue({})
mockDependencies.isUserCollaborator.mockResolvedValue(false)
const consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {})
await createPr(mockReq, mockRes)
expect(mockDependencies.isUserCollaborator).toHaveBeenCalledWith(
{},
"test-owner",
"test-repo",
"test-user",
)
expect(consoleSpy).toHaveBeenCalledWith(
"test-user is not a collaborator on test-owner/test-repo",
)
expect(mockRes.status).toHaveBeenCalledWith(401)
expect(mockRes.json).toHaveBeenCalledWith({ error: "Unauthorized" })
consoleSpy.mockRestore()
})
})
describe("approval workflow", () => {
beforeEach(() => {
mockDependencies.userNickname.mockResolvedValue("test-user")
mockDependencies.getInstallationOctokitByOwner.mockResolvedValue({})
mockDependencies.isUserCollaborator.mockResolvedValue(true)
})
it("should skip approval check when traceId is missing", async () => {
mockReq.body.traceId = ""
mockDependencies.requiresApproval.mockReturnValue(true)
mockDependencies.triggerCreatePr.mockResolvedValue(456)
await createPr(mockReq, mockRes)
expect(mockDependencies.prisma.optimization_features.findUnique).not.toHaveBeenCalled()
expect(mockDependencies.triggerCreatePr).toHaveBeenCalled()
expect(mockRes.json).toHaveBeenCalledWith(456)
})
it("should skip approval check when approval is not required", async () => {
mockDependencies.requiresApproval.mockReturnValue(false)
mockDependencies.triggerCreatePr.mockResolvedValue(456)
await createPr(mockReq, mockRes)
expect(mockDependencies.prisma.optimization_features.findUnique).not.toHaveBeenCalled()
expect(mockDependencies.triggerCreatePr).toHaveBeenCalled()
expect(mockRes.json).toHaveBeenCalledWith(456)
})
it("should return 403 for rejected requests", async () => {
mockDependencies.requiresApproval.mockReturnValue(true)
mockDependencies.prisma.optimization_features.findUnique.mockResolvedValue({
approval_status: "rejected",
approval_required: true,
})
await createPr(mockReq, mockRes)
expect(mockDependencies.prisma.optimization_features.findUnique).toHaveBeenCalledWith({
where: { trace_id: "test-trace-id" },
select: {
approval_required: true,
approval_status: true,
},
})
expect(mockRes.status).toHaveBeenCalledWith(403)
expect(mockRes.json).toHaveBeenCalledWith({
status: "rejected",
message: "This optimization request was rejected.",
})
})
it("should proceed with PR creation for approved requests with successful PR", async () => {
mockDependencies.requiresApproval.mockReturnValue(true)
mockDependencies.prisma.optimization_features.findUnique.mockResolvedValue({
approval_status: "approved",
approval_required: true,
})
mockDependencies.triggerCreatePr.mockResolvedValue(123)
const consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {})
await createPr(mockReq, mockRes)
expect(consoleSpy).toHaveBeenCalledWith(
"Request test-trace-id was previously approved, continuing with PR creation",
)
expect(mockDependencies.triggerCreatePr).toHaveBeenCalled()
expect(mockRes.json).toHaveBeenCalledWith(123)
consoleSpy.mockRestore()
})
it("should return 500 for approved requests with failed PR creation", async () => {
mockDependencies.requiresApproval.mockReturnValue(true)
mockDependencies.prisma.optimization_features.findUnique.mockResolvedValue({
approval_status: "approved",
approval_required: true,
})
mockDependencies.triggerCreatePr.mockResolvedValue(-1)
await createPr(mockReq, mockRes)
expect(mockRes.status).toHaveBeenCalledWith(500)
expect(mockRes.send).toHaveBeenCalledWith("Error creating pull request")
})
it("should request approval for pending requests", async () => {
mockDependencies.requiresApproval.mockReturnValue(true)
mockDependencies.prisma.optimization_features.findUnique.mockResolvedValue({
approval_status: "pending",
approval_required: true,
})
await createPr(mockReq, mockRes)
expect(mockDependencies.requestApproval).toHaveBeenCalledWith(
"test-trace-id",
"test-owner",
"test-repo",
"testFunction",
"test-user-id",
{
type: "create-pr",
owner: "test-owner",
repo: "test-repo",
baseBranch: "main",
diffContents: mockReq.body.diffContents,
prCommentFields: mockReq.body.prCommentFields,
existingTests: [],
generatedTests: [],
coverage_message: "coverage message", // Changed from coverage to coverage_message
userId: "test-user-id",
},
)
expect(mockRes.status).toHaveBeenCalledWith(202)
expect(mockRes.json).toHaveBeenCalledWith({
status: "pending_approval",
message: "This optimization requires approval. You will be notified when it is processed.",
})
})
it("should request approval for new requests (null approval_status)", async () => {
mockDependencies.requiresApproval.mockReturnValue(true)
mockDependencies.prisma.optimization_features.findUnique.mockResolvedValue({
approval_status: null,
approval_required: true,
})
await createPr(mockReq, mockRes)
expect(mockDependencies.requestApproval).toHaveBeenCalled()
expect(mockRes.status).toHaveBeenCalledWith(202)
})
it("should handle missing optimization record", async () => {
mockDependencies.requiresApproval.mockReturnValue(true)
mockDependencies.prisma.optimization_features.findUnique.mockResolvedValue(null)
await createPr(mockReq, mockRes)
expect(mockDependencies.requestApproval).toHaveBeenCalled()
expect(mockRes.status).toHaveBeenCalledWith(202)
})
})
describe("successful PR creation without approval", () => {
beforeEach(() => {
mockDependencies.userNickname.mockResolvedValue("test-user")
mockDependencies.getInstallationOctokitByOwner.mockResolvedValue({})
mockDependencies.isUserCollaborator.mockResolvedValue(true)
mockDependencies.requiresApproval.mockReturnValue(false)
})
it("should create PR successfully", async () => {
mockDependencies.triggerCreatePr.mockResolvedValue(456)
await createPr(mockReq, mockRes)
expect(mockDependencies.triggerCreatePr).toHaveBeenCalledWith(
"test-owner",
"test-repo",
"main",
mockReq.body.diffContents,
mockReq.body.prCommentFields,
mockReq.body.existingTests,
mockReq.body.generatedTests,
mockReq.body.coverage_message, // Changed from coverage to coverage_message
"test-user-id",
"test-user",
{},
"test-trace-id",
)
expect(mockRes.json).toHaveBeenCalledWith(456)
})
it("should return 500 when PR creation fails", async () => {
mockDependencies.triggerCreatePr.mockResolvedValue(-1)
await createPr(mockReq, mockRes)
expect(mockRes.status).toHaveBeenCalledWith(500)
expect(mockRes.send).toHaveBeenCalledWith("Error creating pull request")
})
it("should return 500 when PR creation returns 0", async () => {
mockDependencies.triggerCreatePr.mockResolvedValue(0)
await createPr(mockReq, mockRes)
expect(mockRes.status).toHaveBeenCalledWith(500)
expect(mockRes.send).toHaveBeenCalledWith("Error creating pull request")
})
})
describe("error handling", () => {
it("should handle Error instances and track them", async () => {
const error = new Error("Test error")
error.stack = "Test stack trace"
mockDependencies.userNickname.mockRejectedValue(error)
const consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {})
await createPr(mockReq, mockRes)
expect(consoleSpy).toHaveBeenCalledWith(`Error in /cfapi/create-pr: ${error}`)
expect(consoleSpy).toHaveBeenCalledWith(`Error message: ${error.message}`)
expect(consoleSpy).toHaveBeenCalledWith(`Error stack: ${error.stack}`)
expect(mockDependencies.posthog.capture).toHaveBeenCalledWith({
distinctId: "test-user-id",
event: "cfapi-create-pr-failed-error-creating-standalone-pr",
properties: {
error: error.message,
},
})
expect(mockRes.status).toHaveBeenCalledWith(500)
expect(mockRes.send).toHaveBeenCalledWith(`Error creating pull request: ${error.message}`)
consoleSpy.mockRestore()
})
it("should handle non-Error exceptions", async () => {
mockDependencies.userNickname.mockRejectedValue("String error")
const consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {})
await createPr(mockReq, mockRes)
expect(consoleSpy).toHaveBeenCalledWith("Error in /cfapi/create-pr: String error")
expect(mockRes.status).toHaveBeenCalledWith(500)
expect(mockRes.send).toHaveBeenCalledWith("Error creating pull request")
consoleSpy.mockRestore()
})
it("should handle errors in database queries during approval check", async () => {
mockDependencies.userNickname.mockResolvedValue("test-user")
mockDependencies.getInstallationOctokitByOwner.mockResolvedValue({})
mockDependencies.isUserCollaborator.mockResolvedValue(true)
mockDependencies.requiresApproval.mockReturnValue(true)
mockDependencies.prisma.optimization_features.findUnique.mockRejectedValue(
new Error("Database error"),
)
const consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {})
await createPr(mockReq, mockRes)
expect(mockRes.status).toHaveBeenCalledWith(500)
expect(mockRes.send).toHaveBeenCalledWith("Error creating pull request: Database error")
consoleSpy.mockRestore()
})
it("should handle errors in requestApproval", async () => {
mockDependencies.userNickname.mockResolvedValue("test-user")
mockDependencies.getInstallationOctokitByOwner.mockResolvedValue({})
mockDependencies.isUserCollaborator.mockResolvedValue(true)
mockDependencies.requiresApproval.mockReturnValue(true)
mockDependencies.prisma.optimization_features.findUnique.mockResolvedValue({
approval_status: "pending",
})
mockDependencies.requestApproval.mockRejectedValue(new Error("Approval service error"))
const consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {})
await createPr(mockReq, mockRes)
expect(mockRes.status).toHaveBeenCalledWith(500)
expect(mockRes.send).toHaveBeenCalledWith(
"Error creating pull request: Approval service error",
)
consoleSpy.mockRestore()
})
})
})
describe("triggerCreatePr", () => {
let mockDeps: any
let mockInstallationOctokit: any
let consoleSpy: any
beforeEach(() => {
// Mock console.log to prevent noisy output during tests
consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {})
mockInstallationOctokit = { rest: { pulls: {} } }
const mockPrContentBuilder: PrContentBuilder = {
buildResultHeader: jest.fn(() => "## Header"),
buildBenchmarkInfo: jest.fn(() => ""),
buildResultDetails: jest.fn(() => "Details"),
buildResultTestReport: jest.fn(() => "Test report"),
buildResultFooter: jest.fn(() => "Footer"),
buildPrTitle: jest.fn(() => "Test PR Title"),
}
mockDeps = {
prisma: {
optimization_events: {
update: jest.fn(),
},
optimization_features: {
findUnique: jest.fn(),
update: jest.fn(),
},
} as any,
fileDiffsToMap: jest.fn().mockReturnValue(new Map()),
buildPrTitle: jest.fn().mockReturnValue("Test PR Title"),
createNewBranchFromDiffContents: jest.fn(),
createStandalonePullRequest: jest.fn(),
addLabelToPullRequest: jest.fn(),
assignReviewer: jest.fn(),
posthog: {
capture: jest.fn(),
},
createStandalonePRTitleAndBody: jest.fn().mockReturnValue({
title: "Test PR Title",
body: "Test PR Body",
}),
prContentBuilder: mockPrContentBuilder,
}
setTriggerCreatePrDependencies(mockDeps)
})
afterEach(() => {
resetTriggerCreatePrDependencies()
jest.clearAllMocks()
consoleSpy.mockRestore()
})
describe("successful PR creation", () => {
it("should successfully create a PR with traceId", async () => {
const mockPrCommentFields = {
function_name: "testFunc",
speedup_pct: 50,
speedup_x: 2,
optimization_explanation: "Test optimization",
}
mockDeps.createNewBranchFromDiffContents.mockResolvedValue(true)
mockDeps.createStandalonePullRequest.mockResolvedValue({
data: {
id: 123,
number: 456,
html_url: "https://github.com/test/test/pull/456",
head: { ref: "test-branch" },
},
})
const result = await triggerCreatePr(
"test-owner",
"test-repo",
"main",
[],
mockPrCommentFields,
"existing tests",
"generated tests",
"coverage message",
"user123",
"testuser",
mockInstallationOctokit,
"trace123",
)
expect(result).toBe(456)
expect(mockDeps.fileDiffsToMap).toHaveBeenCalledWith([])
expect(mockDeps.buildPrTitle).toHaveBeenCalledWith("testFunc", 50, 2)
expect(mockDeps.createNewBranchFromDiffContents).toHaveBeenCalledWith(
mockInstallationOctokit,
"test-owner",
"test-repo",
expect.stringContaining("codeflash/optimize-testFunc-"),
"main",
expect.any(Map),
"Test PR Title\nTest optimization",
)
expect(mockDeps.createStandalonePRTitleAndBody).toHaveBeenCalledWith(
mockPrCommentFields,
"existing tests",
"generated tests",
"coverage message",
expect.stringContaining("codeflash/optimize-testFunc-"),
mockDeps.prContentBuilder,
)
expect(mockDeps.createStandalonePullRequest).toHaveBeenCalledWith(
mockInstallationOctokit,
"test-owner",
"test-repo",
"Test PR Title",
"Test PR Body",
expect.stringContaining("codeflash/optimize-testFunc-"),
"main",
)
expect(mockDeps.addLabelToPullRequest).toHaveBeenCalledWith(
mockInstallationOctokit,
"test-owner",
"test-repo",
456,
)
expect(mockDeps.assignReviewer).toHaveBeenCalledWith(
mockInstallationOctokit,
"test-owner",
"test-repo",
456,
"testuser",
)
expect(consoleSpy).toHaveBeenCalledWith("Created new PR #456 with branch test-branch")
expect(mockDeps.posthog.capture).toHaveBeenCalledWith({
distinctId: "user123",
event: "cfapi-create-pr-success-standalone-pr-created",
properties: {
owner: "test-owner",
repo: "test-repo",
newPrNumber: 456,
newPrBranch: "test-branch",
PRURL: "https://github.com/test/test/pull/456",
},
})
})
it("should successfully create a PR without traceId", async () => {
const mockPrCommentFields = {
function_name: "testFunc",
speedup_pct: 50,
speedup_x: 2,
optimization_explanation: "Test optimization",
}
mockDeps.createNewBranchFromDiffContents.mockResolvedValue(true)
mockDeps.createStandalonePullRequest.mockResolvedValue({
data: {
id: 123,
number: 456,
html_url: "https://github.com/test/test/pull/456",
head: { ref: "test-branch" },
},
})
const result = await triggerCreatePr(
"test-owner",
"test-repo",
"main",
[],
mockPrCommentFields,
"",
"",
"",
"user123",
"testuser",
mockInstallationOctokit,
"", // empty traceId
)
expect(result).toBe(456)
expect(mockDeps.prisma.optimization_events.update).toHaveBeenCalledWith({
where: { trace_id: "" },
data: {
pr_id: "123",
is_optimization_found: true,
event_type: "pr_created",
},
})
// Should not call optimization_features operations when traceId is empty
expect(mockDeps.prisma.optimization_features.findUnique).not.toHaveBeenCalled()
expect(mockDeps.prisma.optimization_features.update).not.toHaveBeenCalled()
})
})
describe("branch creation failure", () => {
it("should return -1 when branch creation fails", async () => {
mockDeps.createNewBranchFromDiffContents.mockResolvedValue(false)
const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {})
const result = await triggerCreatePr(
"test-owner",
"test-repo",
"main",
[],
{ function_name: "testFunc" },
"",
"",
"",
"user123",
"testuser",
mockInstallationOctokit,
)
expect(result).toBe(-1)
expect(consoleErrorSpy).toHaveBeenCalledWith(
expect.stringContaining("Error in triggerCreatePr:"),
)
consoleErrorSpy.mockRestore()
})
})
describe("optimization events handling", () => {
beforeEach(() => {
mockDeps.createNewBranchFromDiffContents.mockResolvedValue(true)
mockDeps.createStandalonePullRequest.mockResolvedValue({
data: {
id: 123,
number: 456,
html_url: "https://github.com/test/test/pull/456",
head: { ref: "test-branch" },
},
})
})
it("should handle optimization_events update failure gracefully", async () => {
const mockPrCommentFields = {
function_name: "testFunc",
speedup_pct: 50,
speedup_x: 2,
optimization_explanation: "Test optimization",
}
mockDeps.prisma.optimization_events.update.mockRejectedValue(new Error("DB error"))
const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {})
const result = await triggerCreatePr(
"test-owner",
"test-repo",
"main",
[],
mockPrCommentFields,
"",
"",
"",
"user123",
"testuser",
mockInstallationOctokit,
"trace123",
)
expect(result).toBe(456)
expect(consoleErrorSpy).toHaveBeenCalledWith(
"Failed to update optimization event:",
expect.any(Error),
)
consoleErrorSpy.mockRestore()
})
})
describe("optimization features handling", () => {
beforeEach(() => {
mockDeps.createNewBranchFromDiffContents.mockResolvedValue(true)
mockDeps.createStandalonePullRequest.mockResolvedValue({
data: {
id: 123,
number: 456,
html_url: "https://github.com/test/test/pull/456",
head: { ref: "test-branch" },
},
})
})
it("should update optimization_features when traceId exists and record found", async () => {
const mockPrCommentFields = {
function_name: "testFunc",
speedup_pct: 50,
speedup_x: 2,
optimization_explanation: "Test optimization",
}
mockDeps.prisma.optimization_features.findUnique.mockResolvedValue({
pull_request: { existing: "data" },
})
const result = await triggerCreatePr(
"test-owner",
"test-repo",
"main",
[],
mockPrCommentFields,
"",
"",
"",
"user123",
"testuser",
mockInstallationOctokit,
"trace123",
)
expect(result).toBe(456)
expect(mockDeps.prisma.optimization_features.findUnique).toHaveBeenCalledWith({
where: { trace_id: "trace123" },
select: { pull_request: true },
})
expect(mockDeps.prisma.optimization_features.update).toHaveBeenCalledWith({
where: { trace_id: "trace123" },
data: {
pull_request: {
existing: "data",
new_pr_url: "https://github.com/test/test/pull/456",
},
},
})
})
it("should handle null pull_request in optimization_features", async () => {
const mockPrCommentFields = {
function_name: "testFunc",
speedup_pct: 50,
speedup_x: 2,
optimization_explanation: "Test optimization",
}
mockDeps.prisma.optimization_features.findUnique.mockResolvedValue({
pull_request: null,
})
const result = await triggerCreatePr(
"test-owner",
"test-repo",
"main",
[],
mockPrCommentFields,
"",
"",
"",
"user123",
"testuser",
mockInstallationOctokit,
"trace123",
)
expect(result).toBe(456)
expect(mockDeps.prisma.optimization_features.update).toHaveBeenCalledWith({
where: { trace_id: "trace123" },
data: {
pull_request: {
new_pr_url: "https://github.com/test/test/pull/456",
},
},
})
})
it("should handle undefined pull_request in optimization_features", async () => {
const mockPrCommentFields = {
function_name: "testFunc",
speedup_pct: 50,
speedup_x: 2,
optimization_explanation: "Test optimization",
}
mockDeps.prisma.optimization_features.findUnique.mockResolvedValue({
pull_request: undefined,
})
const result = await triggerCreatePr(
"test-owner",
"test-repo",
"main",
[],
mockPrCommentFields,
"",
"",
"",
"user123",
"testuser",
mockInstallationOctokit,
"trace123",
)
expect(result).toBe(456)
expect(mockDeps.prisma.optimization_features.update).toHaveBeenCalledWith({
where: { trace_id: "trace123" },
data: {
pull_request: {
new_pr_url: "https://github.com/test/test/pull/456",
},
},
})
})
it("should handle when optimization_features record is not found", async () => {
const mockPrCommentFields = {
function_name: "testFunc",
speedup_pct: 50,
speedup_x: 2,
optimization_explanation: "Test optimization",
}
mockDeps.prisma.optimization_features.findUnique.mockResolvedValue(null)
const result = await triggerCreatePr(
"test-owner",
"test-repo",
"main",
[],
mockPrCommentFields,
"",
"",
"",
"user123",
"testuser",
mockInstallationOctokit,
"trace123",
)
expect(result).toBe(456)
expect(mockDeps.prisma.optimization_features.findUnique).toHaveBeenCalled()
expect(mockDeps.prisma.optimization_features.update).not.toHaveBeenCalled()
})
})
describe("error scenarios", () => {
it("should handle errors in PR creation process", async () => {
mockDeps.createNewBranchFromDiffContents.mockResolvedValue(true)
mockDeps.createStandalonePullRequest.mockRejectedValue(new Error("PR creation failed"))
const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {})
const result = await triggerCreatePr(
"test-owner",
"test-repo",
"main",
[],
{ function_name: "testFunc" },
"",
"",
"",
"user123",
"testuser",
mockInstallationOctokit,
)
expect(result).toBe(-1)
expect(consoleErrorSpy).toHaveBeenCalledWith(
expect.stringContaining("Error in triggerCreatePr:"),
)
consoleErrorSpy.mockRestore()
})
it("should handle errors in labeling PR", async () => {
mockDeps.createNewBranchFromDiffContents.mockResolvedValue(true)
mockDeps.createStandalonePullRequest.mockResolvedValue({
data: {
id: 123,
number: 456,
html_url: "https://github.com/test/test/pull/456",
head: { ref: "test-branch" },
},
})
mockDeps.addLabelToPullRequest.mockRejectedValue(new Error("Label failed"))
const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {})
const result = await triggerCreatePr(
"test-owner",
"test-repo",
"main",
[],
{ function_name: "testFunc" },
"",
"",
"",
"user123",
"testuser",
mockInstallationOctokit,
)
expect(result).toBe(-1)
expect(consoleErrorSpy).toHaveBeenCalledWith(
expect.stringContaining("Error in triggerCreatePr:"),
)
consoleErrorSpy.mockRestore()
})
it("should handle errors in assigning reviewer", async () => {
mockDeps.createNewBranchFromDiffContents.mockResolvedValue(true)
mockDeps.createStandalonePullRequest.mockResolvedValue({
data: {
id: 123,
number: 456,
html_url: "https://github.com/test/test/pull/456",
head: { ref: "test-branch" },
},
})
mockDeps.addLabelToPullRequest.mockResolvedValue(undefined)
mockDeps.assignReviewer.mockRejectedValue(new Error("Reviewer assignment failed"))
const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {})
const result = await triggerCreatePr(
"test-owner",
"test-repo",
"main",
[],
{ function_name: "testFunc" },
"",
"",
"",
"user123",
"testuser",
mockInstallationOctokit,
)
expect(result).toBe(-1)
expect(consoleErrorSpy).toHaveBeenCalledWith(
expect.stringContaining("Error in triggerCreatePr:"),
)
consoleErrorSpy.mockRestore()
})
})
})
describe("createStandalonePRTitleAndBody", () => {
let mockBuilder: PrContentBuilder
beforeEach(() => {
mockBuilder = {
buildResultHeader: jest.fn(() => "## Header"),
buildBenchmarkInfo: jest.fn(() => ""),
buildResultDetails: jest.fn(() => "Details"),
buildResultTestReport: jest.fn(() => "Test report"),
buildResultFooter: jest.fn(() => "Footer"),
buildPrTitle: jest.fn(() => "Test PR Title"),
}
})
it("should generate correct title and body without benchmark info", () => {
const prCommentFields = {
function_name: "testFunc",
speedup_pct: 50,
speedup_x: 2,
optimization_explanation: "Test optimization",
// No benchmark_details property
} as any
const result = createStandalonePRTitleAndBody(
prCommentFields,
"existing tests",
"generated tests",
"coverage",
"branch-name",
mockBuilder,
)
expect(result.title).toBe("Test PR Title")
expect(result.body).toBe("## Header\nDetails\nTest report\nFooter")
expect(mockBuilder.buildPrTitle).toHaveBeenCalledWith("testFunc", 50, 2)
expect(mockBuilder.buildResultHeader).toHaveBeenCalledWith(prCommentFields)
expect(mockBuilder.buildResultDetails).toHaveBeenCalledWith(prCommentFields)
expect(mockBuilder.buildResultTestReport).toHaveBeenCalledWith(
prCommentFields,
"existing tests",
"generated tests",
"coverage",
)
expect(mockBuilder.buildResultFooter).toHaveBeenCalledWith("branch-name")
// buildBenchmarkInfo should NOT be called when benchmark_details is undefined
expect(mockBuilder.buildBenchmarkInfo).not.toHaveBeenCalled()
})
it("should generate correct title and body with benchmark info", () => {
mockBuilder.buildBenchmarkInfo = jest.fn(() => "Benchmark data")
const prCommentFields = {
function_name: "testFunc",
speedup_pct: 50,
speedup_x: 2,
optimization_explanation: "Test optimization",
benchmark_details: [
{
benchmark_name: "test",
test_function: "func",
original_timing: 100,
expected_new_timing: 50,
speedup_percent: 50,
},
],
} as any
const result = createStandalonePRTitleAndBody(
prCommentFields,
"existing tests",
"generated tests",
"coverage",
"branch-name",
mockBuilder,
)
expect(result.title).toBe("Test PR Title")
expect(result.body).toBe("## Header\nBenchmark data\nDetails\nTest report\nFooter")
expect(mockBuilder.buildBenchmarkInfo).toHaveBeenCalledWith(prCommentFields)
})
it("should handle empty benchmark details array", () => {
const prCommentFields = {
function_name: "testFunc",
speedup_pct: 50,
speedup_x: 2,
optimization_explanation: "Test optimization",
benchmark_details: [],
} as any
const result = createStandalonePRTitleAndBody(
prCommentFields,
"existing tests",
"generated tests",
"coverage",
"branch-name",
mockBuilder,
)
expect(result.body).toBe("## Header\nDetails\nTest report\nFooter")
// buildBenchmarkInfo should NOT be called when benchmark_details is empty array
expect(mockBuilder.buildBenchmarkInfo).not.toHaveBeenCalled()
})
it("should handle null benchmark_details", () => {
const prCommentFields = {
function_name: "testFunc",
speedup_pct: 50,
speedup_x: 2,
optimization_explanation: "Test optimization",
benchmark_details: null,
} as any
const result = createStandalonePRTitleAndBody(
prCommentFields,
"existing tests",
"generated tests",
"coverage",
"branch-name",
mockBuilder,
)
expect(result.body).toBe("## Header\nDetails\nTest report\nFooter")
// buildBenchmarkInfo should NOT be called when benchmark_details is null
expect(mockBuilder.buildBenchmarkInfo).not.toHaveBeenCalled()
})
})