tests/bundle-upsert.unit.test.ts
Rama main · réplica de lectura del repositorio original.
import { beforeEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => ({
checkPermission: vi.fn(),
orgPolicy: vi.fn(),
existingVersion: vi.fn(),
upsert: vi.fn(),
}))
vi.mock('../supabase/functions/_backend/utils/rbac.ts', () => ({
checkPermission: (...args: unknown[]) => mocks.checkPermission(...args),
}))
vi.mock('../supabase/functions/_backend/utils/pg.ts', () => ({
getPgClient: async () => ({ query: async () => ({ rows: [mocks.orgPolicy()] }) }),
closeClient: () => {},
}))
vi.mock('../supabase/functions/_backend/utils/pg_rls.ts', () => ({
apikeyPrincipal: (_c: unknown, apikey: string) => ({ kind: 'apikey', apikey }),
withRlsContext: (_c: unknown, _principal: unknown, run: (client: unknown) => unknown) => run({}),
queryJsonOne: async (_client: unknown, text: string, values: unknown[]) => {
if (text.startsWith('INSERT'))
return mocks.upsert(text, JSON.parse(values[0] as string))
return mocks.existingVersion()
},
}))
const { upsertBundle } = await import('../supabase/functions/_backend/public/bundle/upsert.ts')
function createContext() {
return {
json: (body: unknown) => Response.json(body),
} as any
}
const apikey = { key: 'test-key', user_id: 'user-1' } as any
function mockOrgPolicy(enforce: boolean, requiredKey: string | null = null) {
mocks.orgPolicy.mockReturnValue({
owner_org: 'org-1',
orgs: {
enforce_encrypted_bundles: enforce,
required_encryption_key: requiredKey,
},
})
}
function mockVersions(existingVersion: unknown) {
mocks.existingVersion.mockReturnValue(existingVersion)
mocks.upsert.mockReturnValue({
id: 99,
app_id: 'com.example.app',
name: '1.0.0',
storage_provider: 'r2-direct',
})
}
describe('bundle upsert endpoint', () => {
beforeEach(() => {
vi.clearAllMocks()
mocks.checkPermission.mockResolvedValue(true)
mockOrgPolicy(false)
mockVersions(null)
})
it('creates bundle version rows for upload finalize writes', async () => {
const response = await upsertBundle(
createContext(),
{
app_id: 'com.example.app',
name: '1.0.0',
checksum: 'abc',
storage_provider: 'r2-direct',
session_key: 'iv-key',
},
apikey,
)
expect(response.status).toBe(200)
const body = await response.json() as { id?: number, name?: string }
expect(body.id).toBe(99)
expect(body.name).toBe('1.0.0')
expect(mocks.checkPermission).toHaveBeenCalledWith(expect.anything(), 'app.upload_bundle', {
appId: 'com.example.app',
})
expect(mocks.upsert).toHaveBeenCalledTimes(1)
expect(mocks.upsert).toHaveBeenCalledWith(
expect.stringContaining('ON CONFLICT (name, app_id) DO UPDATE SET "app_id" = EXCLUDED."app_id"'),
{
app_id: 'com.example.app',
name: '1.0.0',
owner_org: 'org-1',
user_id: 'user-1',
checksum: 'abc',
storage_provider: 'r2-direct',
session_key: 'iv-key',
},
)
})
it('rejects clearing session_key on an existing version when the org enforces encryption', async () => {
mockOrgPolicy(true)
mockVersions({ id: 99, deleted: false, session_key: 'iv-key', key_id: null })
await expect(upsertBundle(
createContext(),
{ app_id: 'com.example.app', name: '1.0.0', session_key: null },
apikey,
)).rejects.toMatchObject({ status: 400, cause: { error: 'encryption_required' } })
expect(mocks.upsert).not.toHaveBeenCalled()
})
it('rejects swapping key_id on an existing version to a non-required key', async () => {
mockOrgPolicy(true, 'required-key-value-0123456789')
mockVersions({ id: 99, deleted: false, session_key: 'iv-key', key_id: 'required-key-value-0' })
await expect(upsertBundle(
createContext(),
{ app_id: 'com.example.app', name: '1.0.0', key_id: 'other-key' },
apikey,
)).rejects.toMatchObject({ status: 400, cause: { error: 'encryption_key_mismatch' } })
expect(mocks.upsert).not.toHaveBeenCalled()
})
it('allows non-encryption updates on an existing version under an encryption policy', async () => {
mockOrgPolicy(true)
mockVersions({ id: 99, deleted: false, session_key: null, key_id: null })
const response = await upsertBundle(
createContext(),
{ app_id: 'com.example.app', name: '1.0.0', r2_path: 'orgs/org-1/apps/com.example.app/1.0.0.zip' },
apikey,
)
expect(response.status).toBe(200)
expect(mocks.upsert).toHaveBeenCalledWith(
expect.stringContaining('ON CONFLICT (name, app_id)'),
expect.objectContaining({ r2_path: 'orgs/org-1/apps/com.example.app/1.0.0.zip' }),
)
})
})