Files
market_sync/tests/test_schema_models.py
T
gao d217553d79 feat(sync): sync_history 表 + base.py 自动记录每次同步
新增 23 张表 sync_history:append-only,每次 task.run() 完成追加一行。

字段:
  dataset_id / run_date / status / trigger_source / started_at / finished_at /
  elapsed_sec / rows_written / message / error / stats (JSONB) / triggered_by
索引:
  (dataset_id, started_at) / (started_at) / (status)

vs dataset_registry 的区别:
  - dataset_registry 只保留「最后一次状态」便于快查
  - sync_history 保留完整历史,支持看板按天/按 task 维度聚合 + 失败回溯

自动落库:
  - SyncTask.run() 成功后调 _record_history()
  - 异常路径也写(避免排查时漏掉崩溃的 run)
  - 落库失败仅 warning 不阻塞主流程

新增 db_ops helpers:
  - insert_sync_history()
  - list_sync_history(dataset_id?, days, limit) 看板/API 用
  - daily_sync_summary(days) 按日期聚合 ok/warning/error/blocked

_ensure_schema 改造:
  从 no-op 改为用 superuser 连接跑 create_all(),SA 2.x IF NOT EXISTS 幂等。
  这样新增 model 不用手动跑 pg_bootstrap。生产首次部署仍建议跑 pg_bootstrap
  (它还要建 role / schema)。

测试: tests/test_schema_models.py 22 → 23 张表
2026-07-07 16:55:51 +08:00

42 lines
2.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""ORM 模型 metadata 一致性测试(PostgreSQL only2026-06-16 重构后)。
历史背景:原 test_schema_models.py 还测了 schema.py vs models.py 的 DDL 漂移。
schema.py 在这次重构中已删除(MySQL DDL 不再需要),所以只保留 ORM metadata 完整性测试。
"""
from __future__ import annotations
def test_orm_metadata_registers_all_tables():
"""ORMBase.metadata 应该注册 23 张 PG 表(项目主业务表)。"""
from app.core.db import models # 触发全部模型 import
from app.core.db.orm import ORMBase
# 注意:SA 2.x 的 MetaData.tables 既能按 bare name 也能按 schema-qualified name 索引
# keyed 容器),所以用 values() 拿 Table 对象再用 .name 拿 bare 名
bare_names = {t.name for t in ORMBase.metadata.tables.values()}
assert len(bare_names) == 23, f"期望 23 张 ORM 表,实际 {len(bare_names)}: {bare_names}"
expected = {
"config", "dataset_registry", "stocks", "indices",
"kline_stock", "kline_index", "kline_5min", "moneyflow", "share",
"sectors", "stock_sector_map", "industry",
"sector_indices", "sector_features_daily", "market_regime_daily",
"tick_trade",
"longhubang_daily", "longhubang_seat", # 2026-07-01 龙虎榜
"kline_stock_ma_daily", # 2026-07-04 mairui_ma_daily
"node_categories", "nodes", "stock_node_map", # 2026-07-02 股票-节点映射
"sync_history", # 2026-07-07 同步历史表
}
assert bare_names == expected, f"ORM 表名集合与期望不符: {bare_names ^ expected}"
def test_orm_tables_in_market_data_schema():
"""所有 ORM 表应该在 market_data PG schema 下(不是 public,不是 None)。"""
from app.core.db import models # 触发全部模型 import
from app.core.db.orm import ORMBase
bad = []
for table in ORMBase.metadata.tables.values():
if table.schema != "market_data":
bad.append((table.name, table.schema))
assert not bad, f"以下表 schema 不是 market_data: {bad}"