generated from john/python-template
This commit is contained in:
+10
-6
@@ -4,6 +4,7 @@ Every test gets a fresh in-memory SQLite database so tests are
|
||||
isolated, fast, and leave no artifacts on disk.
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -28,9 +29,9 @@ from transcription.services.jobs import JobService
|
||||
def isolate_settings_from_local_env_files(tmp_path_factory):
|
||||
"""Point `Settings` at a controlled stub env file instead of a developer one.
|
||||
|
||||
`Settings.model_config` declares `env_file=".env.production"`, resolved against the
|
||||
current working directory, so a repository-root pytest run would otherwise read real
|
||||
deployment values into tests that assert declared defaults.
|
||||
`Settings()` resolves its env file through the shared config seam, so a repository-root
|
||||
pytest run would otherwise read a developer's real `.env.production` into tests that
|
||||
assert declared defaults.
|
||||
|
||||
The stub mirrors what `.github/workflows/quality-gate.yml` writes in CI: only
|
||||
`OPENROUTER_API_KEY`, which is required and which many tests need `get_settings()` to
|
||||
@@ -42,12 +43,15 @@ def isolate_settings_from_local_env_files(tmp_path_factory):
|
||||
stub = tmp_path_factory.mktemp("settings-env") / ".env.test"
|
||||
stub.write_text("OPENROUTER_API_KEY=test-placeholder-not-a-real-key\n", encoding="utf-8")
|
||||
|
||||
original = Settings.model_config.get("env_file")
|
||||
Settings.model_config["env_file"] = str(stub)
|
||||
original = os.environ.get("ENV_FILE")
|
||||
os.environ["ENV_FILE"] = str(stub)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
Settings.model_config["env_file"] = original
|
||||
if original is None:
|
||||
os.environ.pop("ENV_FILE", None)
|
||||
else:
|
||||
os.environ["ENV_FILE"] = original
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -4,6 +4,7 @@ import pytest
|
||||
|
||||
from transcription.config import Settings
|
||||
from transcription.errors import ErrorCategory
|
||||
from transcription.services.errors import PromptLoadError
|
||||
from transcription.services.prompts import PromptStore
|
||||
from transcription.services.prompts import PromptStoreError
|
||||
|
||||
@@ -79,6 +80,13 @@ def test_prompt_creation_and_empty_content_are_rejected(prompt_store):
|
||||
assert empty.value.category == ErrorCategory.VALIDATION
|
||||
|
||||
|
||||
def test_prompt_store_failures_are_catchable_as_prompt_load_errors(prompt_store):
|
||||
store, _ = prompt_store
|
||||
|
||||
with pytest.raises(PromptLoadError):
|
||||
store.read_prompt("missing.md")
|
||||
|
||||
|
||||
def test_recovery_requires_a_backup(prompt_store):
|
||||
store, _ = prompt_store
|
||||
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
"""Suite-wide isolation of `Settings` from developer environment files.
|
||||
|
||||
`Settings.model_config` declares `env_file=".env.production"`, resolved relative to the
|
||||
current working directory. Running pytest from the repository root therefore loads a real
|
||||
developer env file into tests that construct `Settings(...)` directly, and the declared
|
||||
field defaults stop being what the suite actually exercises.
|
||||
|
||||
That breaks verification in both directions: tests asserting default behavior fail locally
|
||||
for environmental reasons, and tests asserting configured behavior can pass locally against
|
||||
values that do not exist in CI. The autouse fixture in `conftest.py` neutralizes the class
|
||||
level `env_file` so defaults are authoritative; tests that need file loading still pass
|
||||
`_env_file=` explicitly, which takes precedence over the class config.
|
||||
"""
|
||||
"""Suite-wide isolation of `Settings` from developer environment files."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import transcription.config as config_module
|
||||
from transcription.config import Settings
|
||||
from transcription.config import resolve_settings_env_file_path
|
||||
|
||||
|
||||
def test_settings_defaults_are_not_overridden_by_a_local_env_file():
|
||||
@@ -37,3 +27,23 @@ def test_explicit_env_file_still_loads(tmp_path):
|
||||
settings = Settings(openrouter_api_key="test-key", _env_file=env_path, _cli_parse_args=False)
|
||||
|
||||
assert settings.port == 9123
|
||||
|
||||
|
||||
def test_settings_env_file_resolves_from_override_environment_variable(tmp_path, monkeypatch):
|
||||
env_path = tmp_path / "custom.env"
|
||||
env_path.write_text("PORT=9123\n", encoding="utf-8")
|
||||
monkeypatch.setenv("ENV_FILE", str(env_path))
|
||||
|
||||
settings = Settings(openrouter_api_key="test-key", _cli_parse_args=False)
|
||||
|
||||
assert settings.port == 9123
|
||||
|
||||
|
||||
def test_settings_default_env_path_is_anchored_to_project_root(tmp_path, monkeypatch):
|
||||
monkeypatch.delenv("ENV_FILE", raising=False)
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setattr(config_module, "PROJECT_ROOT", tmp_path / "project-root")
|
||||
|
||||
resolved = resolve_settings_env_file_path()
|
||||
|
||||
assert resolved == tmp_path / "project-root" / ".env.production"
|
||||
|
||||
@@ -92,10 +92,6 @@ KNOWN_ORPHANS: dict[str, str] = {
|
||||
"Public read helper currently unused by runtime flows, but retained as part of "
|
||||
"the SourceService API pending endpoint consolidation."
|
||||
),
|
||||
"src/transcription/ui/pages/tags_page.py": (
|
||||
"Retained temporarily as explicitly dead code until the planned route-retirement "
|
||||
"cleanup deletes the stranded module."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -107,6 +107,22 @@ def test_runtime_settings_uses_override_env_file(tmp_path: Path, monkeypatch):
|
||||
assert "PORT=9001" in target.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_runtime_settings_falls_back_to_shared_settings_env_file_override(tmp_path: Path, monkeypatch):
|
||||
settings = _settings_for_runtime_editing(tmp_path)
|
||||
target = tmp_path / "shared.env"
|
||||
target.write_text("OPENROUTER_API_KEY=test-key\nPORT=8000\n", encoding="utf-8")
|
||||
monkeypatch.delenv("RUNTIME_SETTINGS_ENV_FILE", raising=False)
|
||||
monkeypatch.setenv("ENV_FILE", str(target))
|
||||
|
||||
snapshot = save_runtime_settings(
|
||||
settings=settings,
|
||||
updates={"port": "9001"},
|
||||
)
|
||||
|
||||
assert snapshot.env_file_path == target
|
||||
assert "PORT=9001" in target.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_save_runtime_settings_surfaces_unreadable_target(tmp_path: Path, monkeypatch):
|
||||
settings = _settings_for_runtime_editing(tmp_path)
|
||||
env_path = tmp_path / ".env"
|
||||
|
||||
Reference in New Issue
Block a user