Files
2026-04-20 18:59:15 +08:00

214 lines
8.9 KiB
Markdown

# 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*