peregrine/web/src/stores/survey.test.ts

173 lines
5.3 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useSurveyStore } from './survey'
vi.mock('../composables/useApi', () => ({
useApiFetch: vi.fn(),
}))
import { useApiFetch } from '../composables/useApi'
describe('useSurveyStore', () => {
beforeEach(() => {
setActivePinia(createPinia())
})
afterEach(() => {
vi.clearAllMocks()
})
it('fetchFor loads history and vision availability in parallel', async () => {
const mockApiFetch = vi.mocked(useApiFetch)
mockApiFetch
.mockResolvedValueOnce({ data: [], error: null }) // history
.mockResolvedValueOnce({ data: { available: true }, error: null }) // vision
const store = useSurveyStore()
await store.fetchFor(1)
expect(store.history).toEqual([])
expect(store.visionAvailable).toBe(true)
expect(store.currentJobId).toBe(1)
expect(mockApiFetch).toHaveBeenCalledTimes(2)
})
it('fetchFor clears state when called for a different job', async () => {
const mockApiFetch = vi.mocked(useApiFetch)
// Job 1
mockApiFetch
.mockResolvedValueOnce({ data: [{ id: 1, llm_output: 'old' }], error: null })
.mockResolvedValueOnce({ data: { available: false }, error: null })
const store = useSurveyStore()
await store.fetchFor(1)
expect(store.history.length).toBe(1)
// Job 2 — state must be cleared before new data arrives
mockApiFetch
.mockResolvedValueOnce({ data: [], error: null })
.mockResolvedValueOnce({ data: { available: true }, error: null })
await store.fetchFor(2)
expect(store.history).toEqual([])
expect(store.currentJobId).toBe(2)
})
it('analyze stores result including mode and rawInput', async () => {
const mockApiFetch = vi.mocked(useApiFetch)
mockApiFetch.mockResolvedValueOnce({
data: { output: '1. B — reason', source: 'text_paste' },
error: null,
})
const store = useSurveyStore()
await store.analyze(1, { text: 'Q1: test', mode: 'quick' })
expect(store.analysis).not.toBeNull()
expect(store.analysis!.output).toBe('1. B — reason')
expect(store.analysis!.source).toBe('text_paste')
expect(store.analysis!.mode).toBe('quick')
expect(store.analysis!.rawInput).toBe('Q1: test')
expect(store.loading).toBe(false)
})
it('analyze sets error on failure', async () => {
const mockApiFetch = vi.mocked(useApiFetch)
mockApiFetch.mockResolvedValueOnce({
data: null,
error: { kind: 'http', status: 500, detail: 'LLM unavailable' },
})
const store = useSurveyStore()
await store.analyze(1, { text: 'Q1: test', mode: 'quick' })
expect(store.analysis).toBeNull()
expect(store.error).toBeTruthy()
expect(store.loading).toBe(false)
})
it('saveResponse prepends to history and clears analysis', async () => {
const mockApiFetch = vi.mocked(useApiFetch)
// Setup: fetchFor
mockApiFetch
.mockResolvedValueOnce({ data: [], error: null })
.mockResolvedValueOnce({ data: { available: true }, error: null })
const store = useSurveyStore()
await store.fetchFor(1)
// Set analysis state manually (as if analyze() was called)
store.analysis = {
output: '1. B — reason',
source: 'text_paste',
mode: 'quick',
rawInput: 'Q1: test',
}
// Save
mockApiFetch.mockResolvedValueOnce({
data: { id: 42 },
error: null,
})
await store.saveResponse(1, { surveyName: 'Round 1', reportedScore: '85%' })
expect(store.history.length).toBe(1)
expect(store.history[0].id).toBe(42)
expect(store.history[0].llm_output).toBe('1. B — reason')
expect(store.analysis).toBeNull()
expect(store.saving).toBe(false)
})
it('saveResponse sets error and preserves analysis on POST failure', async () => {
const mockApiFetch = vi.mocked(useApiFetch)
// Setup: fetchFor
mockApiFetch
.mockResolvedValueOnce({ data: [], error: null })
.mockResolvedValueOnce({ data: { available: true }, error: null })
const store = useSurveyStore()
await store.fetchFor(1)
// Set analysis state manually
store.analysis = {
output: '1. B — reason',
source: 'text_paste',
mode: 'quick',
rawInput: 'Q1: test',
}
// Save fails
mockApiFetch.mockResolvedValueOnce({
data: null,
error: { kind: 'http', status: 500, detail: 'Internal Server Error' },
})
await store.saveResponse(1, { surveyName: 'Round 1', reportedScore: '85%' })
expect(store.saving).toBe(false)
expect(store.error).toBeTruthy()
expect(store.analysis).not.toBeNull()
expect(store.analysis!.output).toBe('1. B — reason')
})
it('clear resets all state to initial values', async () => {
const mockApiFetch = vi.mocked(useApiFetch)
mockApiFetch
.mockResolvedValueOnce({ data: [{ id: 1, llm_output: 'test' }], error: null })
.mockResolvedValueOnce({ data: { available: true }, error: null })
const store = useSurveyStore()
await store.fetchFor(1)
store.clear()
expect(store.history).toEqual([])
expect(store.analysis).toBeNull()
expect(store.visionAvailable).toBe(false)
expect(store.loading).toBe(false)
expect(store.saving).toBe(false)
expect(store.error).toBeNull()
expect(store.currentJobId).toBeNull()
})
})