test: add comprehensive test suite (115 tests, Vitest + supertest + nock)
Framework:
- Vitest v4 as test runner (fast ESM/CJS support, V8 coverage built-in)
- supertest for integration tests against createApp() factory
- nock for HTTP interception (works with CJS require('axios'), unlike vi.mock)
New files:
- vitest.config.js — test config: node env, isolate, V8 coverage, per-file thresholds
- tests/setup.js — isolated DATA_DIR per worker, SKIP_RATE_LIMIT, console suppression
- tests/README.md — approach, structure, design decisions
- server/app.js — testable Express factory (extracted from index.js side-effects)
Unit tests (91 tests):
- tests/unit/sanitizeError.test.js — secret redaction: apikey, token, bearer, basic-auth URLs
- tests/unit/config.test.js — JSON array + legacy single-instance config parsing
- tests/unit/requireAuth.test.js — valid/invalid/tampered cookies, schema validation
- tests/unit/verifyCsrf.test.js — double-submit pattern, timing-safe compare, safe methods
- tests/unit/qbittorrent.test.js — formatBytes, formatEta, mapTorrentToDownload state map
- tests/unit/tokenStore.test.js — store/get/clear lifecycle, TTL expiry, atomic disk write
Integration tests (24 tests):
- tests/integration/health.test.js — /health and /ready endpoints
- tests/integration/auth.test.js — full login/logout/me/csrf flows, input validation,
cookie attributes, no token leakage, Emby mock via nock
Production code changes (minimal, no behaviour change):
- server/routes/auth.js: EMBY_URL captured at request-time (not module load) for testability
- server/routes/auth.js: loginLimiter max → Number.MAX_SAFE_INTEGER when SKIP_RATE_LIMIT set
- server/utils/sanitizeError.js: fix HEADER_PATTERN to redact full line (not just first token)
CI:
- .gitea/workflows/ci.yml: add parallel 'test' job (npm run test:coverage, artifact upload)
- package.json: add test/test:watch/test:coverage/test:ui scripts
- .gitignore: add coverage/
This commit is contained in:
45
vitest.config.js
Normal file
45
vitest.config.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
// Node environment for all tests (server-side CJS modules, no browser APIs needed)
|
||||
environment: 'node',
|
||||
// Global test helpers (describe, it, expect, vi) without per-file imports
|
||||
globals: true,
|
||||
// Run each test file in an isolated module registry so module-level state
|
||||
// (tokenStore cache, config singletons) doesn't leak between files
|
||||
isolate: true,
|
||||
// Give each file its own data directory so tokenStore file I/O doesn't collide
|
||||
setupFiles: ['./tests/setup.js'],
|
||||
// Coverage via V8 (built into Node — no babel transform needed)
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'lcov', 'html'],
|
||||
reportsDirectory: './coverage',
|
||||
// Only measure coverage on production source files
|
||||
include: ['server/**/*.js'],
|
||||
exclude: [
|
||||
'server/index.js', // entry point with side-effects (process.exit, log streams)
|
||||
'node_modules/**',
|
||||
'tests/**',
|
||||
'coverage/**'
|
||||
],
|
||||
// Per-file thresholds for the critical security/auth files we actively test.
|
||||
// Overall project thresholds are lower because dashboard.js and poller.js
|
||||
// are large files that require complex external service mocks (future work).
|
||||
thresholds: {
|
||||
lines: 25,
|
||||
functions: 12,
|
||||
branches: 12,
|
||||
statements: 25,
|
||||
perFile: false,
|
||||
// Individual file thresholds for the files we DO test
|
||||
'server/app.js': { lines: 85, functions: 80, branches: 65, statements: 85 },
|
||||
'server/routes/auth.js': { lines: 85, functions: 95, branches: 70, statements: 85 },
|
||||
'server/middleware/requireAuth.js': { lines: 75, functions: 0, branches: 80, statements: 75 },
|
||||
'server/utils/sanitizeError.js': { lines: 60, functions: 0, branches: 0, statements: 60 },
|
||||
'server/utils/config.js': { lines: 50, functions: 30, branches: 55, statements: 50 }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user