fix: replace os.environ direct assignment with monkeypatch.setenv in test fixtures

Prevents environment variable leaking between tests. All DATA_DIR,
JWT_SECRET_KEY, and SCRIPT_TIMEOUT assignments in fixtures now use
monkeypatch.setenv() which auto-reverts after each test. Removes
manual os.environ.pop() cleanup lines.
This commit is contained in:
ZdenekSrotyr 2026-04-09 07:11:36 +02:00
parent c56511f69f
commit 8e9a0c367a
12 changed files with 50 additions and 52 deletions

View file

@ -6,19 +6,19 @@ from fastapi.testclient import TestClient
@pytest.fixture
def app_client(tmp_path):
os.environ["DATA_DIR"] = str(tmp_path)
os.environ["JWT_SECRET_KEY"] = "test-secret"
def app_client(tmp_path, monkeypatch):
monkeypatch.setenv("DATA_DIR", str(tmp_path))
monkeypatch.setenv("JWT_SECRET_KEY", "test-secret")
from app.main import create_app
app = create_app()
return TestClient(app)
@pytest.fixture
def seeded_client(tmp_path):
def seeded_client(tmp_path, monkeypatch):
"""Client with a pre-created admin user and JWT token."""
os.environ["DATA_DIR"] = str(tmp_path)
os.environ["JWT_SECRET_KEY"] = "test-secret"
monkeypatch.setenv("DATA_DIR", str(tmp_path))
monkeypatch.setenv("JWT_SECRET_KEY", "test-secret")
from app.main import create_app
from src.db import get_system_db
from src.repositories.users import UserRepository

View file

@ -6,9 +6,9 @@ from fastapi.testclient import TestClient
@pytest.fixture
def client(tmp_path):
os.environ["DATA_DIR"] = str(tmp_path)
os.environ["JWT_SECRET_KEY"] = "test-secret-32chars-minimum!!!!!"
def client(tmp_path, monkeypatch):
monkeypatch.setenv("DATA_DIR", str(tmp_path))
monkeypatch.setenv("JWT_SECRET_KEY", "test-secret-32chars-minimum!!!!!")
from app.main import create_app
from src.db import get_system_db

View file

@ -6,10 +6,10 @@ from fastapi.testclient import TestClient
@pytest.fixture
def client(tmp_path):
os.environ["DATA_DIR"] = str(tmp_path)
os.environ["JWT_SECRET_KEY"] = "test-secret-32chars-minimum!!!!!"
os.environ["SCRIPT_TIMEOUT"] = "10"
def client(tmp_path, monkeypatch):
monkeypatch.setenv("DATA_DIR", str(tmp_path))
monkeypatch.setenv("JWT_SECRET_KEY", "test-secret-32chars-minimum!!!!!")
monkeypatch.setenv("SCRIPT_TIMEOUT", "10")
from app.main import create_app
from src.db import get_system_db

View file

@ -6,9 +6,9 @@ from fastapi.testclient import TestClient
@pytest.fixture
def client(tmp_path):
os.environ["DATA_DIR"] = str(tmp_path)
os.environ["JWT_SECRET_KEY"] = "test-secret-32chars-minimum!!!!!"
def client(tmp_path, monkeypatch):
monkeypatch.setenv("DATA_DIR", str(tmp_path))
monkeypatch.setenv("JWT_SECRET_KEY", "test-secret-32chars-minimum!!!!!")
from app.main import create_app
from src.db import get_system_db

View file

@ -6,20 +6,20 @@ from fastapi.testclient import TestClient
@pytest.fixture
def fresh_client(tmp_path):
def fresh_client(tmp_path, monkeypatch):
"""Client with EMPTY database — no users."""
os.environ["DATA_DIR"] = str(tmp_path)
os.environ["JWT_SECRET_KEY"] = "test-secret-32chars-minimum!!!!!"
monkeypatch.setenv("DATA_DIR", str(tmp_path))
monkeypatch.setenv("JWT_SECRET_KEY", "test-secret-32chars-minimum!!!!!")
from app.main import create_app
app = create_app()
return TestClient(app)
@pytest.fixture
def seeded_client(tmp_path):
def seeded_client(tmp_path, monkeypatch):
"""Client with one existing user."""
os.environ["DATA_DIR"] = str(tmp_path)
os.environ["JWT_SECRET_KEY"] = "test-secret-32chars-minimum!!!!!"
monkeypatch.setenv("DATA_DIR", str(tmp_path))
monkeypatch.setenv("JWT_SECRET_KEY", "test-secret-32chars-minimum!!!!!")
from app.main import create_app
from src.db import get_system_db
from src.repositories.users import UserRepository

View file

@ -34,8 +34,8 @@ class TestGetSystemDb:
finally:
conn.close()
def test_idempotent(self, tmp_path):
_setup_data_dir(tmp_path)
def test_idempotent(self, tmp_path, monkeypatch):
_setup_data_dir(tmp_path, monkeypatch)
from src.db import get_system_db
conn = get_system_db()
@ -53,8 +53,8 @@ class TestGetSystemDb:
class TestGetSchemaVersion:
def test_returns_version(self, tmp_path):
_setup_data_dir(tmp_path)
def test_returns_version(self, tmp_path, monkeypatch):
_setup_data_dir(tmp_path, monkeypatch)
from src.db import get_schema_version, get_system_db
conn = get_system_db()
@ -63,8 +63,8 @@ class TestGetSchemaVersion:
finally:
conn.close()
def test_returns_zero_for_empty_db(self, tmp_path):
_setup_data_dir(tmp_path)
def test_returns_zero_for_empty_db(self, tmp_path, monkeypatch):
_setup_data_dir(tmp_path, monkeypatch)
from src.db import get_schema_version
conn = duckdb.connect(str(tmp_path / "empty.duckdb"))
@ -75,9 +75,9 @@ class TestGetSchemaVersion:
class TestV1ToV2Migration:
def test_migration_adds_source_columns(self, tmp_path):
def test_migration_adds_source_columns(self, tmp_path, monkeypatch):
"""Simulate a v1 database and verify v2 migration adds new columns."""
_setup_data_dir(tmp_path)
_setup_data_dir(tmp_path, monkeypatch)
import duckdb as _duckdb
# Create a v1 database manually
@ -133,8 +133,8 @@ class TestV1ToV2Migration:
class TestGetAnalyticsDb:
def test_creates_db(self, tmp_path):
_setup_data_dir(tmp_path)
def test_creates_db(self, tmp_path, monkeypatch):
_setup_data_dir(tmp_path, monkeypatch)
from src.db import get_analytics_db
conn = get_analytics_db()
@ -145,9 +145,9 @@ class TestGetAnalyticsDb:
class TestGetAnalyticsDbReadonly:
def test_analytics_readonly_rejects_malicious_dir_name(self, tmp_path):
def test_analytics_readonly_rejects_malicious_dir_name(self, tmp_path, monkeypatch):
"""Directories with SQL-injection chars in their name are skipped."""
_setup_data_dir(tmp_path)
_setup_data_dir(tmp_path, monkeypatch)
import importlib
import src.db as db_module
importlib.reload(db_module)

View file

@ -6,7 +6,7 @@ import pytest
@pytest.fixture
def migration_env(tmp_path):
def migration_env(tmp_path, monkeypatch):
"""Create temp dir with sample JSON files mimicking production layout."""
data_dir = tmp_path / "data"
(data_dir / "notifications").mkdir(parents=True)
@ -52,7 +52,7 @@ def migration_env(tmp_path):
}
}))
os.environ["DATA_DIR"] = str(data_dir)
monkeypatch.setenv("DATA_DIR", str(data_dir))
return str(data_dir)
@ -79,11 +79,11 @@ def test_migration_idempotent(migration_env):
assert stats2["sync_state"] == 2
def test_migration_with_missing_files(tmp_path):
def test_migration_with_missing_files(tmp_path, monkeypatch):
"""Migration should handle missing JSON files gracefully."""
data_dir = tmp_path / "empty_data"
data_dir.mkdir()
os.environ["DATA_DIR"] = str(data_dir)
monkeypatch.setenv("DATA_DIR", str(data_dir))
from scripts.migrate_json_to_duckdb import migrate_all
stats = migrate_all(str(data_dir))
assert stats["sync_state"] == 0

View file

@ -8,9 +8,9 @@ import pytest
@pytest.fixture
def setup_env(tmp_path):
def setup_env(tmp_path, monkeypatch):
"""Set up DATA_DIR and return paths."""
os.environ["DATA_DIR"] = str(tmp_path)
monkeypatch.setenv("DATA_DIR", str(tmp_path))
extracts_dir = tmp_path / "extracts"
extracts_dir.mkdir()
analytics_dir = tmp_path / "analytics"
@ -22,8 +22,6 @@ def setup_env(tmp_path):
"extracts_dir": extracts_dir,
"analytics_db": str(analytics_dir / "server.duckdb"),
}
# Clean up env var to avoid leaking between tests
os.environ.pop("DATA_DIR", None)
def _create_mock_extract(extracts_dir: Path, source_name: str, tables: list[dict]):

View file

@ -5,8 +5,8 @@ import pytest
@pytest.fixture
def db_conn(tmp_path):
os.environ["DATA_DIR"] = str(tmp_path)
def db_conn(tmp_path, monkeypatch):
monkeypatch.setenv("DATA_DIR", str(tmp_path))
from src.db import get_system_db
conn = get_system_db()
yield conn

View file

@ -5,8 +5,8 @@ import pytest
@pytest.fixture
def setup_db(tmp_path):
os.environ["DATA_DIR"] = str(tmp_path)
def setup_db(tmp_path, monkeypatch):
monkeypatch.setenv("DATA_DIR", str(tmp_path))
from src.db import get_system_db
from src.repositories.users import UserRepository

View file

@ -5,8 +5,8 @@ import pytest
@pytest.fixture
def db_conn(tmp_path):
os.environ["DATA_DIR"] = str(tmp_path)
def db_conn(tmp_path, monkeypatch):
monkeypatch.setenv("DATA_DIR", str(tmp_path))
from src.db import get_system_db
conn = get_system_db()
yield conn

View file

@ -8,10 +8,10 @@ from fastapi.testclient import TestClient
@pytest.fixture
def client(tmp_path):
os.environ["DATA_DIR"] = str(tmp_path)
os.environ["JWT_SECRET_KEY"] = "test-secret-32chars-minimum!!!!!"
os.environ["SCRIPT_TIMEOUT"] = "5"
def client(tmp_path, monkeypatch):
monkeypatch.setenv("DATA_DIR", str(tmp_path))
monkeypatch.setenv("JWT_SECRET_KEY", "test-secret-32chars-minimum!!!!!")
monkeypatch.setenv("SCRIPT_TIMEOUT", "5")
from app.main import create_app
from src.db import get_system_db