"""冒烟测试:不依赖 MySQL/网络,只验证 import + 基本逻辑。 跑:pytest tests/test_smoke.py -v """ import pytest def test_import_config(): from app.core.config import settings assert settings is not None assert hasattr(settings, "pg_host") def test_import_data_source_base(): from app.core.datasource.base import DataSource, FetchResult, registry assert DataSource is not None assert FetchResult is not None assert registry is not None # registry 应该是 dict-like assert hasattr(registry, "register") assert hasattr(registry, "get") def test_import_sync_base(): from app.core.sync.base import SyncTask, _is_market_closed, _effective_sync_end assert SyncTask is not None assert isinstance(_is_market_closed(), bool) assert isinstance(_effective_sync_end(), str) # 同步任务基类不允许空 dataset_id with pytest.raises(ValueError): SyncTask() def test_tasks_registry(): from app.tasks import TASKS, get_task assert isinstance(TASKS, dict) # 注:每次新增 task 都要更新这里的数字 # 当前 13 个:stock_basic, kline_daily, kline_index, kline_5min, tick_trade, # moneyflow, industry_sector, sector_features, share_snapshot, market_regime, # longhubang, mairui_ma_daily, stock_node assert len(TASKS) == 13 # get_task 应该返回实例 task = get_task("kline_daily") assert task.dataset_id == "kline_daily" def test_sync_definitions(): from app.core.sync.registry import SYNC_DEFINITIONS assert len(SYNC_DEFINITIONS) == 13 # 所有 dataset_id 应唯一 ids = [d["dataset_id"] for d in SYNC_DEFINITIONS] assert len(set(ids)) == 13 # tick_trade 应在其中且 sort_order=45 tt = next(d for d in SYNC_DEFINITIONS if d["dataset_id"] == "tick_trade") assert tt["sort_order"] == 45 assert "stock_basic" in tt["dependency_ids"] # longhubang 应在其中且 sort_order=85 lh = next(d for d in SYNC_DEFINITIONS if d["dataset_id"] == "longhubang") assert lh["sort_order"] == 85 assert "stock_basic" in lh["dependency_ids"] # stock_node 应在其中且 sort_order=21 sn = next(d for d in SYNC_DEFINITIONS if d["dataset_id"] == "stock_node") assert sn["sort_order"] == 21 assert "stock_basic" in sn["dependency_ids"] def test_build_default_registry(): """不连接网络,只看是否能完成注册(部分源 may not available 不影响注册)。""" from app.core.datasource.base import registry from app.core.datasource.registry import build_default_registry build_default_registry() keys = [s.key for s in registry.all()] assert "datasource_xinlang" in keys assert "datasource_baostock" in keys assert "datasource_mairui" in keys # akshare:2026-07-01 决策更新——龙虎榜无替代源,烟测通过,可注册。 # 见 [[no-akshare]] 记忆:仅"无替代"+"akshare 烟测 OK"才允许用。 assert "datasource_akshare_lhb" in keys assert "datasource_xueqiu" in keys def test_data_source_abstract(): """DataSource 抽象方法未实现时不应能被直接实例化。""" from app.core.datasource.base import DataSource with pytest.raises(TypeError): DataSource() # 抽象方法未实现 def test_api_health_route(): """FastAPI 加载 + /api/health 路由可访问。""" from fastapi.testclient import TestClient from app.api.main import app client = TestClient(app) resp = client.get("/api/health") assert resp.status_code == 200 body = resp.json() assert body.get("status") == "ok" def test_effective_sync_end_format(): from app.core.sync.base import _effective_sync_end s = _effective_sync_end() # YYYY-MM-DD 共 10 字符 assert len(s) == 10 assert s[4] == "-" assert s[7] == "-"