test_config_env_warnings.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. """S6: warn on unknown MFA_*/BAMBUDDY_* env vars so typos like
  2. ``MFA_ENCYPTION_KEY`` are not silently swallowed by ``extra="ignore"``."""
  3. from __future__ import annotations
  4. import importlib
  5. import logging
  6. import pytest
  7. @pytest.fixture(autouse=True)
  8. def _restore_config_module():
  9. """Undo the ``importlib.reload`` these tests depend on.
  10. Reloading ``backend.app.core.config`` re-executes it, so ``settings`` becomes
  11. a *new* object built from the environment as it stands mid-test. Nothing put
  12. the old one back. ``monkeypatch`` unwinds the env vars, not the reload.
  13. The result is two live ``Settings`` instances in one process: every module
  14. that did ``from ... config import settings`` at import time keeps the
  15. original, while anything resolving ``config.settings`` afterwards gets the
  16. replacement — and under xdist that split persisted for every later test in
  17. the same worker. It surfaced as unrelated path assertions failing with a
  18. ``base_dir`` from *this* module's tmp_path (``TestLibraryPathHelpers``,
  19. ``TestUploadSourceThreeMF``, ``TestArchivePlatesDesignOverrides``,
  20. ``TestSystemHealthAPI``), which is why it looked like a random flake and
  21. moved between runs as the work distribution changed.
  22. Snapshotting the whole module dict rather than just ``settings`` restores
  23. object *identity*, which is what the two views have to agree on.
  24. """
  25. import backend.app.core.config as cfg_mod
  26. saved = dict(cfg_mod.__dict__)
  27. yield
  28. cfg_mod.__dict__.clear()
  29. cfg_mod.__dict__.update(saved)
  30. @pytest.mark.unit
  31. def test_unknown_mfa_env_var_logs_info(monkeypatch, caplog):
  32. """A typo'd MFA_* env var must be logged at INFO so operators see it."""
  33. monkeypatch.setenv("MFA_ENCYPTION_KEY", "typo-value") # missing R
  34. import backend.app.core.config as cfg_mod
  35. with caplog.at_level(logging.INFO):
  36. importlib.reload(cfg_mod)
  37. assert any("MFA_ENCYPTION_KEY" in rec.message for rec in caplog.records)
  38. @pytest.mark.unit
  39. def test_unknown_bambuddy_env_var_logs_info(monkeypatch, caplog):
  40. """An unrecognised BAMBUDDY_* env var must also be logged."""
  41. monkeypatch.setenv("BAMBUDDY_NEW_FEATURE", "v1")
  42. import backend.app.core.config as cfg_mod
  43. with caplog.at_level(logging.INFO):
  44. importlib.reload(cfg_mod)
  45. assert any("BAMBUDDY_NEW_FEATURE" in rec.message for rec in caplog.records)
  46. @pytest.mark.unit
  47. def test_known_intentional_env_var_does_not_log(monkeypatch, caplog):
  48. """MFA_ENCRYPTION_KEY is declared in _INTENTIONAL_UNSETTINGS — must be silent."""
  49. monkeypatch.setenv("MFA_ENCRYPTION_KEY", "x" * 44) # invalid but not a typo
  50. import backend.app.core.config as cfg_mod
  51. with caplog.at_level(logging.INFO):
  52. importlib.reload(cfg_mod)
  53. # The intentional var must not produce a typo warning.
  54. typo_warnings = [
  55. rec for rec in caplog.records if "MFA_ENCRYPTION_KEY" in rec.message and "typo" in rec.message.lower()
  56. ]
  57. assert typo_warnings == []