const assert = require('node:assert/strict') const test = require('node:test') const { getVoiceFailureHint, getVoiceLessonStatusText, getVoicePrimaryAction, getVoicePrimaryActionText } = require('../.test-dist/pages/feedback/voice-state.js') const { runWithSessionRefresh } = require('../.test-dist/pages/feedback/voice-actions.js') function session(overrides = {}) { return { id: 'session-1', profileId: null, feedbackDate: '2026-07-22', content: '', status: 'active', lessonCount: 1, totalDurationMs: 2800, processingSegmentCount: 0, failedSegmentCount: 0, needsGeneration: false, lessons: [], createdAt: 0, updatedAt: 0, ...overrides } } test('failed segments take priority over processing and generation', () => { const value = session({ failedSegmentCount: 2, processingSegmentCount: 1, needsGeneration: true }) const action = getVoicePrimaryAction(value) assert.equal(action, 'retry') assert.equal(getVoicePrimaryActionText(action, value.failedSegmentCount), '重试转录(2)') }) test('voice primary action follows retry, processing, then generation states', () => { assert.equal(getVoicePrimaryAction(session({ failedSegmentCount: 1 })), 'retry') assert.equal(getVoicePrimaryAction(session({ processingSegmentCount: 1 })), 'processing') assert.equal(getVoicePrimaryAction(session({ needsGeneration: true })), 'generate') }) test('no clear speech failures provide an actionable explanation', () => { const value = session({ failedSegmentCount: 1, lessons: [ { segments: [ { id: 'segment-1', sequence: 1, durationMs: 2800, status: 'failed', errorMessage: 'FunASR HTTP 422: no clear speech was recognized' } ] } ] }) const hint = getVoiceFailureHint(value) assert.match(hint, /录音过短、静音或环境噪声/) assert.match(hint, /放弃该失败录音/) }) test('ready lessons explain whether their transcript can be added again', () => { const lesson = { status: 'ready', appliedDirectly: false, includedInGeneration: false } assert.equal(getVoiceLessonStatusText(lesson), '可加入输入框') assert.equal(getVoiceLessonStatusText({ ...lesson, appliedDirectly: true }), '可重新加入') assert.equal(getVoiceLessonStatusText({ ...lesson, appliedDirectly: true }, true), '已在输入框') assert.equal(getVoiceLessonStatusText({ ...lesson, includedInGeneration: true }), '已生成反馈') }) test('session refresh runs after a failed API action', async () => { const expected = new Error('generation rejected') let refreshCount = 0 await assert.rejects( runWithSessionRefresh( async () => { throw expected }, async () => { refreshCount += 1 } ), expected ) assert.equal(refreshCount, 1) }) test('refresh failure does not replace the original API error', async () => { const expected = new Error('generation rejected with request id') await assert.rejects( runWithSessionRefresh( async () => { throw expected }, async () => { throw new Error('refresh failed') } ), expected ) })