Ctrl K
ComunidadGuías…

tests/bundle-list-order.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { randomUUID } from 'node:crypto'
import { escapeLiteral, Pool } from 'pg'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import {
  createDirectApiKeyWithBindings,
  executeSQL,
  fetchTestRequest,
  getAuthHeaders,
  POSTGRES_URL,
  SUPABASE_ANON_KEY,
  SUPABASE_BASE_URL,
  USER_ID,
  USER_ID_2,
  withAnonymousCapgkey,
} from './test-utils.ts'

const suffix = randomUUID().replaceAll('-', '')
const appId = `com.bundlelist.order.${suffix}`
const seedApp = `${appId}.seed`
const deniedApp = `${appId}.denied`
const org = randomUUID()
const deniedOrg = randomUUID()
const pool = new Pool({ connectionString: POSTGRES_URL, max: 4 })
let key = ''
let keyId: number | undefined
let authHeaders: Record<string, string>

/** Derive the expected bundle names without relying on database ordering. */
function expectedNames(deleted: boolean, offset = 0) {
  return Array.from({ length: 40 }, (_, i) => i + 1).reverse()
    .filter(i => (i % 5 === 0) === deleted)
    .slice(offset, offset + 5)
    .map(i => `ordered-${String(i).padStart(3, '0')}`)
}

/** Request the real bundle-list endpoint with the selected caller identity. */
async function page(deleted: boolean, offset = 0, identity: 'key' | 'user' | 'anon' | 'invalid' = 'key', app = appId) {
  const headers: Record<string, string> = {
    apikey: SUPABASE_ANON_KEY,
    Authorization: `Bearer ${SUPABASE_ANON_KEY}`,
    ...(identity === 'user' ? authHeaders : {}),
    ...(identity === 'key' ? { capgkey: key } : identity === 'invalid' ? { capgkey: randomUUID() } : {}),
  }
  const response = await fetchTestRequest(`${SUPABASE_BASE_URL}/rest/v1/app_versions?select=*&app_id=eq.${app}&deleted=eq.${deleted}&order=created_at.desc&limit=5&offset=${offset}`, { headers })
  const body = await response.text()
  expect(response.status, body).toBe(200)
  return JSON.parse(body) as Array<{ name: string, app_id: string, deleted: boolean }>
}

beforeAll(async () => {
  for (const [app, orgId, userId] of [[seedApp, org, USER_ID], [deniedApp, deniedOrg, USER_ID_2]]) {
    await executeSQL('SELECT public.reset_and_seed_app_data($1,$2::uuid,$3::uuid,NULL,$4,NULL)', [app, orgId, userId, `cus_bundlelist_${randomUUID().replaceAll('-', '')}`])
  }
  await executeSQL(`INSERT INTO public.apps(app_id,icon_url,user_id,name,owner_org)
    VALUES ($1,'',$2::uuid,'Ordered bundles',$3::uuid)`, [appId, USER_ID, org])
  await executeSQL(`INSERT INTO public.app_versions(app_id,name,owner_org,user_id,created_at,deleted)
    SELECT $1,'ordered-'||lpad(i::text,3,'0'),$2::uuid,$3::uuid,
      TIMESTAMPTZ '2026-01-01' + i * INTERVAL '1 second',i%5=0 FROM generate_series(1,40) i`, [appId, org, USER_ID])
  const apiKey = await createDirectApiKeyWithBindings({ userId: USER_ID, key: randomUUID(), name: `bundlelist-${suffix}`, orgId: org, roleName: 'org_admin' })
  if (!apiKey.key)
    throw new Error('Bundle-list API key was not created')
  key = apiKey.key
  keyId = apiKey.id
  authHeaders = await getAuthHeaders()
})

afterAll(async () => {
  if (keyId != null)
    await executeSQL('DELETE FROM public.apikeys WHERE id=$1', [keyId])
  for (const app of [appId, seedApp, deniedApp])
    await executeSQL('SELECT public.reset_app_data($1)', [app])
  await executeSQL('DELETE FROM public.orgs WHERE id=ANY($1::uuid[])', [[org, deniedOrg]])
  await pool.end()
})

describe('bundle-list filter and chronological paging', () => {
  for (const deleted of [false, true]) {
    for (const offset of [0, 5]) {
      it.concurrent(`pages deleted=${deleted} bundles at offset ${offset} through PostgREST`, async () => {
        const rows = await page(deleted, offset)
        expect(rows.map(row => row.name)).toEqual(expectedNames(deleted, offset))
        expect(rows.every(row => row.app_id === appId && row.deleted === deleted)).toBe(true)
      })
    }
    for (const mode of ['force_custom_plan', 'force_generic_plan']) {
      it.concurrent(`keeps parameterized deleted=${deleted} reads correct with ${mode}`, async () => {
        const rows = await withAnonymousCapgkey(pool, key, async (client) => {
          await client.query(`SET LOCAL plan_cache_mode=${mode}`)
          // Tiny fixtures can favor a scan or explicit sort; prove ordered index availability.
          await client.query('SET LOCAL enable_seqscan = off')
          await client.query('SET LOCAL enable_sort = off')
          const result = await client.query({
            name: `bundle_list_${mode}`,
            text: `WITH q AS (SELECT * FROM public.app_versions WHERE app_id=$1 AND deleted=$4 ORDER BY created_at DESC LIMIT $2 OFFSET $3)
              SELECT pg_catalog.to_json(q.*) AS row FROM q`,
            values: [appId, 5, 5, deleted],
          })
          const plan = await client.query(`EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
            EXECUTE bundle_list_${mode}(${escapeLiteral(appId)}, 5, 5, ${deleted})`)
          expect(JSON.stringify(plan.rows[0]['QUERY PLAN'])).toContain('idx_app_versions_app_deleted_created_at')
          return result.rows
        })
        expect(rows.map(row => row.row.name)).toEqual(expectedNames(deleted, 5))
      })
    }
  }

  it.concurrent('preserves authenticated user access', async () => {
    const rows = await page(false, 0, 'user')
    expect(rows.map(row => row.name)).toEqual(expectedNames(false))
  })

  for (const identity of ['anon', 'invalid'] as const) {
    it.concurrent(`keeps ${identity} callers from reading bundles`, async () => {
      expect(await page(false, 0, identity)).toEqual([])
    })
  }

  it.concurrent('keeps a valid API key scoped to its organization', async () => {
    expect(await page(false, 0, 'key', deniedApp)).toEqual([])
  })
})