feat(release): prepare 1.1.0 candidate

This commit is contained in:
Yun Chan 2026-08-29 18:33:45 +09:00
parent 5a34f66981
commit 5205dcdfa9
736 changed files with 115667 additions and 12203 deletions

View file

@ -0,0 +1,320 @@
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 clientOptions = {
auth: { persistSession: false, autoRefreshToken: false, detectSessionInUrl: false },
}
const admin = createClient(supabaseUrl, serviceRoleKey, clientOptions)
const ownerClient = createClient(supabaseUrl, anonKey, clientOptions)
const adminClient = createClient(supabaseUrl, anonKey, clientOptions)
const memberClient = createClient(supabaseUrl, anonKey, clientOptions)
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, name) {
const response = await client.auth.signUp({
email: `d3ro.team.${suffix}.${marker}@gmail.com`,
password,
options: { data: { name } },
})
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.user
}
function requireRpcSuccess(result, label) {
assert.equal(result.error, null, `${label}: ${result.error?.message ?? 'unknown error'}`)
assert.ok(result.data, `${label}: response data missing`)
return result.data
}
function requireRpcFailure(result, expectedPattern, label) {
assert.ok(result.error, `${label}: request unexpectedly succeeded`)
const details = `${result.error.code ?? ''} ${result.error.message ?? ''} ${result.error.details ?? ''}`
assert.match(details, expectedPattern, `${label}: unexpected error ${details}`)
assertions += 1
}
try {
const [owner, teamAdmin, member] = await Promise.all([
createDisposableUser(ownerClient, 'owner', 'Owner'),
createDisposableUser(adminClient, 'admin', 'Admin'),
createDisposableUser(memberClient, 'member', 'Member'),
])
const directTeamInsert = await ownerClient
.from('teams')
.insert({ name: 'Direct bypass', owner_id: owner.id })
.select('id')
checked(directTeamInsert.error !== null, 'Direct team creation bypassed the atomic RPC')
const createdTeam = requireRpcSuccess(
await ownerClient.rpc('create_team', { team_name: 'Mobile Secure Team' }),
'create_team',
)
const teamId = createdTeam.id
checked(createdTeam.owner_id === owner.id && createdTeam.role === 'owner', 'Atomic team creation lost owner invariants')
const ownerMembership = await ownerClient
.from('team_members')
.select('role')
.eq('team_id', teamId)
.eq('user_id', owner.id)
.single()
checked(ownerMembership.data?.role === 'owner', 'Atomic team creation did not insert owner membership')
const outsiderTeamRead = await memberClient.from('teams').select('id').eq('id', teamId)
assert.equal(outsiderTeamRead.error, null)
checked(outsiderTeamRead.data.length === 0, 'An outsider could read a private team')
const directMemberInsert = await ownerClient
.from('team_members')
.insert({ team_id: teamId, user_id: teamAdmin.id, role: 'owner' })
.select('user_id')
checked(directMemberInsert.error !== null, 'Direct team member insertion bypassed the role invariant')
const inviteAdmin = requireRpcSuccess(await ownerClient.rpc('create_team_invite', {
target_team_id: teamId,
invited_email: teamAdmin.email,
invited_role: 'admin',
}), 'owner admin invitation')
checked(inviteAdmin.role === 'admin' && inviteAdmin.duplicate === false, 'Owner admin invitation returned an invalid contract')
const duplicateInvite = requireRpcSuccess(await ownerClient.rpc('create_team_invite', {
target_team_id: teamId,
invited_email: teamAdmin.email.toUpperCase(),
invited_role: 'admin',
}), 'duplicate invitation')
checked(duplicateInvite.id === inviteAdmin.id && duplicateInvite.duplicate === true, 'Duplicate active invitation was not idempotent')
requireRpcFailure(
await memberClient.rpc('accept_team_invite', { invite_token: inviteAdmin.token }),
/invite_email_mismatch|42501/i,
'wrong-account acceptance',
)
const acceptedAdmin = requireRpcSuccess(
await adminClient.rpc('accept_team_invite', { invite_token: inviteAdmin.token }),
'admin acceptance',
)
checked(acceptedAdmin.team_id === teamId && acceptedAdmin.role === 'admin', 'Invitation acceptance assigned the wrong role')
const repeatedAcceptance = requireRpcSuccess(
await adminClient.rpc('accept_team_invite', { invite_token: inviteAdmin.token }),
'idempotent repeated acceptance',
)
checked(repeatedAcceptance.duplicate === true && repeatedAcceptance.already_member === true, 'Repeated acceptance was not idempotent')
const adminInviteMember = requireRpcSuccess(await adminClient.rpc('create_team_invite', {
target_team_id: teamId,
invited_email: member.email,
invited_role: 'member',
}), 'admin member invitation')
checked(adminInviteMember.role === 'member', 'Admin could not create a member invitation')
requireRpcFailure(await adminClient.rpc('create_team_invite', {
target_team_id: teamId,
invited_email: `another-${suffix}@gmail.com`,
invited_role: 'admin',
}), /owner_required_for_admin_invite|42501/i, 'admin-to-admin invitation')
const acceptedMember = requireRpcSuccess(
await memberClient.rpc('accept_team_invite', { invite_token: adminInviteMember.token }),
'member acceptance',
)
checked(acceptedMember.team_id === teamId && acceptedMember.role === 'member', 'Member invitation acceptance failed')
const memberDirectory = requireRpcSuccess(
await memberClient.rpc('list_team_members', { target_team_id: teamId }),
'member directory',
)
checked(memberDirectory.length === 3, 'A member could not read the complete team directory')
checked(memberDirectory.some((row) => row.user_id === owner.id && row.display_name === 'Owner'), 'Limited team profile directory omitted the owner display name')
const memberInviteList = requireRpcSuccess(
await memberClient.rpc('list_team_invites', { target_team_id: teamId }),
'member invite list',
)
checked(Array.isArray(memberInviteList) && memberInviteList.length === 0, 'A regular member could read pending invitations')
const ownerInviteList = requireRpcSuccess(
await ownerClient.rpc('list_team_invites', { target_team_id: teamId }),
'owner invite list',
)
checked(ownerInviteList.every((row) => !Object.hasOwn(row, 'token')), 'Invitation listing leaked bearer tokens')
requireRpcFailure(await adminClient.rpc('update_team_member_role', {
target_team_id: teamId,
member_user_id: member.id,
new_role: 'admin',
}), /team_owner_required|42501/i, 'admin role escalation')
const promoted = requireRpcSuccess(await ownerClient.rpc('update_team_member_role', {
target_team_id: teamId,
member_user_id: member.id,
new_role: 'admin',
}), 'owner promotion')
checked(promoted.role === 'admin' && promoted.user_id === member.id, 'Owner role update changed the wrong membership')
requireRpcFailure(await adminClient.rpc('remove_team_member', {
target_team_id: teamId,
member_user_id: owner.id,
}), /team_owner_cannot_leave|42501/i, 'admin owner removal')
requireRpcFailure(await ownerClient.rpc('remove_team_member', {
target_team_id: teamId,
member_user_id: owner.id,
}), /team_owner_cannot_leave|42501/i, 'owner self leave')
const removedSelf = requireRpcSuccess(await memberClient.rpc('remove_team_member', {
target_team_id: teamId,
member_user_id: member.id,
}), 'member self leave')
checked(removedSelf.removed === true, 'Non-owner could not leave the team')
const accessAfterLeave = await memberClient.from('teams').select('id').eq('id', teamId)
checked(accessAfterLeave.data?.length === 0, 'A former member retained team read access')
const expiringInvite = requireRpcSuccess(await ownerClient.rpc('create_team_invite', {
target_team_id: teamId,
invited_email: member.email,
invited_role: 'member',
}), 'expiring invitation')
const expireUpdate = await admin
.from('team_invites')
.update({ expires_at: new Date(Date.now() - 60_000).toISOString() })
.eq('id', expiringInvite.id)
assert.equal(expireUpdate.error, null)
requireRpcFailure(
await memberClient.rpc('accept_team_invite', { invite_token: expiringInvite.token }),
/invite_expired|22023/i,
'expired invitation acceptance',
)
const cancellableInvite = requireRpcSuccess(await ownerClient.rpc('create_team_invite', {
target_team_id: teamId,
invited_email: member.email,
invited_role: 'member',
}), 'replacement invitation')
const cancelled = requireRpcSuccess(
await ownerClient.rpc('cancel_team_invite', { invite_id: cancellableInvite.id }),
'cancel invitation',
)
checked(cancelled.cancelled === true, 'Invitation cancellation did not report removal')
requireRpcFailure(
await memberClient.rpc('accept_team_invite', { invite_token: cancellableInvite.token }),
/invite_not_found|P0002/i,
'cancelled invitation acceptance',
)
const deviceA = await ownerClient
.from('devices')
.insert({
user_id: owner.id,
installation_id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
platform: 'android',
device_name: 'Pixel integration A',
app_version: '1.0.0',
os_version: 'Android 14',
})
.select('id')
.single()
assert.equal(deviceA.error, null, 'Owner device registration failed')
const registrationId = `fcm_${'a'.repeat(128)}`
const registeredPush = requireRpcSuccess(await ownerClient.rpc('register_push_registration', {
push_device_id: deviceA.data.id,
push_provider: 'fcm',
registration_id: registrationId,
}), 'owner push registration')
checked(registeredPush.device_id === deviceA.data.id && registeredPush.provider === 'fcm', 'Push registration lost device/provider association')
const hiddenPushRows = await ownerClient.from('push_tokens').select('*')
assert.equal(hiddenPushRows.error, null)
checked(hiddenPushRows.data.length === 0, 'Client could read its raw push registration secret')
const directPushInsert = await ownerClient.from('push_tokens').insert({
user_id: owner.id,
token: `fcm_${'b'.repeat(128)}`,
platform: 'android',
device_id: deviceA.data.id,
provider: 'fcm',
})
checked(directPushInsert.error !== null, 'Direct push token insertion bypassed the registration RPC')
const deviceB = await adminClient
.from('devices')
.insert({
user_id: teamAdmin.id,
installation_id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
platform: 'android',
device_name: 'Pixel integration B',
app_version: '1.0.0',
os_version: 'Android 14',
})
.select('id')
.single()
assert.equal(deviceB.error, null, 'Admin device registration failed')
requireRpcFailure(await adminClient.rpc('register_push_registration', {
push_device_id: deviceB.data.id,
push_provider: 'fcm',
registration_id: registrationId,
}), /push_registration_owned_by_other_user|23505/i, 'cross-account token takeover')
requireRpcFailure(await adminClient.rpc('register_push_registration', {
push_device_id: deviceA.data.id,
push_provider: 'fcm',
registration_id: `fcm_${'c'.repeat(128)}`,
}), /active_device_not_found|P0002/i, 'cross-account device registration')
const unregisteredPush = requireRpcSuccess(await ownerClient.rpc('unregister_push_registration', {
push_device_id: deviceA.data.id,
}), 'push unregister')
checked(unregisteredPush.removed === true, 'Push unregister did not remove the owned registration')
const revokedDevice = requireRpcSuccess(await ownerClient.rpc('revoke_device', {
target_device_id: deviceA.data.id,
}), 'device revocation')
checked(revokedDevice.id === deviceA.data.id && revokedDevice.revoked_at !== null, 'Device revocation did not persist')
const reviveDevice = await ownerClient
.from('devices')
.update({ revoked_at: null })
.eq('id', deviceA.data.id)
.select('id')
checked(reviveDevice.error !== null || reviveDevice.data.length === 0, 'A client revived a permanently revoked device')
requireRpcFailure(await ownerClient.rpc('register_push_registration', {
push_device_id: deviceA.data.id,
push_provider: 'fcm',
registration_id: `fcm_${'d'.repeat(128)}`,
}), /active_device_not_found|P0002/i, 'revoked device push registration')
const adminRename = await adminClient
.from('teams')
.update({ name: 'Unauthorized rename' })
.eq('id', teamId)
.select('id')
assert.equal(adminRename.error, null)
checked(adminRename.data.length === 0, 'A non-owner renamed the team')
console.log(`teams/push local integration passed: ${assertions} assertions`)
} finally {
await Promise.allSettled(createdUserIds.map((userId) => admin.auth.admin.deleteUser(userId)))
}