PBI-8 (vervolg): Sprint-aware branch + SPRINT-mode draft-PR

T-22 — sprint-aware branch-resolutie (resolveBranchForJob):
  - SPRINT-mode  → feat/sprint-<sprint_run_id-suffix> (één branch voor hele run)
  - STORY-mode   → feat/story-<story_id-suffix>      (één per story)
  - Legacy (zonder sprint_run_id): bestaand gedrag
  Sibling-detection herbruikt branch wanneer een eerdere job in dezelfde
  scope al de branch heeft.

T-24 — SPRINT-mode draft-PR + ready-bij-DONE:
  - createPullRequest accepteert nu draft + enableAutoMerge flags
  - Nieuwe markPullRequestReady-helper voor draft → ready transitie
  - maybeCreateAutoPr in SPRINT-mode: opent één draft-PR per SprintRun met
    sprint_goal als titel; geen auto-merge; sibling-tasks hergebruiken de
    PR
  - update-job-status detecteert sprint-DONE via PropagationResult en zet
    de draft-PR via markPullRequestReady ready-for-review (mens reviewt en
    mergt zelf)

T-23 — STORY-mode dekking: bestaande createPullRequest + auto-merge gedrag
ongewijzigd. Tests uitgebreid met sprint-aware mocks; 6 nieuwe
branch-resolution tests + 2 sprint-mode auto-pr tests + 4 markPullRequest
Ready/draft-PR tests.

Tests: 195/195 groen (180 → 195; 15 nieuwe scenario's voor sprint-aware
branch + SPRINT-mode draft-PR + markPullRequestReady).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Madhura68 2026-05-06 17:15:04 +02:00
parent 7b135e12dd
commit 454d96ee04
7 changed files with 359 additions and 24 deletions

View file

@ -12,7 +12,7 @@ vi.mock('node:util', () => ({
),
}))
import { createPullRequest } from '../../src/git/pr.js'
import { createPullRequest, markPullRequestReady } from '../../src/git/pr.js'
beforeEach(() => {
vi.clearAllMocks()
@ -66,4 +66,80 @@ describe('createPullRequest', () => {
expect(result).toMatchObject({ error: expect.stringContaining('gh pr create failed') })
})
it('passes --draft when draft=true en slaat auto-merge over', async () => {
const calls: string[][] = []
mockExecFile.mockImplementation(
(
_cmd: string,
args: string[],
_opts: unknown,
cb: (err: null, res: { stdout: string; stderr: string }) => void,
) => {
calls.push(args)
cb(null, {
stdout: 'Creating draft pull request...\nhttps://github.com/org/repo/pull/100\n',
stderr: '',
})
},
)
const result = await createPullRequest({
worktreePath: '/wt/sprint-1',
branchName: 'feat/sprint-12345678',
title: 'Sprint: Cascade-flow live',
body: 'Sprint draft',
draft: true,
enableAutoMerge: false,
})
expect(result).toEqual({ url: 'https://github.com/org/repo/pull/100' })
expect(calls.some((a) => a.includes('--draft'))).toBe(true)
// gh pr merge --auto mag NIET gestart zijn voor draft + auto-merge=false
expect(calls.some((a) => a[0] === 'pr' && a[1] === 'merge')).toBe(false)
})
})
describe('markPullRequestReady', () => {
it('roept gh pr ready aan met de PR-URL', async () => {
const calls: string[][] = []
mockExecFile.mockImplementation(
(
_cmd: string,
args: string[],
_opts: unknown,
cb: (err: null, res: { stdout: string; stderr: string }) => void,
) => {
calls.push(args)
cb(null, { stdout: '', stderr: '' })
},
)
const result = await markPullRequestReady({ prUrl: 'https://github.com/org/repo/pull/100' })
expect(result).toEqual({ ok: true })
expect(calls[0]).toEqual(['pr', 'ready', 'https://github.com/org/repo/pull/100'])
})
it('behandelt "already ready" als success', async () => {
mockExecFile.mockImplementation(
(_cmd: string, _args: string[], _opts: unknown, cb: (err: Error) => void) =>
cb(Object.assign(new Error(''), { stderr: 'Pull request is not in draft state' })),
)
const result = await markPullRequestReady({ prUrl: 'https://github.com/org/repo/pull/100' })
expect(result).toEqual({ ok: true })
})
it('retourneert error op onverwachte gh-fout', async () => {
mockExecFile.mockImplementation(
(_cmd: string, _args: string[], _opts: unknown, cb: (err: Error) => void) =>
cb(new Error('rate limit exceeded')),
)
const result = await markPullRequestReady({ prUrl: 'https://github.com/org/repo/pull/100' })
expect(result).toMatchObject({ error: expect.stringContaining('gh pr ready failed') })
})
})