From 3c0213ae740093d6c6f65a6fa74f42a542c07826 Mon Sep 17 00:00:00 2001 From: Lars Date: Mon, 7 Sep 2026 15:00:33 +0200 Subject: [PATCH] Isolate pytest env per file so fail-closed and setup stay script-accurate. Co-authored-by: Cursor --- .gitea/workflows/test.yml | 5 +- backend/tests/conftest.py | 52 +++++++++---------- backend/tests/harness.py | 23 +++++++- backend/tests/test_journal_style_context.py | 3 ++ .../test_journal_style_legacy_immutable.py | 3 ++ 5 files changed, 57 insertions(+), 29 deletions(-) diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 1aeeaae..78ddf62 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -54,7 +54,10 @@ jobs: -e PYTHONUTF8=1 \ backend sh -lc ' set -e - unset KANSHO_DB_PATH + unset KANSHO_DB_PATH KANSHO_FAKE_PROVIDER KANSHO_FAKE_DETECT + unset KANSHO_PROVIDER_KEY KANSHO_DETECT_PROVIDER_KEY + export KANSHO_PROVIDER_KEY= + export KANSHO_DETECT_PROVIDER_KEY= pip install -q -r requirements-dev.txt python -m pytest tests -m "not slow" -ra -vv --tb=short ' diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 9a5dbed..f2a062a 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -2,7 +2,8 @@ Each file stays one case (`main()`) until it is split into native pytest functions. Integration files reset `kansho_test` only; `kansho_dev` is never -the test target. +the test target. Provider keys and fake flags are restored per file so the +shared pytest process matches the old one-script-per-process isolation. """ from __future__ import annotations @@ -17,6 +18,7 @@ if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from tests.harness import ( # noqa: E402 + apply_module_env, is_postgres_test_database, prepare_test_env, reset_postgres_schema, @@ -56,34 +58,32 @@ def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item class KanshoScriptModule(pytest.Module): def collect(self): module = self.obj - - def test_main() -> None: - main = getattr(module, "main", None) - if main is None or not callable(main): - pytest.fail(f"{self.path.name} has no callable main()") - main() - - yield pytest.Function.from_parent(self, name="test_main", callobj=test_main) + main = getattr(module, "main", None) + if main is None or not callable(main): + pytest.fail(f"{self.path.name} has no callable main()") + yield pytest.Function.from_parent(self, name="test_main", callobj=main) @pytest.fixture(scope="module", autouse=True) def _module_backend(request: pytest.FixtureRequest): - stem = Path(str(request.path)).stem - if stem in UNIT_MODULES: - saved = dict(os.environ) - try: + source = Path(str(request.path)) + stem = source.stem + saved = dict(os.environ) + try: + apply_module_env(source) + if stem in UNIT_MODULES: yield - finally: - os.environ.clear() - os.environ.update(saved) - return - if not is_postgres_test_database(): - pytest.skip( - "integration requires KANSHO_DB_BACKEND=postgres and DB_NAME ending _test " - "(Gitea: kansho_test on the Dev Postgres, never kansho_dev)" - ) - reset_postgres_schema() - from db import init_db + return + if not is_postgres_test_database(): + pytest.skip( + "integration requires KANSHO_DB_BACKEND=postgres and DB_NAME ending _test " + "(Gitea: kansho_test on the Dev Postgres, never kansho_dev)" + ) + reset_postgres_schema() + from db import init_db - init_db() - yield + init_db() + yield + finally: + os.environ.clear() + os.environ.update(saved) diff --git a/backend/tests/harness.py b/backend/tests/harness.py index 9c8a11b..1d7c478 100644 --- a/backend/tests/harness.py +++ b/backend/tests/harness.py @@ -39,9 +39,28 @@ def is_postgres_test_database() -> bool: def prepare_test_env() -> None: - os.environ.setdefault("KANSHO_PROVIDER_KEY", "") - os.environ.setdefault("KANSHO_DETECT_PROVIDER_KEY", "") + """Fail-closed defaults. Do not keep Compose provider keys or leftover fakes.""" + os.environ["KANSHO_PROVIDER_KEY"] = "" + os.environ["KANSHO_DETECT_PROVIDER_KEY"] = "" os.environ.pop("KANSHO_DB_PATH", None) + os.environ.pop("KANSHO_FAKE_PROVIDER", None) + os.environ.pop("KANSHO_FAKE_DETECT", None) + + +def apply_module_env(source) -> None: + """Restore per-file env so pytest does not leak fakes/keys across scripts.""" + from pathlib import Path + + prepare_test_env() + text = Path(source).read_text(encoding="utf-8") + if 'os.environ["KANSHO_FAKE_PROVIDER"] = "1"' in text or "os.environ['KANSHO_FAKE_PROVIDER'] = '1'" in text: + os.environ["KANSHO_FAKE_PROVIDER"] = "1" + if ( + 'os.environ["KANSHO_FAKE_DETECT"] = "1"' in text + or "os.environ['KANSHO_FAKE_DETECT'] = '1'" in text + or 'os.environ.setdefault("KANSHO_FAKE_DETECT", "1")' in text + ): + os.environ["KANSHO_FAKE_DETECT"] = "1" def _require_test_database() -> None: diff --git a/backend/tests/test_journal_style_context.py b/backend/tests/test_journal_style_context.py index b4ebe90..778f828 100644 --- a/backend/tests/test_journal_style_context.py +++ b/backend/tests/test_journal_style_context.py @@ -209,6 +209,9 @@ def main() -> None: test_compile_flags() test_budget_omission_in_effective_trace() + from tests.harness import reset_postgres_schema + + reset_postgres_schema() init_db() reset_debug() with TestClient(app) as client: diff --git a/backend/tests/test_journal_style_legacy_immutable.py b/backend/tests/test_journal_style_legacy_immutable.py index caf365f..dc73739 100644 --- a/backend/tests/test_journal_style_legacy_immutable.py +++ b/backend/tests/test_journal_style_legacy_immutable.py @@ -268,6 +268,9 @@ def test_later_semantic_seed_creates_successor() -> None: def test_legacy_generate_trace_and_prompt() -> None: + from tests.harness import reset_postgres_schema + + reset_postgres_schema() init_db() client = TestClient(app) setup = client.post(