Add characterization tests first, then split the hot spots into small helpers: - STT/TTS settings updates move to extensions/piagentui-speech-settings.ts (rule tables) - defaultExtractData is split into diff/diagnostic/output extractors - getToolSummaryCategory becomes an ordered rule table (toolSummary.ts) - sanitizePersistedTerminalLayoutMap is split per level - duplicated diff stats helpers are merged into utils/diffStats.ts
154 lines
5.1 KiB
TypeScript
154 lines
5.1 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { applySttSettingsUpdate, applyTtsSettingsUpdate, isTtsResponseFormat } from './piagentui-speech-settings.js'
|
|
|
|
function stt(update: Record<string, unknown>, existing = {}) {
|
|
return applySttSettingsUpdate(existing, update)
|
|
}
|
|
|
|
function tts(update: Record<string, unknown>, existing = {}) {
|
|
return applyTtsSettingsUpdate(existing, update)
|
|
}
|
|
|
|
describe('applySttSettingsUpdate', () => {
|
|
it('returns the existing settings untouched for an empty update', () => {
|
|
const existing = { enabled: true, language: 'pt' }
|
|
expect(stt({}, existing)).toEqual({ ok: true, next: existing })
|
|
})
|
|
|
|
it('applies a valid update of every field', () => {
|
|
const result = stt({
|
|
enabled: true,
|
|
providerKind: 'openai',
|
|
mode: 'realtime',
|
|
baseUrl: 'https://api.example.com/v1///',
|
|
apiKeyRequired: true,
|
|
transcriptionEndpoint: '/audio/transcriptions',
|
|
transcriptionModel: ' whisper-1 ',
|
|
language: ' en ',
|
|
insertMode: 'replace',
|
|
apiKey: ' secret ',
|
|
})
|
|
expect(result).toEqual({
|
|
ok: true,
|
|
next: {
|
|
enabled: true,
|
|
providerKind: 'openai',
|
|
mode: 'realtime',
|
|
baseUrl: 'https://api.example.com/v1',
|
|
apiKeyRequired: true,
|
|
transcriptionEndpoint: '/audio/transcriptions',
|
|
transcriptionModel: 'whisper-1',
|
|
language: 'en',
|
|
insertMode: 'replace',
|
|
apiKey: 'secret',
|
|
},
|
|
})
|
|
})
|
|
|
|
it('does not mutate the existing settings', () => {
|
|
const existing = { enabled: false }
|
|
stt({ enabled: true }, existing)
|
|
expect(existing).toEqual({ enabled: false })
|
|
})
|
|
|
|
it.each([
|
|
['providerKind', { providerKind: 'other' }],
|
|
['mode', { mode: 'stream' }],
|
|
['baseUrl', { baseUrl: 'ftp://host' }],
|
|
['baseUrl', { baseUrl: 42 }],
|
|
['transcriptionEndpoint', { transcriptionEndpoint: 'audio' }],
|
|
['transcriptionModel', { transcriptionModel: ' ' }],
|
|
['language', { language: 5 }],
|
|
['insertMode', { insertMode: 'prepend' }],
|
|
])('rejects an invalid %s', (field, update) => {
|
|
expect(stt(update)).toEqual({ ok: false, error: `Invalid ${field}` })
|
|
})
|
|
|
|
it('coerces enabled/apiKeyRequired to booleans', () => {
|
|
expect(stt({ enabled: 'yes', apiKeyRequired: 1 })).toEqual({
|
|
ok: true,
|
|
next: { enabled: false, apiKeyRequired: false },
|
|
})
|
|
})
|
|
|
|
it('clears language with null or empty string', () => {
|
|
expect(stt({ language: null }, { language: 'pt' })).toEqual({ ok: true, next: {} })
|
|
expect(stt({ language: '' }, { language: 'pt' })).toEqual({ ok: true, next: {} })
|
|
})
|
|
|
|
it('handles apiKey: null clears, blank string keeps, non-string keeps', () => {
|
|
expect(stt({ apiKey: null }, { apiKey: 'old' })).toEqual({ ok: true, next: {} })
|
|
expect(stt({ apiKey: ' ' }, { apiKey: 'old' })).toEqual({ ok: true, next: { apiKey: 'old' } })
|
|
expect(stt({ apiKey: 12 }, { apiKey: 'old' })).toEqual({ ok: true, next: { apiKey: 'old' } })
|
|
})
|
|
})
|
|
|
|
describe('applyTtsSettingsUpdate', () => {
|
|
it('returns the existing settings untouched for an empty update', () => {
|
|
const existing = { voice: 'alloy' }
|
|
expect(tts({}, existing)).toEqual({ ok: true, next: existing })
|
|
})
|
|
|
|
it('applies a valid update of every field', () => {
|
|
const result = tts({
|
|
enabled: true,
|
|
providerKind: 'openai-compatible',
|
|
baseUrl: 'http://localhost:8880/v1/',
|
|
apiKeyRequired: true,
|
|
speechEndpoint: '/audio/speech',
|
|
model: ' tts-1-hd ',
|
|
voice: ' nova ',
|
|
responseFormat: 'wav',
|
|
speed: 1.5,
|
|
apiKey: ' k ',
|
|
})
|
|
expect(result).toEqual({
|
|
ok: true,
|
|
next: {
|
|
enabled: true,
|
|
providerKind: 'openai-compatible',
|
|
baseUrl: 'http://localhost:8880/v1',
|
|
apiKeyRequired: true,
|
|
speechEndpoint: '/audio/speech',
|
|
model: 'tts-1-hd',
|
|
voice: 'nova',
|
|
responseFormat: 'wav',
|
|
speed: 1.5,
|
|
apiKey: 'k',
|
|
},
|
|
})
|
|
})
|
|
|
|
it.each([
|
|
['providerKind', { providerKind: 'x' }],
|
|
['baseUrl', { baseUrl: 'not a url' }],
|
|
['speechEndpoint', { speechEndpoint: 'speech' }],
|
|
['model', { model: '' }],
|
|
['voice', { voice: 3 }],
|
|
['responseFormat', { responseFormat: 'ogg' }],
|
|
['speed', { speed: '1' }],
|
|
['speed', { speed: 0.1 }],
|
|
['speed', { speed: 4.5 }],
|
|
])('rejects an invalid %s', (field, update) => {
|
|
expect(tts(update)).toEqual({ ok: false, error: `Invalid ${field}` })
|
|
})
|
|
|
|
it('accepts speed at both bounds', () => {
|
|
expect(tts({ speed: 0.25 })).toEqual({ ok: true, next: { speed: 0.25 } })
|
|
expect(tts({ speed: 4 })).toEqual({ ok: true, next: { speed: 4 } })
|
|
})
|
|
|
|
it('handles apiKey: null clears, blank string keeps', () => {
|
|
expect(tts({ apiKey: null }, { apiKey: 'old' })).toEqual({ ok: true, next: {} })
|
|
expect(tts({ apiKey: ' ' }, { apiKey: 'old' })).toEqual({ ok: true, next: { apiKey: 'old' } })
|
|
})
|
|
})
|
|
|
|
describe('isTtsResponseFormat', () => {
|
|
it('accepts supported formats only', () => {
|
|
for (const f of ['mp3', 'opus', 'aac', 'flac', 'wav', 'pcm']) expect(isTtsResponseFormat(f)).toBe(true)
|
|
expect(isTtsResponseFormat('ogg')).toBe(false)
|
|
expect(isTtsResponseFormat(undefined)).toBe(false)
|
|
})
|
|
})
|