"""Shared test fixtures.""" import os import pandas as pd import pytest from pathlib import Path SAMPLES_DIR = Path(__file__).parent.parent / "samples" # --------------------------------------------------------------------------- # License gating bypass # --------------------------------------------------------------------------- # # Every CLI entry point and every GUI page now requires a valid license, # but the test suite shouldn't be in the business of paying for or # generating licenses on every run. The session-scoped autouse fixture # below sets the dev-mode env var BEFORE any test code (including # parametrize-time imports) runs, so all 1900+ existing tests continue # to pass. # # Individual license tests that DO want to exercise the real activation # flow either: # - clear the env var themselves and point the manager at a tmp file, or # - use the explicit ``activated_license_manager`` / ``unactivated_license_manager`` # fixtures defined below. @pytest.fixture(scope="session", autouse=True) def _enable_license_dev_mode(): """Bypass license checks for every test by default. Set in the env so subprocess-based tests (test_install, test_e2e) inherit it without each test needing to plumb the env var. """ previous = os.environ.get("DATATOOLS_DEV_MODE") os.environ["DATATOOLS_DEV_MODE"] = "1" try: yield finally: if previous is None: os.environ.pop("DATATOOLS_DEV_MODE", None) else: os.environ["DATATOOLS_DEV_MODE"] = previous @pytest.fixture def isolated_license_path(tmp_path, monkeypatch): """Point the license manager at a fresh tmp file for one test. Useful when a test wants to exercise the real activation flow: create + sign + verify the license bytes in a controlled location without polluting ``~/.datatools/license.json``. Also clears the dev-mode bypass so the manager actually consults the file. """ path = tmp_path / "license.json" monkeypatch.setenv("DATATOOLS_LICENSE_PATH", str(path)) monkeypatch.delenv("DATATOOLS_DEV_MODE", raising=False) # The manager singleton caches its handle across tests; drop it # so the new env vars take effect. from src.license.manager import reset_singleton_for_tests reset_singleton_for_tests() yield path reset_singleton_for_tests() @pytest.fixture def activated_license_manager(isolated_license_path): """Yield a LicenseManager pointed at a tmp file, pre-activated as a Core user. The license is freshly signed with the current secret so verification succeeds. """ from src.license import LicenseManager, Tier mgr = LicenseManager() mgr._mint(name="Test User", email="test@example.com", tier=Tier.CORE) return mgr @pytest.fixture def unactivated_license_manager(isolated_license_path): """Yield a LicenseManager pointed at a tmp file with NO license file. Useful for testing the activation flow + gate behaviour. """ from src.license import LicenseManager return LicenseManager() @pytest.fixture def sample_csv_path(): return SAMPLES_DIR / "messy_sales.csv" @pytest.fixture def sample_df(sample_csv_path): return pd.read_csv(sample_csv_path, dtype=str, keep_default_na=False) @pytest.fixture def simple_df(): """Small DataFrame with obvious duplicates for unit testing.""" return pd.DataFrame({ "name": ["Alice", "alice", "Bob", "Charlie", "ALICE"], "email": ["alice@test.com", "alice@test.com", "bob@test.com", "charlie@test.com", "alice@test.com"], "phone": ["555-1234", "555-1234", "555-5678", "555-9012", "555-1234"], }) @pytest.fixture def merge_df(): """DataFrame with partial records that benefit from merge.""" return pd.DataFrame({ "name": ["John Doe", "John Doe", "Jane Smith"], "email": ["john@test.com", "john@test.com", "jane@test.com"], "phone": ["555-1111", "", "555-3333"], "address": ["", "123 Main St", "456 Oak Ave"], }) @pytest.fixture def tmp_csv(tmp_path, simple_df): """Write simple_df to a temp CSV and return the path.""" path = tmp_path / "test_input.csv" simple_df.to_csv(path, index=False) return path