feat(01-05): final cutover — delete data/, prune config.py, async-only tests
- Delete backend/data/ tracked files (D-04): flat-file metadata, settings.json, topics.json, and uploaded files removed from git; backend/data/ added to .gitignore (empty dir remains on macOS due to ACL — no tracked files remain) - Prune backend/config.py: remove DATA_DIR, UPLOADS_DIR, METADATA_DIR, TOPICS_FILE, ensure_data_dirs(); rebase SETTINGS_FILE as derived path from settings.data_dir (Phase 1 flat-file settings kept per plan decision) - Prune backend/tests/conftest.py: remove isolated_data_dir autouse fixture and sync TestClient client fixture; add SQLite type compatibility shim (visit_INET/JSONB) so in-memory db_session can create tables with PostgreSQL-specific column types; add live_services_available fixture - Rewrite backend/tests/test_documents.py: delete all legacy sync tests, remove all @pytest.mark.xfail markers; async-only document tests now use async_client + storage service directly for topic wiring - Rewrite backend/tests/test_health.py: delete legacy sync test_health(client); remove @pytest.mark.xfail from test_health_checks_postgres_and_minio - Port backend/tests/test_topics.py to async_client (sync client removed) - Port backend/tests/test_settings.py to async_client with monkeypatch for SETTINGS_FILE isolation (settings remain flat-file in Phase 1)
This commit is contained in:
@@ -1,60 +1,106 @@
|
||||
def test_get_settings_defaults(client):
|
||||
resp = client.get("/api/settings")
|
||||
"""
|
||||
Settings API tests — async only (Plan 05 cutover).
|
||||
|
||||
Settings remain flat-file backed in Phase 1 (D-03 deferred), so these tests
|
||||
use async_client but do not require a real database session.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
async def test_get_settings_defaults(async_client, tmp_path, monkeypatch):
|
||||
# Point SETTINGS_FILE at a temp dir so tests don't clobber each other
|
||||
import config as cfg
|
||||
monkeypatch.setattr(cfg, "SETTINGS_FILE", tmp_path / "settings.json")
|
||||
import services.storage as st
|
||||
monkeypatch.setattr(st, "SETTINGS_FILE", tmp_path / "settings.json")
|
||||
|
||||
resp = await async_client.get("/api/settings")
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["active_provider"] == "lmstudio"
|
||||
assert "system_prompt" in data
|
||||
assert "providers" in data
|
||||
# API keys should be masked or empty
|
||||
for prov in ("anthropic", "openai"):
|
||||
key = data["providers"][prov].get("api_key", "")
|
||||
assert "****" not in key or len(key) <= 8 # masked or empty
|
||||
|
||||
|
||||
def test_patch_system_prompt(client):
|
||||
async def test_patch_system_prompt(async_client, tmp_path, monkeypatch):
|
||||
import config as cfg
|
||||
monkeypatch.setattr(cfg, "SETTINGS_FILE", tmp_path / "settings.json")
|
||||
import services.storage as st
|
||||
monkeypatch.setattr(st, "SETTINGS_FILE", tmp_path / "settings.json")
|
||||
|
||||
new_prompt = "Custom system prompt for testing."
|
||||
resp = client.patch("/api/settings", json={"system_prompt": new_prompt})
|
||||
resp = await async_client.patch("/api/settings", json={"system_prompt": new_prompt})
|
||||
assert resp.status_code == 200
|
||||
|
||||
resp2 = client.get("/api/settings")
|
||||
resp2 = await async_client.get("/api/settings")
|
||||
assert resp2.json()["system_prompt"] == new_prompt
|
||||
|
||||
|
||||
def test_patch_active_provider(client):
|
||||
resp = client.patch("/api/settings", json={"active_provider": "ollama"})
|
||||
async def test_patch_active_provider(async_client, tmp_path, monkeypatch):
|
||||
import config as cfg
|
||||
monkeypatch.setattr(cfg, "SETTINGS_FILE", tmp_path / "settings.json")
|
||||
import services.storage as st
|
||||
monkeypatch.setattr(st, "SETTINGS_FILE", tmp_path / "settings.json")
|
||||
|
||||
resp = await async_client.patch("/api/settings", json={"active_provider": "ollama"})
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["active_provider"] == "ollama"
|
||||
|
||||
|
||||
def test_patch_invalid_provider(client):
|
||||
resp = client.patch("/api/settings", json={"active_provider": "unknown"})
|
||||
async def test_patch_invalid_provider(async_client, tmp_path, monkeypatch):
|
||||
import config as cfg
|
||||
monkeypatch.setattr(cfg, "SETTINGS_FILE", tmp_path / "settings.json")
|
||||
import services.storage as st
|
||||
monkeypatch.setattr(st, "SETTINGS_FILE", tmp_path / "settings.json")
|
||||
|
||||
resp = await async_client.patch("/api/settings", json={"active_provider": "unknown"})
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
def test_patch_provider_config(client):
|
||||
resp = client.patch("/api/settings", json={
|
||||
"providers": {
|
||||
"ollama": {"model": "mistral", "base_url": "http://host.docker.internal:11434"}
|
||||
}
|
||||
})
|
||||
async def test_patch_provider_config(async_client, tmp_path, monkeypatch):
|
||||
import config as cfg
|
||||
monkeypatch.setattr(cfg, "SETTINGS_FILE", tmp_path / "settings.json")
|
||||
import services.storage as st
|
||||
monkeypatch.setattr(st, "SETTINGS_FILE", tmp_path / "settings.json")
|
||||
|
||||
resp = await async_client.patch(
|
||||
"/api/settings",
|
||||
json={
|
||||
"providers": {
|
||||
"ollama": {"model": "mistral", "base_url": "http://host.docker.internal:11434"}
|
||||
}
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["providers"]["ollama"]["model"] == "mistral"
|
||||
|
||||
|
||||
def test_masked_api_key_not_overwritten(client):
|
||||
async def test_masked_api_key_not_overwritten(async_client, tmp_path, monkeypatch):
|
||||
"""Patching with a masked key should not overwrite the real stored key."""
|
||||
# First set a real key
|
||||
client.patch("/api/settings", json={"providers": {"anthropic": {"api_key": "sk-ant-realkey"}}})
|
||||
# Then patch with masked key (simulating frontend re-submitting)
|
||||
client.patch("/api/settings", json={"providers": {"anthropic": {"api_key": "****key"}}})
|
||||
# The stored key should still be the real one
|
||||
import config as cfg
|
||||
monkeypatch.setattr(cfg, "SETTINGS_FILE", tmp_path / "settings.json")
|
||||
import services.storage as st
|
||||
settings = st.load_settings()
|
||||
assert settings["providers"]["anthropic"]["api_key"] == "sk-ant-realkey"
|
||||
monkeypatch.setattr(st, "SETTINGS_FILE", tmp_path / "settings.json")
|
||||
|
||||
# First set a real key
|
||||
await async_client.patch(
|
||||
"/api/settings",
|
||||
json={"providers": {"anthropic": {"api_key": "sk-ant-realkey"}}},
|
||||
)
|
||||
# Then patch with masked key (simulating frontend re-submitting)
|
||||
await async_client.patch(
|
||||
"/api/settings",
|
||||
json={"providers": {"anthropic": {"api_key": "****key"}}},
|
||||
)
|
||||
# The stored key should still be the real one
|
||||
stored = st.load_settings()
|
||||
assert stored["providers"]["anthropic"]["api_key"] == "sk-ant-realkey"
|
||||
|
||||
|
||||
def test_get_default_prompt(client):
|
||||
resp = client.get("/api/settings/default-prompt")
|
||||
async def test_get_default_prompt(async_client):
|
||||
resp = await async_client.get("/api/settings/default-prompt")
|
||||
assert resp.status_code == 200
|
||||
assert "system_prompt" in resp.json()
|
||||
assert len(resp.json()["system_prompt"]) > 0
|
||||
|
||||
Reference in New Issue
Block a user