diff --git a/__tests__/actions/sprint-runs.test.ts b/__tests__/actions/sprint-runs.test.ts index 8b39061..9defa50 100644 --- a/__tests__/actions/sprint-runs.test.ts +++ b/__tests__/actions/sprint-runs.test.ts @@ -201,6 +201,106 @@ describe('startSprintRunAction — pre-flight blockers', () => { }) }) +describe('startSprintRunAction — SPRINT_BATCH', () => { + const SPRINT_BATCH = { + ...SPRINT_OK, + product: { + id: 'prod-1', + pr_strategy: 'SPRINT_BATCH', + repo_url: 'https://github.com/example/main', + }, + } + + it('blokkeert task met afwijkende repo_url', async () => { + mockPrisma.sprint.findUnique.mockResolvedValue(SPRINT_BATCH) + mockPrisma.sprintRun.findFirst.mockResolvedValue(null) + mockPrisma.story.findMany.mockResolvedValue([ + { + ...STORY_OK, + tasks: [ + { + id: 'task-1', + code: 'T-1', + title: 'In main repo', + priority: 1, + sort_order: 1, + implementation_plan: 'plan', + repo_url: null, + }, + { + id: 'task-2', + code: 'T-2', + title: 'Cross-repo', + priority: 1, + sort_order: 2, + implementation_plan: 'plan', + repo_url: 'https://github.com/example/other', + }, + ], + }, + ]) + mockPrisma.claudeQuestion.findMany.mockResolvedValue([]) + + const result = await startSprintRunAction({ sprint_id: 'sprint-1' }) + + expect(result).toMatchObject({ ok: false, error: 'PRE_FLIGHT_BLOCKED' }) + if (result.ok === false && 'blockers' in result) { + expect(result.blockers).toContainEqual({ + type: 'task_cross_repo', + id: 'task-2', + label: 'T-2: Cross-repo', + }) + } + expect(mockPrisma.sprintRun.create).not.toHaveBeenCalled() + }) + + it('staat tasks toe wanneer repo_url leeg is of gelijk aan product.repo_url', async () => { + mockPrisma.sprint.findUnique.mockResolvedValue(SPRINT_BATCH) + mockPrisma.sprintRun.findFirst.mockResolvedValue(null) + mockPrisma.story.findMany.mockResolvedValue([ + { + ...STORY_OK, + tasks: [ + { + id: 'task-1', + code: 'T-1', + title: 'No override', + priority: 1, + sort_order: 1, + implementation_plan: 'plan', + repo_url: null, + }, + { + id: 'task-2', + code: 'T-2', + title: 'Same repo', + priority: 1, + sort_order: 2, + implementation_plan: 'plan', + repo_url: 'https://github.com/example/main', + }, + ], + }, + ]) + mockPrisma.claudeQuestion.findMany.mockResolvedValue([]) + mockPrisma.sprintRun.create.mockResolvedValue({ id: 'run-batch' }) + mockPrisma.claudeJob.create.mockResolvedValue({ id: 'job-sprint' }) + + const result = await startSprintRunAction({ sprint_id: 'sprint-1' }) + + expect(result).toMatchObject({ ok: true, sprint_run_id: 'run-batch' }) + // Eén SPRINT_IMPLEMENTATION-job, niet per-task + expect(mockPrisma.claudeJob.create).toHaveBeenCalledTimes(1) + expect(mockPrisma.claudeJob.create).toHaveBeenCalledWith({ + data: expect.objectContaining({ + kind: 'SPRINT_IMPLEMENTATION', + sprint_run_id: 'run-batch', + product_id: 'prod-1', + }), + }) + }) +}) + describe('startSprintRunAction — guards', () => { it('weigert wanneer Sprint niet ACTIVE is', async () => { mockPrisma.sprint.findUnique.mockResolvedValue({ ...SPRINT_OK, status: 'COMPLETED' })