feat: 微服务架构拆分和前后端优化
后端: - 拆分出 agent_service, runtime_service, trading_service, news_service - Gateway 模块化拆分 (gateway_*.py) - 添加 domains/ 领域层 - 新增 control_client, runtime_client - 更新 start-dev.sh 支持 split 服务模式 前端: - 完善 API 服务层 (newsApi, tradingApi) - 更新 vite.config.js - Explain 组件优化 测试: - 添加多个服务 app 测试 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
107
backend/tests/test_service_clients.py
Normal file
107
backend/tests/test_service_clients.py
Normal file
@@ -0,0 +1,107 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Tests for split-aware shared service clients."""
|
||||
|
||||
import pytest
|
||||
|
||||
from shared.client.control_client import ControlPlaneClient
|
||||
from shared.client.runtime_client import RuntimeServiceClient
|
||||
|
||||
|
||||
class _DummyResponse:
|
||||
def __init__(self, payload):
|
||||
self._payload = payload
|
||||
|
||||
def raise_for_status(self):
|
||||
return None
|
||||
|
||||
def json(self):
|
||||
return self._payload
|
||||
|
||||
|
||||
class _DummyAsyncClient:
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
async def get(self, path, params=None):
|
||||
self.calls.append(("get", path, params))
|
||||
return _DummyResponse({"path": path, "params": params})
|
||||
|
||||
async def post(self, path, json=None):
|
||||
self.calls.append(("post", path, json))
|
||||
return _DummyResponse({"path": path, "json": json})
|
||||
|
||||
async def put(self, path, json=None):
|
||||
self.calls.append(("put", path, json))
|
||||
return _DummyResponse({"path": path, "json": json})
|
||||
|
||||
async def aclose(self):
|
||||
return None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_control_plane_client_hits_current_workspace_and_guard_routes():
|
||||
client = ControlPlaneClient()
|
||||
client._client = _DummyAsyncClient()
|
||||
|
||||
await client.list_workspaces()
|
||||
await client.get_workspace("demo")
|
||||
await client.list_agents("demo")
|
||||
await client.get_agent("demo", "risk_manager")
|
||||
await client.fetch_pending_approvals()
|
||||
await client.approve_pending_approval("ap-1")
|
||||
await client.deny_pending_approval("ap-2", reason="nope")
|
||||
|
||||
assert client._client.calls == [
|
||||
("get", "/workspaces", None),
|
||||
("get", "/workspaces/demo", None),
|
||||
("get", "/workspaces/demo/agents", None),
|
||||
("get", "/workspaces/demo/agents/risk_manager", None),
|
||||
("get", "/guard/pending", None),
|
||||
(
|
||||
"post",
|
||||
"/guard/approve",
|
||||
{
|
||||
"approval_id": "ap-1",
|
||||
"one_time": True,
|
||||
"expires_in_minutes": 30,
|
||||
},
|
||||
),
|
||||
(
|
||||
"post",
|
||||
"/guard/deny",
|
||||
{
|
||||
"approval_id": "ap-2",
|
||||
"reason": "nope",
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runtime_service_client_hits_current_runtime_routes():
|
||||
client = RuntimeServiceClient()
|
||||
client._client = _DummyAsyncClient()
|
||||
|
||||
await client.fetch_context()
|
||||
await client.fetch_agents()
|
||||
await client.fetch_events()
|
||||
await client.fetch_gateway_port()
|
||||
await client.start_runtime({"tickers": ["AAPL"]})
|
||||
await client.stop_runtime(force=True)
|
||||
await client.restart_runtime({"tickers": ["MSFT"]})
|
||||
await client.fetch_current_runtime()
|
||||
await client.get_runtime_config()
|
||||
await client.update_runtime_config({"schedule_mode": "intraday"})
|
||||
|
||||
assert client._client.calls == [
|
||||
("get", "/context", None),
|
||||
("get", "/agents", None),
|
||||
("get", "/events", None),
|
||||
("get", "/gateway/port", None),
|
||||
("post", "/start", {"tickers": ["AAPL"]}),
|
||||
("post", "/stop?force=true", None),
|
||||
("post", "/restart", {"tickers": ["MSFT"]}),
|
||||
("get", "/current", None),
|
||||
("get", "/config", None),
|
||||
("put", "/config", {"schedule_mode": "intraday"}),
|
||||
]
|
||||
Reference in New Issue
Block a user