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
206
apps/mobile-rn/__tests__/knowledge-actions.local.integration.mjs
Normal file
206
apps/mobile-rn/__tests__/knowledge-actions.local.integration.mjs
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
import assert from 'node:assert/strict'
|
||||
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 options = {
|
||||
auth: { persistSession: false, autoRefreshToken: false, detectSessionInUrl: false },
|
||||
}
|
||||
const admin = createClient(supabaseUrl, serviceRoleKey, options)
|
||||
const userAClient = createClient(supabaseUrl, anonKey, options)
|
||||
const userBClient = createClient(supabaseUrl, anonKey, options)
|
||||
const suffix = `${Date.now()}-${Math.floor(Math.random() * 1_000_000)}`
|
||||
const password = `D3ro-${suffix}-Strong!`
|
||||
const createdUserIds = []
|
||||
let assertions = 0
|
||||
let embeddingMode = 'not-run'
|
||||
let searchMode = 'not-run'
|
||||
|
||||
function checked(condition, message) {
|
||||
assert.ok(condition, message)
|
||||
assertions += 1
|
||||
}
|
||||
|
||||
async function createDisposableUser(client, marker) {
|
||||
const response = await client.auth.signUp({
|
||||
email: `d3ro.mobile.${suffix}.${marker}@gmail.com`,
|
||||
password,
|
||||
})
|
||||
assert.equal(response.error, null, `Could not create local user ${marker}`)
|
||||
assert.ok(response.data.user && response.data.session, `Local user ${marker} has no session`)
|
||||
createdUserIds.push(response.data.user.id)
|
||||
return response.data
|
||||
}
|
||||
|
||||
async function invokeEdge(client, functionName, body) {
|
||||
const session = (await client.auth.getSession()).data.session
|
||||
assert.ok(session, `${functionName} requires a local authenticated session`)
|
||||
const response = await fetch(`${supabaseUrl}/functions/v1/${functionName}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.access_token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
return {
|
||||
status: response.status,
|
||||
body: await response.json().catch(() => null),
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const [userA, userB] = await Promise.all([
|
||||
createDisposableUser(userAClient, 'a'),
|
||||
createDisposableUser(userBClient, 'b'),
|
||||
])
|
||||
|
||||
const documentInsert = await userAClient
|
||||
.from('knowledge_documents')
|
||||
.insert({
|
||||
user_id: userA.user.id,
|
||||
title: 'Mobile RLS document',
|
||||
file_name: 'mobile-rls.md',
|
||||
file_type: 'md',
|
||||
chunk_count: 1,
|
||||
indexed: false,
|
||||
indexed_at: null,
|
||||
})
|
||||
.select('id,user_id,indexed')
|
||||
.single()
|
||||
assert.equal(documentInsert.error, null, 'Owner could not insert knowledge document')
|
||||
const documentId = documentInsert.data.id
|
||||
checked(documentInsert.data.user_id === userA.user.id, 'Knowledge owner was not preserved')
|
||||
|
||||
const chunkInsert = await userAClient
|
||||
.from('knowledge_chunks')
|
||||
.insert({ document_id: documentId, chunk_index: 0, content: 'Private mobile knowledge' })
|
||||
.select('id,document_id')
|
||||
.single()
|
||||
assert.equal(chunkInsert.error, null, 'Owner could not insert knowledge chunk')
|
||||
checked(chunkInsert.data.document_id === documentId, 'Knowledge chunk was linked to another document')
|
||||
|
||||
const ownerDocuments = await userAClient
|
||||
.from('knowledge_documents')
|
||||
.select('id')
|
||||
.eq('id', documentId)
|
||||
assert.equal(ownerDocuments.error, null)
|
||||
checked(ownerDocuments.data.length === 1, 'Owner cannot read the knowledge document')
|
||||
|
||||
const strangerDocuments = await userBClient
|
||||
.from('knowledge_documents')
|
||||
.select('id')
|
||||
.eq('id', documentId)
|
||||
assert.equal(strangerDocuments.error, null)
|
||||
checked(strangerDocuments.data.length === 0, 'Another account can read a private knowledge document')
|
||||
|
||||
const strangerChunks = await userBClient
|
||||
.from('knowledge_chunks')
|
||||
.select('id')
|
||||
.eq('document_id', documentId)
|
||||
assert.equal(strangerChunks.error, null)
|
||||
checked(strangerChunks.data.length === 0, 'Another account can read private knowledge chunks')
|
||||
|
||||
const strangerDelete = await userBClient
|
||||
.from('knowledge_documents')
|
||||
.delete()
|
||||
.eq('id', documentId)
|
||||
.select('id')
|
||||
assert.equal(strangerDelete.error, null)
|
||||
checked(strangerDelete.data.length === 0, 'Another account deleted a private knowledge document')
|
||||
|
||||
const documentAfterDeleteAttempt = await userAClient
|
||||
.from('knowledge_documents')
|
||||
.select('id')
|
||||
.eq('id', documentId)
|
||||
checked(documentAfterDeleteAttempt.data?.length === 1, 'Cross-account delete changed owner data')
|
||||
|
||||
const embedding = await invokeEdge(userAClient, 'embed-chunks', { document_id: documentId })
|
||||
checked(
|
||||
(embedding.status === 200 && embedding.body?.indexed === true)
|
||||
|| (embedding.status === 503 && embedding.body?.error === 'embedding_provider_unavailable'),
|
||||
'Embedding did not either complete or fail closed as unavailable',
|
||||
)
|
||||
embeddingMode = embedding.status === 200 ? 'indexed' : 'provider-unavailable'
|
||||
if (embedding.status === 503) {
|
||||
const pending = await userAClient
|
||||
.from('knowledge_documents')
|
||||
.select('indexed,indexed_at')
|
||||
.eq('id', documentId)
|
||||
.single()
|
||||
checked(pending.data?.indexed === false && pending.data?.indexed_at === null, 'Unavailable embeddings marked a document ready')
|
||||
}
|
||||
|
||||
const search = await invokeEdge(userAClient, 'search-knowledge', {
|
||||
query: 'Private mobile knowledge',
|
||||
count: 5,
|
||||
})
|
||||
checked(
|
||||
(search.status === 200 && Array.isArray(search.body?.results))
|
||||
|| (search.status === 503 && search.body?.error === 'embedding_provider_unavailable'),
|
||||
'Knowledge search did not either return verified results or fail closed as unavailable',
|
||||
)
|
||||
searchMode = search.status === 200 ? 'verified-results' : 'provider-unavailable'
|
||||
|
||||
const meetingInsert = await userAClient
|
||||
.from('meetings')
|
||||
.insert({ user_id: userA.user.id, title: 'Mobile action meeting', status: 'recording' })
|
||||
.select('id,user_id,title')
|
||||
.single()
|
||||
assert.equal(meetingInsert.error, null, 'Allowlisted create_meeting could not persist')
|
||||
const meetingId = meetingInsert.data.id
|
||||
checked(meetingInsert.data.user_id === userA.user.id, 'Meeting action changed ownership')
|
||||
|
||||
const strangerMeeting = await userBClient
|
||||
.from('meetings')
|
||||
.select('id')
|
||||
.eq('id', meetingId)
|
||||
assert.equal(strangerMeeting.error, null)
|
||||
checked(strangerMeeting.data.length === 0, 'Another account can read a private action-created meeting')
|
||||
|
||||
const memoInsert = await userAClient
|
||||
.from('meeting_memos')
|
||||
.insert({
|
||||
meeting_id: meetingId,
|
||||
user_id: userA.user.id,
|
||||
content: 'Confirmed mobile action memo',
|
||||
timestamp_ms: 0,
|
||||
})
|
||||
.select('id,user_id,meeting_id')
|
||||
.single()
|
||||
assert.equal(memoInsert.error, null, 'Allowlisted create_memo could not persist')
|
||||
checked(
|
||||
memoInsert.data.user_id === userA.user.id && memoInsert.data.meeting_id === meetingId,
|
||||
'Memo action changed ownership or meeting linkage',
|
||||
)
|
||||
|
||||
const strangerMemo = await userBClient
|
||||
.from('meeting_memos')
|
||||
.insert({
|
||||
meeting_id: meetingId,
|
||||
user_id: userB.user.id,
|
||||
content: 'Cross-account memo',
|
||||
timestamp_ms: 0,
|
||||
})
|
||||
.select('id')
|
||||
checked(strangerMemo.error !== null && strangerMemo.data === null, 'Another account inserted a memo into a private meeting')
|
||||
|
||||
const anonymousLlm = await fetch(`${supabaseUrl}/functions/v1/llm-proxy`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ messages: [{ role: 'user', content: 'parse this' }] }),
|
||||
})
|
||||
checked(anonymousLlm.status === 401, 'llm-proxy accepted an unauthenticated action parser request')
|
||||
|
||||
console.log(
|
||||
`knowledge/actions local integration passed: ${assertions} assertions; embedding=${embeddingMode}; search=${searchMode}`,
|
||||
)
|
||||
} finally {
|
||||
await Promise.allSettled(createdUserIds.map((userId) => admin.auth.admin.deleteUser(userId)))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue