# backend/tests/test_settings.py — 설정 라우터(시스템 스냅샷 + LLM 편집/적용/테스트) + 프로필/토큰 from app import runtime_config from app.llm.provider import get_provider def test_system_snapshot_readonly(client): d = client.get("/api/settings/system").json() assert d["env"] == "dev" assert d["auth_enabled"] is False # 데모 기본 off assert d["database"] == "sqlite" assert set(d["connector_modes"]) == {"calendar", "mail"} # 기본은 전부 mock assert all(v == "mock" for v in d["connector_modes"].values()) def test_llm_config_defaults_match_env(client): d = client.get("/api/settings/llm").json() assert d["provider"] == "ollama" assert d["overridden"] is False # 오버레이 없음 → env 기본 assert "ollama" in d["provider_options"] # 보조 프로바이더 노출 assert "stt_provider" in d and "vision_provider" in d def test_llm_update_persists_and_applies(client): # ollama + 커스텀 모델로 저장 → 영속 + 런타임 반영 r = client.put("/api/settings/llm", json={"provider": "ollama", "model": "my-model"}) assert r.status_code == 200 body = r.json() assert body["provider"] == "ollama" assert body["model"] == "my-model" assert body["overridden"] is True # 다시 GET 해도 유지(영속) again = client.get("/api/settings/llm").json() assert again["provider"] == "ollama" and again["model"] == "my-model" # 런타임 오버레이가 실제 provider 선택에 반영되는가(force 없이) assert runtime_config.effective_llm().provider == "ollama" assert get_provider().name == "ollama" def test_llm_update_reset_to_default(client): client.put("/api/settings/llm", json={"provider": "ollama"}) assert client.get("/api/settings/llm").json()["provider"] == "ollama" # 빈 문자열 → 기본으로 되돌리기 r = client.put("/api/settings/llm", json={"provider": ""}) assert r.status_code == 200 assert r.json()["overridden"] is False assert runtime_config.snapshot() == {} def test_llm_update_rejects_bad_provider(client): r = client.put("/api/settings/llm", json={"provider": "gpt5"}) assert r.status_code == 400 def test_llm_test_uses_ollama(client): # phase-16+: 휴리스틱 옵션 제거 → 연결 테스트는 항상 Ollama 도달성으로 확인. r = client.post("/api/settings/llm/test", json={"provider": "ollama"}) assert r.status_code == 200 d = r.json() assert d["provider"] == "ollama" assert isinstance(d["reachable"], bool) # 실 Ollama 가동 여부에 따라 달라짐(환경 의존) def test_profile_update(client): r = client.patch("/api/me", json={"name": "지우님", "role": "프로덕트 리드"}) assert r.status_code == 200 d = r.json() assert d["name"] == "지우님" assert d["initial"] == "지" assert d["role"] == "프로덕트 리드" # 영속 확인 assert client.get("/api/me").json()["role"] == "프로덕트 리드" def test_api_tokens_create_list_revoke(client): # 처음엔 토큰 없음 assert client.get("/api/me/tokens").json() == [] # 생성 → 평문 1회 노출 created = client.post("/api/me/tokens", params={"name": "CLI"}).json() assert created["token"] and created["name"] == "CLI" tid = created["id"] # 목록(평문 미포함) rows = client.get("/api/me/tokens").json() assert len(rows) == 1 and rows[0]["id"] == tid and "token" not in rows[0] assert rows[0]["revoked"] is False # 폐기 assert client.delete(f"/api/me/tokens/{tid}").status_code == 200 assert client.get("/api/me/tokens").json()[0]["revoked"] is True