New src/git/pr.ts helper wraps 'gh pr create'; returns { url } or { error }.
maybeCreateAutoPr() in update-job-status checks product.auto_pr, builds title
from story.code + task.title, writes pr_url to DB. Non-fatal: gh failure logs
a warning and leaves DONE status intact. Also syncs schema: auto_pr on Product,
pr_url on ClaudeJob.
69 lines
2.2 KiB
TypeScript
69 lines
2.2 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
|
|
const { mockExecFile } = vi.hoisted(() => ({ mockExecFile: vi.fn() }))
|
|
|
|
vi.mock('node:child_process', () => ({ execFile: mockExecFile }))
|
|
vi.mock('node:util', () => ({
|
|
promisify:
|
|
(fn: (...args: unknown[]) => void) =>
|
|
(...args: unknown[]) =>
|
|
new Promise((resolve, reject) =>
|
|
fn(...args, (err: Error | null, result: unknown) => (err ? reject(err) : resolve(result))),
|
|
),
|
|
}))
|
|
|
|
import { createPullRequest } from '../../src/git/pr.js'
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
describe('createPullRequest', () => {
|
|
it('returns PR URL when gh succeeds', async () => {
|
|
mockExecFile.mockImplementation(
|
|
(_cmd: string, _args: string[], _opts: unknown, cb: (err: null, res: { stdout: string; stderr: string }) => void) =>
|
|
cb(null, { stdout: 'Creating pull request...\nhttps://github.com/org/repo/pull/42\n', stderr: '' }),
|
|
)
|
|
|
|
const result = await createPullRequest({
|
|
worktreePath: '/worktrees/job-abc',
|
|
branchName: 'feat/job-abc',
|
|
title: 'SCRUM-1: Add feature',
|
|
body: 'Summary\n\n---\n\n*Auto-generated*',
|
|
})
|
|
|
|
expect(result).toEqual({ url: 'https://github.com/org/repo/pull/42' })
|
|
})
|
|
|
|
it('returns error when gh is not installed (ENOENT)', async () => {
|
|
mockExecFile.mockImplementation(
|
|
(_cmd: string, _args: string[], _opts: unknown, cb: (err: Error) => void) =>
|
|
cb(Object.assign(new Error('spawn gh ENOENT'), { code: 'ENOENT' })),
|
|
)
|
|
|
|
const result = await createPullRequest({
|
|
worktreePath: '/worktrees/job-abc',
|
|
branchName: 'feat/job-abc',
|
|
title: 'My PR',
|
|
body: 'Body',
|
|
})
|
|
|
|
expect(result).toMatchObject({ error: expect.stringContaining('gh CLI not found') })
|
|
})
|
|
|
|
it('returns error on generic gh failure', async () => {
|
|
mockExecFile.mockImplementation(
|
|
(_cmd: string, _args: string[], _opts: unknown, cb: (err: Error) => void) =>
|
|
cb(new Error('authentication required')),
|
|
)
|
|
|
|
const result = await createPullRequest({
|
|
worktreePath: '/worktrees/job-abc',
|
|
branchName: 'feat/job-abc',
|
|
title: 'My PR',
|
|
body: 'Body',
|
|
})
|
|
|
|
expect(result).toMatchObject({ error: expect.stringContaining('gh pr create failed') })
|
|
})
|
|
})
|