Ctrl K
ComunidadGuías…

tests/integration/phase1.test.ts

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

Archivo original ↗Preguntar a KiBot
import { eq } from 'drizzle-orm'
import { beforeEach, describe, expect, it } from 'vitest'

import { buildServer } from '../../src/server.js'
import { environments } from '../../src/db/schema.js'
import {
  createAdminKey,
  createClientKey,
  createProject,
  createRootKey,
} from '../helpers/fixtures.js'
import { getTestDb, truncateAll } from '../helpers/db.js'

const describeIfDb = process.env.TEST_DATABASE_URL ? describe : describe.skip

describeIfDb('phase 1 integration', () => {
  const db = process.env.TEST_DATABASE_URL ? getTestDb() : undefined

  beforeEach(async () => {
    process.env.DATABASE_URL = process.env.TEST_DATABASE_URL
    await truncateAll(db!)
  })

  it('supports the full happy path and environment-specific evaluation', async () => {
    const app = await buildServer({ db })
    const rootKey = await createRootKey(db!)
    const project = await createProject(app, rootKey)
    const adminKey = await createAdminKey(app, rootKey, project.id)

    const envs = await db!.select().from(environments).where(eq(environments.projectId, project.id))
    expect(envs.map((environment) => environment.slug).sort()).toEqual([
      'development',
      'production',
    ])

    await app.inject({
      method: 'POST',
      url: `/api/v1/admin/projects/${project.id}/flags`,
      headers: { authorization: adminKey },
      payload: { name: 'Checkout', key: 'checkout' },
    })
    await app.inject({
      method: 'POST',
      url: `/api/v1/admin/projects/${project.id}/flags/checkout/environments/production/enable`,
      headers: { authorization: adminKey },
    })

    const dev = envs.find((environment) => environment.slug === 'development')!
    const production = envs.find((environment) => environment.slug === 'production')!
    const devClient = await createClientKey(app, adminKey, project.id, dev.id)
    const productionClient = await createClientKey(app, adminKey, project.id, production.id)

    const devResponse = await app.inject({
      method: 'GET',
      url: '/api/v1/client/features/checkout',
      headers: { authorization: devClient },
    })
    const productionResponse = await app.inject({
      method: 'GET',
      url: '/api/v1/client/features/checkout',
      headers: { authorization: productionClient },
    })

    expect(devResponse.json()).toMatchObject({ name: 'checkout', enabled: false })
    expect(productionResponse.json()).toMatchObject({ name: 'checkout', enabled: true })
    await app.close()
  })

  it('evaluation always returns the environment default, ignoring query params', async () => {
    const app = await buildServer({ db })
    const rootKey = await createRootKey(db!)
    const project = await createProject(app, rootKey)
    const adminKey = await createAdminKey(app, rootKey, project.id)
    const [firstEnv] = await db!
      .select()
      .from(environments)
      .where(eq(environments.projectId, project.id))
      .limit(1)
    const clientKey = await createClientKey(app, adminKey, project.id, firstEnv.id)

    await app.inject({
      method: 'POST',
      url: `/api/v1/admin/projects/${project.id}/flags`,
      headers: { authorization: adminKey },
      payload: { name: 'Checkout', key: 'checkout' },
    })
    await app.inject({
      method: 'POST',
      url: `/api/v1/admin/projects/${project.id}/flags/checkout/environments/${firstEnv.slug}/enable`,
      headers: { authorization: adminKey },
    })

    const withQuery = await app.inject({
      method: 'GET',
      url: '/api/v1/client/features/checkout?userId=user_abc123',
      headers: { authorization: clientKey },
    })
    const withoutQuery = await app.inject({
      method: 'GET',
      url: '/api/v1/client/features/checkout',
      headers: { authorization: clientKey },
    })

    expect(withQuery.json()).toMatchObject({ enabled: true, reason: 'default' })
    expect(withoutQuery.json()).toMatchObject({ enabled: true, reason: 'default' })
    await app.close()
  })

  it('enforces auth scopes', async () => {
    const app = await buildServer({ db })
    const rootKey = await createRootKey(db!)
    const project = await createProject(app, rootKey, 'a')
    const other = await createProject(app, rootKey, 'b')
    const adminKey = await createAdminKey(app, rootKey, project.id)
    const [environment] = await db!
      .select()
      .from(environments)
      .where(eq(environments.projectId, project.id))
      .limit(1)
    const clientKey = await createClientKey(app, adminKey, project.id, environment.id)

    expect(
      (
        await app.inject({
          method: 'GET',
          url: `/api/v1/admin/projects/${project.id}/flags`,
          headers: { authorization: clientKey },
        })
      ).statusCode,
    ).toBe(403)

    expect(
      (
        await app.inject({
          method: 'GET',
          url: `/api/v1/admin/projects/${other.id}`,
          headers: { authorization: adminKey },
        })
      ).statusCode,
    ).toBe(403)

    expect(
      (
        await app.inject({
          method: 'POST',
          url: '/api/v1/admin/projects',
          headers: { authorization: adminKey },
          payload: { name: 'Nope', slug: 'nope' },
        })
      ).statusCode,
    ).toBe(403)
    await app.close()
  })

  it('returns conflicts for duplicate resources', async () => {
    const app = await buildServer({ db })
    const rootKey = await createRootKey(db!)
    const project = await createProject(app, rootKey)
    const adminKey = await createAdminKey(app, rootKey, project.id)

    const duplicateProject = await app.inject({
      method: 'POST',
      url: '/api/v1/admin/projects',
      headers: { authorization: rootKey },
      payload: { name: 'Duplicate', slug: project.slug },
    })
    expect(duplicateProject.statusCode).toBe(409)

    await app.inject({
      method: 'POST',
      url: `/api/v1/admin/projects/${project.id}/flags`,
      headers: { authorization: adminKey },
      payload: { name: 'Checkout', key: 'checkout' },
    })
    const duplicateFlag = await app.inject({
      method: 'POST',
      url: `/api/v1/admin/projects/${project.id}/flags`,
      headers: { authorization: adminKey },
      payload: { name: 'Checkout 2', key: 'checkout' },
    })
    expect(duplicateFlag.statusCode).toBe(409)
    await app.close()
  })

  it('cascades project deletion', async () => {
    const app = await buildServer({ db })
    const rootKey = await createRootKey(db!)
    const project = await createProject(app, rootKey)

    const response = await app.inject({
      method: 'DELETE',
      url: `/api/v1/admin/projects/${project.id}`,
      headers: { authorization: rootKey },
    })

    expect(response.statusCode).toBe(204)
    expect(
      await db!.select().from(environments).where(eq(environments.projectId, project.id)),
    ).toEqual([])
    await app.close()
  })
})