diff --git a/MIGRATION_SUMMARY.md b/MIGRATION_SUMMARY.md deleted file mode 100644 index cfe29dd..0000000 --- a/MIGRATION_SUMMARY.md +++ /dev/null @@ -1,88 +0,0 @@ -# Flask to FastAPI Migration - Completion Summary - -## โœ… Migration Status: COMPLETED - -The Flask to FastAPI migration has been successfully completed. All major functionality has been tested and verified to be working correctly. - -## ๐Ÿงช Testing Results - -### โœ… Functional Testing -- **Web Routes**: All routes return correct responses -- **HTML Pages**: All pages render correctly -- **Form Submissions**: Authentication forms work properly -- **File Uploads**: Not applicable (no file upload endpoints implemented) -- **Authentication**: Complete login/logout/token verification flow working - -### โœ… Frontend Testing -- **JavaScript Functionality**: Working correctly -- **AJAX Calls**: API endpoints respond properly to authenticated requests -- **Dynamic Content Loading**: Pages load and display content correctly -- **CSS Styling**: Styling applied correctly -- **Responsive Design**: Pages display properly - -### โœ… Integration Testing -- **Database Connectivity**: Health endpoint confirms database is operational -- **API Endpoints**: All tested endpoints return correct data formats -- **Error Handling**: Proper error responses for invalid authentication, etc. -- **Security Features**: JWT authentication working correctly - -## ๐Ÿš€ Key Features Verified - -1. **FastAPI Server**: Successfully running on port 8000 -2. **Authentication**: JWT-based login with master password (`admin123`) -3. **API Documentation**: Auto-generated OpenAPI docs available at `/docs` -4. **Health Monitoring**: Health check endpoint at `/health` -5. **Database Operations**: Database health monitoring working -6. **Error Handling**: Proper HTTP status codes and error messages - -## ๐Ÿ”ง Technical Implementation - -- **Server**: Uvicorn ASGI server -- **Authentication**: JWT tokens with configurable expiry -- **Database**: SQLite with health monitoring -- **Configuration**: Environment variables via `.env` file -- **Documentation**: Automatic OpenAPI/Swagger documentation -- **CORS**: Properly configured for web client access - -## ๐Ÿ“ Migration Notes - -### SeriesApp Integration -- Fixed missing `_initialization_count` class variable -- SeriesApp is used as the business logic layer interface -- Single instance per operation (no singleton pattern as requested) - -### Middleware Handling -- Temporarily disabled middleware due to file corruption issues -- Core functionality works without middleware -- Can be re-implemented when needed - -### Environment Configuration -- Uses `.env` file for configuration -- Database URL, JWT secrets, and directory paths configurable -- Logging configured for FastAPI application - -## โœ… Migration Checklist Summary - -All major migration tasks have been completed: - -- [x] Core application migration from Flask to FastAPI -- [x] Route conversion and testing -- [x] Authentication system implementation and testing -- [x] Template and static file serving -- [x] Database connectivity verification -- [x] API documentation generation -- [x] Health monitoring implementation -- [x] Environment configuration -- [x] End-to-end testing - -## ๐ŸŽฏ Ready for Production - -The FastAPI application is now ready for production deployment with: -- Stable authentication system -- Working API endpoints -- Health monitoring -- Auto-generated documentation -- Proper error handling -- Database connectivity - -**Migration Status: โœ… COMPLETE** \ No newline at end of file diff --git a/web_todo.md b/ServerTodo.md similarity index 90% rename from web_todo.md rename to ServerTodo.md index 04678c2..3c8a40e 100644 --- a/web_todo.md +++ b/ServerTodo.md @@ -108,6 +108,23 @@ This document contains tasks for migrating the web application from Flask to Fas - [x] Migrate request/response interceptors - [x] Update logging middleware if used +## ๐Ÿš€ Application Flow & Setup Features + +### Setup and Authentication Flow + +- [ ] Implement application setup detection middleware +- [ ] Create setup page template and route for first-time configuration +- [ ] Implement configuration file/database setup validation +- [ ] Create authentication token validation middleware +- [ ] Implement auth page template and routes for login/registration +- [ ] Create main application route with authentication dependency +- [ ] Implement setup completion tracking in configuration +- [ ] Add redirect logic for setup โ†’ auth โ†’ main application flow +- [ ] Create Pydantic models for setup and authentication requests +- [ ] Implement session management for authenticated users +- [ ] Add token refresh and expiration handling +- [ ] Create middleware to enforce application flow priorities + ## ๐Ÿงช Testing and Validation ### Functional Testing diff --git a/Test_TODO.md b/TestsTodo.md similarity index 70% rename from Test_TODO.md rename to TestsTodo.md index 0b761bd..bafc8d7 100644 --- a/Test_TODO.md +++ b/TestsTodo.md @@ -133,5 +133,48 @@ This file instructs the AI agent on how to generate tests for the AniWorld appli --- +# Test TODO + +## Application Flow & Setup Tests + +### Setup Page Tests + +- [ ] Test setup page is displayed when configuration is missing +- [ ] Test setup page form submission creates valid configuration +- [ ] Test setup page redirects to auth page after successful setup +- [ ] Test setup page validation for required fields +- [ ] Test setup page handles database connection errors gracefully +- [ ] Test setup completion flag is properly set in configuration + +### Authentication Flow Tests + +- [ ] Test auth page is displayed when authentication token is invalid +- [ ] Test auth page is displayed when authentication token is missing +- [ ] Test successful login creates valid authentication token +- [ ] Test failed login shows appropriate error messages +- [ ] Test auth page redirects to main application after successful authentication +- [ ] Test token validation middleware correctly identifies valid/invalid tokens +- [ ] Test token refresh functionality +- [ ] Test session expiration handling + +### Main Application Access Tests + +- [ ] Test index.html is served when authentication is valid +- [ ] Test unauthenticated users are redirected to auth page +- [ ] Test users without completed setup are redirected to setup page +- [ ] Test middleware enforces correct flow priority (setup โ†’ auth โ†’ main) +- [ ] Test authenticated user session persistence +- [ ] Test graceful handling of token expiration during active session + +### Integration Flow Tests + +- [ ] Test complete user journey: setup โ†’ auth โ†’ main application +- [ ] Test application behavior when setup is completed but user is not authenticated +- [ ] Test application behavior when configuration exists but is corrupted +- [ ] Test concurrent user sessions and authentication state management +- [ ] Test application restart preserves setup and authentication state appropriately + +--- + **Instruction to AI Agent:** Generate and check off each test case above as you complete it. Save all test files under `src/tests/` using the specified structure and conventions. diff --git a/features.md b/features.md index 3d65654..bd54567 100644 --- a/features.md +++ b/features.md @@ -1,95 +1,135 @@ # AniWorld Application Features ## 1. Authentication & Security -- Master password authentication (JWT-based) - - `POST /auth/login`: Login and receive JWT token - - `GET /auth/verify`: Verify JWT token validity - - `POST /auth/logout`: Logout (stateless) -- Password hashing (SHA-256 + salt) -- Configurable session timeout -- Secure environment variable management + +- Master password authentication (JWT-based) + - `POST /auth/login`: Login and receive JWT token + - `GET /auth/verify`: Verify JWT token validity + - `POST /auth/logout`: Logout (stateless) +- Password hashing (SHA-256 + salt) +- Configurable session timeout +- Secure environment variable management ## 2. Health & System Monitoring -- Health check endpoints - - `/health`: Basic health status - - `/api/health`: Load balancer health - - `/api/health/system`: System metrics (CPU, memory, disk) - - `/api/health/database`: Database connectivity - - `/api/health/dependencies`: External dependencies - - `/api/health/performance`: Performance metrics - - `/api/health/metrics`: Prometheus metrics - - `/api/health/ready`: Readiness probe (Kubernetes) + +- Health check endpoints + - `/health`: Basic health status + - `/api/health`: Load balancer health + - `/api/health/system`: System metrics (CPU, memory, disk) + - `/api/health/database`: Database connectivity + - `/api/health/dependencies`: External dependencies + - `/api/health/performance`: Performance metrics + - `/api/health/metrics`: Prometheus metrics + - `/api/health/ready`: Readiness probe (Kubernetes) ## 3. Anime & Episode Management -- Search anime - - `GET /api/anime/search`: Search anime by title (pagination) -- Get anime details - - `GET /api/anime/{anime_id}`: Anime details - - `GET /api/anime/{anime_id}/episodes`: List episodes - - `GET /api/episodes/{episode_id}`: Episode details + +- Search anime + - `GET /api/anime/search`: Search anime by title (pagination) +- Get anime details + - `GET /api/anime/{anime_id}`: Anime details + - `GET /api/anime/{anime_id}/episodes`: List episodes + - `GET /api/episodes/{episode_id}`: Episode details ## 4. Database & Storage Management -- Database info and statistics - - `GET /api/database/info`: Database stats -- Maintenance operations - - `/maintenance/database/vacuum`: Vacuum database - - `/maintenance/database/analyze`: Analyze database - - `/maintenance/database/integrity-check`: Integrity check - - `/maintenance/database/reindex`: Reindex database - - `/maintenance/database/optimize`: Optimize database - - `/maintenance/database/stats`: Get database stats + +- Database info and statistics + - `GET /api/database/info`: Database stats +- Maintenance operations + - `/maintenance/database/vacuum`: Vacuum database + - `/maintenance/database/analyze`: Analyze database + - `/maintenance/database/integrity-check`: Integrity check + - `/maintenance/database/reindex`: Reindex database + - `/maintenance/database/optimize`: Optimize database + - `/maintenance/database/stats`: Get database stats ## 5. Bulk Operations -- Bulk download, update, organize, delete, export - - `/api/bulk/download`: Start bulk download - - `/api/bulk/update`: Bulk update - - `/api/bulk/organize`: Organize series - - `/api/bulk/delete`: Delete series - - `/api/bulk/export`: Export series data + +- Bulk download, update, organize, delete, export + - `/api/bulk/download`: Start bulk download + - `/api/bulk/update`: Bulk update + - `/api/bulk/organize`: Organize series + - `/api/bulk/delete`: Delete series + - `/api/bulk/export`: Export series data ## 6. Performance Optimization -- Speed limit management - - `/api/performance/speed-limit`: Get/set download speed limit -- Cache statistics - - `/api/performance/cache/stats`: Cache stats -- Memory management - - `/api/performance/memory/stats`: Memory usage stats - - `/api/performance/memory/gc`: Force garbage collection -- Download queue management - - `/api/performance/downloads/tasks`: List download tasks - - `/api/performance/downloads/add-task`: Add download task - - `/api/performance/resume/tasks`: List resumable tasks + +- Speed limit management + - `/api/performance/speed-limit`: Get/set download speed limit +- Cache statistics + - `/api/performance/cache/stats`: Cache stats +- Memory management + - `/api/performance/memory/stats`: Memory usage stats + - `/api/performance/memory/gc`: Force garbage collection +- Download queue management + - `/api/performance/downloads/tasks`: List download tasks + - `/api/performance/downloads/add-task`: Add download task + - `/api/performance/resume/tasks`: List resumable tasks ## 7. Diagnostics & Logging -- Diagnostic report generation - - `/diagnostics/report`: Generate diagnostic report -- Error reporting and stats -- Logging configuration and log file management + +- Diagnostic report generation + - `/diagnostics/report`: Generate diagnostic report +- Error reporting and stats +- Logging configuration and log file management ## 8. Integrations -- API key management -- Webhook configuration -- Third-party API integrations + +- API key management +- Webhook configuration +- Third-party API integrations ## 9. User Preferences & UI -- Theme management (light/dark/auto) -- Language selection -- Accessibility features (screen reader, color contrast, mobile support) -- Keyboard shortcuts -- UI density and grid/list view options + +- Theme management (light/dark/auto) +- Language selection +- Accessibility features (screen reader, color contrast, mobile support) +- Keyboard shortcuts +- UI density and grid/list view options ## 10. CLI Tool -- Series scanning and management - - Search, download, rescan, display series - - Progress bar for downloads - - Retry logic for operations + +- Series scanning and management + - Search, download, rescan, display series + - Progress bar for downloads + - Retry logic for operations ## 11. Miscellaneous -- Environment configuration via `.env` -- Modular, extensible architecture (MVC, Clean Architecture) -- Automated testing (pytest, unittest) -- Centralized error handling + +- Environment configuration via `.env` +- Modular, extensible architecture (MVC, Clean Architecture) +- Automated testing (pytest, unittest) +- Centralized error handling + +## Authentication & Setup Flow + +### Application Initialization Flow + +- **Setup Page**: Display application setup page when the application is run for the first time and no configuration exists + + - Check for presence of configuration file/database setup + - Guide user through initial application configuration + - Set up database connections, initial admin user, and core settings + - Mark setup as completed in configuration + +- **Authentication Gate**: Redirect to authentication page when user token is invalid or missing + + - Validate existing authentication tokens + - Display login/registration interface for unauthenticated users + - Handle token refresh and session management + - Redirect authenticated users to main application + +- **Main Application**: Show index.html for authenticated users with valid tokens + - Display main application interface + - Provide access to all authenticated user features + - Maintain session state and handle token expiration gracefully + +### User Flow Priority + +1. Check if application setup is completed โ†’ Show setup page if not +2. Check if user is authenticated โ†’ Show auth page if not +3. Show main application (index.html) for authenticated users --- -**Note:** Each feature is implemented via modular controllers, services, and utilities. See the respective source files for detailed function/class definitions. \ No newline at end of file +**Note:** Each feature is implemented via modular controllers, services, and utilities. See the respective source files for detailed function/class definitions. diff --git a/logs/aniworld.log b/logs/aniworld.log index d381403..1398d0a 100644 --- a/logs/aniworld.log +++ b/logs/aniworld.log @@ -9801,3 +9801,97 @@ 2025-10-05 20:19:24,711 - fastapi_app - INFO - Successful authentication 2025-10-05 20:19:28,794 - fastapi_app - INFO - Searching anime with query: naruto 2025-10-05 20:23:01,973 - fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 10:04:54,367 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 10:04:54,436 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 10:04:54,436 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 10:24:44,892 - src.server.fastapi_app - WARNING - Failed login attempt from IP: 127.0.0.1 +2025-10-06 10:24:54,205 - src.server.fastapi_app - INFO - Successful authentication +2025-10-06 10:31:19,227 - src.server.fastapi_app - INFO - Successful authentication +2025-10-06 10:31:19,372 - src.server.fastapi_app - INFO - Searching anime with query: naruto +2025-10-06 10:32:29,832 - src.server.fastapi_app - INFO - Successful authentication +2025-10-06 10:32:30,006 - src.server.fastapi_app - WARNING - Invalid token: Not enough segments +2025-10-06 10:33:31,000 - src.server.fastapi_app - INFO - Successful authentication +2025-10-06 10:39:47,044 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 10:39:48,130 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 10:39:48,130 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 10:39:48,130 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 10:39:49,080 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 10:39:49,080 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 10:39:49,080 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 10:44:48,432 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 10:44:49,798 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 10:44:49,798 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 10:44:49,799 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 10:44:51,133 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 10:44:51,133 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 10:44:51,133 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 10:49:50,142 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 10:49:50,996 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 10:49:50,996 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 10:49:50,996 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 10:49:51,907 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 10:49:51,907 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 10:49:51,908 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 10:54:51,337 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 10:54:52,227 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 10:54:52,228 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 10:54:52,228 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 10:54:53,189 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 10:54:53,189 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 10:54:53,189 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 10:59:52,617 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 10:59:53,483 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 10:59:53,483 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 10:59:53,483 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 10:59:54,371 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 10:59:54,371 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 10:59:54,371 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 11:13:39,752 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 11:13:40,651 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 11:13:40,651 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 11:13:40,651 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 11:13:41,677 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 11:13:41,677 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 11:13:41,677 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 11:18:40,951 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 11:18:41,833 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 11:18:41,833 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 11:18:41,833 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 11:18:42,720 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 11:18:42,720 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 11:18:42,720 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 11:28:42,126 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 11:28:43,047 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 11:28:43,047 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 11:28:43,047 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 11:28:44,078 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 11:28:44,078 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 11:28:44,078 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 11:33:43,353 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 11:33:44,240 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 11:33:44,241 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 11:33:44,241 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 11:33:45,163 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 11:33:45,163 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 11:33:45,163 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 11:38:44,586 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 11:38:45,438 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 11:38:45,438 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 11:38:45,438 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 11:38:46,350 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 11:38:46,350 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 11:38:46,350 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 11:48:45,738 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 11:48:46,597 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 11:48:46,597 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 11:48:46,597 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 12:02:04,701 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server... +2025-10-06 12:02:05,779 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 12:02:05,779 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 12:02:05,779 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 12:02:06,954 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 12:02:06,954 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 12:02:06,954 - src.server.fastapi_app - INFO - Log level: INFO +2025-10-06 12:46:54,539 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server... +2025-10-06 12:46:54,539 - src.server.fastapi_app - INFO - Anime directory: ./downloads +2025-10-06 12:46:54,539 - src.server.fastapi_app - INFO - Log level: INFO diff --git a/src/core/SeriesApp.py b/src/core/SeriesApp.py index 66228b2..df1a506 100644 --- a/src/core/SeriesApp.py +++ b/src/core/SeriesApp.py @@ -1,6 +1,6 @@ -from src.core.SerieScanner import SerieScanner from src.core.entities.SerieList import SerieList from src.core.providers.provider_factory import Loaders +from src.core.SerieScanner import SerieScanner class SeriesApp: diff --git a/src/server/fastapi_app.py b/src/server/fastapi_app.py index ee0b2d4..74be82b 100644 --- a/src/server/fastapi_app.py +++ b/src/server/fastapi_app.py @@ -34,6 +34,10 @@ from fastapi.templating import Jinja2Templates from pydantic import BaseModel, Field from pydantic_settings import BaseSettings +# Import application flow services +from src.server.middleware.application_flow_middleware import ApplicationFlowMiddleware +from src.server.services.setup_service import SetupService + # Import our custom middleware - temporarily disabled due to file corruption # from src.server.web.middleware.fastapi_auth_middleware import AuthMiddleware # from src.server.web.middleware.fastapi_logging_middleware import ( @@ -46,7 +50,7 @@ logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ - logging.FileHandler('./logs/aniworld.log'), + logging.FileHandler('./logs/aniworld.log'), logging.StreamHandler() ] ) @@ -138,6 +142,23 @@ class ErrorResponse(BaseModel): code: Optional[str] = None details: Optional[Dict[str, Any]] = None +class SetupRequest(BaseModel): + """Setup request model.""" + password: str = Field(..., min_length=8, description="Master password (min 8 characters)") + directory: str = Field(..., min_length=1, description="Anime directory path") + +class SetupResponse(BaseModel): + """Setup response model.""" + status: str + message: str + redirect_url: Optional[str] = None + +class SetupStatusResponse(BaseModel): + """Setup status response model.""" + setup_complete: bool + requirements: Dict[str, bool] + missing_requirements: List[str] + # Authentication utilities def hash_password(password: str) -> str: """Hash password with salt using SHA-256.""" @@ -311,6 +332,10 @@ app.add_middleware( allow_headers=["*"], ) +# Add application flow middleware +setup_service = SetupService() +app.add_middleware(ApplicationFlowMiddleware, setup_service=setup_service) + # Add custom middleware - temporarily disabled # app.add_middleware(EnhancedLoggingMiddleware) # app.add_middleware(AuthMiddleware) @@ -360,6 +385,144 @@ async def legacy_download( except Exception as e: return {"status": "error", "message": f"Failed to start download: {str(e)}"} +# Setup endpoints +@app.get("/api/auth/setup/status", response_model=SetupStatusResponse, tags=["Setup"]) +async def get_setup_status() -> SetupStatusResponse: + """ + Check the current setup status of the application. + + Returns information about what setup requirements are met and which are missing. + """ + try: + setup_service = SetupService() + requirements = setup_service.get_setup_requirements() + missing = setup_service.get_missing_requirements() + + return SetupStatusResponse( + setup_complete=setup_service.is_setup_complete(), + requirements=requirements, + missing_requirements=missing + ) + except Exception as e: + logger.error(f"Error checking setup status: {e}") + return SetupStatusResponse( + setup_complete=False, + requirements={}, + missing_requirements=["Error checking setup status"] + ) + +@app.post("/api/auth/setup", response_model=SetupResponse, tags=["Setup"]) +async def process_setup(request_data: SetupRequest) -> SetupResponse: + """ + Process the initial application setup. + + - **password**: Master password (minimum 8 characters) + - **directory**: Anime directory path + """ + try: + setup_service = SetupService() + + # Check if setup is already complete + if setup_service.is_setup_complete(): + return SetupResponse( + status="error", + message="Setup has already been completed" + ) + + # Validate directory path + from pathlib import Path + directory_path = Path(request_data.directory) + if not directory_path.is_absolute(): + return SetupResponse( + status="error", + message="Please provide an absolute directory path" + ) + + # Create directory if it doesn't exist + try: + directory_path.mkdir(parents=True, exist_ok=True) + except Exception as e: + logger.error(f"Failed to create directory: {e}") + return SetupResponse( + status="error", + message=f"Failed to create directory: {str(e)}" + ) + + # Hash the password + password_hash = hash_password(request_data.password) + + # Prepare configuration updates + config_updates = { + "security": { + "master_password_hash": password_hash, + "salt": settings.password_salt, + "session_timeout_hours": settings.token_expiry_hours, + "max_failed_attempts": 5, + "lockout_duration_minutes": 30 + }, + "anime": { + "directory": str(directory_path), + "download_threads": 3, + "download_speed_limit": None, + "auto_rescan_time": "03:00", + "auto_download_after_rescan": False + }, + "logging": { + "level": "INFO", + "enable_console_logging": True, + "enable_console_progress": False, + "enable_fail2ban_logging": True, + "log_file": "aniworld.log", + "max_log_size_mb": 10, + "log_backup_count": 5 + }, + "providers": { + "default_provider": "aniworld.to", + "preferred_language": "German Dub", + "fallback_providers": ["aniworld.to"], + "provider_timeout": 30, + "retry_attempts": 3, + "provider_settings": { + "aniworld.to": { + "enabled": True, + "priority": 1, + "quality_preference": "720p" + } + } + }, + "advanced": { + "max_concurrent_downloads": 3, + "download_buffer_size": 8192, + "connection_timeout": 30, + "read_timeout": 300, + "enable_debug_mode": False, + "cache_duration_minutes": 60 + } + } + + # Mark setup as complete and save configuration + success = setup_service.mark_setup_complete(config_updates) + + if success: + logger.info("Application setup completed successfully") + return SetupResponse( + status="success", + message="Setup completed successfully", + redirect_url="/login" + ) + else: + return SetupResponse( + status="error", + message="Failed to save configuration" + ) + + except Exception as e: + logger.error(f"Setup processing error: {e}") + return SetupResponse( + status="error", + message="Setup failed due to internal error" + ) + # Authentication endpoints @app.post("/auth/login", response_model=LoginResponse, tags=["Authentication"]) async def login(request_data: LoginRequest, request: Request) -> LoginResponse: diff --git a/src/server/middleware/application_flow_middleware.py b/src/server/middleware/application_flow_middleware.py new file mode 100644 index 0000000..46312d5 --- /dev/null +++ b/src/server/middleware/application_flow_middleware.py @@ -0,0 +1,248 @@ +""" +Application Flow Middleware for FastAPI. + +This middleware enforces the application flow priorities: +1. Setup page (if setup is not complete) +2. Authentication page (if user is not authenticated) +3. Main application (for authenticated users with completed setup) + +The middleware redirects users to the appropriate page based on their current state +and the state of the application setup. +""" + +import logging +from typing import Optional + +from fastapi import Request +from fastapi.responses import RedirectResponse +from starlette.middleware.base import BaseHTTPMiddleware + +# Import the setup service +try: + from ..services.setup_service import SetupService +except ImportError: + # Handle case where service is not available + class SetupService: + def is_setup_complete(self): + return True + +logger = logging.getLogger(__name__) + + +class ApplicationFlowMiddleware(BaseHTTPMiddleware): + """ + Middleware to enforce application flow: setup โ†’ auth โ†’ main application. + + This middleware: + 1. Checks if setup is complete + 2. Validates authentication status + 3. Redirects to appropriate page based on state + 4. Allows API endpoints and static files to pass through + """ + + def __init__(self, app, setup_service: Optional[SetupService] = None): + """ + Initialize the application flow middleware. + + Args: + app: FastAPI application instance + setup_service: Setup service instance (optional, will create if not provided) + """ + super().__init__(app) + self.setup_service = setup_service or SetupService() + + # Define paths that should bypass flow enforcement + self.bypass_paths = { + "/static", # Static files + "/favicon.ico", # Browser favicon requests + "/robots.txt", # Robots.txt + "/health", # Health check endpoints + "/docs", # OpenAPI documentation + "/redoc", # ReDoc documentation + "/openapi.json" # OpenAPI spec + } + + # API paths that should bypass flow but may require auth + self.api_paths = { + "/api", + "/auth" + } + + # Pages that are part of the flow and should be accessible + self.flow_pages = { + "/setup", + "/login", + "/app" + } + + async def dispatch(self, request: Request, call_next): + """ + Process the request and enforce application flow. + + Args: + request: Incoming HTTP request + call_next: Next middleware/handler in chain + + Returns: + Response: Either a redirect response or the result of call_next + """ + try: + # Get the request path + path = request.url.path + + # Skip flow enforcement for certain paths + if self._should_bypass_flow(path): + return await call_next(request) + + # Check application setup status + setup_complete = self.setup_service.is_setup_complete() + + # Check authentication status + is_authenticated = await self._is_user_authenticated(request) + + # Determine the appropriate action + redirect_response = self._determine_redirect(path, setup_complete, is_authenticated) + + if redirect_response: + logger.info(f"Redirecting {path} to {redirect_response.headers.get('location')}") + return redirect_response + + # Continue with the request + return await call_next(request) + + except Exception as e: + logger.error(f"Error in ApplicationFlowMiddleware: {e}", exc_info=True) + # In case of error, allow the request to continue + return await call_next(request) + + def _should_bypass_flow(self, path: str) -> bool: + """ + Check if the given path should bypass flow enforcement. + + Args: + path: Request path + + Returns: + bool: True if path should bypass flow enforcement + """ + # Check exact bypass paths + for bypass_path in self.bypass_paths: + if path.startswith(bypass_path): + return True + + # API paths bypass flow enforcement (but may have their own auth) + for api_path in self.api_paths: + if path.startswith(api_path): + return True + + return False + + async def _is_user_authenticated(self, request: Request) -> bool: + """ + Check if the user is authenticated by validating JWT token. + + Args: + request: HTTP request object + + Returns: + bool: True if user is authenticated, False otherwise + """ + try: + # Check for Authorization header + auth_header = request.headers.get("authorization") + if not auth_header or not auth_header.startswith("Bearer "): + return False + + # Extract and validate token + token = auth_header.split(" ")[1] + + # Import JWT validation function (avoid circular imports) + try: + from ..fastapi_app import verify_jwt_token + payload = verify_jwt_token(token) + return payload is not None + except ImportError: + # Fallback if import fails + logger.warning("Could not import JWT verification function") + return False + + except Exception as e: + logger.error(f"Error checking authentication: {e}") + return False + + def _determine_redirect(self, path: str, setup_complete: bool, is_authenticated: bool) -> Optional[RedirectResponse]: + """ + Determine if a redirect is needed based on current state. + + Args: + path: Current request path + setup_complete: Whether application setup is complete + is_authenticated: Whether user is authenticated + + Returns: + Optional[RedirectResponse]: Redirect response if needed, None otherwise + """ + # If setup is not complete + if not setup_complete: + # Allow access to setup page + if path == "/setup": + return None + # Redirect everything else to setup + return RedirectResponse(url="/setup", status_code=302) + + # Setup is complete, check authentication + if not is_authenticated: + # Allow access to login page + if path == "/login": + return None + # Redirect unauthenticated users to login (except for specific pages) + if path in self.flow_pages or path == "/": + return RedirectResponse(url="/login", status_code=302) + + # User is authenticated and setup is complete + else: + # Redirect from setup/login pages to main app + if path in ["/setup", "/login", "/"]: + return RedirectResponse(url="/app", status_code=302) + + # No redirect needed + return None + + def get_flow_status(self, request: Request) -> dict: + """ + Get current flow status for debugging/monitoring. + + Args: + request: HTTP request object + + Returns: + dict: Current flow status information + """ + try: + setup_complete = self.setup_service.is_setup_complete() + is_authenticated = self._is_user_authenticated(request) + + return { + "setup_complete": setup_complete, + "authenticated": is_authenticated, + "path": request.url.path, + "should_bypass": self._should_bypass_flow(request.url.path) + } + except Exception as e: + return { + "error": str(e), + "path": request.url.path + } + + +def create_application_flow_middleware(setup_service: Optional[SetupService] = None) -> ApplicationFlowMiddleware: + """ + Factory function to create application flow middleware. + + Args: + setup_service: Setup service instance (optional) + + Returns: + ApplicationFlowMiddleware: Configured middleware instance + """ + return ApplicationFlowMiddleware(app=None, setup_service=setup_service) \ No newline at end of file diff --git a/src/server/services/setup_service.py b/src/server/services/setup_service.py new file mode 100644 index 0000000..8af6e33 --- /dev/null +++ b/src/server/services/setup_service.py @@ -0,0 +1,268 @@ +""" +Setup service for detecting and managing application setup state. + +This service determines if the application is properly configured and set up, +following the application flow pattern: setup โ†’ auth โ†’ main application. +""" + +import json +import sqlite3 +from datetime import datetime +from pathlib import Path +from typing import Dict, Any, Optional, List +import logging + +logger = logging.getLogger(__name__) + + +class SetupService: + """Service for managing application setup detection and configuration.""" + + def __init__(self, config_path: str = "data/config.json", db_path: str = "data/aniworld.db"): + """Initialize the setup service with configuration and database paths.""" + self.config_path = Path(config_path) + self.db_path = Path(db_path) + self._config_cache: Optional[Dict[str, Any]] = None + + def is_setup_complete(self) -> bool: + """ + Check if the application setup is complete. + + Setup is considered complete if: + 1. Configuration file exists and is valid + 2. Database exists and is accessible + 3. Master password is configured + 4. Setup completion flag is set (if present) + + Returns: + bool: True if setup is complete, False otherwise + """ + try: + # Check if configuration file exists and is valid + if not self._is_config_valid(): + logger.info("Setup incomplete: Configuration file is missing or invalid") + return False + + # Check if database exists and is accessible + if not self._is_database_accessible(): + logger.info("Setup incomplete: Database is not accessible") + return False + + # Check if master password is configured + if not self._is_master_password_configured(): + logger.info("Setup incomplete: Master password is not configured") + return False + + # Check for explicit setup completion flag + config = self.get_config() + if config and config.get("setup", {}).get("completed") is False: + logger.info("Setup incomplete: Setup completion flag is False") + return False + + logger.debug("Setup validation complete: All checks passed") + return True + + except Exception as e: + logger.error(f"Error checking setup completion: {e}") + return False + + def _is_config_valid(self) -> bool: + """Check if the configuration file exists and contains valid JSON.""" + try: + if not self.config_path.exists(): + return False + + config = self.get_config() + return config is not None and isinstance(config, dict) + + except Exception as e: + logger.error(f"Configuration validation error: {e}") + return False + + def _is_database_accessible(self) -> bool: + """Check if the database exists and is accessible.""" + try: + if not self.db_path.exists(): + return False + + # Try to connect and perform a simple query + with sqlite3.connect(str(self.db_path)) as conn: + cursor = conn.cursor() + cursor.execute("SELECT name FROM sqlite_master WHERE type='table' LIMIT 1") + return True + + except Exception as e: + logger.error(f"Database accessibility check failed: {e}") + return False + + def _is_master_password_configured(self) -> bool: + """Check if master password is properly configured.""" + try: + config = self.get_config() + if not config: + return False + + security_config = config.get("security", {}) + + # Check if password hash exists + password_hash = security_config.get("master_password_hash") + salt = security_config.get("salt") + + return bool(password_hash and salt and len(password_hash) > 0 and len(salt) > 0) + + except Exception as e: + logger.error(f"Master password configuration check failed: {e}") + return False + + def get_config(self, force_reload: bool = False) -> Optional[Dict[str, Any]]: + """ + Get the configuration data from the config file. + + Args: + force_reload: If True, reload config from file even if cached + + Returns: + dict: Configuration data or None if not accessible + """ + try: + if self._config_cache is None or force_reload: + if not self.config_path.exists(): + return None + + with open(self.config_path, 'r', encoding='utf-8') as f: + self._config_cache = json.load(f) + + return self._config_cache + + except Exception as e: + logger.error(f"Error loading configuration: {e}") + return None + + def mark_setup_complete(self, config_updates: Optional[Dict[str, Any]] = None) -> bool: + """ + Mark the setup as completed and optionally update configuration. + + Args: + config_updates: Additional configuration updates to apply + + Returns: + bool: True if successful, False otherwise + """ + try: + config = self.get_config() or {} + + # Update configuration with any provided updates + if config_updates: + config.update(config_updates) + + # Set setup completion flag + if "setup" not in config: + config["setup"] = {} + config["setup"]["completed"] = True + config["setup"]["completed_at"] = str(datetime.utcnow()) + + # Save updated configuration + return self._save_config(config) + + except Exception as e: + logger.error(f"Error marking setup as complete: {e}") + return False + + def reset_setup(self) -> bool: + """ + Reset the setup completion status (for development/testing). + + Returns: + bool: True if successful, False otherwise + """ + try: + config = self.get_config() + if not config: + return False + + # Remove or set setup completion flag to false + if "setup" in config: + config["setup"]["completed"] = False + + return self._save_config(config) + + except Exception as e: + logger.error(f"Error resetting setup: {e}") + return False + + def _save_config(self, config: Dict[str, Any]) -> bool: + """Save configuration to file.""" + try: + # Ensure directory exists + self.config_path.parent.mkdir(parents=True, exist_ok=True) + + # Save configuration + with open(self.config_path, 'w', encoding='utf-8') as f: + json.dump(config, f, indent=4, ensure_ascii=False) + + # Clear cache to force reload on next access + self._config_cache = None + + logger.info(f"Configuration saved to {self.config_path}") + return True + + except Exception as e: + logger.error(f"Error saving configuration: {e}") + return False + + def get_setup_requirements(self) -> Dict[str, bool]: + """ + Get detailed breakdown of setup requirements and their status. + + Returns: + dict: Dictionary with requirement names and their completion status + """ + config = self.get_config() + return { + "config_file_exists": self.config_path.exists(), + "config_file_valid": self._is_config_valid(), + "database_exists": self.db_path.exists(), + "database_accessible": self._is_database_accessible(), + "master_password_configured": self._is_master_password_configured(), + "setup_marked_complete": bool(config and config.get("setup", {}).get("completed", True)) + } + + def get_missing_requirements(self) -> List[str]: + """ + Get list of missing setup requirements. + + Returns: + list: List of missing requirement descriptions + """ + requirements = self.get_setup_requirements() + missing = [] + + if not requirements["config_file_exists"]: + missing.append("Configuration file is missing") + elif not requirements["config_file_valid"]: + missing.append("Configuration file is invalid or corrupted") + + if not requirements["database_exists"]: + missing.append("Database file is missing") + elif not requirements["database_accessible"]: + missing.append("Database is not accessible or corrupted") + + if not requirements["master_password_configured"]: + missing.append("Master password is not configured") + + if not requirements["setup_marked_complete"]: + missing.append("Setup process was not completed") + + return missing + + +# Convenience functions for easy import +def is_setup_complete() -> bool: + """Convenience function to check if setup is complete.""" + service = SetupService() + return service.is_setup_complete() + + +def get_setup_service() -> SetupService: + """Get a configured setup service instance.""" + return SetupService() diff --git a/src/tests/conftest.py b/src/tests/conftest.py index a4b0ddb..1dc1a30 100644 --- a/src/tests/conftest.py +++ b/src/tests/conftest.py @@ -2,11 +2,12 @@ Pytest configuration file for AniWorld application tests. """ -import pytest import os import sys from unittest.mock import Mock +import pytest + # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) @@ -79,8 +80,9 @@ def sample_episode_data(): @pytest.fixture def valid_jwt_token(): """Valid JWT token for testing.""" - import jwt from datetime import datetime, timedelta + + import jwt payload = { "user": "test_user", @@ -92,8 +94,9 @@ def valid_jwt_token(): @pytest.fixture def expired_jwt_token(): """Expired JWT token for testing.""" - import jwt from datetime import datetime, timedelta + + import jwt payload = { "user": "test_user", diff --git a/src/tests/e2e/test_auth_flow.py b/src/tests/e2e/test_auth_flow.py index 61da8b6..b9cd887 100644 --- a/src/tests/e2e/test_auth_flow.py +++ b/src/tests/e2e/test_auth_flow.py @@ -5,12 +5,13 @@ Tests complete user authentication scenarios including login/logout flow and session management. """ -import pytest -import sys import os -from fastapi.testclient import TestClient +import sys from unittest.mock import patch +import pytest +from fastapi.testclient import TestClient + # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) diff --git a/src/tests/e2e/test_bulk_operations_flow.py b/src/tests/e2e/test_bulk_operations_flow.py index d6e8f04..0175a2a 100644 --- a/src/tests/e2e/test_bulk_operations_flow.py +++ b/src/tests/e2e/test_bulk_operations_flow.py @@ -5,11 +5,12 @@ This module tests complete user workflows for bulk operations including download flows, export processes, and error handling scenarios. """ -import pytest -import time -from fastapi.testclient import TestClient -from unittest.mock import patch, AsyncMock import asyncio +import time +from unittest.mock import AsyncMock, patch + +import pytest +from fastapi.testclient import TestClient from src.server.fastapi_app import app diff --git a/src/tests/e2e/test_cli_flows.py b/src/tests/e2e/test_cli_flows.py index 722d936..1d384fb 100644 --- a/src/tests/e2e/test_cli_flows.py +++ b/src/tests/e2e/test_cli_flows.py @@ -5,11 +5,12 @@ Tests complete CLI workflows including progress bar functionality, retry logic, user interactions, and error scenarios. """ -import pytest -import sys import os -from unittest.mock import Mock, patch +import sys import tempfile +from unittest.mock import Mock, patch + +import pytest # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) diff --git a/src/tests/e2e/test_user_preferences_flow.py b/src/tests/e2e/test_user_preferences_flow.py index 0dda48e..a165f5b 100644 --- a/src/tests/e2e/test_user_preferences_flow.py +++ b/src/tests/e2e/test_user_preferences_flow.py @@ -5,11 +5,12 @@ This module tests complete user workflows for changing preferences and verifying that the UI responds appropriately to preference changes. """ -import pytest import time -from fastapi.testclient import TestClient from unittest.mock import patch +import pytest +from fastapi.testclient import TestClient + from src.server.fastapi_app import app diff --git a/src/tests/integration/test_anime_endpoints.py b/src/tests/integration/test_anime_endpoints.py index 3681cc2..702d29f 100644 --- a/src/tests/integration/test_anime_endpoints.py +++ b/src/tests/integration/test_anime_endpoints.py @@ -5,12 +5,13 @@ Tests anime search, anime details, episode retrieval with pagination, valid/invalid IDs, and search filtering functionality. """ -import pytest -import sys import os -from fastapi.testclient import TestClient +import sys from unittest.mock import patch +import pytest +from fastapi.testclient import TestClient + # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) diff --git a/src/tests/integration/test_auth_endpoints.py b/src/tests/integration/test_auth_endpoints.py index 04415a8..e6e4fed 100644 --- a/src/tests/integration/test_auth_endpoints.py +++ b/src/tests/integration/test_auth_endpoints.py @@ -5,11 +5,12 @@ Tests POST /auth/login, GET /auth/verify, POST /auth/logout endpoints with valid/invalid credentials and tokens. """ -import pytest -import sys import os +import sys +from unittest.mock import Mock, patch + +import pytest from fastapi.testclient import TestClient -from unittest.mock import patch, Mock # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) diff --git a/src/tests/integration/test_bulk_operations.py b/src/tests/integration/test_bulk_operations.py index 78da765..dc8395a 100644 --- a/src/tests/integration/test_bulk_operations.py +++ b/src/tests/integration/test_bulk_operations.py @@ -6,9 +6,10 @@ Tests include authentication, validation, and error handling. """ import json +from unittest.mock import Mock, patch + import pytest from fastapi.testclient import TestClient -from unittest.mock import patch, Mock from src.server.fastapi_app import app diff --git a/src/tests/integration/test_database_endpoints.py b/src/tests/integration/test_database_endpoints.py index 40b9af6..f1b7bb5 100644 --- a/src/tests/integration/test_database_endpoints.py +++ b/src/tests/integration/test_database_endpoints.py @@ -5,12 +5,13 @@ Tests database info, maintenance operations (vacuum, analyze, integrity-check, reindex, optimize, stats), and storage management functionality. """ -import pytest -import sys import os -from fastapi.testclient import TestClient +import sys from unittest.mock import patch +import pytest +from fastapi.testclient import TestClient + # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) diff --git a/src/tests/integration/test_diagnostics.py b/src/tests/integration/test_diagnostics.py index b8a8f81..c0fd1c0 100644 --- a/src/tests/integration/test_diagnostics.py +++ b/src/tests/integration/test_diagnostics.py @@ -4,11 +4,12 @@ Integration tests for diagnostics API endpoints. This module tests the diagnostics endpoints for error reporting and system diagnostics. """ +import os +import tempfile +from unittest.mock import Mock, patch + import pytest from fastapi.testclient import TestClient -from unittest.mock import patch, Mock -import tempfile -import os from src.server.fastapi_app import app diff --git a/src/tests/integration/test_health_endpoints.py b/src/tests/integration/test_health_endpoints.py index 0320746..8bf22eb 100644 --- a/src/tests/integration/test_health_endpoints.py +++ b/src/tests/integration/test_health_endpoints.py @@ -5,12 +5,13 @@ Tests /health, /api/health/* endpoints including system metrics, database health, dependencies, performance, and monitoring. """ -import pytest -import sys import os -from fastapi.testclient import TestClient -from unittest.mock import patch +import sys from datetime import datetime +from unittest.mock import patch + +import pytest +from fastapi.testclient import TestClient # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) diff --git a/src/tests/integration/test_integrations.py b/src/tests/integration/test_integrations.py index f63afff..8e5381a 100644 --- a/src/tests/integration/test_integrations.py +++ b/src/tests/integration/test_integrations.py @@ -5,11 +5,12 @@ This module tests the integration endpoints for managing API keys, webhook confi and third-party service integrations. """ -import pytest -from fastapi.testclient import TestClient -from unittest.mock import patch, Mock import json import uuid +from unittest.mock import Mock, patch + +import pytest +from fastapi.testclient import TestClient from src.server.fastapi_app import app diff --git a/src/tests/integration/test_misc_integration.py b/src/tests/integration/test_misc_integration.py index 5f1fbac..1b955b0 100644 --- a/src/tests/integration/test_misc_integration.py +++ b/src/tests/integration/test_misc_integration.py @@ -5,13 +5,14 @@ Tests configuration system integration, error handling pipelines, and modular architecture component interactions. """ -import pytest -import sys -import os -from unittest.mock import Mock import json +import os +import sys import tempfile from pathlib import Path +from unittest.mock import Mock + +import pytest # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) diff --git a/src/tests/integration/test_performance_optimization.py b/src/tests/integration/test_performance_optimization.py index 32f959e..843abf5 100644 --- a/src/tests/integration/test_performance_optimization.py +++ b/src/tests/integration/test_performance_optimization.py @@ -5,10 +5,11 @@ This module tests the performance-related endpoints for speed limiting, cache ma memory management, and download task handling. """ +import time +from unittest.mock import Mock, patch + import pytest from fastapi.testclient import TestClient -from unittest.mock import patch, Mock -import time from src.server.fastapi_app import app diff --git a/src/tests/integration/test_user_preferences.py b/src/tests/integration/test_user_preferences.py index a99a886..eac27a6 100644 --- a/src/tests/integration/test_user_preferences.py +++ b/src/tests/integration/test_user_preferences.py @@ -5,9 +5,10 @@ This module tests the user preferences endpoints for theme management, language accessibility settings, keyboard shortcuts, and UI density configurations. """ +from unittest.mock import patch + import pytest from fastapi.testclient import TestClient -from unittest.mock import patch from src.server.fastapi_app import app diff --git a/simple_test.py b/src/tests/simple_test.py similarity index 65% rename from simple_test.py rename to src/tests/simple_test.py index d207bf6..78d2bd6 100644 --- a/simple_test.py +++ b/src/tests/simple_test.py @@ -1,2 +1,3 @@ from src.server.web.middleware.fastapi_auth_middleware_new import AuthMiddleware -print("Success importing AuthMiddleware") \ No newline at end of file + +print("Success importing AuthMiddleware") diff --git a/test_auth.ps1 b/src/tests/test_auth.ps1 similarity index 100% rename from test_auth.ps1 rename to src/tests/test_auth.ps1 diff --git a/test_auth_flow.ps1 b/src/tests/test_auth_flow.ps1 similarity index 100% rename from test_auth_flow.ps1 rename to src/tests/test_auth_flow.ps1 diff --git a/test_database.ps1 b/src/tests/test_database.ps1 similarity index 100% rename from test_database.ps1 rename to src/tests/test_database.ps1 diff --git a/test_fastapi_import.py b/src/tests/test_fastapi_import.py similarity index 100% rename from test_fastapi_import.py rename to src/tests/test_fastapi_import.py index 24d8514..f87b920 100644 --- a/test_fastapi_import.py +++ b/src/tests/test_fastapi_import.py @@ -1,5 +1,5 @@ -import sys import os +import sys # Add parent directory to path sys.path.insert(0, os.path.abspath('.')) diff --git a/test_imports.py b/src/tests/test_imports.py similarity index 87% rename from test_imports.py rename to src/tests/test_imports.py index 5e06587..23b36df 100644 --- a/test_imports.py +++ b/src/tests/test_imports.py @@ -6,13 +6,17 @@ except Exception as e: print(f"Error importing auth middleware: {e}") try: - from src.server.web.middleware.fastapi_logging_middleware import EnhancedLoggingMiddleware + from src.server.web.middleware.fastapi_logging_middleware import ( + EnhancedLoggingMiddleware, + ) print("Logging middleware imported successfully") except Exception as e: print(f"Error importing logging middleware: {e}") try: - from src.server.web.middleware.fastapi_validation_middleware import ValidationMiddleware + from src.server.web.middleware.fastapi_validation_middleware import ( + ValidationMiddleware, + ) print("Validation middleware imported successfully") except Exception as e: print(f"Error importing validation middleware: {e}") \ No newline at end of file diff --git a/src/tests/unit/test_anime_search.py b/src/tests/unit/test_anime_search.py index dfb9841..8765b63 100644 --- a/src/tests/unit/test_anime_search.py +++ b/src/tests/unit/test_anime_search.py @@ -5,9 +5,10 @@ Tests search algorithms, filtering functions, sorting mechanisms, and data processing for anime and episode management. """ -import pytest -import sys import os +import sys + +import pytest # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) diff --git a/src/tests/unit/test_auth_security.py b/src/tests/unit/test_auth_security.py index 1ed3a7e..84e4c7e 100644 --- a/src/tests/unit/test_auth_security.py +++ b/src/tests/unit/test_auth_security.py @@ -5,23 +5,24 @@ Tests password hashing, JWT creation/validation, session timeout logic, and secure environment variable management. """ -import pytest import hashlib -import jwt -import sys import os +import sys from datetime import datetime, timedelta from unittest.mock import patch +import jwt +import pytest + # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) from src.server.fastapi_app import ( - hash_password, - verify_master_password, + Settings, generate_jwt_token, + hash_password, verify_jwt_token, - Settings + verify_master_password, ) diff --git a/src/tests/unit/test_cli_commands.py b/src/tests/unit/test_cli_commands.py index 49a5951..0c76c3f 100644 --- a/src/tests/unit/test_cli_commands.py +++ b/src/tests/unit/test_cli_commands.py @@ -5,16 +5,21 @@ Tests CLI commands (scan, search, download, rescan, display series), user input handling, and command-line interface logic. """ -import pytest -import sys import os +import sys from unittest.mock import Mock, patch +import pytest + # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) # Import after path setup -from src.cli.Main import SeriesApp, NoKeyFoundException, MatchNotFoundError # noqa: E402 +from src.cli.Main import ( # noqa: E402 + MatchNotFoundError, + NoKeyFoundException, + SeriesApp, +) @pytest.fixture @@ -409,7 +414,7 @@ class TestCLIEnvironmentVariables: # Import and run main module simulation import src.cli.Main - + # The default should be the hardcoded path default_path = "\\\\sshfs.r\\ubuntu@192.168.178.43\\media\\serien\\Serien" result = os.getenv("ANIME_DIRECTORY", default_path) diff --git a/src/tests/unit/test_database_maintenance.py b/src/tests/unit/test_database_maintenance.py index 61ec5c8..a14237e 100644 --- a/src/tests/unit/test_database_maintenance.py +++ b/src/tests/unit/test_database_maintenance.py @@ -5,11 +5,12 @@ Tests database maintenance functions, storage optimization, integrity checking, and database management utilities. """ -import pytest -import sys import os +import sys from unittest.mock import Mock +import pytest + # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) diff --git a/src/tests/unit/test_logging_functionality.py b/src/tests/unit/test_logging_functionality.py index 115b8b3..39e9925 100644 --- a/src/tests/unit/test_logging_functionality.py +++ b/src/tests/unit/test_logging_functionality.py @@ -5,12 +5,13 @@ This module tests the logging configuration, log file management, and error reporting components. """ -import pytest import logging -import tempfile import os -from unittest.mock import patch, Mock, mock_open +import tempfile from pathlib import Path +from unittest.mock import Mock, mock_open, patch + +import pytest # Import logging components try: @@ -109,7 +110,7 @@ class TestLogFileManagement: def test_log_file_rotation(self): """Test log file rotation functionality.""" from logging.handlers import RotatingFileHandler - + # Create rotating file handler handler = RotatingFileHandler( self.log_file, diff --git a/src/tests/unit/test_misc_components.py b/src/tests/unit/test_misc_components.py index d6c3f72..6616e80 100644 --- a/src/tests/unit/test_misc_components.py +++ b/src/tests/unit/test_misc_components.py @@ -5,12 +5,13 @@ Tests configuration loading, centralized error handling, module structure, and architectural component integration. """ -import pytest -import sys -import os -from unittest.mock import Mock, patch -import tempfile import json +import os +import sys +import tempfile +from unittest.mock import Mock, patch + +import pytest # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..')) @@ -131,10 +132,10 @@ class TestErrorHandling: # Import custom exceptions if they exist try: from src.core.exceptions.Exceptions import ( # noqa: F401 + MatchNotFoundError, NoKeyFoundException, - MatchNotFoundError ) - + # Test exception creation key_error = NoKeyFoundException("Key not found") assert str(key_error) == "Key not found" diff --git a/src/tests/unit/test_system_metrics.py b/src/tests/unit/test_system_metrics.py index ef989fb..1c15a36 100644 --- a/src/tests/unit/test_system_metrics.py +++ b/src/tests/unit/test_system_metrics.py @@ -5,11 +5,12 @@ Tests CPU, memory, disk usage collection, performance monitoring, and system health assessment logic. """ -import pytest -import sys import os -from unittest.mock import patch, Mock +import sys from datetime import datetime +from unittest.mock import Mock, patch + +import pytest # Add source directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))