"""Shared test fixtures for E2E tests.""" import os from pathlib import Path import duckdb import pytest @pytest.fixture def e2e_env(tmp_path): """Set up complete E2E environment with DATA_DIR, create dirs.""" os.environ["DATA_DIR"] = str(tmp_path) os.environ["JWT_SECRET_KEY"] = "test-secret-e2e" (tmp_path / "extracts").mkdir() (tmp_path / "analytics").mkdir() (tmp_path / "state").mkdir() yield { "data_dir": tmp_path, "extracts_dir": tmp_path / "extracts", "analytics_db": str(tmp_path / "analytics" / "server.duckdb"), } os.environ.pop("DATA_DIR", None) os.environ.pop("JWT_SECRET_KEY", None) def create_mock_extract(extracts_dir: Path, source_name: str, tables: list[dict]): """Create a mock extract.duckdb with _meta and data tables. tables: [{"name": "orders", "data": [{"id": "1", "total": "100"}], "query_mode": "local"}] """ source_dir = extracts_dir / source_name source_dir.mkdir(exist_ok=True) data_dir = source_dir / "data" data_dir.mkdir(exist_ok=True) db_path = source_dir / "extract.duckdb" conn = duckdb.connect(str(db_path)) conn.execute("""CREATE TABLE IF NOT EXISTS _meta ( table_name VARCHAR, description VARCHAR, rows BIGINT, size_bytes BIGINT, extracted_at TIMESTAMP, query_mode VARCHAR DEFAULT 'local' )""") # Delete existing meta rows to allow re-calling conn.execute("DELETE FROM _meta") for t in tables: name = t["name"] rows_data = t.get("data", []) query_mode = t.get("query_mode", "local") if rows_data and query_mode == "local": # Write actual parquet file pq_path = str(data_dir / f"{name}.parquet") # Build SQL from data selects = [] for row in rows_data: vals = ", ".join(f"'{v}' AS {k}" for k, v in row.items()) selects.append(f"SELECT {vals}") union_sql = " UNION ALL ".join(selects) conn.execute(f"COPY ({union_sql}) TO '{pq_path}' (FORMAT PARQUET)") rows = len(rows_data) size = os.path.getsize(pq_path) conn.execute(f'CREATE OR REPLACE VIEW "{name}" AS SELECT * FROM read_parquet(\'{pq_path}\')') conn.execute("INSERT INTO _meta VALUES (?, ?, ?, ?, current_timestamp, 'local')", [name, t.get("description", ""), rows, size]) else: # Remote or empty table conn.execute(f'CREATE TABLE IF NOT EXISTS "{name}" (id VARCHAR)') conn.execute("INSERT INTO _meta VALUES (?, ?, 0, 0, current_timestamp, ?)", [name, t.get("description", ""), query_mode]) conn.close() return db_path def write_test_parquet(path: str, data: list[dict]): """Create a parquet file from list of dicts.""" conn = duckdb.connect() selects = [] for row in data: vals = ", ".join(f"'{v}' AS {k}" for k, v in row.items()) selects.append(f"SELECT {vals}") union_sql = " UNION ALL ".join(selects) conn.execute(f"COPY ({union_sql}) TO '{path}' (FORMAT PARQUET)") conn.close() @pytest.fixture def seeded_app(e2e_env): """FastAPI TestClient with seeded admin + analyst users, JWT tokens.""" from src.db import get_system_db from src.repositories.users import UserRepository from app.auth.jwt import create_access_token from app.main import create_app from fastapi.testclient import TestClient conn = get_system_db() repo = UserRepository(conn) repo.create(id="admin1", email="admin@test.com", name="Admin", role="admin") repo.create(id="analyst1", email="analyst@test.com", name="Analyst", role="analyst") conn.close() app = create_app() client = TestClient(app) admin_token = create_access_token("admin1", "admin@test.com", "admin") analyst_token = create_access_token("analyst1", "analyst@test.com", "analyst") return { "client": client, "admin_token": admin_token, "analyst_token": analyst_token, "env": e2e_env, }