fix: survey store quality issues — loading in fetchFor, source guard, saveResponse failure test

This commit is contained in:
pyr0ball 2026-03-21 00:21:21 -07:00
parent ac8f949a19
commit 7b634cb46a
2 changed files with 57 additions and 13 deletions

View file

@ -119,6 +119,38 @@ describe('useSurveyStore', () => {
expect(store.saving).toBe(false) 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 () => { it('clear resets all state to initial values', async () => {
const mockApiFetch = vi.mocked(useApiFetch) const mockApiFetch = vi.mocked(useApiFetch)
mockApiFetch mockApiFetch

View file

@ -2,6 +2,12 @@ import { ref } from 'vue'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { useApiFetch } from '../composables/useApi' import { useApiFetch } from '../composables/useApi'
const validSources = ['text_paste', 'screenshot'] as const
type ValidSource = typeof validSources[number]
function isValidSource(s: string): s is ValidSource {
return validSources.includes(s as ValidSource)
}
export interface SurveyAnalysis { export interface SurveyAnalysis {
output: string output: string
source: 'text_paste' | 'screenshot' source: 'text_paste' | 'screenshot'
@ -40,18 +46,23 @@ export const useSurveyStore = defineStore('survey', () => {
currentJobId.value = jobId currentJobId.value = jobId
} }
const [historyResult, visionResult] = await Promise.all([ loading.value = true
useApiFetch<SurveyResponse[]>(`/api/jobs/${jobId}/survey/responses`), try {
useApiFetch<{ available: boolean }>('/api/vision/health'), const [historyResult, visionResult] = await Promise.all([
]) useApiFetch<SurveyResponse[]>(`/api/jobs/${jobId}/survey/responses`),
useApiFetch<{ available: boolean }>('/api/vision/health'),
])
if (historyResult.error) { if (historyResult.error) {
error.value = 'Could not load survey history.' error.value = 'Could not load survey history.'
} else { } else {
history.value = historyResult.data ?? [] history.value = historyResult.data ?? []
}
visionAvailable.value = visionResult.data?.available ?? false
} finally {
loading.value = false
} }
visionAvailable.value = visionResult.data?.available ?? false
} }
async function analyze( async function analyze(
@ -71,7 +82,7 @@ export const useSurveyStore = defineStore('survey', () => {
} }
analysis.value = { analysis.value = {
output: data.output, output: data.output,
source: data.source as 'text_paste' | 'screenshot', source: isValidSource(data.source) ? data.source : 'text_paste',
mode: payload.mode, mode: payload.mode,
rawInput: payload.text ?? null, rawInput: payload.text ?? null,
} }
@ -103,6 +114,7 @@ export const useSurveyStore = defineStore('survey', () => {
return return
} }
// Prepend the saved response to history // Prepend the saved response to history
const now = new Date().toISOString()
const saved: SurveyResponse = { const saved: SurveyResponse = {
id: data.id, id: data.id,
survey_name: args.surveyName || null, survey_name: args.surveyName || null,
@ -112,8 +124,8 @@ export const useSurveyStore = defineStore('survey', () => {
image_path: null, image_path: null,
llm_output: analysis.value.output, llm_output: analysis.value.output,
reported_score: args.reportedScore || null, reported_score: args.reportedScore || null,
received_at: new Date().toISOString(), received_at: now,
created_at: new Date().toISOString(), created_at: now,
} }
history.value = [saved, ...history.value] history.value = [saved, ...history.value]
analysis.value = null analysis.value = null