d3ro-voice/server/supabase/functions/realtime-token/policy.test.ts

52 lines
2 KiB
TypeScript

import {
buildRealtimeSessionConfig,
DEFAULT_VOICE,
MAX_INSTRUCTIONS_LENGTH,
resolveRealtimeModel,
} from './policy.ts'
function assertEquals(actual: unknown, expected: unknown, message: string): void {
const a = JSON.stringify(actual)
const e = JSON.stringify(expected)
if (a !== e) throw new Error(`${message}: expected ${e}, got ${a}`)
}
Deno.test('resolveRealtimeModel applies tier defaults and allow-lists', () => {
assertEquals(resolveRealtimeModel('pro', undefined), { allowed: true, model: 'gpt-realtime-2.1-mini' }, 'pro default')
assertEquals(resolveRealtimeModel('pro_plus', undefined), { allowed: true, model: 'gpt-realtime-2.1' }, 'pro+ default')
assertEquals(
resolveRealtimeModel('team', 'gpt-realtime-2.1-mini'),
{ allowed: true, model: 'gpt-realtime-2.1-mini' },
'team explicit mini',
)
assertEquals(resolveRealtimeModel('free', undefined), {
allowed: false,
error: 'tier_not_allowed',
requested: null,
allowedModels: [],
}, 'free blocked')
assertEquals(resolveRealtimeModel('pro', 'gpt-realtime-2.1'), {
allowed: false,
error: 'model_not_allowed',
requested: 'gpt-realtime-2.1',
allowedModels: ['gpt-realtime-2.1-mini'],
}, 'pro cannot pick full model')
})
Deno.test('buildRealtimeSessionConfig clamps instructions and defaults voice', () => {
const long = 'x'.repeat(MAX_INSTRUCTIONS_LENGTH + 50)
const config = buildRealtimeSessionConfig({ instructions: long }, 'gpt-realtime-2.1-mini')
assertEquals(config.instructions?.length, MAX_INSTRUCTIONS_LENGTH, 'instructions clamped')
assertEquals(config.audio.output.voice, DEFAULT_VOICE, 'default voice')
assertEquals(
buildRealtimeSessionConfig({ voice: 'cedar', instructions: '' }, 'm'),
{ type: 'realtime', model: 'm', audio: { output: { voice: 'cedar' } } },
'empty instructions omitted',
)
assertEquals(
buildRealtimeSessionConfig({ voice: 42, instructions: 7 }, 'm'),
{ type: 'realtime', model: 'm', audio: { output: { voice: DEFAULT_VOICE } } },
'non-string fields ignored',
)
})