# Testing Patterns **Analysis Date:** 2026-04-20 ## Test Framework **Runner:** - Use `vitest` via `vitest.config.ts` and `pnpm test` from `package.json`. - `vitest.config.ts` sets `environment: "node"`, resolves `@` to `./src`, disables watch mode, and only includes `src/**/*.test.ts`. - Current verification on 2026-04-20: `pnpm test` passed with 8 test files and 26 tests. **Assertion Library:** - Use Vitest built-ins from `vitest`, including `describe`, `it`, `expect`, `beforeEach`, `afterEach`, and `vi`, as seen in `src/lib/auth.test.ts`, `src/lib/prisma-url.test.ts`, and `src/app/api/tags/maintenance/route.test.ts`. **Run Commands:** ```bash pnpm test # Run all configured Vitest suites pnpm lint # Run ESLint quality checks pnpm build # Run production build checks ``` - `AGENTS.md` also reserves `pnpm test:e2e` for Playwright, but no `test:e2e` script or `playwright.config.*` file is present in the current workspace. ## Test File Organization **Location:** - Keep tests co-located beside the code they exercise under `src`, for example `src/lib/auth.test.ts`, `src/lib/prisma-url.test.ts`, `src/app/api/tags/route.test.ts`, and `src/app/api/tags/maintenance/service.test.ts`. - No `e2e/` directory is present in the repository snapshot analyzed on 2026-04-20. **Naming:** - Use `*.test.ts`. No `*.spec.ts` or `*.test.tsx` files were detected by `rg --files` or included by `vitest.config.ts`. - Keep route tests adjacent to `route.ts` or `service.ts` files, for example `src/app/api/tags/reset-projects/route.test.ts` next to `src/app/api/tags/reset-projects/route.ts`. **Structure:** ```text src/ lib/ auth.ts auth.test.ts prisma-url.ts prisma-url.test.ts app/api/tags/ route.ts route.test.ts maintenance/ route.ts route.test.ts service.ts service.test.ts ``` ## Test Structure **Suite Organization:** ```typescript import { beforeEach, describe, expect, it, vi } from "vitest"; import { NextRequest } from "next/server"; import { POST } from "./route"; const { transactionMock, revalidatePathMock, txMock } = vi.hoisted(() => { const tx = { projectTag: { deleteMany: vi.fn(), createMany: vi.fn(), }, }; return { transactionMock: vi.fn(async (callback) => callback(tx)), revalidatePathMock: vi.fn(), txMock: tx, }; }); ``` **Patterns:** - Use one top-level `describe(...)` block per module or endpoint, with behavior-based test names such as `returns 401 for wrong API key` in `src/app/api/tags/maintenance/route.test.ts` and `returns 500 when prisma query fails` in `src/app/api/tags/route.test.ts`. - Build local request helpers for route tests, such as `buildRequest(...)` in `src/app/api/tags/maintenance/route.test.ts` and `src/app/api/tags/reset-projects/route.test.ts`. - Use inline fixture builders for reusable payloads, such as `buildValidPayload(...)` in `src/app/api/tags/reset-projects/route.test.ts` and `baseProjectInput` in `src/lib/validations.test.ts`. - Reset mocks and environment state in `beforeEach` or `afterEach`, as seen in `src/app/api/tags/route.test.ts`, `src/app/api/tags/maintenance/route.test.ts`, and `src/lib/prisma-url.test.ts`. ## Mocking **Framework:** - Use Vitest mocks via `vi.fn`, `vi.mock`, `vi.hoisted`, `mockResolvedValue`, and `mockRejectedValue`. **Patterns:** ```typescript vi.mock("@/lib/prisma", () => ({ prisma: { tag: { findMany: findManyMock, }, }, })); vi.mock("next/cache", () => ({ revalidatePath: revalidatePathMock, })); ``` - Hoist shared mocks before module import so route modules capture mocked dependencies, as in `src/app/api/tags/route.test.ts`, `src/app/api/tags/maintenance/route.test.ts`, and `src/app/api/tags/reset-projects/route.test.ts`. - Mock Prisma reads and writes rather than using a real database in route tests. - Mock `next/cache` side effects when mutation routes call `revalidatePath`, as in `src/app/api/tags/maintenance/route.test.ts` and `src/app/api/tags/reset-projects/route.test.ts`. **What to Mock:** - Mock Prisma modules for handler and service tests, as in `src/app/api/tags/route.test.ts`, `src/app/api/tags/maintenance/route.test.ts`, and `src/app/api/tags/reset-projects/route.test.ts`. - Set `process.env.WEBHOOK_API_KEY` inline for auth-path tests, as in `src/app/api/tags/maintenance/route.test.ts` and `src/app/api/tags/reset-projects/route.test.ts`. - Mock cache revalidation side effects instead of asserting real filesystem or ISR behavior. **What NOT to Mock:** - Test pure helpers directly without mocks when possible, as in `src/lib/auth.test.ts` and `src/lib/validations.tag-maintenance.test.ts`. - There is no current pattern for DOM, browser, or component mocking because no component tests are checked in. ## Fixtures and Environment Handling **Test Data:** ```typescript const baseProjectInput = { name: "Agent Park", description: "A curated list of practical AI agent tools.", tags: [{ name: "ai-agent" }], links: [{ type: "GITHUB" as const, url: "https://github.com/example/repo" }], }; ``` ```typescript const ENV_KEYS = [ "DATABASE_URL", "PG_SSL_ROOT_CERT_B64", "PG_SSL_IDENTITY_P12_B64", "PG_SSL_IDENTITY_PASSWORD", "PG_SSL_MODE", "PG_SSL_CERT_DIR", ] as const; ``` - `src/lib/prisma-url.test.ts` snapshots selected env vars and restores them in `afterEach`, which is the current pattern for tests that mutate `process.env`. - Fixtures are inline per file. No shared `test-utils` or factory directory exists. ## Coverage **Requirements:** - No coverage thresholds or coverage command are configured in `package.json` or `vitest.config.ts`. - No CI workflow, coverage upload, or Playwright setup was detected under `.github/` or repository root. **View Coverage:** ```bash Not configured ``` ## Test Types **Unit Tests:** - Pure utility coverage exists for `src/lib/auth.ts`, `src/lib/prisma-url.ts`, `src/lib/validations.ts`, and tag-maintenance schema rules in `src/lib/validations.tag-maintenance.test.ts`. **Service Tests:** - Business logic in `src/app/api/tags/maintenance/service.ts` is tested separately in `src/app/api/tags/maintenance/service.test.ts`, including deduped tag migration and validation failure behavior. **Route-Level Tests:** - Direct route-handler tests exist for `src/app/api/tags/route.ts`, `src/app/api/tags/maintenance/route.ts`, and `src/app/api/tags/reset-projects/route.ts`. - These tests assert status codes, JSON bodies, auth failures, validation failures, and `revalidatePath(...)` side effects. **n8n-Related Coverage:** - `src/app/api/search/ai/route.ts` has no corresponding `src/app/api/search/ai/route.test.ts`. - `src/app/api/webhook/signals/route.ts` has no corresponding `src/app/api/webhook/signals/route.test.ts`. - This means the n8n-facing search proxy and the authenticated signals webhook currently rely on runtime behavior rather than automated handler tests. **E2E Tests:** - Not detected. `AGENTS.md` reserves `e2e/` for Playwright, but there is no `e2e/` directory, no Playwright config, and no `pnpm test:e2e` script in `package.json`. ## Common Patterns **Async Route Testing:** ```typescript const response = await POST(buildRequest(payload)); const json = await response.json(); expect(response.status).toBe(200); expect(json.success).toBe(true); ``` **Error Testing:** ```typescript findManyMock.mockRejectedValue(new Error("db unavailable")); const response = await GET(); const json = await response.json(); expect(response.status).toBe(500); expect(json.error).toBe("Internal server error"); expect(json.details).toContain("db unavailable"); ``` ## Current Gaps **Untested API Routes:** - No tests were detected for `src/app/api/projects/route.ts`, `src/app/api/projects/[slug]/route.ts`, `src/app/api/signals/route.ts`, `src/app/api/search/ai/route.ts`, or `src/app/api/webhook/signals/route.ts`. - The missing n8n-related tests are especially important because `src/app/api/search/ai/route.ts` depends on the `N8N_AI_SEARCH_WEBHOOK` contract and `src/app/api/webhook/signals/route.ts` handles authenticated ingestion plus Prisma upserts. **Untested Query and UI Modules:** - No tests were detected for `src/hooks/useProjects.ts`, `src/hooks/useHome.ts`, `src/lib/cache.ts`, `src/lib/signal-hotness.ts`, `src/lib/tag-taxonomy.ts`, `src/lib/github/badges.ts`, or large client components under `src/components` and `src/app/[locale]`. **Infrastructure Gaps:** - No shared fixture library, no DOM test environment, no Playwright harness, and no CI workflow files were detected. ## Verification Signals **Current Quality Checks:** - `pnpm test` passed on 2026-04-20 with 8 test files and 26 tests. - `pnpm lint` passed on 2026-04-20 with no warnings or errors. **Observed Output Notes:** - `pnpm test` prints expected stderr from the deliberate error-path assertion in `src/app/api/tags/route.test.ts` because `src/app/api/tags/route.ts` logs failures with `console.error`. - The current Vitest run emits a Vite deprecation notice about the CJS Node API. This is a tooling signal, not a failing test. --- *Testing analysis: 2026-04-20*