Please use a larger screen to view this site.
Technical reference for the helldivers.bot testing infrastructure. Audience: project owner and AI assistants.
The project uses Vitest for all tests, with two separate configs:
vitest.config.mjs) — utilities, validators, API route handlers, queries, features, and shared modulesvitest.smoke.config.mjs) — HTTP-level checks against a running dev serverTests live in src/__tests__/ following conventions from the euraika/aegis projects.
graph TD
subgraph Unit["VITEST UNIT TESTS"]
UTILS["src/__tests__/unit/utils/"]
VALS["src/__tests__/unit/validators/"]
SHARED["src/__tests__/unit/shared/"]
QUERIES["src/__tests__/unit/queries/"]
ROUTES["src/__tests__/unit/routes/"]
FEATURES["src/__tests__/unit/features/"]
DB["src/__tests__/unit/db/"]
UPDATE["src/__tests__/unit/update/"]
APP["src/__tests__/unit/app/"]
end
subgraph Smoke["VITEST SMOKE TESTS"]
SMOKETEST["smoke.test.mjs<br/><small>Page loads + API checks</small>"]
end
subgraph Mocks["GLOBAL MOCKS (vitest.setup.mjs)"]
AUTH_M["BetterAuth mock<br/><small>null session default</small>"]
PRISMA_M["Prisma mock<br/><small>All model CRUD stubs</small>"]
NEXT_M["Next.js mocks<br/><small>navigation, headers, image</small>"]
end
subgraph Utils["TEST UTILITIES (@test-utils)"]
MR["createMockRequest()"]
MS["createMockSession()"]
MM["createMockModel()"]
end
Mocks --> Unit
Utils --> Unit
SMOKETEST -->|"http://localhost:3000"| DEV["Dev Server"]
style Unit fill:#0f1a0f,stroke:#22c55e,color:#4ade80
style Smoke fill:#1e293b,stroke:#3b82f6,color:#60a5fa
style Mocks fill:#1c1917,stroke:#f59e0b,color:#fbbf24
style Utils fill:#1a1a2e,stroke:#a855f7,color:#c084fc
src/__tests__/
├── unit/ # Vitest unit tests
│ ├── app/ # Tests for App Router routes (e.g. api/h1/live)
│ ├── db/ # Tests for src/db/* helpers
│ ├── features/ # Tests for src/features/* (archives, galaxy, stats, timeline, admin)
│ ├── queries/ # Tests for src/db/queries/*
│ ├── routes/ # Tests for API route handlers
│ ├── shared/ # Tests for src/shared/* (enums, hooks, utils)
│ ├── update/ # Tests for update pipeline (season, status, fetch, push)
│ ├── utils/ # Tests for src/utils/* (admin, format)
│ └── validators/ # Tests for src/validators/*
├── smoke/ # Vitest smoke tests (against running dev server)
│ └── smoke.test.mjs # Page loads + API endpoint checks
└── utils/ # Shared test utilities
└── index.mjs # Mock factories and helpers
Root-level config files:
| File | Purpose |
|---|---|
vitest.config.mjs | Vitest unit test config (env, aliases, coverage) |
vitest.setup.mjs | Global mocks (auth, Prisma, Next.js modules) |
vitest.smoke.config.mjs | Vitest smoke test config (30s timeout, node env) |
| Script | Description |
|---|---|
npm test | Run unit tests then smoke tests sequentially |
npm run test:unit | Vitest single run (unit tests only) |
npm run test:coverage | Vitest with v8 coverage report |
npm run test:e2e | Smoke tests via vitest.smoke.config.mjs |
Note: Smoke tests require a running dev server at http://localhost:3000. Never start the dev server from Claude — ask the user.
Environment: node (default). Component tests opt in to jsdom via per-file comment:
// @vitest-environment jsdom
Globals: true — describe, test, expect, vi are available without import.
Path aliases:
@ → ./src (matches jsconfig.json)@test-utils → ./src/__tests__/utilsCoverage:
src/generated/**, test files, src/__tests__/**, malformed enum filesNo global thresholds are set yet — add them once meaningful coverage exists.
The setup file runs before each test file and provides mocks for:
// Default: logged out (null session)
// Override in tests:
import { auth } from '@/auth';
vi.mocked(auth.api.getSession).mockResolvedValue(createMockSession());
All models from the schema are mocked with stub CRUD methods (findMany, findUnique, create, update, delete, etc.). The mock targets @/db/db (the singleton export).
import db from '@/db/db';
vi.mocked(db.h1_season.findMany).mockResolvedValue([{ id: 1, season: 1 }]);
next/navigation — useRouter, usePathname, useSearchParams, redirect, notFoundnext/headers — headers, cookiesnext/image — passthrough (no optimization)next/link — passthroughAll mocks are cleared via vi.clearAllMocks() in beforeEach.
Location: src/__tests__/utils/index.mjs (importable as @test-utils)
Wraps new Request() for API route handler tests. Sets content-type: application/json by default.
Returns a BetterAuth session object with user and session sub-objects (test user, 24h expiry). Merge overrides for custom scenarios.
Returns a Prisma model stub with all standard CRUD methods as vi.fn(). Useful for ad-hoc mocks beyond what vitest.setup.mjs provides.
| Type | Pattern | Location |
|---|---|---|
| Unit test | *.test.mjs | src/__tests__/unit/ |
| Smoke test | *.test.mjs | src/__tests__/smoke/ |
| Test utility | *.mjs | src/__tests__/utils/ |
App Router route handlers are tested by importing the handler directly:
vi.mock('@/db/db', () => ({ default: { $queryRaw: vi.fn() } }));
import db from '@/db/db';
import { GET } from '@/app/api/healthcheck/route';
test('returns 200 when database is reachable', async () => {
db.$queryRaw.mockResolvedValue([{ '?column?': 1 }]);
const res = await GET(new Request('http://localhost/api/healthcheck'));
expect(res.status).toBe(200);
});
For routes that depend on Prisma or auth, mock those modules first — the global mocks in vitest.setup.mjs handle the defaults.
Smoke tests use a separate Vitest config (vitest.smoke.config.mjs) — not Playwright.
src/__tests__/smoke/vitest.smoke.config.mjshttp://localhost:3000Test artifacts are excluded from version control:
/coverage # Vitest coverage reports
/test-results/ # Legacy Playwright output
/playwright-report/ # Playwright HTML report
/playwright/ # Playwright screenshots & artifacts
.playwright-mcp/ # Playwright MCP state