Spaces:
Running
Running
| from __future__ import annotations | |
| from pathlib import Path | |
| from unittest.mock import patch | |
| import httpx | |
| from fastapi.testclient import TestClient | |
| from api.admin_config import MASKED_SECRET | |
| from api.admin_urls import local_admin_url | |
| from api.app import create_app | |
| from config.settings import Settings | |
| def _local_client(app): | |
| return TestClient(app, client=("127.0.0.1", 50000)) | |
| def _set_home(monkeypatch, tmp_path: Path) -> None: | |
| monkeypatch.setenv("HOME", str(tmp_path)) | |
| monkeypatch.setenv("USERPROFILE", str(tmp_path)) | |
| monkeypatch.chdir(tmp_path) | |
| def _clear_process_config(monkeypatch) -> None: | |
| for key in ( | |
| "MODEL", | |
| "NVIDIA_NIM_API_KEY", | |
| "OPENROUTER_API_KEY", | |
| "ANTHROPIC_AUTH_TOKEN", | |
| "FCC_ENV_FILE", | |
| "HOST", | |
| "PORT", | |
| "LOG_FILE", | |
| "ZAI_BASE_URL", | |
| "CLAUDE_WORKSPACE", | |
| "CLAUDE_CLI_BIN", | |
| ): | |
| monkeypatch.delenv(key, raising=False) | |
| def test_admin_page_is_loopback_only(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| app = create_app(lifespan_enabled=False) | |
| assert _local_client(app).get("/admin").status_code == 200 | |
| remote_client = TestClient(app, client=("203.0.113.10", 50000)) | |
| assert remote_client.get("/admin").status_code == 403 | |
| def test_admin_page_no_longer_renders_generated_env_panel(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).get("/admin") | |
| assert response.status_code == 200 | |
| assert "Generated Env" not in response.text | |
| assert "envPreview" not in response.text | |
| def test_admin_page_no_longer_renders_global_status_header(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).get("/admin") | |
| assert response.status_code == 200 | |
| assert "Local Admin" not in response.text | |
| assert "serverStatus" not in response.text | |
| assert "modelBadge" not in response.text | |
| def test_admin_static_no_longer_fetches_global_status_header(): | |
| script = Path("api/admin_static/admin.js").read_text(encoding="utf-8") | |
| assert 'api("/admin/api/status")' not in script | |
| assert "updateHeader" not in script | |
| assert '"Running"' not in script | |
| assert "serverStatus" not in script | |
| assert "modelBadge" not in script | |
| def test_admin_static_hides_managed_source_label(): | |
| script = Path("api/admin_static/admin.js").read_text(encoding="utf-8") | |
| assert 'managed_env: "",' in script | |
| assert "hasOwnProperty.call(labels, source)" in script | |
| assert 'parts.push("locked")' in script | |
| assert "sourceEl.textContent = source" in script | |
| def test_admin_config_masks_secrets_and_exposes_manifest(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).get("/admin/api/config") | |
| assert response.status_code == 200 | |
| body = response.json() | |
| keys = {field["key"] for field in body["fields"]} | |
| assert "ANTHROPIC_AUTH_TOKEN" in keys | |
| assert "OPENROUTER_API_KEY" in keys | |
| assert "FIREWORKS_API_KEY" in keys | |
| assert "GEMINI_API_KEY" in keys | |
| assert "GROQ_API_KEY" in keys | |
| assert "CEREBRAS_API_KEY" in keys | |
| assert "ZAI_BASE_URL" not in keys | |
| assert "CLAUDE_WORKSPACE" not in keys | |
| assert "CLAUDE_CLI_BIN" not in keys | |
| assert "LOG_FILE" not in keys | |
| auth_field = next( | |
| field for field in body["fields"] if field["key"] == "ANTHROPIC_AUTH_TOKEN" | |
| ) | |
| assert auth_field["secret"] is True | |
| assert auth_field["value"] == MASKED_SECRET | |
| assert auth_field["source"] == "template" | |
| def test_admin_config_preserves_managed_env_source_contract(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| env_file = tmp_path / ".fcc" / ".env" | |
| env_file.parent.mkdir(parents=True) | |
| env_file.write_text("MODEL=open_router/managed-model\n", encoding="utf-8") | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).get("/admin/api/config") | |
| assert response.status_code == 200 | |
| body = response.json() | |
| model_field = next(field for field in body["fields"] if field["key"] == "MODEL") | |
| assert model_field["source"] == "managed_env" | |
| assert model_field["locked"] is False | |
| def test_admin_validate_rejects_bad_model_shape(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).post( | |
| "/admin/api/config/validate", | |
| json={"values": {"MODEL": "missing-provider-prefix"}}, | |
| ) | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["valid"] is False | |
| assert any("provider type" in error for error in body["errors"]) | |
| def test_admin_apply_writes_complete_managed_env_and_masks_preview( | |
| monkeypatch, tmp_path | |
| ): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).post( | |
| "/admin/api/config/apply", | |
| json={ | |
| "values": { | |
| "MODEL": "open_router/test-model", | |
| "OPENROUTER_API_KEY": "router-secret", | |
| } | |
| }, | |
| ) | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["applied"] is True | |
| assert "OPENROUTER_API_KEY=********" in body["env_preview"] | |
| env_file = tmp_path / ".fcc" / ".env" | |
| text = env_file.read_text("utf-8") | |
| assert "MODEL=open_router/test-model" in text | |
| assert "OPENROUTER_API_KEY=router-secret" in text | |
| assert "ANTHROPIC_AUTH_TOKEN=" in text | |
| assert body["restart"] == { | |
| "required": False, | |
| "automatic": False, | |
| "admin_url": None, | |
| "fields": [], | |
| } | |
| def test_admin_apply_writes_fireworks_key_and_masks_preview(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).post( | |
| "/admin/api/config/apply", | |
| json={ | |
| "values": { | |
| "MODEL": "fireworks/test-model", | |
| "FIREWORKS_API_KEY": "fw-secret", | |
| } | |
| }, | |
| ) | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["applied"] is True | |
| assert "FIREWORKS_API_KEY=********" in body["env_preview"] | |
| env_file = tmp_path / ".fcc" / ".env" | |
| text = env_file.read_text(encoding="utf-8") | |
| assert "MODEL=fireworks/test-model" in text | |
| assert "FIREWORKS_API_KEY=fw-secret" in text | |
| def test_admin_apply_writes_gemini_key_and_masks_preview(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).post( | |
| "/admin/api/config/apply", | |
| json={ | |
| "values": { | |
| "MODEL": "gemini/models/gemini-3.1-flash-lite", | |
| "GEMINI_API_KEY": "gm-secret", | |
| } | |
| }, | |
| ) | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["applied"] is True | |
| assert "GEMINI_API_KEY=********" in body["env_preview"] | |
| env_file = tmp_path / ".fcc" / ".env" | |
| text = env_file.read_text(encoding="utf-8") | |
| assert "MODEL=gemini/models/gemini-3.1-flash-lite" in text | |
| assert "GEMINI_API_KEY=gm-secret" in text | |
| def test_admin_apply_writes_groq_key_and_masks_preview(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).post( | |
| "/admin/api/config/apply", | |
| json={ | |
| "values": { | |
| "MODEL": "groq/llama-3.3-70b-versatile", | |
| "GROQ_API_KEY": "gq-secret", | |
| } | |
| }, | |
| ) | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["applied"] is True | |
| assert "GROQ_API_KEY=********" in body["env_preview"] | |
| env_file = tmp_path / ".fcc" / ".env" | |
| text = env_file.read_text(encoding="utf-8") | |
| assert "MODEL=groq/llama-3.3-70b-versatile" in text | |
| assert "GROQ_API_KEY=gq-secret" in text | |
| def test_admin_apply_writes_cerebras_key_and_masks_preview(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).post( | |
| "/admin/api/config/apply", | |
| json={ | |
| "values": { | |
| "MODEL": "cerebras/llama3.1-8b", | |
| "CEREBRAS_API_KEY": "cb-secret", | |
| } | |
| }, | |
| ) | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["applied"] is True | |
| assert "CEREBRAS_API_KEY=********" in body["env_preview"] | |
| env_file = tmp_path / ".fcc" / ".env" | |
| text = env_file.read_text(encoding="utf-8") | |
| assert "MODEL=cerebras/llama3.1-8b" in text | |
| assert "CEREBRAS_API_KEY=cb-secret" in text | |
| def test_admin_apply_preserves_hidden_diagnostics_and_smoke_values( | |
| monkeypatch, tmp_path | |
| ): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| env_file = tmp_path / ".fcc" / ".env" | |
| env_file.parent.mkdir(parents=True) | |
| env_file.write_text( | |
| "\n".join( | |
| [ | |
| "MODEL=nvidia_nim/old-model", | |
| "LOG_RAW_API_PAYLOADS=true", | |
| "FCC_SMOKE_MODEL_ZAI=zai/smoke-model", | |
| "", | |
| ] | |
| ), | |
| encoding="utf-8", | |
| ) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).post( | |
| "/admin/api/config/apply", | |
| json={"values": {"MODEL": "open_router/test-model"}}, | |
| ) | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["applied"] is True | |
| text = env_file.read_text("utf-8") | |
| assert "MODEL=open_router/test-model" in text | |
| assert "LOG_RAW_API_PAYLOADS=true" in text | |
| assert "FCC_SMOKE_MODEL_ZAI=zai/smoke-model" in text | |
| def test_admin_apply_omits_stale_zai_base_url(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| env_file = tmp_path / ".fcc" / ".env" | |
| env_file.parent.mkdir(parents=True) | |
| env_file.write_text( | |
| "\n".join( | |
| [ | |
| "MODEL=zai/glm-5.1", | |
| "ZAI_API_KEY=zai-secret", | |
| "ZAI_BASE_URL=https://custom.zai.invalid/v1", | |
| "", | |
| ] | |
| ), | |
| encoding="utf-8", | |
| ) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).post( | |
| "/admin/api/config/apply", | |
| json={"values": {"MODEL": "zai/glm-5.1"}}, | |
| ) | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["applied"] is True | |
| text = env_file.read_text("utf-8") | |
| assert "ZAI_API_KEY=zai-secret" in text | |
| assert "ZAI_BASE_URL" not in text | |
| def test_admin_apply_omits_stale_fixed_claude_runtime_settings(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| env_file = tmp_path / ".fcc" / ".env" | |
| env_file.parent.mkdir(parents=True) | |
| env_file.write_text( | |
| "\n".join( | |
| [ | |
| "MODEL=open_router/test-model", | |
| "CLAUDE_WORKSPACE=C:/custom/workspace", | |
| "CLAUDE_CLI_BIN=claude-custom", | |
| "", | |
| ] | |
| ), | |
| encoding="utf-8", | |
| ) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).post( | |
| "/admin/api/config/apply", | |
| json={"values": {"MODEL": "open_router/test-model"}}, | |
| ) | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["applied"] is True | |
| text = env_file.read_text("utf-8") | |
| assert "MODEL=open_router/test-model" in text | |
| assert "CLAUDE_WORKSPACE" not in text | |
| assert "CLAUDE_CLI_BIN" not in text | |
| def test_admin_apply_restart_required_reports_automatic_restart(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| app = create_app(lifespan_enabled=False) | |
| callbacks: list[str] = [] | |
| async def restart_callback() -> None: | |
| callbacks.append("restart") | |
| app.state.admin_restart_callback = restart_callback | |
| response = _local_client(app).post( | |
| "/admin/api/config/apply", | |
| json={"values": {"PORT": "9090"}}, | |
| ) | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["applied"] is True | |
| assert body["pending_fields"] == ["PORT"] | |
| assert body["restart"] == { | |
| "required": True, | |
| "automatic": True, | |
| "admin_url": "http://127.0.0.1:9090/admin", | |
| "fields": ["PORT"], | |
| } | |
| assert callbacks == ["restart"] | |
| def test_admin_apply_restart_required_reports_manual_fallback(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| app = create_app(lifespan_enabled=False) | |
| response = _local_client(app).post( | |
| "/admin/api/config/apply", | |
| json={"values": {"PORT": "9091"}}, | |
| ) | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["applied"] is True | |
| assert body["pending_fields"] == ["PORT"] | |
| assert body["restart"] == { | |
| "required": True, | |
| "automatic": False, | |
| "admin_url": None, | |
| "fields": ["PORT"], | |
| } | |
| def test_admin_process_env_values_are_locked_and_not_written(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| monkeypatch.setenv("MODEL", "open_router/process-model") | |
| app = create_app(lifespan_enabled=False) | |
| config = _local_client(app).get("/admin/api/config").json() | |
| model_field = next(field for field in config["fields"] if field["key"] == "MODEL") | |
| assert model_field["locked"] is True | |
| assert model_field["source"] == "process" | |
| response = _local_client(app).post( | |
| "/admin/api/config/apply", | |
| json={"values": {"MODEL": "deepseek/managed-model"}}, | |
| ) | |
| assert response.status_code == 200 | |
| env_file = tmp_path / ".fcc" / ".env" | |
| assert "deepseek/managed-model" not in env_file.read_text("utf-8") | |
| def test_admin_first_apply_migrates_repo_env(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| monkeypatch.chdir(tmp_path) | |
| (tmp_path / ".env").write_text( | |
| "MODEL=deepseek/deepseek-chat\nDEEPSEEK_API_KEY=deepseek-secret\n", | |
| encoding="utf-8", | |
| ) | |
| app = create_app(lifespan_enabled=False) | |
| config = _local_client(app).get("/admin/api/config").json() | |
| model_field = next(field for field in config["fields"] if field["key"] == "MODEL") | |
| assert model_field["value"] == "deepseek/deepseek-chat" | |
| assert model_field["source"] == "repo_env" | |
| response = _local_client(app).post( | |
| "/admin/api/config/apply", | |
| json={"values": {}}, | |
| ) | |
| assert response.status_code == 200 | |
| managed_text = (tmp_path / ".fcc" / ".env").read_text("utf-8") | |
| assert "MODEL=deepseek/deepseek-chat" in managed_text | |
| assert "DEEPSEEK_API_KEY=deepseek-secret" in managed_text | |
| def test_admin_local_provider_status_reports_reachable(monkeypatch, tmp_path): | |
| _set_home(monkeypatch, tmp_path) | |
| _clear_process_config(monkeypatch) | |
| app = create_app(lifespan_enabled=False) | |
| class FakeAsyncClient: | |
| def __init__(self, *args, **kwargs): | |
| pass | |
| async def __aenter__(self): | |
| return self | |
| async def __aexit__(self, *args): | |
| return None | |
| async def get(self, url: str): | |
| return httpx.Response(200, json={"data": []}) | |
| with patch("api.admin_routes.httpx.AsyncClient", FakeAsyncClient): | |
| response = _local_client(app).get("/admin/api/providers/local-status") | |
| assert response.status_code == 200 | |
| providers = response.json()["providers"] | |
| assert {provider["status"] for provider in providers} == {"reachable"} | |
| def test_admin_launch_url_uses_loopback_for_wildcard_host(): | |
| settings = Settings.model_construct(host="0.0.0.0", port=8082) | |
| assert local_admin_url(settings) == "http://127.0.0.1:8082/admin" | |