"""Shared fixtures for the volumen test suite.""" from __future__ import annotations import tempfile from collections.abc import Generator from pathlib import Path import pytest from fastapi.testclient import TestClient from volumen import reset_login_attempts from volumen.config import Config from volumen.password import hash_password as hash_pw from volumen.store import Store from volumen.users import Users @pytest.fixture(autouse=True) def _reset_login_attempts() -> Generator[None, None, None]: """Reset the global login-attempts tracker before every test.""" reset_login_attempts() yield @pytest.fixture def tmp_dir() -> Generator[Path, None, None]: """Create a temporary directory that is cleaned up after the test.""" with tempfile.TemporaryDirectory() as d: yield Path(d) @pytest.fixture def tmp_content_dir(tmp_path: Path) -> Path: """Temporary content directory with sample posts.""" content = tmp_path / "posts" content.mkdir() (content / "hello.md").write_text( '+++\ntitle = "Hello"\ntags = ["intro"]\n+++\n\nHello **world**.\n' ) (content / "draft.md").write_text('+++\ntitle = "Draft"\ndraft = true\n+++\n\nSecret.\n') return content @pytest.fixture def config(tmp_content_dir: Path) -> Config: """Test Config instance pointing at a temp content directory.""" return Config.load( path="/nonexistent/volumen.toml", overrides={"content": str(tmp_content_dir)}, ) @pytest.fixture def store(tmp_content_dir: Path) -> Store: """Test Store instance backed by a temp content directory.""" return Store(str(tmp_content_dir), default_lang="en") @pytest.fixture def users_path(tmp_path: Path) -> str: """Path for a temporary users.toml file.""" return str(tmp_path / "users.toml") @pytest.fixture def bootstrap_hash() -> str: """A pre-hashed password for bootstrap tests.""" return hash_pw("secret") @pytest.fixture def users(users_path: str, bootstrap_hash: str) -> Users: """Test Users instance with bootstrap admin user.""" return Users(path=users_path, bootstrap_hash=bootstrap_hash) @pytest.fixture def client(config: Config, store: Store) -> TestClient: """FastAPI TestClient for public API tests.""" from volumen.server import create_app app = create_app(config, store) return TestClient(app) @pytest.fixture def admin_config_dir(tmp_path: Path) -> Path: """Create a directory with config and posts for admin tests.""" posts_dir = tmp_path / "posts" posts_dir.mkdir() users_path = tmp_path / "users.toml" config_path = tmp_path / "config.toml" pw_hash = hash_pw("secret") config_path.write_text( f'content_dir = "{posts_dir}"\n' f'users_file = "{users_path}"\n\n' "[admin]\n" f'password_hash = "{pw_hash}"\n' ) return tmp_path @pytest.fixture def admin_client(admin_config_dir: Path) -> TestClient: """FastAPI TestClient for admin tests with bootstrap admin.""" from volumen.config import Config from volumen.server import create_app from volumen.store import Store config = Config.load(path=str(admin_config_dir / "config.toml")) store = Store(config.content_dir, default_lang="en") app = create_app(config, store) return TestClient(app) @pytest.fixture def sample_post_md() -> str: """Sample post in Markdown with TOML frontmatter.""" return ( '+++\ntitle = "Test Post"\nslug = "test-post"\nlang = "en"\n' 'tags = ["python", "blog"]\ndate = 2026-01-15\ndraft = false\n+++\n\n' "This is the **body** of the post.\n\nSecond paragraph." )