feat(release): prepare 1.1.0 candidate
This commit is contained in:
parent
5a34f66981
commit
5205dcdfa9
736 changed files with 115667 additions and 12203 deletions
135
apps/mobile-rn/__tests__/talk.local.integration.mjs
Normal file
135
apps/mobile-rn/__tests__/talk.local.integration.mjs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
import assert from 'node:assert/strict'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
const supabaseUrl = process.env.D3RO_LOCAL_SUPABASE_URL?.trim() ?? ''
|
||||
const anonKey = process.env.D3RO_LOCAL_SUPABASE_ANON_KEY?.trim() ?? ''
|
||||
const serviceRoleKey = process.env.D3RO_LOCAL_SUPABASE_SERVICE_ROLE_KEY?.trim() ?? ''
|
||||
|
||||
if (!supabaseUrl.startsWith('http://127.0.0.1:55321') || anonKey.length < 20 || serviceRoleKey.length < 20) {
|
||||
throw new Error('D3RO local Supabase credentials for port 55321 are required')
|
||||
}
|
||||
|
||||
const clientOptions = {
|
||||
auth: { persistSession: false, autoRefreshToken: false, detectSessionInUrl: false },
|
||||
}
|
||||
const admin = createClient(supabaseUrl, serviceRoleKey, clientOptions)
|
||||
const userClient = createClient(supabaseUrl, anonKey, clientOptions)
|
||||
const suffix = `${Date.now()}-${Math.floor(Math.random() * 1_000_000)}`
|
||||
const password = `D3ro-${suffix}-Strong!`
|
||||
let createdUserId = null
|
||||
let assertions = 0
|
||||
|
||||
function checked(condition, message) {
|
||||
assert.ok(condition, message)
|
||||
assertions += 1
|
||||
}
|
||||
|
||||
async function invokeAnonymous(name, body, contentType = 'application/json') {
|
||||
return fetch(`${supabaseUrl}/functions/v1/${name}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': contentType },
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
function parseAnthropicSse(raw) {
|
||||
let text = ''
|
||||
let stopped = false
|
||||
for (const block of raw.replace(/\r\n/g, '\n').split('\n\n')) {
|
||||
const data = block
|
||||
.split('\n')
|
||||
.filter((line) => line.startsWith('data:'))
|
||||
.map((line) => line.slice(5).trimStart())
|
||||
.join('\n')
|
||||
if (!data || data === '[DONE]') continue
|
||||
const event = JSON.parse(data)
|
||||
if (event.type === 'content_block_delta' && event.delta?.type === 'text_delta') {
|
||||
text += typeof event.delta.text === 'string' ? event.delta.text : ''
|
||||
}
|
||||
if (event.type === 'message_stop') stopped = true
|
||||
}
|
||||
return { text: text.trim(), stopped }
|
||||
}
|
||||
|
||||
try {
|
||||
const signUp = await userClient.auth.signUp({
|
||||
email: `d3ro.talk.${suffix}@gmail.com`,
|
||||
password,
|
||||
})
|
||||
assert.equal(signUp.error, null, 'Could not create the disposable Talk user')
|
||||
assert.ok(signUp.data.user && signUp.data.session, 'Disposable Talk user has no local session')
|
||||
createdUserId = signUp.data.user.id
|
||||
const accessToken = signUp.data.session.access_token
|
||||
|
||||
const anonymousLlm = await invokeAnonymous(
|
||||
'llm-proxy',
|
||||
JSON.stringify({ messages: [{ role: 'user', content: 'hello' }], stream: true }),
|
||||
)
|
||||
checked(anonymousLlm.status === 401, 'llm-proxy accepted an unauthenticated Talk request')
|
||||
|
||||
const anonymousStt = await invokeAnonymous(
|
||||
'stt-proxy',
|
||||
JSON.stringify({ audio: 'not-authorized' }),
|
||||
)
|
||||
checked(anonymousStt.status === 401, 'stt-proxy accepted an unauthenticated Talk request')
|
||||
|
||||
const llmResponse = await fetch(`${supabaseUrl}/functions/v1/llm-proxy`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
apikey: anonKey,
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: 'text/event-stream',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: [{ role: 'user', content: 'Reply with the single word ready.' }],
|
||||
max_tokens: 32,
|
||||
stream: true,
|
||||
}),
|
||||
})
|
||||
const llmBody = await llmResponse.text()
|
||||
if (llmResponse.status === 503) {
|
||||
checked(JSON.parse(llmBody).error === 'provider_unavailable', 'Missing LLM provider did not fail closed')
|
||||
} else {
|
||||
checked(llmResponse.status === 200, `Authenticated llm-proxy returned ${llmResponse.status}`)
|
||||
checked(
|
||||
llmResponse.headers.get('content-type')?.includes('text/event-stream') === true,
|
||||
'Authenticated streaming Talk response was not SSE',
|
||||
)
|
||||
const parsed = parseAnthropicSse(llmBody)
|
||||
checked(parsed.text.length > 0 && parsed.stopped, 'Authenticated SSE did not contain a complete real response')
|
||||
}
|
||||
|
||||
const wavBytes = await readFile(new URL('../../desktop/resources/sounds/recording-start.wav', import.meta.url))
|
||||
const form = new FormData()
|
||||
form.append('audio', new Blob([wavBytes], { type: 'audio/wav' }), 'recording-start.wav')
|
||||
form.append('language_code', 'ko')
|
||||
const sttResponse = await fetch(`${supabaseUrl}/functions/v1/stt-proxy`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
apikey: anonKey,
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
body: form,
|
||||
})
|
||||
const sttBody = await sttResponse.json().catch(() => null)
|
||||
if (sttResponse.status === 503) {
|
||||
checked(sttBody?.error === 'stt_provider_unavailable', 'Missing STT provider did not fail closed')
|
||||
} else if (sttResponse.status === 502) {
|
||||
checked(sttBody?.error === 'stt_upstream_failed', 'STT upstream failure returned an unsafe contract')
|
||||
} else {
|
||||
checked(sttResponse.status === 200, `Authenticated stt-proxy returned ${sttResponse.status}`)
|
||||
checked(
|
||||
typeof sttBody?.transcript === 'string'
|
||||
&& typeof sttBody?.provider === 'string'
|
||||
&& typeof sttBody?.language_code === 'string'
|
||||
&& typeof sttBody?.duration_seconds === 'number',
|
||||
'Authenticated stt-proxy returned an invalid real-provider result',
|
||||
)
|
||||
}
|
||||
|
||||
console.log(`Talk local auth integration passed: ${assertions} assertions`)
|
||||
} finally {
|
||||
if (createdUserId !== null) await admin.auth.admin.deleteUser(createdUserId)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue