Files
sofarr/tests
Gronod 0a54d0d302
All checks were successful
Docs Check / Markdown lint (push) Successful in 51s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 1m16s
CI / Tests & coverage (push) Successful in 1m37s
CI / Security audit (push) Successful in 1m44s
Docs Check / Mermaid diagram parse check (push) Successful in 1m52s
refactor: use qBittorrent Sync API (/api/v2/sync/maindata) with fallback
- QBittorrentClient now uses the incremental Sync API instead of repeatedly
  fetching the full torrent list via /api/v2/torrents/info.
- Per-client state: lastRid, torrentMap, fallbackThisCycle.
- Handles full_update, delta updates, and torrents_removed.
- Falls back to legacy torrents/info at most once per poll cycle.
- getAllTorrents() resets fallback flags before each cycle.
- Added 9 new unit tests covering: first sync, delta merge, full_update,
  torrents_removed, fallback path, direct-legacy-after-fallback, 403 re-auth,
  completed-field computation, and fallback reset.
2026-05-19 09:33:20 +01:00
..

Testing

Stack

Layer Tool
Test runner Vitest v4
HTTP integration supertest
HTTP interception nock (intercepts at Node http layer — works with CJS require('axios'))
Coverage V8 (built-in, no Babel needed)

Running tests

# Run all tests once
npm test

# Watch mode (re-runs on file change)
npm run test:watch

# With coverage report
npm run test:coverage

# Interactive UI
npm run test:ui

Coverage output lands in coverage/ (gitignored). Open coverage/index.html for the HTML report.

Structure

tests/
├── setup.js               # Global setup: isolated DATA_DIR, SKIP_RATE_LIMIT, console suppression
├── unit/
│   ├── sanitizeError.test.js  # Secret redaction patterns (API keys, tokens, passwords)
│   ├── config.test.js         # JSON array + legacy single-instance config parsing
│   ├── requireAuth.test.js    # Auth middleware: valid/invalid/tampered cookies
│   ├── verifyCsrf.test.js     # CSRF double-submit cookie pattern + timing-safe compare
│   ├── qbittorrent.test.js    # Pure utils: formatBytes, formatEta, mapTorrentToDownload
│   └── tokenStore.test.js     # JSON file token store: store/get/clear, TTL expiry
└── integration/
    ├── health.test.js         # GET /health and /ready endpoints
    └── auth.test.js           # Full login/logout/me/csrf flows via supertest + nock

Key design decisions

  • server/app.js — Express factory extracted from server/index.js. Tests import createApp() without triggering the log-file setup, process.exit() calls, or background poller in the entry point.
  • nock over vi.mock('axios') — Vitest's vi.mock only intercepts ESM import statements. Since auth.js uses CJS require('axios'), nock (which patches Node's http/https modules) is the correct tool for intercepting outbound requests.
  • SKIP_RATE_LIMIT=1 — All supertest requests originate from 127.0.0.1, which would quickly exhaust per-IP rate-limit windows. Setting this env var raises the limits to Number.MAX_SAFE_INTEGER in both the API limiter and the login limiter.
  • Isolated DATA_DIR — Each test worker gets a unique temp directory so tokenStore.js file I/O never conflicts with a running dev server.
  • createApp({ skipRateLimits: true }) — The app factory accepts an option to disable the general API rate limiter in addition to the env var for the login-specific limiter.

Coverage targets

The tested files meet these per-file minimums (enforced in CI):

File Lines Branches
server/app.js 85% 65%
server/routes/auth.js 85% 70%
server/middleware/requireAuth.js 75% 80%
server/utils/sanitizeError.js 60%
server/utils/config.js 50% 55%

dashboard.js and poller.js are large files requiring complex external-service mocks (Sonarr, Radarr, qBittorrent, Emby) and are tracked as future test coverage work.