fix(data): 修三环境 session 反馈的 3 个数据层问题

D1: 删 test_circuit_breaker.py(测已归档 raw_redownload.check_circuit_breaker 死代码,全仓零活跃引用,致 data_platform 套件 collection error)
D2: datareader.py read_db_daily/read_index_daily 两处 vnpy_db 硬访问→.get()+清晰报错防崩溃(根治切 dbbardata 读指数列待办)
D3: high_limit/low_limit close±10% 兜底是有意设计非 bug(填 NaN 会复活 bullet_trade 误判停牌)—get_price 加 round(.,2) 对齐 get_current_tick 口径;测试期望从 NaN 改兜底估算
This commit is contained in:
2026-07-29 21:17:15 +08:00
parent f701f10bc5
commit 384bcc56d7
4 changed files with 15 additions and 163 deletions
-155
View File
@@ -1,155 +0,0 @@
"""断路器单元测试(task: Phase 2 可靠性增强)。
测试 check_circuit_breaker 纯函数——不启动整个下载流程,
直接构造 recent_results 列表验证触发逻辑。
覆盖场景:
1. 35% 失败率 → 触发
2. 25% 失败率 → 不触发
3. 北交所 920xxx 的 fail 不计入 → 不触发
4. 不足窗口 → 不触发
5. 恰好 30% → 不触发(阈值是 > 0.30,不含等于)
"""
import os
import sys
import pytest
_SCRIPT_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "scripts", "data_platform")
_SCRIPT_DIR = os.path.abspath(_SCRIPT_DIR)
if _SCRIPT_DIR not in sys.path:
sys.path.insert(0, _SCRIPT_DIR)
from raw_redownload import ( # noqa: E402
CIRCUIT_BREAKER_FAIL_RATE,
CIRCUIT_BREAKER_WINDOW,
KNOWN_UNSUPPORTED_PREFIX,
check_circuit_breaker,
)
def _make_results(n_ok: int, n_fail: int, fail_prefix: str = "600") -> list:
"""构造 recent_results 列表:n_ok 个 ok + n_fail 个 fail。
fail_prefix 控制失败码的前缀(用于测试北交所排除)。
"""
ok_list = [("600001", True)] * n_ok
fail_list = [(f"{fail_prefix}9999", False)] * n_fail
return ok_list + fail_list
class TestCircuitBreakerTrigger:
"""断路器触发阈值测试。"""
def test_35_percent_fail_triggers(self):
"""200 只里 70 只 fail35%)→ 触发。"""
results = _make_results(n_ok=130, n_fail=70)
assert len(results) == 200
assert check_circuit_breaker(results) is True
def test_25_percent_fail_no_trigger(self):
"""200 只里 50 只 fail25%)→ 不触发。"""
results = _make_results(n_ok=150, n_fail=50)
assert len(results) == 200
assert check_circuit_breaker(results) is False
def test_exactly_30_percent_no_trigger(self):
"""恰好 30%(60/200)→ 不触发(阈值是 > 0.30,不含等于)。"""
results = _make_results(n_ok=140, n_fail=60)
assert len(results) == 200
fail_rate = 60 / 200
assert fail_rate == pytest.approx(CIRCUIT_BREAKER_FAIL_RATE)
assert check_circuit_breaker(results) is False
def test_31_percent_triggers(self):
"""31% 失败率 → 触发(刚过阈值)。"""
results = _make_results(n_ok=138, n_fail=62)
assert len(results) == 200
assert check_circuit_breaker(results) is True
class TestCircuitBreakerBjExclusion:
"""北交所码不计入断路器分母测试。"""
def test_bj_fails_excluded_from_denominator(self):
"""北交所 920xxx 的 fail 不计入 → 不触发。
200 只非北交所全部 ok + 100 只北交所全部 fail
有效分母=200,有效失败=0 → 0% → 不触发。
"""
ok_list = [("600001", True)] * 200
bj_fail_list = [("920001", False)] * 100
results = ok_list + bj_fail_list
assert check_circuit_breaker(results) is False
def test_bj_fails_do_not_inflate_rate(self):
"""北交所 fail 混在 200 窗口内不抬高失败率。
150 只非北交所 ok + 50 只北交所 fail = 200 只总数:
有效分母=150(< 窗口 200)→ 不足窗口,不触发。
"""
ok_list = [("600001", True)] * 150
bj_fail_list = [("920001", False)] * 50
results = ok_list + bj_fail_list
assert check_circuit_breaker(results) is False
def test_mixed_bj_and_normal_fails(self):
"""混合场景:200 非北交所(60 fail=30%+ 50 北交所 fail。
有效:200 非北交所,60 fail = 30%,恰好阈值(> 0.30 不含等于)→ 不触发。
北交所 50 fail 被排除,不影响计算。
"""
ok_normal = [("600001", True)] * 140
fail_normal = [("600002", False)] * 60
fail_bj = [("920001", False)] * 50
results = ok_normal + fail_normal + fail_bj
assert check_circuit_breaker(results) is False
def test_all_bj_prefixes_excluded(self):
"""所有已知不支持前缀都排除:920/921/83/87。"""
ok_list = [("600001", True)] * 200
bj_fails = (
[("920001", False)] * 30
+ [("921002", False)] * 30
+ [("830003", False)] * 30
+ [("870004", False)] * 30
)
results = ok_list + bj_fails
assert check_circuit_breaker(results) is False
class TestCircuitBreakerWindow:
"""窗口大小边界测试。"""
def test_insufficient_window_no_trigger(self):
"""不足 200 只 → 不触发(即使全部 fail)。"""
results = [("600001", False)] * 199
assert check_circuit_breaker(results) is False
def test_exactly_window_triggers_if_high_fail(self):
"""恰好 200 只且失败率超阈值 → 触发。"""
results = _make_results(n_ok=100, n_fail=100)
assert len(results) == 200
assert check_circuit_breaker(results) is True
def test_empty_results_no_trigger(self):
"""空列表 → 不触发。"""
assert check_circuit_breaker([]) is False
def test_all_ok_no_trigger(self):
"""200 只全部 ok → 不触发。"""
results = [("600001", True)] * 200
assert check_circuit_breaker(results) is False
class TestCircuitBreakerConstants:
"""常量值校验(防止意外修改)。"""
def test_window_is_200(self):
assert CIRCUIT_BREAKER_WINDOW == 200
def test_fail_rate_is_030(self):
assert CIRCUIT_BREAKER_FAIL_RATE == 0.30
def test_known_unsupported_prefixes(self):
assert KNOWN_UNSUPPORTED_PREFIX == ("920", "921", "83", "87")