{"content":"# Integration Tests\n\nTest API routes by importing handlers directly with Bun's test runner. Fast, reliable tests without HTTP overhead.\n\n## Folder Structure\n\nIntegration tests live in `tests/integration/` and test pure API endpoints:\n\n```\ntests/\n├── integration/\n│   ├── llms.test.ts              # /llms.txt endpoint tests\n│   ├── mcp/\n│   │   └── route.test.ts         # MCP server endpoint tests\n│   ├── r.test.ts                 # Shadcn registry tests\n│   └── recipes/\n│       └── [slug]/\n│           └── route.test.ts     # Recipe API tests\n```\n\n---\n\n## When to Write Integration Tests\n\nUse integration tests for:\n\n- API routes and endpoints that don't require authentication\n- Public endpoints (e.g., /llms.txt, /api/mcp, /api/recipes/[slug])\n- Testing JSON-RPC or other protocol-level interactions\n\n**Use Playwright tests** for:\n\n- User interactions (clicking, typing, navigation)\n- Visual feedback (toasts, loading states, error messages)\n- Complex UI flows\n- Authentication flows (sign-in, sign-up, sign-out)\n- Any feature that requires a logged-in user\n\n---\n\n## Writing Integration Tests\n\n### Direct Handler Import (Preferred)\n\nImport route handlers directly for faster, more reliable tests without HTTP overhead:\n\n```typescript\nimport { describe, it, expect } from \"bun:test\";\nimport { GET } from \"@/app/llms.txt/route\";\n\ndescribe(\"GET /llms.txt\", () => {\n  it(\"should return 200 with plain text content type\", async () => {\n    const response = await GET();\n\n    expect(response.status).toBe(200);\n    expect(response.headers.get(\"Content-Type\")).toBe(\n      \"text/plain; charset=utf-8\",\n    );\n  });\n\n  it(\"should return non-empty content\", async () => {\n    const response = await GET();\n    const content = await response.text();\n\n    expect(content.length).toBeGreaterThan(0);\n    expect(content).toContain(\"# Fullstack Recipes\");\n  });\n});\n```\n\n### Testing with Data Dependencies\n\nWhen tests need to verify content from application data, import and use the same data sources:\n\n```typescript\nimport { describe, it, expect } from \"bun:test\";\nimport { GET } from \"@/app/llms.txt/route\";\nimport { getAllRecipes } from \"@/lib/recipes/data\";\n\ndescribe(\"content includes all recipes\", () => {\n  const recipes = getAllRecipes();\n\n  for (const recipe of recipes) {\n    it(`should include recipe \"${recipe.title}\"`, async () => {\n      const response = await GET();\n      const content = await response.text();\n\n      expect(content).toContain(recipe.title);\n      expect(content).toContain(`/recipes/${recipe.slug}`);\n    });\n  }\n});\n```\n\n---\n\n## Running Integration Tests\n\n```bash\nbun run test:integration  # Run all integration tests\n```\n\nThe test script creates an isolated Neon database branch, runs tests, then cleans up.\n\n---\n\n## CI with GitHub Actions\n\n```yaml\n# .github/workflows/test.yml\nname: Test\n\non:\n  push:\n    branches: [\"**\"]\n  pull_request:\n    branches: [\"**\"]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    env:\n      NEON_API_KEY: ${{ secrets.NEON_API_KEY }}\n      NEON_PROJECT_ID: ${{ secrets.NEON_PROJECT_ID }}\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: oven-sh/setup-bun@v2\n        with:\n          bun-version: latest\n\n      - run: bun install --frozen-lockfile\n\n      - name: Run integration tests\n        run: bun run test:integration\n```\n\nNote: Integration tests require the Neon API credentials to create test branches. Add `NEON_API_KEY` and `NEON_PROJECT_ID` as repository secrets."}