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 anonymousClient = createClient(supabaseUrl, anonKey, 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 function checked(condition, message) { assert.ok(condition, message) assertions += 1 } async function createDisposableUser(client, marker) { const response = await client.auth.signUp({ email: `d3ro.templates.${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 countRows(table, filters) { let query = admin.from(table).select('*', { count: 'exact', head: true }) for (const [column, value] of Object.entries(filters)) query = query.eq(column, value) const result = await query assert.equal(result.error, null, `Could not count ${table}`) return result.count ?? 0 } try { const [userA, userB] = await Promise.all([ createDisposableUser(userAClient, 'a'), createDisposableUser(userBClient, 'b'), ]) const userAId = userA.user.id const userBId = userB.user.id // Local GoTrue and PostgREST can straddle adjacent whole-second JWT clocks. // Let the freshly issued `iat` become current before the first REST RPC. await new Promise((resolve) => setTimeout(resolve, 1_100)) const anonymousGeneration = await fetch(`${supabaseUrl}/functions/v1/generate-meeting-document`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}), }) checked(anonymousGeneration.status === 401, 'Document generation accepted an anonymous request') const invalidGeneration = await fetch(`${supabaseUrl}/functions/v1/generate-meeting-document`, { method: 'POST', headers: { Authorization: `Bearer ${userA.session.access_token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ meetingId: crypto.randomUUID(), templateId: crypto.randomUUID(), idempotencyKey: crypto.randomUUID(), title: 'Invalid override', prompt: 'Client prompts must never override the server template', }), }) const invalidGenerationBody = await invalidGeneration.json().catch(() => null) checked( invalidGeneration.status === 400 && invalidGenerationBody?.error === 'invalid_request', 'Document generation accepted unsupported client-controlled prompt fields', ) const bootstrapRuns = await Promise.all( Array.from({ length: 8 }, () => userAClient.rpc('bootstrap_user_templates_v1')), ) for (const run of bootstrapRuns) assert.equal(run.error, null, 'Concurrent template bootstrap failed') const templatesA = await userAClient .from('user_templates') .select('*') .eq('user_id', userAId) .order('builtin_key') assert.equal(templatesA.error, null) checked(templatesA.data.length === 7, 'Concurrent bootstrap created duplicate builtins') checked( new Set(templatesA.data.map((row) => `${row.template_kind}:${row.builtin_key}`)).size === 7, 'Builtin kind/key identities are not unique', ) checked( templatesA.data.find((row) => row.builtin_key === 'builtin-email')?.output_format === 'To: {{recipient}}\nSubject: {{subject}}\n\n{{body}}', 'Desktop email builtin semantics changed', ) checked( templatesA.data.find((row) => row.builtin_key === 'builtin-minutes')?.system_prompt ?.includes('전사록에 없는 내용을 추가하지 마세요') === true, 'Desktop minutes builtin prompt semantics changed', ) checked( await countRows('user_template_selections', { user_id: userAId }) === 2, 'Concurrent bootstrap did not create exactly two default selections', ) const builtinEmail = templatesA.data.find((row) => row.builtin_key === 'builtin-email') assert.ok(builtinEmail) const builtinMutation = await userAClient.rpc('update_user_template_v1', { p_template_id: builtinEmail.id, p_expected_revision: builtinEmail.revision, p_name: 'Tampered', p_description: null, p_fields: builtinEmail.fields, p_output_format: builtinEmail.output_format, p_system_prompt: null, }) checked(builtinMutation.error?.code === '42501', 'Builtin template mutation was not rejected') const customCreate = await userAClient.rpc('create_user_template_v1', { p_template_kind: 'dictation', p_name: 'Concurrent custom', p_description: 'CAS verification', p_fields: [{ id: 'content', name: 'content', label: 'Content', promptText: 'Dictate content', required: true, maxDurationSec: 120, }], p_output_format: '{{content}}', p_system_prompt: null, }) assert.equal(customCreate.error, null, 'Could not create custom template') const custom = customCreate.data const strangerTemplateRead = await userBClient .from('user_templates') .select('id') .eq('id', custom.id) assert.equal(strangerTemplateRead.error, null) checked(strangerTemplateRead.data.length === 0, 'Another account can read a private template') const updateArgs = { p_template_id: custom.id, p_expected_revision: custom.revision, p_description: 'Concurrent update', p_fields: custom.fields, p_output_format: custom.output_format, p_system_prompt: null, } const concurrentUpdateStartedAt = Date.now() const concurrentUpdates = await Promise.all([ userAClient.rpc('update_user_template_v1', { ...updateArgs, p_name: 'Winner A' }), userAClient.rpc('update_user_template_v1', { ...updateArgs, p_name: 'Winner B' }), ]) const concurrentUpdateEvidence = concurrentUpdates.map((result) => ({ ok: result.error === null, code: result.error?.code ?? null, message: result.error?.message ?? null, })) checked( concurrentUpdates.filter((result) => result.error === null).length === 1 && concurrentUpdates.filter((result) => result.error?.code === 'PT409').length === 1, `Template revision CAS did not admit exactly one concurrent update: ${JSON.stringify(concurrentUpdateEvidence)}`, ) checked( Date.now() - concurrentUpdateStartedAt < 5_000, 'Template revision conflict was retried or delayed instead of returning immediately', ) const historyInsert = await userAClient .from('history') .insert({ user_id: userAId, title: 'Private launch memo', original_text: 'Ship only after rollback validation', duration: 1, mode: 'dictation', status: 'completed', }) .select('id') .single() assert.equal(historyInsert.error, null, 'Could not create memo history row') const historyId = historyInsert.data.id const concurrentTags = await Promise.all([ userAClient.rpc('mobile_add_memo_tag_v1', { p_history_id: historyId, p_tag: 'Launch Plan' }), userAClient.rpc('mobile_add_memo_tag_v1', { p_history_id: historyId, p_tag: 'launch plan' }), ]) for (const result of concurrentTags) assert.equal(result.error, null, 'Concurrent tag insert failed') checked( concurrentTags[0].data.id === concurrentTags[1].data.id, 'Case-insensitive concurrent tag insert was not idempotent', ) checked( await countRows('memo_tags', { history_id: historyId }) === 1, 'Case-insensitive duplicate memo tags were stored', ) const strangerTag = await userBClient.rpc('mobile_add_memo_tag_v1', { p_history_id: historyId, p_tag: 'stolen', }) checked(strangerTag.error?.code === 'P0002', 'Another account attached a tag to private history') const forgedDirectTag = await userBClient.from('memo_tags').insert({ user_id: userBId, history_id: historyId, tag: 'forged', normalized_tag: 'forged', }) checked(forgedDirectTag.error !== null, 'Authenticated clients retained direct memo-tag write access') const ownerMemoSearch = await userAClient.rpc('mobile_search_memos_v1', { p_query: 'launch', p_tag: 'LAUNCH PLAN', p_limit: 20, p_offset: 0, }) assert.equal(ownerMemoSearch.error, null) checked(ownerMemoSearch.data.length === 1, 'Owner tag/content search did not return the memo') const strangerMemoSearch = await userBClient.rpc('mobile_search_memos_v1', { p_query: 'launch', p_tag: null, p_limit: 20, p_offset: 0, }) assert.equal(strangerMemoSearch.error, null) checked(strangerMemoSearch.data.length === 0, 'Another account can search private memos') const caseRename = await userAClient.rpc('mobile_rename_memo_tag_v1', { p_old_tag: 'launch plan', p_new_tag: 'LAUNCH PLAN', }) assert.equal(caseRename.error, null) checked(caseRename.data === 1, 'Case-only rename did not update exactly one memo') checked( await countRows('memo_tags', { history_id: historyId }) === 1, 'Case-only rename removed or duplicated the tag', ) const bootstrapB = await userBClient.rpc('bootstrap_user_templates_v1') assert.equal(bootstrapB.error, null) const minutesA = templatesA.data.find((row) => row.builtin_key === 'builtin-minutes') const minutesB = bootstrapB.data.find((row) => row.builtin_key === 'builtin-minutes') assert.ok(minutesA && minutesB) const anonymousCreation = await anonymousClient.rpc('mobile_create_meeting_workspace_v2', { p_title: 'Anonymous attempt', p_attendees: [], p_language: 'ko', p_template_id: minutesA.id, p_idempotency_key: crypto.randomUUID(), }) checked(anonymousCreation.error?.code === '42501', 'Anonymous meeting creation was accepted') const creationKey = crypto.randomUUID() const creationArgs = { p_title: ' H-006 launch review ', p_attendees: ['Yun Chan', 'Product Team'], p_language: 'EN', p_template_id: minutesA.id, p_idempotency_key: creationKey, } const concurrentCreations = await Promise.all( Array.from({ length: 8 }, () => ( userAClient.rpc('mobile_create_meeting_workspace_v2', creationArgs) )), ) for (const result of concurrentCreations) { assert.equal(result.error, null, 'Concurrent meeting creation failed') } const createdMeeting = concurrentCreations[0].data checked( concurrentCreations.every((result) => result.data.id === createdMeeting.id) && await countRows('meetings', { user_id: userAId, creation_idempotency_key: creationKey, }) === 1, 'Concurrent meeting creation was not idempotent', ) checked( createdMeeting.user_id === userAId && createdMeeting.title === 'H-006 launch review' && createdMeeting.status === 'recording' && createdMeeting.language === 'en' && JSON.stringify(createdMeeting.attendees) === JSON.stringify(['Yun Chan', 'Product Team']) && createdMeeting.template_id === minutesA.id && createdMeeting.creation_idempotency_key === creationKey, 'Atomic meeting creation did not return the normalized server row', ) const forgedCreationMetadata = await userAClient .from('meetings') .insert({ user_id: userAId, title: 'Direct metadata bypass', status: 'recording', language: 'ja', attendees: ['Bypass'], template_id: minutesA.id, }) checked( forgedCreationMetadata.error?.code === '42501', 'Direct INSERT bypassed the atomic H-006 meeting creation RPC', ) const forgedIdentityUpdate = await userAClient .from('meetings') .update({ creation_idempotency_key: crypto.randomUUID() }) .eq('id', createdMeeting.id) checked( forgedIdentityUpdate.error?.code === '42501', 'Direct UPDATE mutated an atomic meeting creation identity', ) const preservedCreationIdentity = await userAClient .from('meetings') .select('creation_idempotency_key, creation_request_hash') .eq('id', createdMeeting.id) .single() assert.equal(preservedCreationIdentity.error, null) checked( preservedCreationIdentity.data.creation_idempotency_key === creationKey && /^[0-9a-f]{64}$/.test(preservedCreationIdentity.data.creation_request_hash), 'Rejected identity mutation changed the committed creation metadata', ) const strangerMeetingRead = await userBClient .from('meetings') .select('id') .eq('id', createdMeeting.id) assert.equal(strangerMeetingRead.error, null) checked(strangerMeetingRead.data.length === 0, 'Another account can read a private created meeting') const creationConflictStartedAt = Date.now() const creationConflict = await userAClient.rpc('mobile_create_meeting_workspace_v2', { ...creationArgs, p_title: 'Different request with the same key', }) checked( creationConflict.error?.code === 'PT409' && Date.now() - creationConflictStartedAt < 5_000, `Meeting creation conflict did not return immediate HTTP 409 semantics: ${JSON.stringify(creationConflict.error)}`, ) const unchangedCreation = await userAClient .from('meetings') .select('title, attendees, language, template_id') .eq('id', createdMeeting.id) .single() assert.equal(unchangedCreation.error, null) checked( unchangedCreation.data.title === 'H-006 launch review' && unchangedCreation.data.language === 'en' && unchangedCreation.data.template_id === minutesA.id, 'Idempotency conflict mutated the already committed meeting', ) const foreignTemplateCreationKey = crypto.randomUUID() const foreignTemplateCreation = await userBClient.rpc('mobile_create_meeting_workspace_v2', { p_title: 'Foreign template attempt', p_attendees: [], p_language: 'ko', p_template_id: minutesA.id, p_idempotency_key: foreignTemplateCreationKey, }) checked( foreignTemplateCreation.error?.code === 'P0002' && await countRows('meetings', { user_id: userBId, creation_idempotency_key: foreignTemplateCreationKey, }) === 0, 'Another account created a meeting with a private template or left a partial row', ) const invalidAttendeesCreationKey = crypto.randomUUID() const invalidAttendeesCreation = await userAClient.rpc('mobile_create_meeting_workspace_v2', { p_title: 'Invalid attendees rollback', p_attendees: ['Yun Chan', 'yun chan'], p_language: 'ko', p_template_id: minutesA.id, p_idempotency_key: invalidAttendeesCreationKey, }) checked( invalidAttendeesCreation.error?.code === '22023' && await countRows('meetings', { user_id: userAId, creation_idempotency_key: invalidAttendeesCreationKey, }) === 0, 'Invalid meeting creation left a partial row instead of rolling back', ) const nullTitleCreationKey = crypto.randomUUID() const nullTitleCreation = await userAClient.rpc('mobile_create_meeting_workspace_v2', { p_title: null, p_attendees: [], p_language: 'ko', p_template_id: minutesA.id, p_idempotency_key: nullTitleCreationKey, }) checked( nullTitleCreation.error?.code === '22023' && await countRows('meetings', { user_id: userAId, creation_idempotency_key: nullTitleCreationKey, }) === 0, 'NULL meeting title bypassed validation or left a partial row', ) const competingCreationKey = crypto.randomUUID() const competingCreations = await Promise.all([ userAClient.rpc('mobile_create_meeting_workspace_v2', { ...creationArgs, p_title: 'Competing payload A', p_idempotency_key: competingCreationKey, }), userAClient.rpc('mobile_create_meeting_workspace_v2', { ...creationArgs, p_title: 'Competing payload B', p_idempotency_key: competingCreationKey, }), ]) checked( competingCreations.filter((result) => result.error === null).length === 1 && competingCreations.filter((result) => result.error?.code === 'PT409').length === 1 && await countRows('meetings', { user_id: userAId, creation_idempotency_key: competingCreationKey, }) === 1, `Different concurrent meeting payloads did not admit exactly one winner: ${JSON.stringify(competingCreations.map((result) => ({ error: result.error?.code ?? null, id: result.data?.id ?? null })))}`, ) const referencedTemplateCreate = await userAClient.rpc('create_user_template_v1', { p_template_kind: 'meeting_document', p_name: 'Referenced document template', p_description: 'Deletion must fail while a meeting references this revision', p_fields: [], p_output_format: null, p_system_prompt: 'Use only the supplied transcript.', }) assert.equal(referencedTemplateCreate.error, null, 'Could not create referenced document template') const referencedTemplate = referencedTemplateCreate.data const referencedMeeting = await userAClient.rpc('mobile_create_meeting_workspace_v2', { p_title: 'Template retention proof', p_attendees: [], p_language: 'zh-cn', p_template_id: referencedTemplate.id, p_idempotency_key: crypto.randomUUID(), }) assert.equal(referencedMeeting.error, null, 'Could not create custom-template meeting') const referencedTemplateDelete = await userAClient.rpc('delete_user_template_v1', { p_template_id: referencedTemplate.id, p_expected_revision: referencedTemplate.revision, }) checked( referencedTemplateDelete.error?.code === '23503' && await countRows('user_templates', { id: referencedTemplate.id }) === 1 && await countRows('meetings', { id: referencedMeeting.data.id, template_id: referencedTemplate.id, }) === 1, 'Referenced meeting template was deleted or its selection link was lost', ) const userBCreationKey = crypto.randomUUID() const userBCreation = await userBClient.rpc('mobile_create_meeting_workspace_v2', { p_title: 'Owner template trigger proof', p_attendees: [], p_language: 'ko', p_template_id: minutesB.id, p_idempotency_key: userBCreationKey, }) assert.equal(userBCreation.error, null, 'Second account could not create its own meeting') const forgedTemplateUpdate = await userBClient .from('meetings') .update({ template_id: minutesA.id }) .eq('id', userBCreation.data.id) checked(forgedTemplateUpdate.error?.code === '42501', 'Direct update bypassed template ownership') const preservedUserBMeeting = await userBClient .from('meetings') .select('template_id') .eq('id', userBCreation.data.id) .single() assert.equal(preservedUserBMeeting.error, null) checked( preservedUserBMeeting.data.template_id === minutesB.id, 'Rejected cross-owner template update changed the meeting row', ) const meetingInsert = await userAClient .from('meetings') .insert({ user_id: userAId, title: 'Atomic generation meeting', status: 'completed', raw_transcript: 'Yun: Ship after every rollback check passes.', }) .select('id') .single() assert.equal(meetingInsert.error, null, 'Could not create generation meeting') const meetingId = meetingInsert.data.id const strangerClaim = await admin.rpc('claim_meeting_document_generation_v1', { p_actor_id: userBId, p_idempotency_key: crypto.randomUUID(), p_meeting_id: meetingId, p_template_id: minutesB.id, p_title: 'Forbidden document', p_model: 'claude-haiku-4-5-20251001', }) checked(strangerClaim.error?.code === '42501', 'Another account claimed a private meeting generation') const successKey = crypto.randomUUID() const successClaim = await admin.rpc('claim_meeting_document_generation_v1', { p_actor_id: userAId, p_idempotency_key: successKey, p_meeting_id: meetingId, p_template_id: minutesA.id, p_title: 'Atomic minutes', p_model: 'claude-haiku-4-5-20251001', }) assert.equal(successClaim.error, null, 'Owner could not claim generation') checked(successClaim.data.claimed === true && successClaim.data.transcript.includes('rollback'), 'Claim did not bind the authenticated transcript') const successCommit = await admin.rpc('commit_meeting_document_generation_v1', { p_actor_id: userAId, p_idempotency_key: successKey, p_content: '## 요약\n모든 롤백 검증 후 배포한다.', p_latency_ms: 42, p_input_tokens: 10, p_output_tokens: 8, }) assert.equal(successCommit.error, null, 'Atomic generation commit failed') const generatedDocumentId = successCommit.data.document.id checked( await countRows('meeting_documents', { id: generatedDocumentId }) === 1 && await countRows('meeting_document_generation_audit', { idempotency_key: successKey }) === 1 && await countRows('daily_usage', { user_id: userAId, feature: 'llm_haiku' }) === 1, 'Document, usage, and audit were not committed together', ) const idempotentCommit = await admin.rpc('commit_meeting_document_generation_v1', { p_actor_id: userAId, p_idempotency_key: successKey, p_content: 'This different content must never replace the committed document.', p_latency_ms: 99, p_input_tokens: 99, p_output_tokens: 99, }) assert.equal(idempotentCommit.error, null) checked( idempotentCommit.data.idempotent === true && idempotentCommit.data.document.id === generatedDocumentId && await countRows('meeting_document_generation_audit', { idempotency_key: successKey }) === 1, 'Idempotent retry duplicated or replaced the committed document', ) const rollbackKey = crypto.randomUUID() const rollbackClaim = await admin.rpc('claim_meeting_document_generation_v1', { p_actor_id: userAId, p_idempotency_key: rollbackKey, p_meeting_id: meetingId, p_template_id: minutesA.id, p_title: 'Rollback proof', p_model: 'claude-haiku-4-5-20251001', }) assert.equal(rollbackClaim.error, null) const rollbackCommit = await admin.rpc('commit_meeting_document_generation_v1', { p_actor_id: userAId, p_idempotency_key: rollbackKey, p_content: '', p_latency_ms: 1, p_input_tokens: 1, p_output_tokens: 1, }) checked(rollbackCommit.error?.code === '22023', 'Invalid provider output was accepted') checked( await countRows('meeting_documents', { generation_idempotency_key: rollbackKey }) === 0 && await countRows('meeting_document_generation_audit', { idempotency_key: rollbackKey }) === 0 && (await admin.from('daily_usage').select('count').eq('user_id', userAId).eq('feature', 'llm_haiku').single()).data.count === 1, 'Failed atomic commit left document, audit, or usage side effects', ) const failureMark = await admin.rpc('fail_meeting_document_generation_v1', { p_actor_id: userAId, p_idempotency_key: rollbackKey, p_error_code: 'provider_invalid_response', }) assert.equal(failureMark.error, null) checked(failureMark.data === true, 'Failed request was not terminally marked') const providerUnavailableResponse = await fetch(`${supabaseUrl}/functions/v1/generate-meeting-document`, { method: 'POST', headers: { Authorization: `Bearer ${userA.session.access_token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ meetingId, templateId: minutesA.id, idempotencyKey: crypto.randomUUID(), title: 'Provider fail-closed proof', }), }) const providerUnavailableBody = await providerUnavailableResponse.json().catch(() => null) checked( providerUnavailableResponse.status === 503 && providerUnavailableBody?.error === 'provider_unavailable', `Local provider-unavailable path did not fail closed with a stable error: ${providerUnavailableResponse.status} ${JSON.stringify(providerUnavailableBody)}`, ) console.log(`templates/memos local integration passed: ${assertions} assertions`) } finally { await Promise.allSettled(createdUserIds.map((userId) => admin.auth.admin.deleteUser(userId))) }