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/
44 lines
1.1 KiB
JSON
44 lines
1.1 KiB
JSON
{
|
|
"name": "sofarr",
|
|
"version": "0.1.5",
|
|
"description": "A personal media download dashboard that shows your downloads 'so far' while you relax on the sofa waiting for your *arr services to finish",
|
|
"main": "server/index.js",
|
|
"scripts": {
|
|
"dev": "nodemon server/index.js",
|
|
"start": "node server/index.js",
|
|
"install:all": "npm install",
|
|
"test": "vitest run",
|
|
"test:watch": "vitest",
|
|
"test:coverage": "vitest run --coverage",
|
|
"test:ui": "vitest --ui",
|
|
"audit": "npm audit --audit-level=high",
|
|
"audit:fix": "npm audit fix",
|
|
"audit:critical": "npm audit --audit-level=critical"
|
|
},
|
|
"dependencies": {
|
|
"axios": "^1.6.0",
|
|
"cookie-parser": "^1.4.6",
|
|
"dotenv": "^16.3.1",
|
|
"express": "^4.18.2",
|
|
"express-rate-limit": "^7.0.0",
|
|
"helmet": "^7.0.0"
|
|
},
|
|
"devDependencies": {
|
|
"@vitest/coverage-v8": "^4.1.6",
|
|
"concurrently": "^7.6.0",
|
|
"nock": "^14.0.15",
|
|
"nodemon": "^3.1.14",
|
|
"supertest": "^7.2.2",
|
|
"vitest": "^4.1.6"
|
|
},
|
|
"keywords": [
|
|
"sabnzbd",
|
|
"sonarr",
|
|
"radarr",
|
|
"emby",
|
|
"dashboard"
|
|
],
|
|
"author": "",
|
|
"license": "MIT"
|
|
}
|