This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
import traderai.server as server
|
||||
|
||||
|
||||
def test_config_update_rebuilds_runtime_without_restart(monkeypatch, tmp_path):
|
||||
state = {"settings": make_settings(tmp_path, model_provider="ollama", ollama_model="qwen3.5:9b")}
|
||||
|
||||
class FakeScheduler:
|
||||
def __init__(self, memory):
|
||||
self.memory = memory
|
||||
|
||||
def bind_agent(self, agent):
|
||||
self.agent = agent
|
||||
|
||||
def bind_plan_runner(self, plan_runner):
|
||||
self.plan_runner = plan_runner
|
||||
|
||||
def bind_uex_notifications(self, uex, poll_seconds=60):
|
||||
self.uex = uex
|
||||
self.poll_seconds = poll_seconds
|
||||
|
||||
def start(self):
|
||||
return None
|
||||
|
||||
def shutdown(self):
|
||||
return None
|
||||
|
||||
def list_jobs(self):
|
||||
return []
|
||||
|
||||
class FakeUEXClient:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
async def get_user(self, username=None, authenticated=False):
|
||||
return {}
|
||||
|
||||
class FakeToolRegistry:
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.pending_actions = {}
|
||||
self.plan_runner = None
|
||||
|
||||
async def approve(self, action_id):
|
||||
return {"approved": action_id}
|
||||
|
||||
async def decline(self, action_id):
|
||||
return {"declined": action_id}
|
||||
|
||||
class FakePlanRunner:
|
||||
def __init__(self, store, tools, memory, agent=None):
|
||||
self.store = store
|
||||
self.tools = tools
|
||||
self.memory = memory
|
||||
self.agent = agent
|
||||
|
||||
def bind_agent(self, agent):
|
||||
self.agent = agent
|
||||
|
||||
class FakeClient:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
async def fake_health(self):
|
||||
return {
|
||||
"online": True,
|
||||
"provider": self.provider,
|
||||
"model": self.model,
|
||||
"model_available": True,
|
||||
"message": f"{self.provider} ready",
|
||||
}
|
||||
|
||||
async def fake_chat(self, content, thread_id=None, images=None):
|
||||
return {"message": f"{self.provider}:{self.model}", "pending_actions": [], "thread_id": thread_id}
|
||||
|
||||
def fake_get_settings():
|
||||
return state["settings"]
|
||||
|
||||
def fake_save_settings(values):
|
||||
state["settings"] = make_settings(
|
||||
tmp_path,
|
||||
model_provider=values.get("model_provider", state["settings"].model_provider),
|
||||
ollama_model=values.get("ollama_model", state["settings"].ollama_model),
|
||||
codex_model=values.get("codex_model", state["settings"].codex_model),
|
||||
)
|
||||
return {"values": values, "fields": {}, "secrets_configured": {}, "app_data_dir": str(tmp_path)}
|
||||
|
||||
monkeypatch.setattr(server, "WakeScheduler", FakeScheduler)
|
||||
monkeypatch.setattr(server, "UEXClient", FakeUEXClient)
|
||||
monkeypatch.setattr(server, "ToolRegistry", FakeToolRegistry)
|
||||
monkeypatch.setattr(server, "ContinualPlanRunner", FakePlanRunner)
|
||||
monkeypatch.setattr(server, "SCMDBClient", FakeClient)
|
||||
monkeypatch.setattr(server, "CornerstoneClient", FakeClient)
|
||||
monkeypatch.setattr(server, "StarCitizenWikiClient", FakeClient)
|
||||
monkeypatch.setattr(server, "get_settings", fake_get_settings)
|
||||
monkeypatch.setattr(server, "save_settings", fake_save_settings)
|
||||
monkeypatch.setattr(
|
||||
server,
|
||||
"settings_payload",
|
||||
lambda settings=None: {"app_data_dir": str(tmp_path), "values": {}, "fields": {}, "secrets_configured": {}},
|
||||
)
|
||||
monkeypatch.setattr(server.OllamaAgent, "health", fake_health)
|
||||
monkeypatch.setattr(server.OllamaAgent, "chat", fake_chat)
|
||||
|
||||
app = server.create_app()
|
||||
with TestClient(app) as client:
|
||||
before = client.get("/api/health").json()
|
||||
assert before["model_provider"] == "ollama"
|
||||
assert before["inference"]["provider"] == "ollama"
|
||||
|
||||
updated = client.post(
|
||||
"/api/config",
|
||||
json={"values": {"model_provider": "codex", "codex_model": "gpt-5.4"}},
|
||||
).json()
|
||||
assert updated["restart_required"] is False
|
||||
|
||||
after = client.get("/api/health").json()
|
||||
assert after["model_provider"] == "codex"
|
||||
assert after["inference"]["provider"] == "codex"
|
||||
|
||||
chat = client.post("/api/chat", json={"message": "hi", "thread_id": "thread-1", "images": []}).json()
|
||||
assert chat["message"] == "codex:gpt-5.4"
|
||||
|
||||
|
||||
def make_settings(tmp_path, model_provider="ollama", ollama_model="qwen3.5:9b", codex_model="gpt-5.4"):
|
||||
return SimpleNamespace(
|
||||
traderai_memory_path=str(tmp_path / "memory.sqlite3"),
|
||||
model_provider=model_provider,
|
||||
ollama_base_url="http://localhost:11434",
|
||||
ollama_model=ollama_model,
|
||||
ollama_num_ctx=64512,
|
||||
openai_base_url="https://api.openai.com/v1",
|
||||
openai_api_key=None,
|
||||
openai_model="gpt-5.4-mini",
|
||||
model_reasoning_effort="medium",
|
||||
codex_command="codex",
|
||||
codex_model=codex_model,
|
||||
uex_base_url="https://api.uexcorp.space/2.0",
|
||||
uex_secret_key=None,
|
||||
uex_bearer_token=None,
|
||||
traderai_user_name=None,
|
||||
uex_notification_poll_seconds=60,
|
||||
require_write_approval=True,
|
||||
scmdb_base_url="https://scmdb.net",
|
||||
cornerstone_base_url="https://finder.cstone.space",
|
||||
scwiki_base_url="https://starcitizen.tools",
|
||||
scwiki_api_base_url="https://api.star-citizen.wiki",
|
||||
)
|
||||
Reference in New Issue
Block a user