# -*- coding: utf-8 -*-
"""
全部单测一次跑完 (零外部依赖, 不连库)
======================================
运行:  docker compose run --rm pms-web python scripts/run_tests.py

包含:
  test_core_units.py    仓位规划器 / 安全垫与成本账                     (14 例)
  test_batch2_units.py  命令状态机 / 方案生成器 / 回放对账纯逻辑         (35 例)
  test_batch3_units.py  规则闸 / 择时执行器实现B 纯逻辑                 (21 例)
  test_batch4_units.py  动作引擎 四类自主动作触发与数量口径             (11 例)
  test_batch5_units.py  决策系统信号流解析与消化口径                    (8 例)
  test_batch6_units.py  ws 通道: 测试向量/签名/公钥/水位/DDL/逐笔入账    (65 例)
  test_batch7_units.py  上游选股计划: 解析/新鲜度/候选筛选/取数守卫      (32 例)
  test_batch8_units.py  榜单变化: 名册指纹/三种语义/尾部闸/落库往返      (60 例)
  test_batch9_units.py  成本价体检 / 对账按日推进 / 行业闸 / 取整记账      (49 例)
  test_batch10_units.py 静默失败专项: 关键路径不许丢返回值 + 八条实例      (48 例)
  test_batch11_units.py 择时实现A: 本地检查等价/应答折算/缓存冷却/退B      (24 例)
  test_batch12_units.py 自主新建仓: 选票与滚动扣减/硬数字裁剪/漂移/预算/
                        转多信号插队与留痕/窗口末日只对命令/跳过原因分得开/
                        研判请求体的数据库类型                          (44 例)
  test_batch13_units.py 决策系统卖出采纳: 门槛分档/清场标记/确认加速/闭仓清场 (4 例)
  test_batch14_units.py 宏观择时: 指数计算与对齐/区域迟滞/周期与对数映射/
                        分方向触发/让路与冷却/宏观闸/建议采纳            (25 例)
  test_wiring.py        装配自检: 服务层→核心→落表 全链路 (内存桩)      (58 例)
                                                                  共 498 例
任一子集失败即整体失败 (退出码 1)。
"""
import os
import subprocess
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)
SUITES = ["test_core_units.py", "test_batch2_units.py", "test_batch3_units.py",
          "test_batch4_units.py", "test_batch5_units.py", "test_batch6_units.py",
          "test_batch7_units.py", "test_batch8_units.py", "test_batch9_units.py",
          "test_batch10_units.py", "test_batch11_units.py", "test_batch12_units.py",
          "test_batch13_units.py", "test_batch14_units.py", "test_wiring.py"]


def main():
    # 先把「我到底在跑哪份代码」打出来。源码是打进镜像的, git pull 之后不 build 的话
    # 这里跑的还是旧代码, 而它照样会 ALL SUITES PASS —— `make test` 会拿这个指纹跟
    # 工作树对一遍, 对不上就喊。详见 scripts/code_fingerprint.py。
    try:
        from code_fingerprint import fingerprint
        print(f"代码指纹 {fingerprint()}  (与工作树对不上 = 容器跑的是旧镜像, 先 make deploy)")
    except Exception as e:
        print(f"代码指纹取不到: {type(e).__name__}: {e}")

    failed = []
    missing = []
    for s in SUITES:
        path = os.path.join(HERE, s)
        if not os.path.exists(path):
            # 少一个测试文件绝不能只印一行"跳过"就当没事 —— 镜像是旧的时候,
            # 新增的那一批测试正好全部"不存在", 于是一条没跑却报 ALL SUITES PASS。
            print(f"== 缺失 {s} —— **这一批一条都没跑**")
            missing.append(s)
            continue
        print(f"\n=== {s} " + "=" * (52 - len(s)))
        r = subprocess.run([sys.executable, path], cwd=ROOT)
        if r.returncode != 0:
            failed.append(s)
    print("\n" + "=" * 62)
    if missing:
        print(f"SUITE MISSING: {', '.join(missing)} —— 这些测试文件不在当前代码里, "
              f"一条都没跑。镜像旧了就 make deploy; 真删了就把 SUITES 清单一起改")
    if failed:
        print(f"SUITE FAILED: {', '.join(failed)}")
        sys.exit(1)
    if missing:
        sys.exit(1)          # 「没跑」不许算「通过」
    print("ALL SUITES PASS")


if __name__ == "__main__":
    main()
