import pytest from pathlib import Path import json from retoors.main import create_app from retoors.services.config_service import ConfigService from pytest_mock import MockerFixture # Import MockerFixture import datetime # Import datetime from aiohttp.test_utils import TestClient # Import TestClient from aiohttp_session import setup as setup_session # Import setup_session from aiohttp_session.cookie_storage import EncryptedCookieStorage # Import EncryptedCookieStorage from retoors.helpers.env_manager import get_or_create_session_secret_key # Import get_or_create_session_secret_key from retoors.middlewares import user_middleware, error_middleware # Import middlewares from retoors.services.user_service import UserService # Import UserService from retoors.routes import setup_routes # Import setup_routes import aiohttp_jinja2 # Import aiohttp_jinja2 import jinja2 # Import jinja2 import aiohttp # Import aiohttp from retoors.services.file_service import FileService # Import FileService @pytest.fixture def temp_user_files_dir(tmp_path): """Fixture to create a temporary directory for user files.""" user_files_dir = tmp_path / "user_files" user_files_dir.mkdir() return user_files_dir @pytest.fixture def temp_users_json(tmp_path): """Fixture to create a temporary users.json file.""" users_json_path = tmp_path / "users.json" initial_users_data = [ { "email": "test@example.com", "full_name": "Test User", "password": "hashed_password", "storage_quota_gb": 10, "storage_used_gb": 0, "parent_email": None, "shared_items": {} }, { "email": "child@example.com", "full_name": "Child User", "email": "child@example.com", "password": "hashed_password", "storage_quota_gb": 5, "storage_used_gb": 0, "parent_email": "test@example.com", "shared_items": {} } ] with open(users_json_path, "w") as f: json.dump(initial_users_data, f) return users_json_path @pytest.fixture def file_service_instance(temp_user_files_dir, temp_users_json): """Fixture to provide a FileService instance with temporary directories.""" user_service = UserService(temp_users_json) # Create a UserService instance return FileService(temp_user_files_dir, user_service) # Pass the UserService instance @pytest.fixture def create_test_app(mocker, temp_user_files_dir, temp_users_json): """Fixture to create a test aiohttp application with mocked services.""" from aiohttp import web app = web.Application() # Setup session for the test app project_root = Path(__file__).parent.parent env_file_path = project_root / ".env" secret_key = get_or_create_session_secret_key(env_file_path) setup_session(app, EncryptedCookieStorage(secret_key.decode("utf-8"))) app.middlewares.append(error_middleware) app.middlewares.append(user_middleware) # Use a real UserService with a temporary users.json for the app app["user_service"] = UserService(temp_users_json) # Mock scheduler mock_scheduler = mocker.MagicMock() mock_scheduler.spawn = mocker.AsyncMock() mock_scheduler.close = mocker.AsyncMock() app["file_service"] = FileService(temp_user_files_dir, app["user_service"]) app["scheduler"] = mock_scheduler # Setup Jinja2 for templates base_path = Path(__file__).parent.parent / "retoors" templates_path = base_path / "templates" aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader(str(templates_path))) setup_routes(app) return app @pytest.fixture async def client( aiohttp_client, mocker: MockerFixture, create_test_app ): app = create_test_app # Use create_test_app for consistent test environment # Directly set app["scheduler"] to a mock object mock_scheduler_instance = mocker.MagicMock() mock_scheduler_instance.spawn = mocker.AsyncMock() mock_scheduler_instance.close = mocker.AsyncMock() # Ensure close is awaitable app["scheduler"] = mock_scheduler_instance # The UserService and ConfigService are now set up in create_test_app with temporary files. # No need to manually create users.json or config.json here. client = await aiohttp_client(app) # The UserService is now a real instance, so we don't need to mock its methods here. # The mock_users_db_fixture is also no longer needed in this context. try: yield client finally: # Clean up temporary files created by create_test_app if necessary, # but tmp_path usually handles this. pass @pytest.fixture async def logged_in_client(aiohttp_client, create_test_app, mocker): """Fixture to provide an aiohttp client with a logged-in user.""" app = create_test_app client = await aiohttp_client(app) user_service = app["user_service"] # The UserService is now a real instance, so we don't need to mock its methods here. # The create_user, authenticate_user, and get_user_by_email methods will interact # with the real UserService instance. await client.post( "/register", data={ "full_name": "Test User", "email": "test@example.com", "password": "password", "confirm_password": "password", }, ) await client.post( "/login", data={"email": "test@example.com", "password": "password"} ) return client @pytest.fixture async def logged_in_admin_client(aiohttp_client, create_test_app, mocker): """Fixture to provide an aiohttp client with a logged-in admin user.""" app = create_test_app client = await aiohttp_client(app) user_service = app["user_service"] # The UserService is now a real instance, so we don't need to mock its methods here. # The create_user, authenticate_user, and get_user_by_email methods will interact # with the real UserService instance. await client.post( "/register", data={ "full_name": "Admin User", "email": "admin@example.com", "password": "password", "confirm_password": "password", }, ) await client.post( "/login", data={"email": "admin@example.com", "password": "password"} ) return client @pytest.fixture def mock_send_email(mocker: MockerFixture): """ Fixture to mock the send_email function. This fixture will return the mock that was patched globally by the client fixture. """ # Access the globally patched mock return mocker.patch("retoors.helpers.email_sender.send_email")