""" LinkSyncServer - Test Configuration Database lifecycle: - pytest_configure: Creates fresh test database before any tests run - pytest_unconfigure: Destroys test database after all tests complete - Per-test: Transaction rollback ensures test isolation """ import os import pytest from fastapi.testclient import TestClient from sqlalchemy import create_engine, event from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import StaticPool from models.base import Base import models.base TEST_DATABASE_URL = "sqlite:///test_linksync.db" def pytest_configure(config): """Called before any tests run. Creates fresh test database.""" if os.path.exists("test_linksync.db"): os.remove("test_linksync.db") test_engine = create_engine( TEST_DATABASE_URL, connect_args={"check_same_thread": False}, poolclass=StaticPool, ) Base.metadata.create_all(bind=test_engine) # Override the module-level get_engine to return our test engine original_get_engine = models.base.get_engine original_get_session = models.base.get_session def test_get_engine(): return test_engine def test_get_session(): Session = sessionmaker(bind=test_engine) return Session() models.base.get_engine = test_get_engine models.base.get_session = test_get_session # Store originals for cleanup config._test_engine = test_engine config._original_get_engine = original_get_engine config._original_get_session = original_get_session def pytest_unconfigure(config): """Called after all tests complete. Destroys test database.""" # Restore original functions if hasattr(config, "_original_get_engine"): models.base.get_engine = config._original_get_engine models.base.get_session = config._original_get_session # Drop all tables if hasattr(config, "_test_engine"): Base.metadata.drop_all(bind=config._test_engine) config._test_engine.dispose() # Remove the test database file if os.path.exists("test_linksync.db"): os.remove("test_linksync.db") @pytest.fixture(scope="session") def test_engine(pytestconfig): """Provides the test engine for direct database access in tests.""" return pytestconfig._test_engine @pytest.fixture def db_session(test_engine): """Provides a transactional session that rolls back after each test.""" connection = test_engine.connect() transaction = connection.begin() Session = sessionmaker(bind=connection) session = Session() yield session session.close() transaction.rollback() connection.close() @pytest.fixture def client(): """Provides a TestClient with the test database already configured.""" from app import app with TestClient(app) as c: yield c @pytest.fixture def admin_token(client): response = client.post( "/api/auth/login", data={"username": "admin", "password": "admin123"}, ) assert response.status_code == 200 return response.json()["access_token"] @pytest.fixture def auth_headers(admin_token): return {"Authorization": f"Bearer {admin_token}"} @pytest.fixture def sample_bookmark_data(): return { "url": "https://example.com", "title": "Example Site", "description": "An example website", "notes": "Test notes", "tags": ["test", "example"], "favicon_url": "https://example.com/favicon.ico", "path": "/Test", "visit_count": 0, "is_bookmarked": True, } @pytest.fixture def sample_collection_data(): return { "name": "Test Collection", "description": "A test collection", "query_type": "static", "query_expression": None, "is_public": False, "link_ids": [], }