from datetime import datetime from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import StaticPool from app.core.config import get_settings from app.db.models import AgentRun, Base, Signal from app.db.session import get_db from app.main import create_app def test_health_endpoint_returns_app_identity(monkeypatch) -> None: # 健康检查只暴露服务身份,不触发Agent运行或数据库写入。 monkeypatch.setenv("SIGNALSCOUT_ENV", "test") get_settings.cache_clear() with TestClient(create_app()) as client: response = client.get("/health") get_settings.cache_clear() assert response.status_code == 200 assert response.json()["status"] == "ok" assert response.json()["app"] == "SignalScout" def test_home_page_returns_dashboard_shell(monkeypatch) -> None: # 根路径应该展示业务工作台,而不是返回接口404。 monkeypatch.setenv("SIGNALSCOUT_ENV", "test") get_settings.cache_clear() with TestClient(create_app()) as client: response = client.get("/") get_settings.cache_clear() assert response.status_code == 200 assert "SignalScout" in response.text assert "今日 AI 信号" in response.text assert "renderMarkdownReport" in response.text assert "自动记录" not in response.text assert "采集今日情报" not in response.text def test_manual_agent_run_endpoint_is_not_exposed(monkeypatch) -> None: # Agent只通过后台调度器自动运行,HTTP API不提供手动启动入口。 monkeypatch.setenv("SIGNALSCOUT_ENV", "test") get_settings.cache_clear() with TestClient(create_app()) as client: response = client.post("/agent/runs/daily") stream_response = client.post("/agent/runs/daily/stream") get_settings.cache_clear() assert response.status_code == 404 assert stream_response.status_code == 404 def test_dashboard_returns_news_and_github_signals_separately(monkeypatch) -> None: # 仪表盘接口直接返回分组后的业务数据,前端不再用标题或来源名自行猜测分类。 monkeypatch.setenv("SIGNALSCOUT_ENV", "test") get_settings.cache_clear() engine = create_engine( "sqlite:///:memory:", connect_args={"check_same_thread": False}, poolclass=StaticPool, future=True, ) TestingSessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False, future=True) Base.metadata.create_all(engine) with TestingSessionLocal() as session: run = AgentRun(status="completed", started_at=datetime(2026, 7, 8, 8, 0), request_payload={}) session.add(run) session.flush() session.add_all( [ Signal( run_id=run.id, source_type="news", topic="AI 新闻", title="New model release", summary="模型发布动态。", source_url="https://example.com/news", source_url_hash="news-hash", source_name="Example News", published_at=datetime(2026, 7, 8, 9, 0), importance=4, entities=["Example"], ), Signal( run_id=run.id, source_type="github", topic="GitHub 热门项目", title="example/agent-kit", summary="开源项目动态。", source_url="https://github.com/example/agent-kit", source_url_hash="github-hash", source_name="GitHub", published_at=datetime(2026, 7, 8, 10, 0), importance=5, entities=["example/agent-kit"], ), ] ) session.commit() def override_get_db(): # 测试依赖把接口请求限制在内存库里,避免触碰开发数据库。 db = TestingSessionLocal() try: yield db finally: db.close() app = create_app() app.dependency_overrides[get_db] = override_get_db with TestClient(app) as client: dashboard_response = client.get("/dashboard") github_response = client.get("/signals", params={"source_type": "github"}) get_settings.cache_clear() dashboard = dashboard_response.json() assert dashboard_response.status_code == 200 assert [item["source_type"] for item in dashboard["news_signals"]] == ["news"] assert [item["source_type"] for item in dashboard["github_signals"]] == ["github"] assert github_response.status_code == 200 assert [item["title"] for item in github_response.json()] == ["example/agent-kit"]