import { describe, it, expect, beforeEach } from 'vitest' import { useIdeaStore } from '@/stores/idea-store' beforeEach(() => { // Reset store between tests — Zustand persists state across tests otherwise. useIdeaStore.setState({ jobByIdea: {}, ideaStatuses: {}, openQuestionsByIdea: {}, }) }) describe('useIdeaStore — handleIdeaJobEvent', () => { it('queued IDEA_GRILL → ideaStatuses[id] = grilling', () => { useIdeaStore.getState().handleIdeaJobEvent({ type: 'claude_job_enqueued', job_id: 'job-1', idea_id: 'idea-1', user_id: 'u-1', kind: 'IDEA_GRILL', status: 'queued', }) const s = useIdeaStore.getState() expect(s.jobByIdea['idea-1']?.status).toBe('queued') expect(s.ideaStatuses['idea-1']).toBe('grilling') }) it('failed IDEA_GRILL → ideaStatuses[id] = grill_failed', () => { useIdeaStore.getState().handleIdeaJobEvent({ type: 'claude_job_status', job_id: 'job-1', idea_id: 'idea-1', user_id: 'u-1', kind: 'IDEA_GRILL', status: 'failed', error: 'oops', }) expect(useIdeaStore.getState().ideaStatuses['idea-1']).toBe('grill_failed') expect(useIdeaStore.getState().jobByIdea['idea-1']?.error).toBe('oops') }) it('failed IDEA_MAKE_PLAN → plan_failed', () => { useIdeaStore.getState().handleIdeaJobEvent({ type: 'claude_job_status', job_id: 'job-2', idea_id: 'idea-2', user_id: 'u-1', kind: 'IDEA_MAKE_PLAN', status: 'failed', }) expect(useIdeaStore.getState().ideaStatuses['idea-2']).toBe('plan_failed') }) it('done does NOT auto-derive status (server is source-of-truth)', () => { useIdeaStore.getState().setIdeaStatus('idea-3', 'grilled') useIdeaStore.getState().handleIdeaJobEvent({ type: 'claude_job_status', job_id: 'job-3', idea_id: 'idea-3', user_id: 'u-1', kind: 'IDEA_GRILL', status: 'done', }) expect(useIdeaStore.getState().ideaStatuses['idea-3']).toBe('grilled') }) }) describe('useIdeaStore — handleIdeaQuestionEvent', () => { it('non-open status removes question from list', () => { useIdeaStore.getState().initQuestions('idea-1', [ { id: 'q-1', idea_id: 'idea-1', question: 'Q', options: null, status: 'open', created_at: '', expires_at: '', }, ]) useIdeaStore.getState().handleIdeaQuestionEvent({ op: 'U', entity: 'question', id: 'q-1', product_id: 'p-1', story_id: null, idea_id: 'idea-1', status: 'answered', }) expect(useIdeaStore.getState().openQuestionsByIdea['idea-1']).toEqual([]) }) it('open status keeps existing list (no detail in payload)', () => { const q = { id: 'q-1', idea_id: 'idea-1', question: 'Q', options: null, status: 'open' as const, created_at: '', expires_at: '', } useIdeaStore.getState().initQuestions('idea-1', [q]) useIdeaStore.getState().handleIdeaQuestionEvent({ op: 'I', entity: 'question', id: 'q-2', product_id: 'p-1', story_id: null, idea_id: 'idea-1', status: 'open', }) // List length blijft 1 (server-fetch leveert de detail) expect(useIdeaStore.getState().openQuestionsByIdea['idea-1']).toHaveLength(1) }) }) describe('useIdeaStore — clearForIdea', () => { it('removes job + status + questions for one idea, leaves others', () => { const s = useIdeaStore.getState() s.setJobStatus({ job_id: 'j-1', idea_id: 'idea-1', kind: 'IDEA_GRILL', status: 'running', }) s.setJobStatus({ job_id: 'j-2', idea_id: 'idea-2', kind: 'IDEA_GRILL', status: 'running', }) s.setIdeaStatus('idea-1', 'grilling') s.setIdeaStatus('idea-2', 'grilling') s.clearForIdea('idea-1') const after = useIdeaStore.getState() expect(after.jobByIdea['idea-1']).toBeUndefined() expect(after.jobByIdea['idea-2']).toBeDefined() expect(after.ideaStatuses['idea-1']).toBeUndefined() expect(after.ideaStatuses['idea-2']).toBe('grilling') }) })