d3ro-voice/apps/desktop/tests/unit/renderer-models-redteam-r2-1.test.ts

64 lines
3.1 KiB
TypeScript

// tests/unit/renderer-models-redteam-r2-1.test.ts
// 렌더러 순수 모델:
// - 녹음 화면 복원: main 사본과 먼저 도착한 이벤트를 id 로 합친다
// - 회의 채팅 패널: main 이 기억하는 대화를 표시 메시지로 바꾼다(키 충돌 없음)
// - 명령어 폼: 저장 거부 사유를 검증 메시지로 바꾼다(빈 프롬프트·길이 초과)
import { describe, expect, it } from 'vitest'
import { ErrorCode } from '@d3ro/core/errors'
import { mergeRestoredItems } from '../../src/renderer/pages/meeting/live-restore'
import { toPanelMessages, withRestoredHistory } from '../../src/renderer/components/meeting/meeting-chat-history'
import { commandSaveError } from '../../src/renderer/pages/commands/command-form'
describe('mergeRestoredItems', () => {
it('main 사본을 기준으로, 조회 중 먼저 온 항목은 중복 없이 합치고 시간 순으로 둔다', () => {
const restored = [
{ id: 'a', t: 1 },
{ id: 'b', t: 2 },
]
const arrived = [
{ id: 'b', t: 2 },
{ id: 'c', t: 3 },
]
expect(mergeRestoredItems(restored, arrived, (x) => x.t).map((x) => x.id)).toEqual(['a', 'b', 'c'])
})
})
describe('meeting chat history', () => {
it('보관된 턴을 순서 번호 timestamp 로 바꿔 목록 키가 겹치지 않는다', () => {
const messages = toPanelMessages([
{ role: 'user', content: 'q1' },
{ role: 'assistant', content: 'a1' },
{ role: 'user', content: 'q2' },
])
const keys = messages.map((m) => `${m.role}-${m.timestamp}`)
expect(new Set(keys).size).toBe(3)
expect(messages.map((m) => m.content)).toEqual(['q1', 'a1', 'q2'])
})
it('조회가 끝나기 전에 보낸 새 메시지는 지우지 않고 뒤에 둔다', () => {
const merged = withRestoredHistory(
[{ role: 'user', content: '새 질문', timestamp: 1_700_000_000_000 }],
[{ role: 'user', content: '이전 질문' }, { role: 'assistant', content: '이전 답' }],
)
expect(merged.map((m) => m.content)).toEqual(['이전 질문', '이전 답', '새 질문'])
})
})
describe('commandSaveError', () => {
it('빈 프롬프트·길이 초과는 저장 전에 검증 메시지다', () => {
expect(commandSaveError({ kind: 'draft', draft: { name: 'n', description: '', prompt: ' ' } })).toEqual({
key: 'mobile.commands.syncError.validation',
})
expect(commandSaveError({ kind: 'draft', draft: { name: 'n'.repeat(81), description: '', prompt: 'p' } })).not.toBeNull()
expect(commandSaveError({ kind: 'draft', draft: { name: 'n', description: '', prompt: 'p' } })).toBeNull()
})
it('main 의 검증 거부는 검증 메시지, 그 밖의 실패는 호출자가 원문을 보여 준다(null)', () => {
expect(
commandSaveError({ kind: 'ipc', error: { code: ErrorCode.ConfigWriteFailed, message: 'Failed: Instruction prompt is too long (max 4000 characters)' } }),
).not.toBeNull()
expect(commandSaveError({ kind: 'ipc', error: { code: ErrorCode.ConfigInvalidValue, message: 'x' } })).not.toBeNull()
expect(commandSaveError({ kind: 'ipc', error: { code: ErrorCode.ConfigWriteFailed, message: 'disk full' } })).toBeNull()
})
})