diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..f76fedb --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,19 @@ +"""Pytest configuration -- shared markers for the Sentinel test suite. + +Day 7 Phase 6 (2026-05-24). The CI workflow at .github/workflows/ci.yml runs +`pytest tests/ -m "not requires_data"` so the GitHub runner skips tests that +need the DVC-tracked data and trained model artifacts (which are not in git). +This conftest registers the `requires_data` marker so pytest does not emit a +PytestUnknownMarkWarning when the marker is used. +""" + +from __future__ import annotations + + +def pytest_configure(config) -> None: + config.addinivalue_line( + "markers", + "requires_data: test depends on DVC-tracked data and/or the trained " + "model artifact in models/. Skipped in CI; run locally after " + "`dvc repro train`.", + ) diff --git a/tests/test_api.py b/tests/test_api.py index 1f10a86..86ebbcd 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -15,6 +15,13 @@ from fastapi.testclient import TestClient +# The FastAPI app boots from models/fraud_model.pkl + data/processed/X_test.csv, +# neither of which is in git -- they come from `dvc repro train`. CI skips this +# whole module via the `-m "not requires_data"` marker filter; run locally +# after a successful pipeline run. +pytestmark = pytest.mark.requires_data + + @pytest.fixture(scope="module") def client(tmp_path_factory: pytest.TempPathFactory) -> TestClient: tmp_dir = tmp_path_factory.mktemp("api-test") diff --git a/tests/test_data_loader.py b/tests/test_data_loader.py index fc46b19..198d6da 100644 --- a/tests/test_data_loader.py +++ b/tests/test_data_loader.py @@ -5,6 +5,7 @@ from pathlib import Path import pandas as pd +import pytest from src.data.loader import LoaderConfig, SentinelDataLoader, dvc_status @@ -17,6 +18,8 @@ def test_loader_resolves_paths_from_params() -> None: assert paths.model.name == "fraud_model.pkl" +# Reads data/processed/X_test.csv + y_test.csv which are not in git (DVC). +@pytest.mark.requires_data def test_loader_reads_x_test_and_y_test() -> None: loader = SentinelDataLoader() X = loader.load_x_test()