docs: refresh codebase map
This commit is contained in:
+76
-130
@@ -1,38 +1,34 @@
|
||||
# Testing Patterns
|
||||
|
||||
**Analysis Date:** 2026-04-18
|
||||
**Analysis Date:** 2026-04-20
|
||||
|
||||
## Test Framework
|
||||
|
||||
**Runner:**
|
||||
- `vitest` via `vitest.config.ts`.
|
||||
- `package.json` declares `vitest` in `devDependencies` and exposes `pnpm test`.
|
||||
- Verified on 2026-04-18: `pnpm test` executed with Vitest `v2.1.9` and passed all current test files.
|
||||
- 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:**
|
||||
- Vitest built-ins: `describe`, `it`, `expect`, `beforeEach`, and `vi`, as seen in `src/lib/auth.test.ts`, `src/lib/validations.test.ts`, and `src/app/api/tags/maintenance/route.test.ts`.
|
||||
|
||||
**Config:**
|
||||
- `vitest.config.ts` sets `environment: "node"`, aliases `@` to `./src`, disables watch mode, and includes only `src/**/*.test.ts`.
|
||||
- No separate setup file, coverage config, browser environment, or integration test project was detected in `vitest.config.ts`.
|
||||
- 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, lint, and type validation
|
||||
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:**
|
||||
- Tests are co-located beside the source they exercise.
|
||||
- Library tests live next to utilities, for example `src/lib/auth.test.ts`, `src/lib/validations.test.ts`, and `src/lib/validations.tag-maintenance.test.ts`.
|
||||
- API tests live next to route or service modules, for example `src/app/api/tags/route.test.ts`, `src/app/api/tags/reset-projects/route.test.ts`, `src/app/api/tags/maintenance/route.test.ts`, and `src/app/api/tags/maintenance/service.test.ts`.
|
||||
- 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` files were detected under `src` on 2026-04-18.
|
||||
- The include pattern in `vitest.config.ts` means `*.test.tsx` and files outside `src` are not picked up by default.
|
||||
- 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
|
||||
@@ -40,6 +36,8 @@ src/
|
||||
lib/
|
||||
auth.ts
|
||||
auth.test.ts
|
||||
prisma-url.ts
|
||||
prisma-url.test.ts
|
||||
app/api/tags/
|
||||
route.ts
|
||||
route.test.ts
|
||||
@@ -59,50 +57,34 @@ import { NextRequest } from "next/server";
|
||||
import { POST } from "./route";
|
||||
|
||||
const { transactionMock, revalidatePathMock, txMock } = vi.hoisted(() => {
|
||||
// create hoisted mocks here
|
||||
});
|
||||
const tx = {
|
||||
projectTag: {
|
||||
deleteMany: vi.fn(),
|
||||
createMany: vi.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
vi.mock("@/lib/prisma", () => ({ prisma: { $transaction: transactionMock } }));
|
||||
vi.mock("next/cache", () => ({ revalidatePath: revalidatePathMock }));
|
||||
|
||||
function buildRequest(body: unknown): NextRequest {
|
||||
return new NextRequest("http://localhost:3000/api/tags/maintenance", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
}
|
||||
|
||||
describe("POST /api/tags/maintenance", () => {
|
||||
beforeEach(() => {
|
||||
process.env.WEBHOOK_API_KEY = "k".repeat(32);
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("returns 401 for wrong API key", async () => {
|
||||
const response = await POST(buildRequest({ apiKey: "a".repeat(32), updates: [], merges: [] }));
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
return {
|
||||
transactionMock: vi.fn(async (callback) => callback(tx)),
|
||||
revalidatePathMock: vi.fn(),
|
||||
txMock: tx,
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
**Patterns:**
|
||||
- Use a top-level `describe(...)` per module or endpoint, with test names phrased as behavior statements.
|
||||
- Reset spies and mock state in `beforeEach`, as in `src/app/api/tags/route.test.ts`, `src/app/api/tags/reset-projects/route.test.ts`, and `src/app/api/tags/maintenance/route.test.ts`.
|
||||
- For API routes, call the exported `GET` or `POST` function directly and assert on both `response.status` and `await response.json()`.
|
||||
- For pure helpers and schemas, call the function directly and assert return values or thrown errors, as in `src/lib/auth.test.ts` and `src/lib/validations.test.ts`.
|
||||
- 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:**
|
||||
- Vitest mocking via `vi.mock`, `vi.fn`, `vi.hoisted`, `mockResolvedValue`, and `mockRejectedValue`.
|
||||
- Use Vitest mocks via `vi.fn`, `vi.mock`, `vi.hoisted`, `mockResolvedValue`, and `mockRejectedValue`.
|
||||
|
||||
**Patterns:**
|
||||
```typescript
|
||||
const { findManyMock } = vi.hoisted(() => ({
|
||||
findManyMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/prisma", () => ({
|
||||
prisma: {
|
||||
tag: {
|
||||
@@ -111,54 +93,26 @@ vi.mock("@/lib/prisma", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
findManyMock.mockResolvedValue([
|
||||
{
|
||||
id: "tag-1",
|
||||
name: "机器学习",
|
||||
slug: "machine-learning",
|
||||
_count: { projects: 4 },
|
||||
},
|
||||
]);
|
||||
```
|
||||
|
||||
```typescript
|
||||
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 client calls for route and service tests instead of hitting a real database. This is the established pattern in `src/app/api/tags/route.test.ts`, `src/app/api/tags/reset-projects/route.test.ts`, and `src/app/api/tags/maintenance/route.test.ts`.
|
||||
- Mock Next.js side effects such as `revalidatePath` when testing mutation endpoints, as in `src/app/api/tags/reset-projects/route.test.ts` and `src/app/api/tags/maintenance/route.test.ts`.
|
||||
- Set environment variables inline per suite when auth behavior depends on them, as in `process.env.WEBHOOK_API_KEY` usage in `src/app/api/tags/reset-projects/route.test.ts` and `src/app/api/tags/maintenance/route.test.ts`.
|
||||
- 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:**
|
||||
- Do not mock pure validation or auth utilities when they can be tested directly. `src/lib/auth.test.ts` and `src/lib/validations.tag-maintenance.test.ts` exercise real implementation logic without mocks.
|
||||
- There is no current pattern for browser, React component, or DOM mocking because no component tests are checked in.
|
||||
- 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 Helpers
|
||||
## Fixtures and Environment Handling
|
||||
|
||||
**Test Data:**
|
||||
```typescript
|
||||
function buildValidPayload(apiKey: string) {
|
||||
return {
|
||||
apiKey,
|
||||
projects: [
|
||||
{
|
||||
projectSlug: "project-one",
|
||||
selectedTagSlugsByCategory: {
|
||||
FIXED_PROJECT_TYPE: ["agent-tooling"],
|
||||
TECH_STACK: ["typescript"],
|
||||
AI_PARADIGM: ["ai-agents"],
|
||||
PRODUCT_FORM: ["web-application"],
|
||||
DOMAIN_SCENARIO: ["code-dev"],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
const baseProjectInput = {
|
||||
name: "Agent Park",
|
||||
@@ -167,16 +121,24 @@ const baseProjectInput = {
|
||||
links: [{ type: "GITHUB" as const, url: "https://github.com/example/repo" }],
|
||||
};
|
||||
```
|
||||
|
||||
**Location:**
|
||||
- Builders and fixtures are defined inline in each test file. No shared `test-utils`, factory module, or fixture directory was detected under `src` or repository root.
|
||||
- Reuse small local helpers such as `buildRequest`, `buildValidPayload`, `createTxMock`, and `baseProjectInput` instead of creating cross-suite abstractions.
|
||||
```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 coverage upload or report configuration was detected.
|
||||
- No CI workflow, coverage upload, or Playwright setup was detected under `.github/` or repository root.
|
||||
|
||||
**View Coverage:**
|
||||
```bash
|
||||
@@ -186,23 +148,26 @@ Not configured
|
||||
## Test Types
|
||||
|
||||
**Unit Tests:**
|
||||
- Pure utility tests cover security and schema logic in `src/lib/auth.test.ts`, `src/lib/validations.test.ts`, and `src/lib/validations.tag-maintenance.test.ts`.
|
||||
- Service-level unit tests cover tag maintenance merge behavior in `src/app/api/tags/maintenance/service.test.ts`.
|
||||
- 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:**
|
||||
- Route handler tests call Next.js App Router handlers directly with mocked dependencies in `src/app/api/tags/route.test.ts`, `src/app/api/tags/reset-projects/route.test.ts`, and `src/app/api/tags/maintenance/route.test.ts`.
|
||||
- These are closer to isolated handler tests than full integration tests because Prisma and cache modules are mocked.
|
||||
- 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.
|
||||
|
||||
**Integration Tests:**
|
||||
- Not detected. No test currently exercises a real Prisma client, live database, or full Next.js server.
|
||||
**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 in the repository. No `e2e/` directory and no `playwright.config.*` file were found on 2026-04-18.
|
||||
- Repository guidance mentions `pnpm test:e2e`, but `package.json` does not define that script. Treat E2E support as undocumented or not yet checked in.
|
||||
- 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 Testing:**
|
||||
**Async Route Testing:**
|
||||
```typescript
|
||||
const response = await POST(buildRequest(payload));
|
||||
const json = await response.json();
|
||||
@@ -210,11 +175,9 @@ 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();
|
||||
|
||||
@@ -223,45 +186,28 @@ expect(json.error).toBe("Internal server error");
|
||||
expect(json.details).toContain("db unavailable");
|
||||
```
|
||||
|
||||
```typescript
|
||||
await expect(
|
||||
executeTagMaintenance(tx as Parameters<typeof executeTagMaintenance>[0], {
|
||||
updates: [{ tagId: "missing-tag", nameEn: "Missing" }],
|
||||
merges: [],
|
||||
})
|
||||
).rejects.toMatchObject({
|
||||
status: 400,
|
||||
message: "Validation error",
|
||||
});
|
||||
```
|
||||
|
||||
## Current Gaps
|
||||
|
||||
**Untested Server Modules:**
|
||||
- No tests were detected for `src/app/api/projects/route.ts`, `src/app/api/projects/[slug]/route.ts`, `src/app/api/search/ai/route.ts`, `src/app/api/signals/route.ts`, or `src/app/api/webhook/signals/route.ts`.
|
||||
- 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`, or `src/lib/github/badges.ts`.
|
||||
**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 UI:**
|
||||
- No component tests were detected for large client components such as `src/app/[locale]/projects/ProjectsResultsClient.tsx` (784 lines), `src/components/project/TagFilterPanel.tsx` (698 lines), or `src/components/signals/SignalFeedClient.tsx` (603 lines).
|
||||
- No tests were detected for route pages under `src/app/[locale]`.
|
||||
**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]`.
|
||||
|
||||
**Test Infrastructure Gaps:**
|
||||
- No shared test helpers, factories, or fixture libraries are present.
|
||||
- No browser or DOM test environment is configured.
|
||||
- No E2E harness or Playwright configuration is present.
|
||||
- No CI workflow files were detected under `.github/`.
|
||||
**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-18 with 7 test files and 24 tests passing.
|
||||
- `pnpm lint` passed on 2026-04-18 with no warnings or errors.
|
||||
- `pnpm build` passed on 2026-04-18. The build completed static generation and route analysis successfully.
|
||||
- `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 Test Output Notes:**
|
||||
- `pnpm test` prints expected stderr from the error-path test in `src/app/api/tags/route.test.ts` because `src/app/api/tags/route.ts` logs with `console.error`.
|
||||
- Vitest emitted a Vite deprecation notice about the CJS Node API during the run. This is a tooling signal, not a failing test.
|
||||
**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-18*
|
||||
*Testing analysis: 2026-04-20*
|
||||
|
||||
Reference in New Issue
Block a user