"""Shared fixtures for the test suite.""" import argparse import zipfile from pathlib import Path from typing import Generator import pytest from httpx import ASGITransport, AsyncClient import main def _reset_state() -> None: """Reset all global state to defaults.""" main.file_mapping.clear() main.indexers.clear() main.expected_password = None @pytest.fixture(autouse=True) def reset_globals() -> Generator[None, None, None]: """Reset global state before and after each test.""" _reset_state() yield _reset_state() @pytest.fixture def sample_files(tmp_path: Path) -> dict[str, Path]: """Create a directory with sample files for testing. Returns: Dict mapping logical names to file paths. """ subdir = tmp_path / "subdir" subdir.mkdir() files: dict[str, Path] = {} files["root_file"] = tmp_path / "root.txt" files["root_file"].write_text("root content") files["sub_file"] = subdir / "nested.txt" files["sub_file"].write_text("nested content") files["binary_file"] = tmp_path / "data.bin" files["binary_file"].write_bytes(b"\x00\x01\x02\x03") files["image_file"] = tmp_path / "photo.jpg" files["image_file"].write_bytes(b"\xff\xd8\xff\xe0fake_jpeg_data") return files @pytest.fixture def sample_zip(tmp_path: Path) -> dict[str, Path]: """Create a ZIP file with sample files for testing. Returns: Dict mapping logical names to file paths inside the zip. """ zip_path = tmp_path / "test_archive.zip" inner_files: dict[str, Path] = {} with zipfile.ZipFile(zip_path, "w") as zf: zf.writestr("top.txt", "top level content") inner_files["top"] = Path("top.txt") zf.writestr("folder/deep.txt", "deep content") inner_files["deep"] = Path("folder/deep.txt") zf.writestr("folder/image.png", b"\x89PNG fake png") inner_files["image"] = Path("folder/image.png") return inner_files @pytest.fixture def args_directory(sample_files: dict[str, Path], tmp_path: Path) -> argparse.Namespace: """Argparse namespace pointing at the sample directory.""" return argparse.Namespace( source=str(tmp_path), host="127.0.0.1", port=0, salt="test-salt", password=None, ) @pytest.fixture def args_zip(sample_zip: dict[str, Path], tmp_path: Path) -> argparse.Namespace: """Argparse namespace pointing at the sample zip.""" return argparse.Namespace( source=str(tmp_path / "test_archive.zip"), host="127.0.0.1", port=0, salt="test-salt", password=None, ) @pytest.fixture def initialized_dir(args_directory: argparse.Namespace) -> None: """Initialize the server with sample directory files (no auth).""" main.initialize_server(args_directory) main.set_auth_password(None) @pytest.fixture def initialized_zip(args_zip: argparse.Namespace) -> None: """Initialize the server with sample zip files (no auth).""" main.initialize_server(args_zip) main.set_auth_password(None) def _dummy_auth_header() -> str: """Create a dummy Basic Auth header (any creds work when no password is set).""" import base64 creds = "test:test" return f"Basic {base64.b64encode(creds.encode()).decode()}" @pytest.fixture async def client_dir(initialized_dir: None) -> Generator[AsyncClient, None, None]: """Async HTTP client against the app initialized with directory files. Sends dummy auth headers since HTTPBasic() always requires them. """ transport = ASGITransport(app=main.app) async with AsyncClient(transport=transport, base_url="http://test") as ac: ac.headers["Authorization"] = _dummy_auth_header() yield ac @pytest.fixture async def client_zip(initialized_zip: None) -> Generator[AsyncClient, None, None]: """Async HTTP client against the app initialized with zip files. Sends dummy auth headers since HTTPBasic() always requires them. """ transport = ASGITransport(app=main.app) async with AsyncClient(transport=transport, base_url="http://test") as ac: ac.headers["Authorization"] = _dummy_auth_header() yield ac