fix(settings): reconcile secret status and model drafts

This commit is contained in:
caoxiaozhu
2026-07-20 10:29:49 +08:00
parent 044a5669fe
commit 15da295963
9 changed files with 377 additions and 23 deletions

View File

@@ -0,0 +1,40 @@
from __future__ import annotations
import base64
import pytest
from app.core import secret_box
@pytest.mark.parametrize(
"encoded_key",
[
"not-valid-base64!",
base64.urlsafe_b64encode(b"short-key").decode("ascii"),
],
)
def test_encrypt_rejects_invalid_existing_secret_key(
monkeypatch,
tmp_path,
encoded_key: str,
) -> None:
key_file = tmp_path / "settings.key"
key_file.write_text(encoded_key, encoding="utf-8")
monkeypatch.setattr(secret_box, "SECRET_KEY_FILE", key_file)
with pytest.raises(ValueError):
secret_box.encrypt_secret("probe")
assert key_file.read_text(encoding="utf-8") == encoded_key
def test_encrypt_replaces_empty_key_file_with_valid_key(monkeypatch, tmp_path) -> None:
key_file = tmp_path / "settings.key"
key_file.write_text("", encoding="utf-8")
monkeypatch.setattr(secret_box, "SECRET_KEY_FILE", key_file)
encrypted = secret_box.encrypt_secret("probe")
assert secret_box.decrypt_secret(encrypted) == "probe"
assert len(secret_box.read_secret_key()) == secret_box.KEY_BYTES

View File

@@ -123,6 +123,60 @@ def test_blank_secret_input_does_not_clear_saved_secret(monkeypatch) -> None:
assert service.load_saved_model_api_key("main") == "persisted-key"
def test_snapshot_marks_undecryptable_model_keys_as_unconfigured(monkeypatch) -> None:
temp_dir = build_temp_secret_dir()
monkeypatch.setattr(secret_box, "SECRET_KEY_FILE", temp_dir / "settings.key")
monkeypatch.setattr(Base.metadata, "create_all", lambda *args, **kwargs: None)
monkeypatch.setenv("HERMES_HOME", str(temp_dir / ".hermes"))
with build_session(temp_dir / "settings.db") as db:
service = SettingsService(db)
payload = service.get_settings_snapshot().model_dump()
payload["llmForm"]["mainApiKey"] = "main-secret"
payload["llmForm"]["embeddingApiKey"] = "embedding-secret"
payload["llmForm"]["rerankerApiKey"] = "reranker-secret"
service.save_settings_snapshot(SettingsWrite(**payload))
secret_box.SECRET_KEY_FILE.unlink()
secret_box.get_or_create_secret_key()
snapshot = service.get_settings_snapshot()
configured_by_slot = {
model.slot: model.apiKeyConfigured for model in snapshot.llmForm.models
}
assert snapshot.llmForm.mainApiKeyConfigured is False
assert snapshot.llmForm.embeddingApiKeyConfigured is False
assert snapshot.llmForm.rerankerApiKeyConfigured is False
assert configured_by_slot["main"] is False
assert configured_by_slot["embedding"] is False
assert configured_by_slot["reranker"] is False
def test_snapshot_does_not_create_missing_secret_key_and_marks_secrets_unconfigured(monkeypatch) -> None:
temp_dir = build_temp_secret_dir()
monkeypatch.setattr(secret_box, "SECRET_KEY_FILE", temp_dir / "settings.key")
monkeypatch.setattr(Base.metadata, "create_all", lambda *args, **kwargs: None)
monkeypatch.setenv("HERMES_HOME", str(temp_dir / ".hermes"))
with build_session(temp_dir / "settings.db") as db:
service = SettingsService(db)
payload = service.get_settings_snapshot().model_dump()
payload["llmForm"]["mainApiKey"] = "main-secret"
payload["renderForm"]["jwtSecret"] = "onlyoffice-secret"
payload["mailForm"]["password"] = "smtp-secret"
service.save_settings_snapshot(SettingsWrite(**payload))
secret_box.SECRET_KEY_FILE.unlink()
snapshot = service.get_settings_snapshot()
assert secret_box.SECRET_KEY_FILE.exists() is False
assert snapshot.llmForm.mainApiKeyConfigured is False
assert snapshot.renderForm.jwtSecretConfigured is False
assert snapshot.mailForm.passwordConfigured is False
def test_runtime_model_config_returns_decrypted_main_model(monkeypatch) -> None:
temp_dir = build_temp_secret_dir()
monkeypatch.setattr(secret_box, "SECRET_KEY_FILE", temp_dir / "settings.key")