diff --git a/__tests__/actions/claude-jobs.test.ts b/__tests__/actions/claude-jobs.test.ts index 28b8783..1da99ef 100644 --- a/__tests__/actions/claude-jobs.test.ts +++ b/__tests__/actions/claude-jobs.test.ts @@ -5,6 +5,7 @@ const { mockFindFirstTask, mockFindManyTask, mockFindFirstProduct, + mockFindFirstSprint, mockFindFirstJob, mockCreateJob, mockUpdateJob, @@ -15,6 +16,7 @@ const { mockFindFirstTask: vi.fn(), mockFindManyTask: vi.fn(), mockFindFirstProduct: vi.fn(), + mockFindFirstSprint: vi.fn(), mockFindFirstJob: vi.fn(), mockCreateJob: vi.fn(), mockUpdateJob: vi.fn(), @@ -32,6 +34,7 @@ vi.mock('@/lib/prisma', () => ({ prisma: { task: { findFirst: mockFindFirstTask, findMany: mockFindManyTask }, product: { findFirst: mockFindFirstProduct }, + sprint: { findFirst: mockFindFirstSprint }, claudeJob: { findFirst: mockFindFirstJob, create: mockCreateJob, @@ -121,9 +124,10 @@ describe('enqueueClaudeJobAction', () => { }) describe('enqueueAllTodoJobsAction', () => { - it('happy path: queues a job for every TO_DO task without active job', async () => { + it('happy path: scopes to active sprint + assignee, queues all queueable tasks', async () => { mockGetSession.mockResolvedValue(SESSION_USER) mockFindFirstProduct.mockResolvedValue({ id: PRODUCT_ID }) + mockFindFirstSprint.mockResolvedValue({ id: 'sprint-1' }) mockFindManyTask.mockResolvedValue([{ id: 'task-a' }, { id: 'task-b' }]) mockTransaction.mockResolvedValue([ { id: 'job-a', task_id: 'task-a' }, @@ -133,12 +137,33 @@ describe('enqueueAllTodoJobsAction', () => { const result = await enqueueAllTodoJobsAction(PRODUCT_ID) expect(result).toEqual({ success: true, count: 2 }) + expect(mockFindManyTask).toHaveBeenCalledWith( + expect.objectContaining({ + where: expect.objectContaining({ + status: 'TO_DO', + story: { sprint_id: 'sprint-1', assignee_id: SESSION_USER.userId }, + }), + }) + ) expect(mockExecuteRaw).toHaveBeenCalledTimes(2) }) - it('returns count=0 when no queueable tasks', async () => { + it('returns count=0 when product has no active sprint', async () => { mockGetSession.mockResolvedValue(SESSION_USER) mockFindFirstProduct.mockResolvedValue({ id: PRODUCT_ID }) + mockFindFirstSprint.mockResolvedValue(null) + + const result = await enqueueAllTodoJobsAction(PRODUCT_ID) + + expect(result).toEqual({ success: true, count: 0 }) + expect(mockFindManyTask).not.toHaveBeenCalled() + expect(mockTransaction).not.toHaveBeenCalled() + }) + + it('returns count=0 when no queueable tasks in sprint+assignee scope', async () => { + mockGetSession.mockResolvedValue(SESSION_USER) + mockFindFirstProduct.mockResolvedValue({ id: PRODUCT_ID }) + mockFindFirstSprint.mockResolvedValue({ id: 'sprint-1' }) mockFindManyTask.mockResolvedValue([]) const result = await enqueueAllTodoJobsAction(PRODUCT_ID) diff --git a/actions/claude-jobs.ts b/actions/claude-jobs.ts index 1ed1fef..17c55d3 100644 --- a/actions/claude-jobs.ts +++ b/actions/claude-jobs.ts @@ -78,10 +78,20 @@ export async function enqueueAllTodoJobsAction(productId: string): Promise