|
@@ -25,11 +25,11 @@ class TestDomainUpdaterP0P1Bugs(unittest.TestCase):
|
|
|
self.tmp_dir.cleanup()
|
|
self.tmp_dir.cleanup()
|
|
|
|
|
|
|
|
def test_p0_yd_score_polarity_fix_and_healthy_ranking(self):
|
|
def test_p0_yd_score_polarity_fix_and_healthy_ranking(self):
|
|
|
- """[P0-1 修复验证] 验证 yd_score 极性修复与平局决胜:
|
|
|
|
|
- 1. 加权公式仅计算 avg_latency (1.0) 与 loss_rate (10.0)
|
|
|
|
|
|
|
+ """[P0-1 修复验证] 验证 yd_score 极性与平局决胜:
|
|
|
|
|
+ 1. 加权公式仅计算 yd_latency (1.0) 与 yd_pkg_lost_rate (10.0)
|
|
|
2. 恶化前 fastsoso (36ms) 优于 competitor (49ms)
|
|
2. 恶化前 fastsoso (36ms) 优于 competitor (49ms)
|
|
|
3. 恶化后 fastsoso (74ms) 得分劣于 competitor (49ms),competitor 成功登顶第 1
|
|
3. 恶化后 fastsoso (74ms) 得分劣于 competitor (49ms),competitor 成功登顶第 1
|
|
|
- 4. 平局时 tie_breakers 优先选用 yd_score 降序(健康分高者胜出)
|
|
|
|
|
|
|
+ 4. 平局时 tie_breakers 选用 yd_score 升序(成本惩罚分越低者胜出,越小越优)
|
|
|
"""
|
|
"""
|
|
|
config_path = PROJECT_ROOT / "configs" / "config.server.json"
|
|
config_path = PROJECT_ROOT / "configs" / "config.server.json"
|
|
|
with open(config_path, "r", encoding="utf-8") as f:
|
|
with open(config_path, "r", encoding="utf-8") as f:
|
|
@@ -47,7 +47,7 @@ class TestDomainUpdaterP0P1Bugs(unittest.TestCase):
|
|
|
"avgPkgLostRate": "0",
|
|
"avgPkgLostRate": "0",
|
|
|
"ydLatency": 36.0,
|
|
"ydLatency": 36.0,
|
|
|
"ydPkgLostRate": 0.0,
|
|
"ydPkgLostRate": 0.0,
|
|
|
- "ydScore": 109.0, # 健康分高
|
|
|
|
|
|
|
+ "ydScore": 83.0, # 优质节点成本惩罚分低
|
|
|
}
|
|
}
|
|
|
degraded_candidate = {
|
|
degraded_candidate = {
|
|
|
"ip": "www.fastsoso.cn",
|
|
"ip": "www.fastsoso.cn",
|
|
@@ -56,7 +56,7 @@ class TestDomainUpdaterP0P1Bugs(unittest.TestCase):
|
|
|
"avgPkgLostRate": "0",
|
|
"avgPkgLostRate": "0",
|
|
|
"ydLatency": 74.0, # 延迟翻倍恶化
|
|
"ydLatency": 74.0, # 延迟翻倍恶化
|
|
|
"ydPkgLostRate": 0.0,
|
|
"ydPkgLostRate": 0.0,
|
|
|
- "ydScore": 78.0, # 健康分暴跌
|
|
|
|
|
|
|
+ "ydScore": 205.0, # 恶化后成本惩罚分激增
|
|
|
}
|
|
}
|
|
|
competitor = {
|
|
competitor = {
|
|
|
"ip": "saas.sin.fan",
|
|
"ip": "saas.sin.fan",
|
|
@@ -65,7 +65,7 @@ class TestDomainUpdaterP0P1Bugs(unittest.TestCase):
|
|
|
"avgPkgLostRate": "0",
|
|
"avgPkgLostRate": "0",
|
|
|
"ydLatency": 49.0,
|
|
"ydLatency": 49.0,
|
|
|
"ydPkgLostRate": 0.0,
|
|
"ydPkgLostRate": 0.0,
|
|
|
- "ydScore": 86.0,
|
|
|
|
|
|
|
+ "ydScore": 164.0,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
# 1. 正常时算分:fastsoso 延迟更低,加权分更低更优,排名第一
|
|
# 1. 正常时算分:fastsoso 延迟更低,加权分更低更优,排名第一
|
|
@@ -79,7 +79,7 @@ class TestDomainUpdaterP0P1Bugs(unittest.TestCase):
|
|
|
self.assertEqual(ranked_degraded[0]["domain"], "saas.sin.fan", "恶化后 competitor 夺得第 1 名")
|
|
self.assertEqual(ranked_degraded[0]["domain"], "saas.sin.fan", "恶化后 competitor 夺得第 1 名")
|
|
|
self.assertEqual(ranked_degraded[1]["domain"], "www.fastsoso.cn", "恶化节点被挤至第 2 名")
|
|
self.assertEqual(ranked_degraded[1]["domain"], "www.fastsoso.cn", "恶化节点被挤至第 2 名")
|
|
|
|
|
|
|
|
- # 3. 平局决胜验证:延迟丢包相同,yd_score 高者胜出
|
|
|
|
|
|
|
+ # 3. 平局决胜验证:延迟丢包相同,yd_score 升序胜出(Cost / Penalty 成本惩罚分越低越优)
|
|
|
tie_candidate_a = {
|
|
tie_candidate_a = {
|
|
|
"ip": "tie-a.com",
|
|
"ip": "tie-a.com",
|
|
|
"createdTime": "2026-05-30 20:00:00",
|
|
"createdTime": "2026-05-30 20:00:00",
|
|
@@ -98,9 +98,9 @@ class TestDomainUpdaterP0P1Bugs(unittest.TestCase):
|
|
|
"ydPkgLostRate": 0.0,
|
|
"ydPkgLostRate": 0.0,
|
|
|
"ydScore": 70.0,
|
|
"ydScore": 70.0,
|
|
|
}
|
|
}
|
|
|
- scored_tie = du.parse_scored_records([tie_candidate_b, tie_candidate_a], field_map, record_mapping_cfg, scoring_cfg)
|
|
|
|
|
|
|
+ scored_tie = du.parse_scored_records([tie_candidate_a, tie_candidate_b], field_map, record_mapping_cfg, scoring_cfg)
|
|
|
ranked_tie = du.rank_scored_records(scored_tie, scoring_cfg)
|
|
ranked_tie = du.rank_scored_records(scored_tie, scoring_cfg)
|
|
|
- self.assertEqual(ranked_tie[0]["domain"], "tie-a.com", "平局时 yd_score 降序胜出")
|
|
|
|
|
|
|
+ self.assertEqual(ranked_tie[0]["domain"], "tie-b.com", "平局时 yd_score 升序胜出(分低成本小者优胜)")
|
|
|
|
|
|
|
|
def test_p0_notify_stdout_isolation_and_timeout(self):
|
|
def test_p0_notify_stdout_isolation_and_timeout(self):
|
|
|
"""[P0/P1 修复验证] 验证 run_notify() 标准输出重定向至 stderr,且具备超时保护"""
|
|
"""[P0/P1 修复验证] 验证 run_notify() 标准输出重定向至 stderr,且具备超时保护"""
|
|
@@ -304,6 +304,45 @@ class TestDomainUpdaterP0P1Bugs(unittest.TestCase):
|
|
|
for sym in required_facade_symbols:
|
|
for sym in required_facade_symbols:
|
|
|
self.assertTrue(hasattr(du, sym), f"Facade 必须导出符号: {sym}")
|
|
self.assertTrue(hasattr(du, sym), f"Facade 必须导出符号: {sym}")
|
|
|
|
|
|
|
|
|
|
+ def test_empirical_score_metric_asc_tie_breaking_and_consistency(self):
|
|
|
|
|
+ """[实证极性验证] 验证 vps789 API 中 Score 成本惩罚分语义及平局决胜升序(低分优胜)"""
|
|
|
|
|
+ config_path = PROJECT_ROOT / "configs" / "config.server.json"
|
|
|
|
|
+ with open(config_path, "r", encoding="utf-8") as f:
|
|
|
|
|
+ cfg = json.load(f)
|
|
|
|
|
+
|
|
|
|
|
+ field_map = cfg["record_mapping"]["field_map"]
|
|
|
|
|
+ record_mapping_cfg = cfg["record_mapping"]
|
|
|
|
|
+ scoring_cfg = cfg["scoring"]
|
|
|
|
|
+
|
|
|
|
|
+ # 验证 config.server.json 中 tie_breakers 的 yd_score 必须为 asc
|
|
|
|
|
+ yd_tie_breaker = next((tb for tb in scoring_cfg.get("tie_breakers", []) if tb.get("field") == "yd_score"), None)
|
|
|
|
|
+ self.assertIsNotNone(yd_tie_breaker)
|
|
|
|
|
+ self.assertEqual(yd_tie_breaker.get("order"), "asc", "yd_score 作为成本惩罚分必须以 asc (升序) 决胜")
|
|
|
|
|
+
|
|
|
|
|
+ # 真实节点数据仿真:节点 A (baka.fun 移动优,ydScore 83) vs 节点 B (swowd.com 移动次优,ydScore 173)
|
|
|
|
|
+ # 假设两者加权平均分偶然相同
|
|
|
|
|
+ cand_a = {
|
|
|
|
|
+ "ip": "baka.fun",
|
|
|
|
|
+ "createdTime": "2026-05-28 20:14:47",
|
|
|
|
|
+ "avgLatency": "135",
|
|
|
|
|
+ "avgPkgLostRate": "0",
|
|
|
|
|
+ "ydLatency": 37.0,
|
|
|
|
|
+ "ydPkgLostRate": 0.0,
|
|
|
|
|
+ "ydScore": 83.0,
|
|
|
|
|
+ }
|
|
|
|
|
+ cand_b = {
|
|
|
|
|
+ "ip": "www.swowd.com",
|
|
|
|
|
+ "createdTime": "2026-05-30 23:44:16",
|
|
|
|
|
+ "avgLatency": "96",
|
|
|
|
|
+ "avgPkgLostRate": "0",
|
|
|
|
|
+ "ydLatency": 37.0,
|
|
|
|
|
+ "ydPkgLostRate": 0.0,
|
|
|
|
|
+ "ydScore": 173.0,
|
|
|
|
|
+ }
|
|
|
|
|
+ scored = du.parse_scored_records([cand_b, cand_a], field_map, record_mapping_cfg, scoring_cfg)
|
|
|
|
|
+ ranked = du.rank_scored_records(scored, scoring_cfg)
|
|
|
|
|
+ self.assertEqual(ranked[0]["domain"], "baka.fun", "平局决胜时 ydScore 83.0 (低惩罚) 必须优于 173.0")
|
|
|
|
|
+
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if __name__ == "__main__":
|
|
|
unittest.main()
|
|
unittest.main()
|