fix(stt-proxy): stop paid fan-out on empty transcripts and redact provider errors
This commit is contained in:
parent
1a4c39cb94
commit
1eb22af1f3
9 changed files with 1107 additions and 380 deletions
|
|
@ -0,0 +1,44 @@
|
|||
// Source-level guards for the split stt-proxy (index.ts = composition root,
|
||||
// handler.ts = use case, providers.ts = adapters). Behavior is covered by
|
||||
// handler.test.ts and providers.test.ts; these checks keep the wiring honest.
|
||||
const read = (file: string) => Deno.readTextFile(new URL(file, import.meta.url))
|
||||
const [index, handler, providers] = await Promise.all([
|
||||
read('./index.ts'),
|
||||
read('./handler.ts'),
|
||||
read('./providers.ts'),
|
||||
])
|
||||
const all = [index, handler, providers].join('\n')
|
||||
|
||||
function assert(condition: boolean, message: string): asserts condition {
|
||||
if (!condition) throw new Error(message)
|
||||
}
|
||||
|
||||
Deno.test('STT proxy has no synthetic success fallback', () => {
|
||||
for (const forbidden of ['d3ro-cloud-mock', '음성 전사 완료', 'D3RO Cloud STT']) {
|
||||
assert(!all.includes(forbidden), `forbidden synthetic fallback remains: ${forbidden}`)
|
||||
}
|
||||
assert(handler.includes("const error = status === 503 ? 'stt_provider_unavailable' : 'stt_upstream_failed'"),
|
||||
'provider failures must produce an explicit 502/503 error')
|
||||
})
|
||||
|
||||
Deno.test('gateway uses a dedicated D3RO API token', () => {
|
||||
assert(index.includes("gatewayToken: Deno.env.get('D3RO_API_TOKEN')"), 'dedicated backend token is required')
|
||||
assert(providers.includes('if (config.gatewayUrl && config.gatewayToken)'), 'gateway must be skipped without its own token')
|
||||
assert(!all.includes("req.headers.get('Authorization')"), 'Supabase user JWT must not be forwarded to D3RO API')
|
||||
assert(providers.includes('createInternalSttGatewayUrl(config.url)'),
|
||||
'quota-owning internal endpoint must use the canonical URL guard')
|
||||
assert(providers.includes("'X-D3RO-STT-Gateway-Token': config.token"),
|
||||
'dedicated token must use the internal gateway header')
|
||||
})
|
||||
|
||||
Deno.test('index.ts is only the composition root', () => {
|
||||
assert(index.includes('Deno.serve(createSttProxyHandler('), 'index must serve the injected handler')
|
||||
assert(!index.includes('await fetch('), 'provider IO belongs in providers.ts')
|
||||
assert(!index.includes('runSttChain'), 'fallback policy belongs in handler.ts')
|
||||
})
|
||||
|
||||
Deno.test('attempts never carry raw exception messages', () => {
|
||||
assert(!all.includes('err.message}`'), 'raw exception text must not be formatted into a response')
|
||||
assert(!all.includes('JSON.stringify({ error: message })'), 'raw exception messages must not be returned')
|
||||
assert(handler.includes("json(500, { error: 'internal_error' })"), 'generic internal error response is missing')
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue