后端: - 拆分出 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>
140 lines
5.5 KiB
Python
140 lines
5.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Tests for data_tools preferring split services when configured."""
|
|
|
|
from backend.tools import data_tools
|
|
from shared.schema import CompanyNews, FinancialMetrics, InsiderTrade, LineItem, Price
|
|
|
|
|
|
def test_data_tools_prefers_trading_service(monkeypatch):
|
|
monkeypatch.setenv("TRADING_SERVICE_URL", "http://localhost:8001")
|
|
monkeypatch.setenv("SERVICE_NAME", "agent_service")
|
|
monkeypatch.setattr(data_tools._cache, "get_prices", lambda key: None)
|
|
monkeypatch.setattr(data_tools._cache, "get_financial_metrics", lambda key: None)
|
|
monkeypatch.setattr(data_tools._cache, "get_insider_trades", lambda key: None)
|
|
monkeypatch.setattr(data_tools._cache, "get_company_news", lambda key: None)
|
|
|
|
def fake_service_get_json(base_url, path, *, params):
|
|
if path == "/api/prices":
|
|
return {
|
|
"ticker": "AAPL",
|
|
"prices": [
|
|
Price(
|
|
open=1,
|
|
close=2,
|
|
high=3,
|
|
low=1,
|
|
volume=10,
|
|
time="2026-03-16",
|
|
).model_dump()
|
|
],
|
|
}
|
|
if path == "/api/financials":
|
|
return {
|
|
"financial_metrics": [
|
|
FinancialMetrics(
|
|
ticker="AAPL",
|
|
report_period="2026-03-16",
|
|
period="ttm",
|
|
currency="USD",
|
|
market_cap=123.0,
|
|
enterprise_value=None,
|
|
price_to_earnings_ratio=None,
|
|
price_to_book_ratio=None,
|
|
price_to_sales_ratio=None,
|
|
enterprise_value_to_ebitda_ratio=None,
|
|
enterprise_value_to_revenue_ratio=None,
|
|
free_cash_flow_yield=None,
|
|
peg_ratio=None,
|
|
gross_margin=None,
|
|
operating_margin=None,
|
|
net_margin=None,
|
|
return_on_equity=None,
|
|
return_on_assets=None,
|
|
return_on_invested_capital=None,
|
|
asset_turnover=None,
|
|
inventory_turnover=None,
|
|
receivables_turnover=None,
|
|
days_sales_outstanding=None,
|
|
operating_cycle=None,
|
|
working_capital_turnover=None,
|
|
current_ratio=None,
|
|
quick_ratio=None,
|
|
cash_ratio=None,
|
|
operating_cash_flow_ratio=None,
|
|
debt_to_equity=None,
|
|
debt_to_assets=None,
|
|
interest_coverage=None,
|
|
revenue_growth=None,
|
|
earnings_growth=None,
|
|
book_value_growth=None,
|
|
earnings_per_share_growth=None,
|
|
free_cash_flow_growth=None,
|
|
operating_income_growth=None,
|
|
ebitda_growth=None,
|
|
payout_ratio=None,
|
|
earnings_per_share=None,
|
|
book_value_per_share=None,
|
|
free_cash_flow_per_share=None,
|
|
).model_dump()
|
|
]
|
|
}
|
|
if path == "/api/insider-trades":
|
|
return {
|
|
"insider_trades": [
|
|
InsiderTrade(ticker="AAPL", filing_date="2026-03-16").model_dump()
|
|
]
|
|
}
|
|
if path == "/api/news":
|
|
return {
|
|
"news": [
|
|
CompanyNews(
|
|
ticker="AAPL",
|
|
title="Title",
|
|
source="polygon",
|
|
url="https://example.com",
|
|
).model_dump()
|
|
]
|
|
}
|
|
if path == "/api/market-cap":
|
|
return {"ticker": "AAPL", "end_date": "2026-03-16", "market_cap": 2.5e12}
|
|
if path == "/api/line-items":
|
|
return {
|
|
"search_results": [
|
|
LineItem(
|
|
ticker="AAPL",
|
|
report_period="2026-03-16",
|
|
period="ttm",
|
|
currency="USD",
|
|
free_cash_flow=321.0,
|
|
).model_dump()
|
|
]
|
|
}
|
|
raise AssertionError(path)
|
|
|
|
monkeypatch.setattr(data_tools, "_service_get_json", fake_service_get_json)
|
|
|
|
prices = data_tools.get_prices("AAPL", "2026-03-01", "2026-03-16")
|
|
metrics = data_tools.get_financial_metrics("AAPL", "2026-03-16")
|
|
trades = data_tools.get_insider_trades("AAPL", "2026-03-16")
|
|
news = data_tools.get_company_news("AAPL", "2026-03-16")
|
|
market_cap = data_tools.get_market_cap("AAPL", "2026-03-16")
|
|
line_items = data_tools.search_line_items(
|
|
"AAPL",
|
|
["free_cash_flow"],
|
|
"2026-03-16",
|
|
)
|
|
|
|
assert prices[0].close == 2
|
|
assert metrics[0].ticker == "AAPL"
|
|
assert trades[0].ticker == "AAPL"
|
|
assert news[0].ticker == "AAPL"
|
|
assert market_cap == 2.5e12
|
|
assert line_items[0].free_cash_flow == 321.0
|
|
|
|
|
|
def test_data_tools_skips_self_recursion_for_trading_service(monkeypatch):
|
|
monkeypatch.setenv("TRADING_SERVICE_URL", "http://localhost:8001")
|
|
monkeypatch.setenv("SERVICE_NAME", "trading_service")
|
|
|
|
assert data_tools._trading_service_url() is None
|