import sys import pytest from pathlib import Path from typing import Generator # Add backend to path sys.path.insert(0, str(Path(__file__).parent.parent)) from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, Session # NOTE: This import will fail until models are created in Task 2.1 # This is expected behavior - the models module doesn't exist yet from app.models import Base @pytest.fixture(scope="function") def db_path(tmp_path): """临时数据库路径.""" return tmp_path / "test.db" @pytest.fixture(scope="function") def db_engine(db_path): """临时数据库引擎.""" engine = create_engine(f"sqlite:///{db_path}", connect_args={"check_same_thread": False}) Base.metadata.create_all(engine) yield engine engine.dispose() @pytest.fixture(scope="function") def db_session(db_engine): """临时数据库会话.""" SessionLocal = sessionmaker(bind=db_engine, autocommit=False, autoflush=False) session = SessionLocal() yield session session.close() @pytest.fixture(scope="function") def test_encrypt_key(): """测试加密密钥.""" import base64 return base64.b64encode(b'test-key-32-bytes-long-123456789').decode() @pytest.fixture(scope="function") def test_env_vars(db_path, test_encrypt_key, monkeypatch): """设置测试环境变量.""" # Clear global settings to ensure fresh config import app.config app.config._settings = None monkeypatch.setenv("GM_ENCRYPT_KEY", test_encrypt_key) monkeypatch.setenv("GM_API_TOKEN", "test-token") monkeypatch.setenv("GM_DATA_DIR", str(db_path.parent)) return { "GM_ENCRYPT_KEY": test_encrypt_key, "GM_API_TOKEN": "test-token", } @pytest.fixture(scope="function") def client(db_session: Session, test_env_vars: dict, monkeypatch): """ FastAPI test client fixture. Provides a test client for the FastAPI application with: - In-memory database session - Test environment variables - Disabled lifespan (for faster tests) Args: db_session: Database session fixture test_env_vars: Test environment variables fixture monkeypatch: Pytest monkeypatch fixture Yields: FastAPI test client Example: def test_create_ssh_key(client): response = client.post( "/api/ssh-keys", json={"name": "test-key", "private_key": "..."}, headers={"Authorization": "Bearer test-token"} ) assert response.status_code == 201 """ from fastapi.testclient import TestClient from unittest.mock import AsyncMock, patch # Mock the lifespan context manager async def mock_lifespan(app): # noqa: ARG001 - Unused app parameter # Database is already initialized by db_session fixture yield # Import after setting env vars import app.database import app.config # Initialize database with test session app.database._engine = db_session.bind app.database._session_factory = sessionmaker(bind=db_session.bind, autocommit=False, autoflush=False) # Create app with mocked lifespan from app.main import create_app test_app = create_app(lifespan_handler=mock_lifespan) # Override get_db_session dependency to use test session from app.api import deps def override_get_db_session(): try: yield db_session finally: pass test_app.dependency_overrides[deps.get_db_session] = override_get_db_session with TestClient(test_app) as test_client: yield test_client # Clean up test_app.dependency_overrides = {} app.database._engine = None app.database._session_factory = None app.config._settings = None