|
|
|
|
@ -4,7 +4,7 @@ from pathlib import Path
|
|
|
|
|
import pytest |
|
|
|
|
|
|
|
|
|
from voice_transcriptor.models import AppSettings |
|
|
|
|
from voice_transcriptor.services.settings import SettingsRepository |
|
|
|
|
from voice_transcriptor.services.settings import SettingsError, SettingsRepository |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_load_missing_file_returns_documented_defaults(monkeypatch, tmp_path: Path) -> None: |
|
|
|
|
@ -78,6 +78,27 @@ def test_save_replaces_existing_file_atomically(monkeypatch, tmp_path: Path) ->
|
|
|
|
|
assert json.loads(path.read_text(encoding="utf-8"))["model"] == "model" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_save_preserves_settings_error_when_replace_and_cleanup_fail( |
|
|
|
|
monkeypatch, tmp_path: Path |
|
|
|
|
) -> None: |
|
|
|
|
path = tmp_path / "settings.json" |
|
|
|
|
replacement_failure = OSError("target is locked") |
|
|
|
|
|
|
|
|
|
def failing_replace(source: Path, destination: Path) -> Path: |
|
|
|
|
raise replacement_failure |
|
|
|
|
|
|
|
|
|
def failing_unlink(path: Path, missing_ok: bool = False) -> None: |
|
|
|
|
raise OSError("temporary file is locked") |
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(Path, "replace", failing_replace) |
|
|
|
|
monkeypatch.setattr(Path, "unlink", failing_unlink) |
|
|
|
|
|
|
|
|
|
with pytest.raises(SettingsError) as caught: |
|
|
|
|
SettingsRepository(path).save(AppSettings("model", "pt-BR", tmp_path / "out")) |
|
|
|
|
|
|
|
|
|
assert caught.value.__cause__ is replacement_failure |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_saved_settings_never_include_api_key_fields(tmp_path: Path) -> None: |
|
|
|
|
path = tmp_path / "settings.json" |
|
|
|
|
|
|
|
|
|
|