2025-11-08 18:20:25 +01:00
|
|
|
import pytest
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
import json
|
|
|
|
|
from retoors.main import create_app
|
|
|
|
|
from retoors.services.config_service import ConfigService
|
2025-11-08 22:05:07 +01:00
|
|
|
from pytest_mock import MockerFixture # Import MockerFixture
|
|
|
|
|
import datetime # Import datetime
|
2025-11-09 02:14:26 +01:00
|
|
|
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."""
|
2025-11-09 08:14:14 +01:00
|
|
|
user_service = UserService(temp_users_json) # Create a UserService instance
|
|
|
|
|
return FileService(temp_user_files_dir, user_service) # Pass the UserService instance
|
|
|
|
|
|
2025-11-08 18:20:25 +01:00
|
|
|
|
|
|
|
|
|
2025-11-08 22:05:07 +01:00
|
|
|
|
2025-11-09 02:14:26 +01:00
|
|
|
@pytest.fixture
|
2025-11-09 08:14:14 +01:00
|
|
|
def create_test_app(mocker, temp_user_files_dir, temp_users_json):
|
2025-11-09 02:14:26 +01:00
|
|
|
"""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)
|
|
|
|
|
|
2025-11-09 08:14:14 +01:00
|
|
|
# Use a real UserService with a temporary users.json for the app
|
|
|
|
|
app["user_service"] = UserService(temp_users_json)
|
|
|
|
|
|
2025-11-09 02:14:26 +01:00
|
|
|
# Mock scheduler
|
|
|
|
|
mock_scheduler = mocker.MagicMock()
|
|
|
|
|
mock_scheduler.spawn = mocker.AsyncMock()
|
|
|
|
|
mock_scheduler.close = mocker.AsyncMock()
|
|
|
|
|
|
2025-11-09 08:14:14 +01:00
|
|
|
app["file_service"] = FileService(temp_user_files_dir, app["user_service"])
|
2025-11-09 02:14:26 +01:00
|
|
|
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
|
|
|
|
|
|
2025-11-08 22:05:07 +01:00
|
|
|
|
2025-11-09 08:14:14 +01:00
|
|
|
|
2025-11-08 22:05:07 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
async def client(
|
2025-11-09 08:14:14 +01:00
|
|
|
aiohttp_client, mocker: MockerFixture, create_test_app
|
2025-11-08 22:05:07 +01:00
|
|
|
):
|
2025-11-09 08:14:14 +01:00
|
|
|
app = create_test_app # Use create_test_app for consistent test environment
|
2025-11-08 19:39:25 +01:00
|
|
|
|
|
|
|
|
# Directly set app["scheduler"] to a mock object
|
|
|
|
|
mock_scheduler_instance = mocker.MagicMock()
|
|
|
|
|
mock_scheduler_instance.spawn = mocker.AsyncMock()
|
2025-11-08 22:05:07 +01:00
|
|
|
mock_scheduler_instance.close = mocker.AsyncMock() # Ensure close is awaitable
|
2025-11-08 19:39:25 +01:00
|
|
|
app["scheduler"] = mock_scheduler_instance
|
2025-11-08 18:20:25 +01:00
|
|
|
|
2025-11-09 08:14:14 +01:00
|
|
|
# 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.
|
2025-11-08 22:05:07 +01:00
|
|
|
|
|
|
|
|
client = await aiohttp_client(app)
|
|
|
|
|
|
2025-11-09 08:14:14 +01:00
|
|
|
# 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.
|
2025-11-08 18:20:25 +01:00
|
|
|
|
2025-11-08 22:05:07 +01:00
|
|
|
try:
|
|
|
|
|
yield client
|
|
|
|
|
finally:
|
2025-11-09 08:14:14 +01:00
|
|
|
# Clean up temporary files created by create_test_app if necessary,
|
|
|
|
|
# but tmp_path usually handles this.
|
|
|
|
|
pass
|
2025-11-08 19:39:25 +01:00
|
|
|
|
|
|
|
|
|
2025-11-09 02:14:26 +01:00
|
|
|
@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"]
|
|
|
|
|
|
2025-11-09 08:14:14 +01:00
|
|
|
# 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.
|
2025-11-09 02:14:26 +01:00
|
|
|
|
|
|
|
|
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"]
|
|
|
|
|
|
2025-11-09 08:14:14 +01:00
|
|
|
# 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.
|
2025-11-09 02:14:26 +01:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2025-11-08 19:39:25 +01:00
|
|
|
@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")
|