From c693c6572b59edf6da53e3217a17341c4f263478 Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 27 Jan 2026 18:10:16 +0100 Subject: [PATCH] Fix NFO batch endpoint route priority and test fixture --- TESTING_SUMMARY.md | 105 +-- docs/instructions.md | 1029 ++++++++++-------------------- src/server/api/nfo.py | 381 +++++------ tests/api/test_nfo_endpoints.py | 3 - tests/unit/test_database_init.py | 3 +- 5 files changed, 597 insertions(+), 924 deletions(-) diff --git a/TESTING_SUMMARY.md b/TESTING_SUMMARY.md index b09faaa..3266985 100644 --- a/TESTING_SUMMARY.md +++ b/TESTING_SUMMARY.md @@ -19,50 +19,55 @@ ## ๐ŸŽฏ Tasks Completed (11/11) ### Phase 1: Critical Production Components (P0) + Target: 90%+ coverage -| Task | File | Tests | Coverage | Status | -|------|------|-------|----------|--------| -| Task 1 | test_security_middleware.py | 48 | 92.86% | โœ… | -| Task 2 | test_notification_service.py | 50 | 93.98% | โœ… | -| Task 3 | test_database_service.py | 20 | 88.78% | โœ… | -| **Phase 1 Total** | | **118** | **91.88%** | โœ… | +| Task | File | Tests | Coverage | Status | +| ----------------- | ---------------------------- | ------- | ---------- | ------ | +| Task 1 | test_security_middleware.py | 48 | 92.86% | โœ… | +| Task 2 | test_notification_service.py | 50 | 93.98% | โœ… | +| Task 3 | test_database_service.py | 20 | 88.78% | โœ… | +| **Phase 1 Total** | | **118** | **91.88%** | โœ… | ### Phase 2: Core Features (P1) + Target: 85%+ coverage -| Task | File | Tests | Coverage | Status | -|------|------|-------|----------|--------| -| Task 4 | test_initialization_service.py | 46 | 96.96% | โœ… | -| Task 5 | test_nfo_service.py | 73 | 96.97% | โœ… | -| Task 6 | test_page_controller.py | 37 | 95.00% | โœ… | -| **Phase 2 Total** | | **156** | **96.31%** | โœ… | +| Task | File | Tests | Coverage | Status | +| ----------------- | ------------------------------ | ------- | ---------- | ------ | +| Task 4 | test_initialization_service.py | 46 | 96.96% | โœ… | +| Task 5 | test_nfo_service.py | 73 | 96.97% | โœ… | +| Task 6 | test_page_controller.py | 37 | 95.00% | โœ… | +| **Phase 2 Total** | | **156** | **96.31%** | โœ… | ### Phase 3: Performance & Optimization (P2) + Target: 80%+ coverage -| Task | File | Tests | Coverage | Status | -|------|------|-------|----------|--------| -| Task 7 | test_background_loader_service.py | 46 | 82.00% | โœ… | -| Task 8 | test_cache_service.py | 66 | 80.06% | โœ… | -| **Phase 3 Total** | | **112** | **81.03%** | โœ… | +| Task | File | Tests | Coverage | Status | +| ----------------- | --------------------------------- | ------- | ---------- | ------ | +| Task 7 | test_background_loader_service.py | 46 | 82.00% | โœ… | +| Task 8 | test_cache_service.py | 66 | 80.06% | โœ… | +| **Phase 3 Total** | | **112** | **81.03%** | โœ… | ### Phase 4: Observability & Monitoring (P3) + Target: 80-85%+ coverage -| Task | File | Tests | Coverage | Status | -|------|------|-------|----------|--------| -| Task 9 | test_error_tracking.py | 39 | 100.00% | โœ… | -| Task 10 | test_settings_validation.py | 69 | 100.00% | โœ… | -| **Phase 4 Total** | | **108** | **100.00%** | โœ… | +| Task | File | Tests | Coverage | Status | +| ----------------- | --------------------------- | ------- | ----------- | ------ | +| Task 9 | test_error_tracking.py | 39 | 100.00% | โœ… | +| Task 10 | test_settings_validation.py | 69 | 100.00% | โœ… | +| **Phase 4 Total** | | **108** | **100.00%** | โœ… | ### Phase 5: End-to-End Workflows (P1) + Target: 75%+ coverage -| Task | File | Tests | Coverage | Status | -|------|------|-------|----------|--------| -| Task 11 | test_end_to_end_workflows.py | 41 | 77.00% | โœ… | -| **Phase 5 Total** | | **41** | **77.00%** | โœ… | +| Task | File | Tests | Coverage | Status | +| ----------------- | ---------------------------- | ------ | ---------- | ------ | +| Task 11 | test_end_to_end_workflows.py | 41 | 77.00% | โœ… | +| **Phase 5 Total** | | **41** | **77.00%** | โœ… | --- @@ -70,14 +75,14 @@ Target: 75%+ coverage ### Coverage Targets vs Actual -| Phase | Target | Actual | Difference | Status | -|-------|--------|--------|------------|--------| -| Phase 1 (P0) | 90%+ | 91.88% | +1.88% | โœ… EXCEEDED | -| Phase 2 (P1) | 85%+ | 96.31% | +11.31% | โœ… EXCEEDED | -| Phase 3 (P2) | 80%+ | 81.03% | +1.03% | โœ… EXCEEDED | -| Phase 4 (P3) | 80-85%+ | 100.00% | +15-20% | โœ… EXCEEDED | -| Phase 5 (P1) | 75%+ | 77.00% | +2.00% | โœ… EXCEEDED | -| **Overall** | **85%+** | **91.24%** | **+6.24%** | โœ… **EXCEEDED** | +| Phase | Target | Actual | Difference | Status | +| ------------ | -------- | ---------- | ---------- | --------------- | +| Phase 1 (P0) | 90%+ | 91.88% | +1.88% | โœ… EXCEEDED | +| Phase 2 (P1) | 85%+ | 96.31% | +11.31% | โœ… EXCEEDED | +| Phase 3 (P2) | 80%+ | 81.03% | +1.03% | โœ… EXCEEDED | +| Phase 4 (P3) | 80-85%+ | 100.00% | +15-20% | โœ… EXCEEDED | +| Phase 5 (P1) | 75%+ | 77.00% | +2.00% | โœ… EXCEEDED | +| **Overall** | **85%+** | **91.24%** | **+6.24%** | โœ… **EXCEEDED** | ### Phase-by-Phase Breakdown @@ -94,6 +99,7 @@ Phase 5: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 77.00% ## ๐Ÿงช Test Categories ### Unit Tests (494 tests) + - **Security Middleware**: JWT auth, token validation, master password - **Notification Service**: Email/Discord, templates, error handling - **Database Connection**: Pooling, sessions, transactions @@ -106,14 +112,15 @@ Phase 5: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 77.00% - **Settings Validation**: Config validation, env parsing, defaults ### Integration Tests (41 tests) + - **End-to-End Workflows**: Complete system workflows - - Initialization and setup flows - - Library scanning and episode discovery - - NFO creation and TMDB integration - - Download queue management - - Error recovery and retry logic - - Progress reporting integration - - Module structure validation + - Initialization and setup flows + - Library scanning and episode discovery + - NFO creation and TMDB integration + - Download queue management + - Error recovery and retry logic + - Progress reporting integration + - Module structure validation --- @@ -131,6 +138,7 @@ Phase 5: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 77.00% ## ๐Ÿ“ Test Quality Metrics ### Code Quality + - โœ… All tests follow PEP8 standards - โœ… Clear test names and docstrings - โœ… Proper arrange-act-assert pattern @@ -138,12 +146,14 @@ Phase 5: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 77.00% - โœ… Edge cases and error scenarios covered ### Coverage Quality + - โœ… Statement coverage: 91.24% average - โœ… Branch coverage: Included in all tests - โœ… Error path coverage: Comprehensive - โœ… Edge case coverage: Extensive ### Maintainability + - โœ… Tests are independent and isolated - โœ… Fixtures properly defined in conftest.py - โœ… Clear test organization by component @@ -154,16 +164,19 @@ Phase 5: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 77.00% ## ๐Ÿš€ Running the Tests ### Run All Tests + ```bash pytest tests/ -v ``` ### Run with Coverage + ```bash pytest tests/ --cov --cov-report=html ``` ### Run Specific Task Tests + ```bash # Run Task 8-11 tests (created in this session) pytest tests/unit/test_cache_service.py -v @@ -173,6 +186,7 @@ pytest tests/integration/test_end_to_end_workflows.py -v ``` ### View Coverage Report + ```bash open htmlcov/index.html ``` @@ -182,6 +196,7 @@ open htmlcov/index.html ## ๐Ÿ“ฆ Deliverables ### Test Files Created + 1. โœ… `tests/unit/test_security_middleware.py` (48 tests) 2. โœ… `tests/unit/test_notification_service.py` (50 tests) 3. โœ… `tests/unit/test_database_service.py` (20 tests) @@ -195,10 +210,12 @@ open htmlcov/index.html 11. โœ… `tests/integration/test_end_to_end_workflows.py` (41 tests) ### Documentation Updates + - โœ… `docs/instructions.md` - Comprehensive task documentation - โœ… `TESTING_SUMMARY.md` - This file ### Git Commits + - โœ… 14 commits documenting all work - โœ… Clear commit messages for each task - โœ… Proper commit history for traceability @@ -208,16 +225,19 @@ open htmlcov/index.html ## ๐ŸŽ‰ Key Achievements ### Coverage Excellence + - ๐Ÿ† **All phases exceeded target coverage** - ๐Ÿ† **Phase 4 achieved 100% coverage** (both tasks) - ๐Ÿ† **Overall 91.24% coverage** (6.24% above minimum target) ### Test Quantity + - ๐Ÿ† **581 comprehensive tests** - ๐Ÿ† **100% passing rate** - ๐Ÿ† **215 tests created in final session** (Tasks 8-11) ### Quality Standards + - ๐Ÿ† **Production-ready test suite** - ๐Ÿ† **Proper async test patterns** - ๐Ÿ† **Comprehensive mocking strategies** @@ -228,18 +248,21 @@ open htmlcov/index.html ## ๐Ÿ“‹ Next Steps ### Maintenance + - Monitor test execution time and optimize if needed - Add tests for new features as they're developed - Keep dependencies updated (pytest, pytest-asyncio, etc.) - Review and update fixtures as codebase evolves ### Continuous Integration + - Integrate tests into CI/CD pipeline - Set up automated coverage reporting - Configure test failure notifications - Enable parallel test execution for speed ### Monitoring + - Track test coverage trends over time - Identify and test newly uncovered code paths - Review and address any flaky tests diff --git a/docs/instructions.md b/docs/instructions.md index e062d28..9e6ca4b 100644 --- a/docs/instructions.md +++ b/docs/instructions.md @@ -119,728 +119,367 @@ For each task completed: ## TODO List: -### Phase 1: Critical Security & Infrastructure Tests (P0) +### ๐Ÿ”ด TIER 1: Critical Priority (Security & Data Integrity) -#### Task 1: Implement Security Middleware Tests โœ… +#### Test Infrastructure Fixes -**Priority**: P0 | **Effort**: Medium | **Coverage Target**: 90%+ | **Status**: COMPLETE +- [ ] **Fix authenticated_client + mock_download_service fixture conflict** in tests/conftest.py + - Refactor fixture dependency chain to prevent conflicts + - Enable 34 currently failing tests in tests/api/test_download_endpoints.py + - Verify all downstream tests pass after fix + - Target: 100% of previously failing download endpoint tests passing -**Objective**: Test all security middleware components to ensure security headers and rate limiting work correctly. +- [ ] **Fix authenticated_client auth issues** in tests/api/test_config_endpoints.py + - Resolve dependency override timing issues + - Enable 18 currently failing configuration endpoint tests + - Verify authentication state properly propagates + - Target: 100% of config endpoint tests passing -**Files to Test**: +- [ ] **Fix rate limiting state bleeding** in tests/security/test_auth.py + - Implement proper rate limit reset in fixtures + - Fix 1 failing test with trio backend + - Ensure rate limiting state isolated between tests + - Target: All auth security tests passing -- [src/server/middleware/security.py](src/server/middleware/security.py) - `SecurityHeadersMiddleware`, `CSPMiddleware`, `XSSProtectionMiddleware` -- [src/server/middleware/error_handler.py](src/server/middleware/error_handler.py) - Error handling -- [src/server/middleware/auth.py](src/server/middleware/auth.py) - `AuthMiddleware` rate limiting +#### Scheduler System Tests (0% Coverage) -**What Was Tested**: +- [ ] **Create tests/api/test_scheduler_endpoints.py** - Scheduler API endpoint tests + - Test GET /api/scheduler/config (retrieve current configuration) + - Test POST /api/scheduler/config (update scheduler settings) + - Test POST /api/scheduler/trigger-rescan (manual trigger) + - Test scheduler enable/disable functionality + - Test interval configuration validation (minimum/maximum values) + - Test unauthorized access rejection (authentication required) + - Test invalid configuration rejection (validation errors) + - Target: 80%+ coverage of src/server/api/scheduler_api.py -1. Security headers correctly added (HSTS, X-Frame-Options, CSP, Referrer-Policy, X-Content-Type-Options) โœ… -2. CSP policy directives properly formatted โœ… -3. XSS protection escaping works correctly โœ… -4. Rate limiting tracks requests per IP and enforces limits โœ… -5. Rate limit cleanup removes old history to prevent memory leaks โœ… -6. Middleware order doesn't cause conflicts โœ… -7. Error responses include security headers โœ… -8. Request sanitization blocks SQL injection and XSS attacks โœ… -9. Content type and request size validation โœ… -10. Origin-based rate limiting for CORS requests โœ… +- [ ] **Create tests/unit/test_scheduler_service.py** - Scheduler service logic tests + - Test scheduled library rescan execution + - Test scheduler state persistence across restarts + - Test background task execution and lifecycle + - Test scheduler conflict resolution (manual vs automated scans) + - Test error handling during scheduled operations + - Target: 80%+ coverage of scheduler service logic -**Results**: +- [ ] **Create tests/integration/test_scheduler_workflow.py** - End-to-end scheduler tests + - Test scheduler trigger โ†’ library rescan โ†’ database update workflow + - Test scheduler configuration changes apply immediately + - Test scheduler persistence after application restart + - Test concurrent manual and automated scan handling + - Target: Full workflow validation -- **Test File**: `tests/unit/test_security_middleware.py` -- **Tests Created**: 48 comprehensive tests -- **Coverage Achieved**: 95% total (security.py: 97%, auth.py: 92%) -- **Target**: 90%+ โœ… **EXCEEDED** -- **All Tests Passing**: โœ… +#### NFO Batch Operations Tests (Currently Skipped) -**Bug Fixes**: +- [ ] **Fix NFO batch creation dependency override** in tests/api/test_nfo_endpoints.py + - Fix TestNFOBatchCreateEndpoint tests (currently skipped) + - Resolve dependency override timing with authenticated_client + - Test POST /api/nfo/batch/create endpoint with multiple series + - Test max_concurrent parameter enforcement + - Target: All batch endpoint tests passing -- Fixed `MutableHeaders.pop()` AttributeError in security.py (lines 100-101) - changed to use `del` with try/except +- [ ] **Create tests/unit/test_nfo_batch_operations.py** - NFO batch logic tests + - Test concurrent NFO creation with max_concurrent limits + - Test batch operation error handling (partial failures) + - Test batch operation progress tracking + - Test batch operation cancellation + - Target: 80%+ coverage of batch operation logic in src/core/services/nfo_service.py -**Notes**: +- [ ] **Create tests/integration/test_nfo_batch_workflow.py** - Batch NFO workflow tests + - Test creating NFO files for 10+ series simultaneously + - Test media file download (poster, logo, fanart) in batch + - Test TMDB API rate limiting during batch operations + - Test batch operation status updates via WebSocket + - Target: Full batch workflow validation -- Documented current limitation where '/' in PUBLIC_PATHS causes all paths to match as public -- Rate limiting functionality thoroughly tested including cleanup and per-IP tracking -- All security header configurations tested with various options -- CSP tested in both enforcement and report-only modes +#### Download Queue Tests (2/36 Passing) + +- [ ] **Fix download queue fixture issues** enabling 34 failing tests + - Fix mock_download_service fixture conflicts + - Test GET /api/queue endpoint (retrieve current queue) + - Test POST /api/queue/start endpoint (manual start) + - Test POST /api/queue/stop endpoint (manual stop) + - Test DELETE /api/queue/clear-completed endpoint + - Test DELETE /api/queue/clear-failed endpoint + - Test POST /api/queue/retry endpoint (retry failed downloads) + - Target: 90%+ of download queue endpoint tests passing + +- [ ] **Create tests/unit/test_queue_operations.py** - Queue logic tests + - Test FIFO queue ordering validation + - Test single download mode enforcement + - Test queue statistics accuracy (pending/active/completed/failed counts) + - Test queue reordering functionality + - Test concurrent queue modifications (race condition prevention) + - Target: 80%+ coverage of queue management logic + +- [ ] **Create tests/integration/test_queue_persistence.py** - Queue persistence tests + - Test queue state persists after application restart + - Test download progress restoration after restart + - Test failed download state recovery + - Test completed download history persistence + - Target: Full persistence workflow validation + +#### NFO Auto-Create Integration Tests + +- [ ] **Create tests/integration/test_nfo_download_workflow.py** - NFO auto-create during download + - Test NFO file created automatically before episode download + - Test media files (poster/logo/fanart) downloaded before episode + - Test NFO creation failure handling (download continues/aborts based on config) + - Test NFO auto-create configuration toggle (enable/disable) + - Test NFO update during library scan (configuration option) + - Test integration between download_service and nfo_service + - Target: 100% of NFO auto-create workflow scenarios covered + +- [ ] **Create tests/unit/test_nfo_auto_create.py** - NFO auto-create logic tests + - Test NFO file existence check before creation + - Test NFO file path resolution + - Test media file existence checks + - Test configuration-based behavior (auto-create on/off) + - Target: 80%+ coverage of auto-create logic + +### ๐ŸŸก TIER 2: High Priority (Core UX Features) + +#### Dark Mode Tests + +- [ ] **Set up JavaScript testing framework** (Jest/Vitest + Playwright) + - Install and configure Vitest for unit tests + - Install and configure Playwright for E2E tests + - Create test script commands in package.json + - Set up CI integration for JavaScript tests + - Target: Working test infrastructure for frontend code + +- [ ] **Create tests/frontend/test_darkmode.js** - Dark mode toggle tests + - Test dark mode toggle button click event + - Test theme class applied to document root + - Test theme persistence in localStorage + - Test theme loaded from localStorage on page load + - Test theme switching animation/transitions + - Test theme affects all UI components (buttons, cards, modals) + - Target: 80%+ coverage of src/server/web/static/js/darkmode.js + +#### Setup Page Tests + +- [ ] **Create tests/frontend/e2e/test_setup_page.spec.js** - Setup page E2E tests + - Test form validation (required fields, password strength) + - Test password strength indicator updates in real-time + - Test form submission with valid data + - Test form submission with invalid data (error messages) + - Test setup completion redirects to main application + - Test all configuration sections (general, security, directories, scheduler, logging, backup, NFO) + - Target: 100% of setup page user flows covered + +- [ ] **Create tests/api/test_setup_endpoints.py** - Setup API tests (if not existing) + - Test POST /api/setup endpoint (initial configuration) + - Test setup page access when already configured (redirect) + - Test configuration validation during setup + - Test setup completion state persists + - Target: 80%+ coverage of setup endpoint logic + +#### Settings Modal Tests + +- [ ] **Create tests/frontend/e2e/test_settings_modal.spec.js** - Settings modal E2E tests + - Test settings modal opens/closes correctly + - Test all configuration fields editable + - Test configuration changes saved with feedback + - Test configuration validation prevents invalid settings + - Test backup creation from modal + - Test backup restoration from modal + - Test export/import configuration + - Test browse directory functionality + - Target: 100% of settings modal user flows covered + +- [ ] **Create tests/integration/test_config_backup_restore.py** - Configuration backup/restore tests + - Test backup creation with timestamp + - Test backup restoration with validation + - Test backup list retrieval + - Test backup deletion + - Test configuration export format (JSON) + - Test configuration import validation + - Target: 100% of backup/restore workflows covered + +#### WebSocket Reconnection Tests + +- [ ] **Create tests/frontend/test_websocket_reconnection.js** - WebSocket client tests + - Test WebSocket connection established on page load + - Test WebSocket authentication with JWT token + - Test WebSocket reconnection after connection loss + - Test WebSocket connection retry with exponential backoff + - Test WebSocket error handling (connection refused, timeout) + - Test WebSocket message parsing and dispatch + - Target: 80%+ coverage of src/server/web/static/js/websocket.js + +- [ ] **Create tests/integration/test_websocket_resilience.py** - WebSocket resilience tests + - Test multiple concurrent WebSocket clients (stress test 100+ clients) + - Test WebSocket connection recovery after server restart + - Test WebSocket authentication token refresh + - Test WebSocket message ordering guarantees + - Test WebSocket broadcast filtering (specific clients) + - Target: Full resilience scenario coverage + +#### Queue UI Tests + +- [ ] **Create tests/frontend/test_queue_ui.js** - Queue management UI tests + - Test start/stop button click handlers + - Test clear completed button functionality + - Test clear failed button functionality + - Test retry failed button functionality + - Test queue item display updates in real-time + - Test queue statistics display (pending/active/completed/failed counts) + - Target: 80%+ coverage of src/server/web/static/js/queue/ modules + +- [ ] **Create tests/frontend/e2e/test_queue_interactions.spec.js** - Queue E2E tests + - Test adding items to download queue from library page + - Test starting download manually + - Test stopping download manually + - Test queue reordering (if implemented) + - Test bulk operations (clear all, retry all) + - Test queue state persists across page refreshes + - Target: 100% of queue user interaction flows covered + +### ๐ŸŸข TIER 3: Medium Priority (Edge Cases & Performance) + +#### TMDB Integration Tests + +- [ ] **Create tests/unit/test_tmdb_rate_limiting.py** - TMDB rate limiting tests + - Test TMDB API rate limit detection (429 response) + - Test exponential backoff retry logic + - Test TMDB API quota exhaustion handling + - Test TMDB API error response parsing + - Test TMDB API timeout handling + - Target: 80%+ coverage of rate limiting logic in src/core/providers/tmdb_client.py + +- [ ] **Create tests/integration/test_tmdb_resilience.py** - TMDB API resilience tests + - Test TMDB API unavailable (503 error) + - Test TMDB API partial data response + - Test TMDB API invalid response format + - Test TMDB API network timeout + - Test fallback behavior when TMDB unavailable + - Target: Full error handling coverage + +#### Performance Tests + +- [ ] **Create tests/performance/test_large_library.py** - Large library scanning performance + - Test library scan with 1000+ series + - Test scan completion time benchmarks (< 5 minutes for 1000 series) + - Test memory usage during large scans (< 500MB) + - Test database query performance during scan + - Test concurrent scan operation handling + - Target: Performance baselines established for large libraries + +- [ ] **Create tests/performance/test_nfo_batch_performance.py** - Batch NFO performance tests + - Test concurrent NFO creation (10, 50, 100 series) + - Test TMDB API request batching optimization + - Test media file download concurrency + - Test memory usage during batch operations + - Target: Performance baselines for batch operations + +- [ ] **Create tests/performance/test_websocket_load.py** - WebSocket performance tests + - Test WebSocket broadcast to 100+ concurrent clients + - Test message throughput (messages per second) + - Test connection pool limits + - Test progress update throttling (avoid flooding) + - Target: Performance baselines for WebSocket broadcasting + +#### Edge Case Tests + +- [ ] **Create tests/unit/test_concurrent_scans.py** - Concurrent scan operation tests + - Test multiple simultaneous scan requests handled gracefully + - Test scan cancellation/interruption handling + - Test database race condition prevention during scans + - Test scan state consistency with concurrent requests + - Target: 100% of concurrent operation scenarios covered + +- [ ] **Create tests/unit/test_download_retry.py** - Download retry logic tests + - Test automatic retry after download failure + - Test retry attempt count tracking + - Test exponential backoff between retries + - Test maximum retry limit enforcement + - Test retry state persistence + - Target: 80%+ coverage of retry logic in download service + +- [ ] **Create tests/integration/test_series_parsing_edge_cases.py** - Series parsing edge cases + - Test series folder names with year variations (e.g., "Series (2020)", "Series [2020]") + - Test series names with special characters + - Test series names with multiple spaces + - Test series names in different languages (Unicode) + - Test malformed folder structures + - Target: 100% of parsing edge cases covered + +### ๐Ÿ”ต TIER 4: Low Priority (Polish & Future Features) + +#### Internationalization Tests + +- [ ] **Create tests/unit/test_i18n.py** - Internationalization tests + - Test language file loading (src/server/web/static/i18n/) + - Test language switching functionality + - Test translation placeholder replacement + - Test fallback to English for missing translations + - Test all UI strings translatable + - Target: 80%+ coverage of i18n implementation + +#### Accessibility Tests + +- [ ] **Create tests/frontend/e2e/test_accessibility.spec.js** - Accessibility tests + - Test keyboard navigation (Tab, Enter, Escape) + - Test screen reader compatibility (ARIA labels) + - Test focus management (modals, dropdowns) + - Test color contrast ratios (WCAG AA compliance) + - Test responsive design breakpoints (mobile, tablet, desktop) + - Target: WCAG 2.1 AA compliance + +#### User Preferences Tests + +- [ ] **Create tests/unit/test_user_preferences.py** - User preferences tests + - Test preferences saved to localStorage + - Test preferences loaded on page load + - Test preferences synced across tabs (BroadcastChannel) + - Test preferences reset to defaults + - Target: 80%+ coverage of preferences logic + +#### Media Server Compatibility Tests + +- [ ] **Create tests/integration/test_media_server_compatibility.py** - NFO format compatibility tests + - Test Kodi NFO parsing (manual validation with Kodi) + - Test Plex NFO parsing (manual validation with Plex) + - Test Jellyfin NFO parsing (manual validation with Jellyfin) + - Test Emby NFO parsing (manual validation with Emby) + - Test NFO XML schema validation + - Target: Compatibility verified with all major media servers --- -#### Task 2: Implement Notification Service Tests โœ… +### ๐Ÿ“Š Test Coverage Goals -**Priority**: P0 | **Effort**: Large | **Coverage Target**: 85%+ | **Status**: COMPLETE +**Current Coverage:** 36% overall -**Objective**: Comprehensively test email sending, webhook delivery, and in-app notifications. +- NFO Service: 16% (Critical - needs improvement) +- TMDB Client: 30% (Critical - needs improvement) +- Scheduler: 0% (Critical - needs tests) +- Download Queue API: 6% (2/36 tests passing) +- Configuration API: 0% (0/18 tests passing) -**Files to Test**: +**Target Coverage:** -- [src/server/services/notification_service.py](src/server/services/notification_service.py) - `EmailService`, `WebhookService`, `NotificationService`, `InAppNotificationStore` - -**What Was Tested**: - -1. Email sending via SMTP with credentials validation โœ… -2. Email template rendering (plain text and HTML) โœ… -3. Webhook payload creation and delivery โœ… -4. HTTP retries with exponential backoff โœ… -5. In-app notification storage and retrieval โœ… -6. Notification history pagination and filtering โœ… -7. Multi-channel dispatch (email + webhook + in-app) โœ… -8. Error handling and logging for failed notifications โœ… -9. Notification preferences (quiet hours, priority filtering) โœ… -10. Notification deduplication and limits โœ… - -**Results**: - -- **Test File**: `tests/unit/test_notification_service.py` -- **Tests Created**: 50 comprehensive tests (47 passed, 3 skipped) -- **Coverage Achieved**: 90% -- **Target**: 85%+ โœ… **EXCEEDED** -- **All Required Tests Passing**: โœ… - -**Test Coverage by Component**: - -- `EmailNotificationService`: Initialization, SMTP sending, error handling -- `WebhookNotificationService`: HTTP requests, retries, exponential backoff, timeout handling -- `InAppNotificationService`: Add, retrieve, mark as read, clear notifications, max limits -- `NotificationService`: Preferences, quiet hours, priority filtering, multi-channel dispatch -- Helper functions: Notification type-specific helpers (download complete, failed, queue complete, system error) - -**Notes**: - -- 3 tests skipped if aiosmtplib not installed (optional dependency) -- Comprehensive testing of retry logic with exponential backoff (2^attempt) -- Quiet hours tested including midnight-spanning periods -- Critical notifications bypass quiet hours as expected -- All notification channels tested independently and together +- **Overall:** 80%+ +- **Critical Services (Scheduler, NFO, Download):** 80%+ +- **High Priority (Config, WebSocket):** 70%+ +- **Medium Priority (Edge cases, Performance):** 60%+ +- **Frontend JavaScript:** 70%+ --- -#### Task 3: Implement Database Transaction Tests โœ… +### ๐Ÿ”„ Test Execution Priority Order -**Priority**: P0 | **Effort**: Large | **Coverage Target**: 90%+ | **Status**: COMPLETE +**Week 1 - Infrastructure & Critical:** -**Objective**: Ensure database transactions handle rollback, nesting, and error recovery correctly. +1. Fix test fixture conflicts (52 tests enabled) +2. Create scheduler endpoint tests (0% โ†’ 80%) +3. Enable NFO batch tests and add unit tests +4. Fix download queue tests (6% โ†’ 90%) -**Files to Test**: +**Week 2 - Integration & UX:** 5. Add NFO auto-create integration tests 6. Set up JavaScript test framework 7. Add dark mode and WebSocket reconnection tests 8. Add setup page and settings modal E2E tests -- [src/server/database/transaction.py](src/server/database/transaction.py) - `TransactionContext`, `AsyncTransactionContext`, `SavepointContext`, `AsyncSavepointContext` +**Week 3 - Performance & Edge Cases:** 9. Add large library performance tests 10. Add TMDB rate limiting tests 11. Add concurrent operation tests 12. Add download retry logic tests -**What Was Tested**: - -1. Basic transaction commit and rollback (sync and async) โœ… -2. Nested transactions using savepoints โœ… -3. Async transaction context manager โœ… -4. Savepoint creation and rollback โœ… -5. Error during transaction rolls back all changes โœ… -6. @transactional decorator for sync and async functions โœ… -7. Transaction propagation modes (REQUIRED, REQUIRES_NEW, NESTED) โœ… -8. atomic() and atomic_sync() context managers โœ… -9. Explicit commit/rollback within transactions โœ… -10. Transaction logging and error handling โœ… - -**Results**: - -- **Test File**: `tests/unit/test_transaction.py` -- **Tests Created**: 66 comprehensive tests -- **Coverage Achieved**: 90% (213/226 statements, 48/64 branches) -- **Target**: 90%+ โœ… **MET EXACTLY** -- **All Tests Passing**: โœ… - -**Test Coverage by Component**: - -- `TransactionPropagation`: Enum values and members -- `TransactionContext`: Enter/exit, commit/rollback, savepoints, multiple nesting -- `SavepointContext`: Rollback, idempotency, commit behavior -- `AsyncTransactionContext`: All async equivalents of sync tests -- `AsyncSavepointContext`: Async savepoint operations -- `atomic()`: REQUIRED, NESTED propagation, commit/rollback -- `atomic_sync()`: Sync context manager operations -- `@transactional`: Decorator on async/sync functions, propagation, error handling -- `_extract_session()`: Session extraction from kwargs/args -- Utility functions: `is_in_transaction()`, `get_transaction_depth()` -- Complex scenarios: Nested transactions, partial rollback, multiple operations - -**Notes**: - -- Comprehensive testing of both synchronous and asynchronous transaction contexts -- Transaction propagation modes thoroughly tested with different scenarios -- Savepoint functionality validated including automatic naming and explicit rollback -- Decorator tested with various parameter configurations -- All error paths tested to ensure proper rollback behavior -- Fixed file name discrepancy: actual file is `transaction.py` (not `transactions.py`) +**Week 4+ - Polish:** 13. Add i18n tests 14. Add accessibility tests 15. Add user preferences tests 16. Add media server compatibility tests --- - -**Test File**: `tests/unit/test_database_transactions.py` - ---- - -### Phase 2: Core Service & Initialization Tests (P1) - -#### Task 4: Implement Initialization Service Tests โœ… - -**Priority**: P1 | **Effort**: Large | **Coverage Target**: 85%+ | **Status**: COMPLETE - -**Objective**: Test complete application startup orchestration and configuration loading. - -**Files to Test**: - -- [src/server/services/initialization_service.py](src/server/services/initialization_service.py) - Initialization orchestration - -**What Was Tested**: - -1. Generic scan status checking and marking functions โœ… -2. Initial scan status checking and completion marking โœ… -3. Anime folder syncing with series database โœ… -4. Series loading into memory cache โœ… -5. Anime directory validation โœ… -6. Complete initial setup orchestration โœ… -7. NFO scan status, configuration, and execution โœ… -8. Media scan status and execution โœ… -9. Error handling and recovery (OSError, RuntimeError, ValueError) โœ… -10. Full initialization sequences with progress tracking โœ… - -**Results**: - -- **Test File**: `tests/unit/test_initialization_service.py` -- **Tests Created**: 46 comprehensive tests -- **Coverage Achieved**: 96.65% (135/137 statements, 38/42 branches) -- **Target**: 85%+ โœ… **SIGNIFICANTLY EXCEEDED** -- **All Tests Passing**: โœ… - -**Test Coverage by Component**: - -- `_check_scan_status()`: Generic status checking with error handling -- `_mark_scan_completed()`: Generic completion marking with error handling -- Initial scan: Status checking, marking, and validation -- `_sync_anime_folders()`: With/without progress service -- `_load_series_into_memory()`: With/without progress service -- `_validate_anime_directory()`: Configuration validation -- `perform_initial_setup()`: Full orchestration, error handling, idempotency -- NFO scan: Configuration checks, execution, error handling -- `perform_nfo_scan_if_needed()`: Complete NFO scan flow with progress -- Media scan: Status, execution, completion marking -- `perform_media_scan_if_needed()`: Complete media scan flow -- Integration tests: Full sequences, partial recovery, idempotency - -**Notes**: - -- All initialization phases tested (initial setup, NFO scan, media scan) -- Progress service integration tested thoroughly -- Error handling validated for all scan types -- Idempotency verified - repeated calls don't re-execute completed scans -- Partial initialization recovery tested -- Configuration validation prevents execution when directory not set -- NFO scan configuration checks (API key, feature flags) -- All patches correctly target imported functions - ---- - -#### Task 5: Implement Series NFO Management Tests โœ… - -**Priority**: P1 | **Effort**: Large | **Coverage Target**: 80%+ | **Status**: COMPLETE - -**Objective**: Test NFO metadata creation, updates, and media file downloads. - -**Files to Test**: - -- [src/core/services/nfo_service.py](src/core/services/nfo_service.py) - NFO processing - -**What Was Tested**: - -1. NFO file creation from TMDB data โœ… -2. NFO file updates with fresh metadata โœ… -3. Media file downloads (poster, logo, fanart) โœ… -4. Concurrent NFO processing for multiple series โœ… -5. Error recovery if TMDB API fails โœ… -6. Year extraction from series names โœ… -7. TMDB-to-NFO model conversion โœ… -8. FSK rating extraction from German content ratings โœ… -9. NFO ID parsing (TMDB, TVDB, IMDb) โœ… -10. Edge cases (empty data, malformed XML, missing fields) โœ… - -**Results**: - -- **Test File**: `tests/unit/test_nfo_service.py` -- **Tests Created**: 73 comprehensive tests -- **Coverage Achieved**: 90.65% (202/222 statements, 79/88 branches) -- **Target**: 80%+ โœ… **SIGNIFICANTLY EXCEEDED** -- **All Tests Passing**: โœ… - -**Test Coverage by Component**: - -- FSK rating extraction with German content ratings mapping -- Year extraction from series names with various formats -- TMDB-to-NFO model conversion with all fields -- NFO creation from TMDB search and details -- NFO updates with fresh data and optional media refresh -- Media file downloads (poster, logo, fanart) with size configuration -- NFO ID parsing (uniqueid elements and fallback elements) -- Error handling for API failures, missing data, invalid XML -- Configuration options (image sizes, auto-create) -- Concurrent operations and cleanup - -**Notes**: - -- Comprehensive testing of TMDB integration with mocked API client -- All media download paths tested (poster, logo, fanart) -- FSK rating extraction handles multiple German rating formats -- Year extraction from series names works with parentheses format -- NFO model conversion preserves all metadata from TMDB -- Concurrent operations tested to ensure no conflicts -- Edge cases covered for robustness - ---- - -#### Task 6: Implement Page Controller Tests โœ… - -**Priority**: P1 | **Effort**: Medium | **Coverage Target**: 85%+ | **Status**: COMPLETE - -**Objective**: Test page rendering, routing, and error handling. - -**Files to Test**: - -- [src/server/controllers/page_controller.py](src/server/controllers/page_controller.py) - Page endpoints -- [src/server/utils/template_helpers.py](src/server/utils/template_helpers.py) - Template utilities - -**What Was Tested**: - -1. Root endpoint (/) rendering index.html โœ… -2. Setup endpoint (/setup) rendering setup.html โœ… -3. Login endpoint (/login) rendering login.html โœ… -4. Queue endpoint (/queue) rendering queue.html โœ… -5. Loading endpoint (/loading) rendering loading.html โœ… -6. Template context generation with base context โœ… -7. Series context preparation and sorting โœ… -8. Template validation and availability checking โœ… -9. Series lookup by key โœ… -10. Filter series by missing episodes โœ… - -**Results**: - -- **Test File**: `tests/unit/test_page_controller.py` -- **Tests Created**: 37 comprehensive tests -- **Page Controller Coverage**: 100% (19/19 statements) -- **Template Helpers Coverage**: 98.28% (42/42 statements, 15/16 branches) -- **Target**: 85%+ โœ… **SIGNIFICANTLY EXCEEDED** -- **All Tests Passing**: โœ… - -**Test Coverage by Component**: - -- All 5 page endpoints tested with mocked render_template -- Base context generation with request and title -- Title generation from template names -- Series context preparation with sorting options -- Series lookup and filtering by missing episodes -- Template existence validation -- Available templates listing -- Edge cases (empty data, missing fields, case sensitivity) - -**Notes**: - -- 100% coverage of page_controller.py endpoints -- 98.28% coverage of template_helpers.py utilities -- All template helper functions tested comprehensively -- Request object properly mocked for all endpoint tests -- Series data preparation validates required 'key' field -- Filtering logic correctly identifies series with missing episodes - ---- - -### Phase 3: Background Tasks & Cache Tests (P2) - -#### Task 7: Implement Background Task Tests โœ… - -**Priority**: P2 | **Effort**: Medium | **Coverage Target**: 80%+ | **Status**: COMPLETE - -**Objective**: Test background loading tasks and error recovery. - -**Files to Test**: - -- [src/server/services/background_loader_service.py](src/server/services/background_loader_service.py) - background task orchestration - -**What Was Tested**: - -1. Task queuing and worker orchestration โœ… -2. Series loading task initialization and status tracking โœ… -3. LoadingStatus enumeration values โœ… -4. Service startup with configurable workers โœ… -5. Service shutdown and graceful cleanup โœ… -6. Adding tasks to the loading queue โœ… -7. Duplicate task prevention โœ… -8. Status broadcasting via WebSocket โœ… -9. Finding series directories โœ… -10. Scanning episodes from series directories โœ… -11. NFO creation (new and existing files) โœ… -12. Checking missing data (episodes, NFO, logos, images) โœ… -13. Missing episodes scanning and sync โœ… -14. Error handling and recovery โœ… -15. Concurrent task processing โœ… -16. Task progress tracking lifecycle โœ… - -**Results**: - -- **Test File**: `tests/unit/test_background_loader_service.py` -- **Tests Created**: 46 comprehensive tests -- **Coverage Achieved**: 82% (247/300 statements, 52/80 branches) -- **Target**: 80%+ โœ… **EXCEEDED BY 2%** -- **All Tests Passing**: โœ… - -**Test Coverage by Component**: - -- SeriesLoadingTask data class initialization -- LoadingStatus enumeration and status values -- Service initialization with proper configuration -- Start/stop lifecycle with worker management -- Queue operations (add, duplicate prevention, processing) -- Missing data detection (episodes, NFO, logos, images) -- WebSocket status broadcasting with all payload types -- Directory operations (finding, scanning episodes, error handling) -- NFO loading (new creation, existing files, without NFO service) -- Episode scanning with anime service sync -- Error handling for API failures, missing data, invalid operations -- Concurrent task processing and worker limit enforcement -- Task progress tracking and status lifecycle - -**Notes**: - -- Service supports configurable number of concurrent workers (default: 5) -- Workers run indefinitely until shutdown, processing tasks from queue -- Task queuing prevents duplicates for the same series key -- WebSocket broadcasts include metadata and timestamp for frontend sync -- Error handling ensures failures in one task don't affect others -- All async operations properly tested with pytest-asyncio -- Task progress individually tracks episodes, NFO, logos, images - ---- - -#### Task 8: Implement Cache Service Tests โœ… - -**Priority**: P2 | **Effort**: Medium | **Coverage Target**: 80%+ | **Status**: COMPLETE - -**Objective**: Test caching layers and cache invalidation. - -**Files to Test**: - -- [src/server/services/cache_service.py](src/server/services/cache_service.py) - `InMemoryCacheBackend`, `RedisCacheBackend`, `CacheService` - -**What Was Tested**: - -1. In-memory cache backend operations (get, set, delete, exists, clear) โœ… -2. TTL expiration and timeout handling โœ… -3. LRU eviction when cache reaches max size โœ… -4. Pattern-based cache deletion with wildcards โœ… -5. Multiple value operations (get_many, set_many) โœ… -6. Redis backend error handling and recovery โœ… -7. Concurrent cache access with thread safety โœ… -8. CacheService high-level API (get, set, delete, exists) โœ… -9. get_or_set() pattern with sync and async factories โœ… -10. Cache key generation and hashing for long keys โœ… -11. Anime-specific caching methods (list, detail, invalidation) โœ… -12. Configuration caching and invalidation โœ… -13. Global cache service singleton and factory functions โœ… -14. Custom TTL overrides and default TTL application โœ… -15. Key prefix namespacing โœ… -16. Edge cases (None values, falsy values, overwrites) โœ… - -**Results**: - -- **Test File**: `tests/unit/test_cache_service.py` -- **Tests Created**: 66 comprehensive tests -- **Coverage Achieved**: 80.06% (221/272 statements, 40/54 branches) -- **Target**: 80%+ โœ… **EXCEEDED BY 0.06%** -- **All Tests Passing**: โœ… - -**Test Coverage by Component**: - -- InMemoryCacheBackend: All CRUD operations, TTL handling, LRU eviction -- RedisCacheBackend: Error handling for all operations, connection management -- CacheService: High-level API, key generation, get_or_set pattern -- Anime caching: List caching, detail caching, pattern invalidation -- Config caching: Configuration storage and invalidation -- Global functions: Singleton pattern, factory configuration -- Edge cases: None values, zero/false values, concurrent access -- Pattern deletion: Wildcard matching, complex patterns, exact matches - -**Notes**: - -- In-memory backend uses asyncio.Lock for thread-safe concurrent access -- TTL expiration properly removes expired items on access -- LRU eviction removes oldest item when max_size reached -- Redis backend gracefully handles connection errors -- get_or_set() supports both sync and async factory functions -- None values treated as cache miss (return default value) -- Pattern deletion uses fnmatch for wildcard matching -- Global cache service configured via factory functions - ---- - -### Phase 4: Error Tracking & Utilities (P3) - -#### Task 9: Implement Error Tracking Tests โœ… - -**Priority**: P3 | **Effort**: Medium | **Coverage Target**: 85%+ | **Status**: COMPLETE - -**Objective**: Test error tracking and observability features. - -**Files to Test**: - -- [src/server/utils/error_tracking.py](src/server/utils/error_tracking.py) - `ErrorTracker`, `RequestContextManager` - -**What Was Tested**: - -1. Error tracking and history storage with timestamps โœ… -2. Error statistics calculation (types, status codes, counts) โœ… -3. Request context management (push, pop, get current) โœ… -4. Error correlation with request IDs โœ… -5. Error history retention and size limits โœ… -6. Error history pagination and recent errors โœ… -7. Error cleanup and history clearing โœ… -8. Global singleton instances โœ… -9. Context stack LIFO operations โœ… -10. Edge cases (unique IDs, empty history, trimming) โœ… - -**Results**: - -- **Test File**: `tests/unit/test_error_tracking.py` -- **Tests Created**: 39 comprehensive tests -- **Coverage Achieved**: 100% (56/56 statements, 10/10 branches) -- **Target**: 85%+ โœ… **EXCEEDED BY 15%** -- **All Tests Passing**: โœ… - -**Test Coverage by Component**: - -- ErrorTracker: Initialization, track_error with all parameters, multiple errors -- History management: Size limits, clear history, get recent errors -- Statistics: Error types, status codes, total counts, last error -- RequestContextManager: Push/pop context, LIFO ordering, timestamps -- Context operations: Get current, empty stack handling -- Global singletons: get_error_tracker, reset_error_tracker, get_context_manager -- Edge cases: Unique IDs, history trimming, empty collections - -**Notes**: - -- 100% coverage achieved for all error tracking functionality -- Error history automatically trims to max_history_size (1000) -- Each error receives unique UUID identifier -- Request context stack follows LIFO ordering -- Global instances use singleton pattern -- All timestamps in UTC with ISO format -- Error statistics track by type and status code - ---- - ---- - -#### Task 10: Implement Settings Validation Tests โœ… - -**Priority**: P3 | **Effort**: Small | **Coverage Target**: 80%+ | **Status**: COMPLETE - -**Objective**: Test configuration settings validation and defaults. - -**Files to Test**: - -- [src/config/settings.py](src/config/settings.py) - Settings model and validation - -**What Was Tested**: - -1. Default values for all settings (JWT, passwords, timeouts, paths) โœ… -2. NFO-related settings defaults (TMDB, image downloads, ratings) โœ… -3. Environment variable parsing for all fields โœ… -4. NFO environment variables (booleans, strings) โœ… -5. CORS origins parsing (single, multiple, wildcards, empty) โœ… -6. Settings validation (type checking, error raising) โœ… -7. Global settings singleton instance โœ… -8. Extra/unknown environment variables handling โœ… -9. Edge cases (numeric strings, boolean variations, paths, URLs) โœ… -10. Security considerations (JWT uniqueness, password warnings) โœ… - -**Results**: - -- **Test File**: `tests/unit/test_settings_validation.py` -- **Tests Created**: 69 comprehensive tests -- **Coverage Achieved**: 100% (36/36 statements) -- **Target**: 80%+ โœ… **EXCEEDED BY 20%** -- **All Tests Passing**: โœ… - -**Test Coverage by Component**: - -- Default values: JWT secret generation, password salt, expiry hours, directories -- NFO defaults: Auto-create, update on scan, image downloads, FSK ratings -- Environment parsing: All 25+ settings from environment variables -- CORS origins: Single/multiple parsing, wildcard safety, empty handling -- Validation: Type errors for invalid numeric/boolean values -- Global instance: Singleton pattern, state persistence -- Edge cases: String-to-number conversion, boolean variations, path handling -- Security: Unique JWT secrets, dev password warnings - -**Notes**: - -- 100% coverage achieved for all settings functionality -- JWT secret auto-generated with unique values per instance -- CORS wildcard (\*) safely falls back to localhost origins -- Pydantic validation catches type errors early -- Settings use Field with validation_alias for environment mapping -- Extra environment variables ignored (extra="ignore") -- All boolean environment variables accept multiple formats (true/1/yes, false/0/no) -- Global settings instance accessible via `settings` import - ---- - -- Invalid configurations caught early -- Test coverage โ‰ฅ80% - -**Test File**: `tests/unit/test_settings_validation.py` - ---- - -### Phase 5: Integration Tests (P1) - -#### Task 11: Implement End-to-End Workflow Tests - -**Priority**: P1 | **Effort**: Extra Large | **Coverage Target**: 75%+ - -**Objective**: Test complete workflows from start to finish. - -**What to Test**: - -1. **Setup Flow**: Initialize app โ†’ Configure settings โ†’ Create master password โ†’ Ready -2. **Library Scan Flow**: Scan filesystem โ†’ Find missing episodes โ†’ Update database โ†’ Display in UI -3. **NFO Creation Flow**: Select series โ†’ Fetch TMDB data โ†’ Create NFO files โ†’ Download media -4. **Download Flow**: Add episode to queue โ†’ Start download โ†’ Monitor progress โ†’ Complete -5. **Error Recovery Flow**: Download fails โ†’ Retry โ†’ Success or permanently failed -6. **Multi-Series Flow**: Multiple series in library โ†’ Concurrent NFO processing โ†’ Concurrent downloads - -**Success Criteria**: - -- Full workflows complete without errors -- Database state consistent throughout -- UI reflects actual system state -- Error recovery works for all failure points -- Test coverage โ‰ฅ75% - -**Test File**: `tests/integration/test_end_to_end_workflows.py` - ---- - -## Coverage Summary - -| Phase | Priority | Tasks | Target Coverage | Status | Results | -| ------- | -------- | ------- | --------------- | ----------- | ------------------------------ | -| Phase 1 | P0 | 3 tasks | 85-90% | โœ… COMPLETE | 164 tests, 91.88% avg coverage | -| Phase 2 | P1 | 3 tasks | 80-85% | โœ… COMPLETE | 156 tests, 96.31% avg coverage | -| Phase 3 | P2 | 2 tasks | 80% | โœ… COMPLETE | 112 tests, 81.03% avg coverage | -| Phase 4 | P3 | 2 tasks | 80-85% | Not Started | 0/2 complete | -| Phase 5 | P1 | 1 task | 75% | Not Started | 0/1 complete | - -### Phases 1-3 Summary (COMPLETE) - -- **Phase 1-2 Total Tests**: 320 tests -- **Phase 1-2 Total Coverage**: 93.76% average -- **Phase 3 Tests**: 112 tests (Task 7: 46, Task 8: 66) -- **Phase 3 Coverage**: 81.03% average (Task 7: 82%, Task 8: 80.06%) -- **Total Tests (Phases 1-3)**: 432 tests -- **Overall Coverage (Phases 1-3)**: 90.20% average -- **Phase 3 Task 7 Coverage**: 82% -- **All Tests Passing**: โœ… 100% -- **Tasks**: 7/11 complete with git commits - -## Testing Guidelines for AI Agents - -When implementing these tests: - -1. **Use existing fixtures** from [tests/conftest.py](tests/conftest.py) - `db_session`, `app`, `mock_config` -2. **Mock external services** - TMDB API, SMTP, Redis, webhooks -3. **Test both happy paths and edge cases** - success, errors, timeouts, retries -4. **Verify database state** - Use `db_session` to check persisted data -5. **Test async code** - Use `pytest.mark.asyncio` and proper async test patterns -6. **Measure coverage** - Run `pytest --cov` to verify targets met -7. **Document test intent** - Use clear test names and docstrings -8. **Follow project conventions** - 80+ line limit per test method, clear arrange-act-assert pattern - -## Execution Order - -1. Start with Phase 1 (P0) - These are critical for production stability -2. Then Phase 2 (P1) - Core features depend on these -3. Then Phase 5 (P1) - End-to-end validation -4. Then Phase 3 (P2) - Performance and optimization -5. Finally Phase 4 (P3) - Observability and monitoring - -Run tests continuously: `pytest tests/ -v --cov --cov-report=html` after each task completion. - - -### Task 11: End-to-End Workflow Tests โœ… COMPLETE -**File**: `tests/integration/test_end_to_end_workflows.py` -**Target Coverage**: 75%+ -**Actual Coverage**: **77%** (137 statements, 32 missed) -**Tests Implemented**: **41 tests** -**Status**: โœ… ALL PASSING - -#### Test Classes -1. **TestInitializationWorkflow** (3 tests) - Complete initialization, NFO scan guards, media scan loader -2. **TestServiceIntegration** (3 tests) - Function exports, helpers, module imports -3. **TestWorkflowErrorHandling** (2 tests) - Scan status/mark completed error handling -4. **TestProgressReporting** (3 tests) - Progress service integration -5. **TestFunctionSignatures** (6 tests) - All function signature validation -6. **TestModuleStructure** (4 tests) - Module exports and configuration -7. **TestRealWorldScenarios** (2 tests) - Mock database and workflow sequences -8. **TestValidationFunctions** (6 tests) - Directory/NFO/scan validation -9. **TestSyncAndLoadFunctions** (2 tests) - Series loading operations -10. **TestMarkScanCompleted** (2 tests) - Scan completion marking -11. **TestInitialSetupWorkflow** (5 tests) - Setup scenarios and error handling -12. **TestNFOScanWorkflow** (4 tests) - NFO scan execution and skipping - -#### Git Commit -``` -Task 11: End-to-End Workflow Tests - 41 tests, 77% coverage -``` - ---- - -## ๐ŸŽ‰ COMPREHENSIVE TEST SUITE COMPLETE - ALL 11 TASKS - -### Final Summary - -| Phase | Task | File | Tests | Coverage | Status | -|-------|------|------|-------|----------|--------| -| **Phase 1 (P0)** | | | | | | -| | Task 1 | test_security_middleware.py | 48 | 92.86% | โœ… | -| | Task 2 | test_notification_service.py | 50 | 93.98% | โœ… | -| | Task 3 | test_database_service.py | 20 | 88.78% | โœ… | -| **Phase 2 (P1)** | | | | | | -| | Task 4 | test_initialization_service.py | 46 | 96.96% | โœ… | -| | Task 5 | test_nfo_service.py | 73 | 96.97% | โœ… | -| | Task 6 | test_page_controller.py | 37 | 95.00% | โœ… | -| **Phase 3 (P2)** | | | | | | -| | Task 7 | test_background_loader_service.py | 46 | 82.00% | โœ… | -| | Task 8 | test_cache_service.py | 66 | 80.06% | โœ… | -| **Phase 4 (P3)** | | | | | | -| | Task 9 | test_error_tracking.py | 39 | 100.00% | โœ… | -| | Task 10 | test_settings_validation.py | 69 | 100.00% | โœ… | -| **Phase 5 (P1)** | | | | | | -| | Task 11 | test_end_to_end_workflows.py | 41 | 77.00% | โœ… | -| **TOTALS** | **11/11** | **11 files** | **535 tests** | **91.24% avg** | โœ… | - -### Coverage Breakdown by Phase -- **Phase 1**: 118 tests, 91.88% average coverage โœ… -- **Phase 2**: 156 tests, 96.31% average coverage โœ… -- **Phase 3**: 112 tests, 81.03% average coverage โœ… -- **Phase 4**: 108 tests, 100.00% average coverage โœ… -- **Phase 5**: 41 tests, 77.00% coverage โœ… - -### Key Achievements -โœ… **All 11 Tasks Completed** -โœ… **535 Total Tests Passing** (532 passed, 3 skipped) -โœ… **91.24% Average Coverage** (exceeds all targets) -โœ… **16 Git Commits** (11 tasks + 5 docs/updates) -โœ… **100% Test Success Rate** -โœ… **Production Ready Test Suite** - -### Coverage Targets vs Actual -- Phase 1 (P0): Target 90%+ โ†’ Achieved 91.88% โœ… +1.88% -- Phase 2 (P1): Target 85%+ โ†’ Achieved 96.31% โœ… +11.31% -- Phase 3 (P2): Target 80%+ โ†’ Achieved 81.03% โœ… +1.03% -- Phase 4 (P3): Target 80-85%+ โ†’ Achieved 100.00% โœ… +15-20% -- Phase 5 (P1): Target 75%+ โ†’ Achieved 77.00% โœ… +2.00% - diff --git a/src/server/api/nfo.py b/src/server/api/nfo.py index f997eef..c3485ed 100644 --- a/src/server/api/nfo.py +++ b/src/server/api/nfo.py @@ -59,6 +59,203 @@ async def get_nfo_service() -> NFOService: ) from e +# ============================================================================= +# IMPORTANT: Literal path routes must be defined BEFORE path parameter routes +# to avoid route matching conflicts. For example, /batch/create must come +# before /{serie_id}/create, otherwise "batch" is treated as a serie_id. +# ============================================================================= + + +@router.post("/batch/create", response_model=NFOBatchCreateResponse) +async def batch_create_nfo( + request: NFOBatchCreateRequest, + _auth: dict = Depends(require_auth), + series_app: SeriesApp = Depends(get_series_app), + nfo_service: NFOService = Depends(get_nfo_service) +) -> NFOBatchCreateResponse: + """Batch create NFO files for multiple series. + + Args: + request: Batch creation options + _auth: Authentication dependency + series_app: Series app dependency + nfo_service: NFO service dependency + + Returns: + NFOBatchCreateResponse with results + """ + results: List[NFOBatchResult] = [] + successful = 0 + failed = 0 + skipped = 0 + + # Get all series + series_list = series_app.list.GetList() + series_map = { + getattr(s, 'key', None): s + for s in series_list + if getattr(s, 'key', None) + } + + # Process each series + semaphore = asyncio.Semaphore(request.max_concurrent) + + async def process_serie(serie_id: str) -> NFOBatchResult: + """Process a single series.""" + async with semaphore: + try: + serie = series_map.get(serie_id) + if not serie: + return NFOBatchResult( + serie_id=serie_id, + serie_folder="", + success=False, + message="Series not found" + ) + + # Ensure folder name includes year if available + serie_folder = serie.ensure_folder_with_year() + + # Check if NFO exists + if request.skip_existing: + has_nfo = await nfo_service.check_nfo_exists(serie_folder) + if has_nfo: + return NFOBatchResult( + serie_id=serie_id, + serie_folder=serie_folder, + success=False, + message="Skipped - NFO already exists" + ) + + # Create NFO + nfo_path = await nfo_service.create_tvshow_nfo( + serie_name=serie.name or serie_folder, + serie_folder=serie_folder, + download_poster=request.download_media, + download_logo=request.download_media, + download_fanart=request.download_media + ) + + return NFOBatchResult( + serie_id=serie_id, + serie_folder=serie_folder, + success=True, + message="NFO created successfully", + nfo_path=str(nfo_path) + ) + + except Exception as e: + logger.error( + f"Error creating NFO for {serie_id}: {e}", + exc_info=True + ) + return NFOBatchResult( + serie_id=serie_id, + serie_folder=serie.folder if serie else "", + success=False, + message=f"Error: {str(e)}" + ) + + # Process all series concurrently + tasks = [process_serie(sid) for sid in request.serie_ids] + results = await asyncio.gather(*tasks) + + # Count results + for result in results: + if result.success: + successful += 1 + elif "Skipped" in result.message: + skipped += 1 + else: + failed += 1 + + return NFOBatchCreateResponse( + total=len(request.serie_ids), + successful=successful, + failed=failed, + skipped=skipped, + results=list(results) + ) + + +@router.get("/missing", response_model=NFOMissingResponse) +async def get_missing_nfo( + _auth: dict = Depends(require_auth), + series_app: SeriesApp = Depends(get_series_app), + nfo_service: NFOService = Depends(get_nfo_service) +) -> NFOMissingResponse: + """Get list of series without NFO files. + + Args: + _auth: Authentication dependency + series_app: Series app dependency + nfo_service: NFO service dependency + + Returns: + NFOMissingResponse with series list + """ + try: + series_list = series_app.list.GetList() + missing_series: List[NFOMissingSeries] = [] + + for serie in series_list: + serie_id = getattr(serie, 'key', None) + if not serie_id: + continue + + # Ensure folder name includes year if available + serie_folder = serie.ensure_folder_with_year() + has_nfo = await nfo_service.check_nfo_exists(serie_folder) + + if not has_nfo: + # Build full path and check media files + folder_path = Path(settings.anime_directory) / serie_folder + media_status = check_media_files(folder_path) + file_paths = get_media_file_paths(folder_path) + + media_files = MediaFilesStatus( + has_poster=media_status.get("poster", False), + has_logo=media_status.get("logo", False), + has_fanart=media_status.get("fanart", False), + poster_path=str(file_paths["poster"]) if file_paths.get("poster") else None, + logo_path=str(file_paths["logo"]) if file_paths.get("logo") else None, + fanart_path=str(file_paths["fanart"]) if file_paths.get("fanart") else None + ) + + has_media = ( + media_files.has_poster + or media_files.has_logo + or media_files.has_fanart + ) + + missing_series.append(NFOMissingSeries( + serie_id=serie_id, + serie_folder=serie_folder, + serie_name=serie.name or serie_folder, + has_media=has_media, + media_files=media_files + )) + + return NFOMissingResponse( + total_series=len(series_list), + missing_nfo_count=len(missing_series), + series=missing_series + ) + + except Exception as e: + logger.error(f"Error getting missing NFOs: {e}", exc_info=True) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to get missing NFOs: {str(e)}" + ) from e + + +# ============================================================================= +# Series-specific endpoints (with {serie_id} path parameter) +# These must come AFTER literal path routes like /batch/create and /missing +# ============================================================================= + + @router.get("/{serie_id}/check", response_model=NFOCheckResponse) async def check_nfo( serie_id: str, @@ -559,187 +756,3 @@ async def download_media( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to download media: {str(e)}" ) from e - - -@router.post("/batch/create", response_model=NFOBatchCreateResponse) -async def batch_create_nfo( - request: NFOBatchCreateRequest, - _auth: dict = Depends(require_auth), - series_app: SeriesApp = Depends(get_series_app), - nfo_service: NFOService = Depends(get_nfo_service) -) -> NFOBatchCreateResponse: - """Batch create NFO files for multiple series. - - Args: - request: Batch creation options - _auth: Authentication dependency - series_app: Series app dependency - nfo_service: NFO service dependency - - Returns: - NFOBatchCreateResponse with results - """ - results: List[NFOBatchResult] = [] - successful = 0 - failed = 0 - skipped = 0 - - # Get all series - series_list = series_app.list.GetList() - series_map = { - getattr(s, 'key', None): s - for s in series_list - if getattr(s, 'key', None) - } - - # Process each series - semaphore = asyncio.Semaphore(request.max_concurrent) - - async def process_serie(serie_id: str) -> NFOBatchResult: - """Process a single series.""" - async with semaphore: - try: - serie = series_map.get(serie_id) - if not serie: - return NFOBatchResult( - serie_id=serie_id, - serie_folder="", - success=False, - message="Series not found" - ) - - # Ensure folder name includes year if available - serie_folder = serie.ensure_folder_with_year() - - # Check if NFO exists - if request.skip_existing: - has_nfo = await nfo_service.check_nfo_exists(serie_folder) - if has_nfo: - return NFOBatchResult( - serie_id=serie_id, - serie_folder=serie_folder, - success=False, - message="Skipped - NFO already exists" - ) - - # Create NFO - nfo_path = await nfo_service.create_tvshow_nfo( - serie_name=serie.name or serie_folder, - serie_folder=serie_folder, - download_poster=request.download_media, - download_logo=request.download_media, - download_fanart=request.download_media - ) - - return NFOBatchResult( - serie_id=serie_id, - serie_folder=serie_folder, - success=True, - message="NFO created successfully", - nfo_path=str(nfo_path) - ) - - except Exception as e: - logger.error( - f"Error creating NFO for {serie_id}: {e}", - exc_info=True - ) - return NFOBatchResult( - serie_id=serie_id, - serie_folder=serie.folder if serie else "", - success=False, - message=f"Error: {str(e)}" - ) - - # Process all series concurrently - tasks = [process_serie(sid) for sid in request.serie_ids] - results = await asyncio.gather(*tasks) - - # Count results - for result in results: - if result.success: - successful += 1 - elif "Skipped" in result.message: - skipped += 1 - else: - failed += 1 - - return NFOBatchCreateResponse( - total=len(request.serie_ids), - successful=successful, - failed=failed, - skipped=skipped, - results=results - ) - - -@router.get("/missing", response_model=NFOMissingResponse) -async def get_missing_nfo( - _auth: dict = Depends(require_auth), - series_app: SeriesApp = Depends(get_series_app), - nfo_service: NFOService = Depends(get_nfo_service) -) -> NFOMissingResponse: - """Get list of series without NFO files. - - Args: - _auth: Authentication dependency - series_app: Series app dependency - nfo_service: NFO service dependency - - Returns: - NFOMissingResponse with series list - """ - try: - series_list = series_app.list.GetList() - missing_series: List[NFOMissingSeries] = [] - - for serie in series_list: - serie_id = getattr(serie, 'key', None) - if not serie_id: - continue - - # Ensure folder name includes year if available - serie_folder = serie.ensure_folder_with_year() - has_nfo = await nfo_service.check_nfo_exists(serie_folder) - - if not has_nfo: - # Build full path and check media files - folder_path = Path(settings.anime_directory) / serie_folder - media_status = check_media_files(folder_path) - file_paths = get_media_file_paths(folder_path) - - media_files = MediaFilesStatus( - has_poster=media_status.get("poster", False), - has_logo=media_status.get("logo", False), - has_fanart=media_status.get("fanart", False), - poster_path=str(file_paths["poster"]) if file_paths.get("poster") else None, - logo_path=str(file_paths["logo"]) if file_paths.get("logo") else None, - fanart_path=str(file_paths["fanart"]) if file_paths.get("fanart") else None - ) - - has_media = ( - media_files.has_poster - or media_files.has_logo - or media_files.has_fanart - ) - - missing_series.append(NFOMissingSeries( - serie_id=serie_id, - serie_folder=serie_folder, - serie_name=serie.name or serie_folder, - has_media=has_media, - media_files=media_files - )) - - return NFOMissingResponse( - total_series=len(series_list), - missing_nfo_count=len(missing_series), - series=missing_series - ) - - except Exception as e: - logger.error(f"Error getting missing NFOs: {e}", exc_info=True) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to get missing NFOs: {str(e)}" - ) from e diff --git a/tests/api/test_nfo_endpoints.py b/tests/api/test_nfo_endpoints.py index e90782d..57008c6 100644 --- a/tests/api/test_nfo_endpoints.py +++ b/tests/api/test_nfo_endpoints.py @@ -421,9 +421,6 @@ class TestNFOBatchCreateEndpoint: ) assert response.status_code in (401, 503) - @pytest.mark.skip( - reason="TODO: Fix dependency override timing with authenticated_client" - ) @pytest.mark.asyncio async def test_batch_create_success( self, diff --git a/tests/unit/test_database_init.py b/tests/unit/test_database_init.py index 463daa4..97dc197 100644 --- a/tests/unit/test_database_init.py +++ b/tests/unit/test_database_init.py @@ -473,11 +473,12 @@ async def test_validate_schema_with_inspection_error(): def test_schema_constants(): """Test that schema constants are properly defined.""" assert CURRENT_SCHEMA_VERSION == "1.0.0" - assert len(EXPECTED_TABLES) == 4 + assert len(EXPECTED_TABLES) == 5 assert "anime_series" in EXPECTED_TABLES assert "episodes" in EXPECTED_TABLES assert "download_queue" in EXPECTED_TABLES assert "user_sessions" in EXPECTED_TABLES + assert "system_settings" in EXPECTED_TABLES if __name__ == "__main__":