import { randomUUID } from 'node:crypto' import { afterAll, beforeAll, describe, expect, it } from 'vitest' import { BASE_URL, createAppVersions, createDirectApiKeyWithBindings, getSupabaseClient, headers, ORG_ID_2, resetAndSeedAppData, resetAppData, USER_ID_2, } from './test-utils.ts' const fixtureId = randomUUID() const APP_A = `com.test.bundle.lookup.a.${fixtureId}` const APP_B = `com.test.bundle.lookup.b.${fixtureId}` const VERSION_NAME = `9.8.7-lookup-${fixtureId}` let versionIdOnA: number let deniedHeaders: typeof headers let deniedKeyId: number async function getBundles(query: Record, requestHeaders: typeof headers = headers) { const params = new URLSearchParams(query) const response = await fetch(`${BASE_URL}/bundle?${params.toString()}`, { method: 'GET', headers: requestHeaders, }) const data = await response.json() return { response, data } } beforeAll(async () => { await resetAndSeedAppData(APP_A) await resetAndSeedAppData(APP_B) const version = await createAppVersions(VERSION_NAME, APP_A) versionIdOnA = version.id const deniedKey = await createDirectApiKeyWithBindings({ userId: USER_ID_2, key: randomUUID(), name: `bundle-lookup-denied-${fixtureId}`, orgId: ORG_ID_2, roleName: 'org_admin', }) if (!deniedKey.key) throw new Error('Failed to create denied API key') deniedKeyId = deniedKey.id deniedHeaders = { 'Content-Type': 'application/json', Authorization: deniedKey.key, } }) afterAll(async () => { await resetAppData(APP_A) await resetAppData(APP_B) if (deniedKeyId) await getSupabaseClient().from('apikeys').delete().eq('id', deniedKeyId) }) describe('[GET] /bundle version and id filters', () => { it.concurrent('returns one bundle when version exists on the app', async () => { const { response, data } = await getBundles({ app_id: APP_A, version: VERSION_NAME }) expect(response.status).toBe(200) expect(Array.isArray(data)).toBe(true) expect(data).toHaveLength(1) expect((data as { id: number, name: string }[])[0].id).toBe(versionIdOnA) expect((data as { name: string }[])[0].name).toBe(VERSION_NAME) }) it.concurrent('returns empty array when version does not exist', async () => { const { response, data } = await getBundles({ app_id: APP_A, version: `missing-${fixtureId}`, }) expect(response.status).toBe(200) expect(data).toEqual([]) }) it.concurrent('returns empty array when version exists on another app', async () => { const { response, data } = await getBundles({ app_id: APP_B, version: VERSION_NAME }) expect(response.status).toBe(200) expect(data).toEqual([]) }) it.concurrent('rejects unsafe bundle id query values', async () => { const { response, data } = await getBundles({ app_id: APP_A, id: '99999999999999999999', }) expect(response.status).toBe(400) expect((data as { error: string }).error).toBe('invalid_query') }) it.concurrent('returns one bundle when id exists on the app', async () => { const { response, data } = await getBundles({ app_id: APP_A, id: String(versionIdOnA), }) expect(response.status).toBe(200) expect(data).toHaveLength(1) expect((data as { id: number }[])[0].id).toBe(versionIdOnA) }) it.concurrent('rejects API key without access to the app', async () => { const { response, data } = await getBundles({ app_id: APP_A, version: VERSION_NAME }, deniedHeaders) expect(response.status).toBe(404) expect((data as { error: string }).error).toBe('app_not_found') }) it.concurrent('keeps unpaginated list paging behavior unchanged', async () => { const { response, data } = await getBundles({ app_id: APP_A, page: '99' }) expect(response.status).toBe(200) expect(data).toEqual([]) }) it.concurrent('fields=summary leaves out manifest and native_packages', async () => { const { response, data } = await getBundles({ app_id: APP_A, version: VERSION_NAME, fields: 'summary' }) expect(response.status).toBe(200) const row = (data as Record[])[0] expect(row.id).toBe(versionIdOnA) expect(row).toHaveProperty('checksum') expect(row).not.toHaveProperty('native_packages') expect(row).not.toHaveProperty('manifest') }) it.concurrent('limit returns larger list pages', async () => { const [{ response: small, data: smallPage }, { response: large, data: largePage }] = await Promise.all([ getBundles({ app_id: APP_A, limit: '1', fields: 'summary' }), getBundles({ app_id: APP_A, limit: '1000', fields: 'summary' }), ]) expect(small.status).toBe(200) expect(large.status).toBe(200) expect(smallPage).toHaveLength(1) expect((largePage as unknown[]).length).toBeGreaterThan(1) expect((largePage as { id: number }[]).map(row => row.id)).toContain(versionIdOnA) }) it.concurrent('rejects limits above the PostgREST page cap', async () => { const { response, data } = await getBundles({ app_id: APP_A, limit: '1001' }) expect(response.status).toBe(400) expect((data as { error: string }).error).toBe('invalid_query') }) })