Files
codex-app-server/cloud/tests/test_scorecard_mech_mcdt.py
T
host b7e2f29868 v7 mech 8: MCDT acceptance/key-fact nodes as scorecard input model only
(cherry picked from commit 93e491b6a1b83fcdbbacf3493cdd2fd76418d0b6)
2026-11-03 02:49:27 +00:00

236 lines
9.7 KiB
Python

"""v7 机制第 8 项:MCDT 结果指标(评分卡输入模型)定向测试。
钉住的规则(对应任务 (a)(b)(c)):
(a) 某维无对应验收节点时该维输入为 unknown,不得用过程指标填补。
(b) 过程指标(test_count、coverage)只作为证据字段透传,绝不直接
进入得分输入:只改 test_count/coverage 而节点状态不变时,每维
的得分输入不变;只改节点状态(pending->pass 或 pass->fail)时,
映射输出变化。
(c) 节点状态为 fail 时对应维不得为 pass;节点状态 unknown 时对应维
为 unknown。
诚实边界:此前 v6 头全仓 grep MCDT / 关键事实节点 / 验收节点为
0 命中,本项只是评分卡输入模型,不是产品功能;测试不声称 MCDT
已接入生产评分、门禁或真实审计循环。阈值一律仍是
test_default_unapproved,除非调用方显式传入全 user_approved。
"""
import sys
import unittest
from pathlib import Path
try:
from cloud.audit_scorecard import DIMENSION_IDS as SCORECARD_DIMS
from cloud.mcdt_nodes import (
AcceptanceNode,
KeyFactNode,
DIMENSION_IDS,
nodes_to_dimension_inputs,
)
except ImportError:
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from cloud.audit_scorecard import DIMENSION_IDS as SCORECARD_DIMS
from cloud.mcdt_nodes import (
AcceptanceNode,
KeyFactNode,
DIMENSION_IDS,
nodes_to_dimension_inputs,
)
def _acc(node_id, dim, status, **extra):
node = {
"id": node_id,
"title": "t-%s" % node_id,
"acceptance_criteria": ["criterion 1"],
"status": status,
"evidence_refs": ["ev-1"],
"dimension": dim,
}
node.update(extra)
return node
def _fact(node_id, dim, status):
return {
"id": node_id,
"fact": "fact-%s" % node_id,
"status": status,
"source": "src-1",
"dimension": dim,
}
def _walk(obj):
if isinstance(obj, dict):
for key, value in obj.items():
yield key
yield from _walk(value)
elif isinstance(obj, (list, tuple)):
for value in obj:
yield from _walk(value)
class McdtNodesTestCase(unittest.TestCase):
def test_dimension_ids_match_scorecard(self):
self.assertEqual(DIMENSION_IDS, SCORECARD_DIMS)
self.assertEqual(len(DIMENSION_IDS), 7)
def test_no_nodes_all_dimensions_unknown(self):
out = nodes_to_dimension_inputs([], [])
for dim in DIMENSION_IDS:
entry = out["dimensions"][dim]
self.assertEqual(entry["status"], "unknown")
self.assertIn("no_acceptance_node_mapped",
entry["missing_items"])
self.assertEqual(entry["score_input"]["node_status"], "unknown")
def test_only_one_dimension_mapped_others_unknown(self):
out = nodes_to_dimension_inputs(
[_acc("a1", "delivery_quality", "pass")], [])
self.assertEqual(out["dimensions"]["delivery_quality"]["status"],
"pass")
for dim in DIMENSION_IDS:
if dim != "delivery_quality":
self.assertEqual(out["dimensions"][dim]["status"], "unknown")
def test_process_metrics_do_not_change_dimension_inputs(self):
nodes = [_acc("a1", "delivery_quality", "pass"),
_acc("a2", "gate_audit", "pending")]
base = nodes_to_dimension_inputs(
nodes, [], process_metrics={"test_count": 100,
"coverage": 0.5})
changed = nodes_to_dimension_inputs(
nodes, [], process_metrics={"test_count": 10000,
"coverage": 0.99})
self.assertEqual(base["dimensions"], changed["dimensions"])
# 透传只在顶层,不在任何一维里。
self.assertEqual(changed["process_evidence"]["test_count"], 10000)
self.assertEqual(changed["process_evidence"]["coverage"], 0.99)
for entry in changed["dimensions"].values():
keys = set(_walk(entry))
self.assertNotIn("test_count", keys)
self.assertNotIn("coverage", keys)
def test_node_carried_process_keys_are_ignored(self):
plain = nodes_to_dimension_inputs(
[_acc("a1", "delivery_quality", "pass")], [])
noisy = nodes_to_dimension_inputs(
[_acc("a1", "delivery_quality", "pass",
test_count=99999, coverage=0.01)], [])
self.assertEqual(plain["dimensions"], noisy["dimensions"])
def test_pending_to_pass_changes_output(self):
before = nodes_to_dimension_inputs(
[_acc("a1", "delivery_quality", "pending")], [])
after = nodes_to_dimension_inputs(
[_acc("a1", "delivery_quality", "pass")], [])
self.assertEqual(
before["dimensions"]["delivery_quality"]["status"], "unknown")
self.assertEqual(
after["dimensions"]["delivery_quality"]["status"], "pass")
self.assertNotEqual(before["dimensions"], after["dimensions"])
def test_pass_to_fail_changes_output_and_is_not_pass(self):
before = nodes_to_dimension_inputs(
[_acc("a1", "arch_contract", "pass")], [])
after = nodes_to_dimension_inputs(
[_acc("a1", "arch_contract", "fail")], [])
self.assertEqual(before["dimensions"]["arch_contract"]["status"],
"pass")
self.assertEqual(after["dimensions"]["arch_contract"]["status"],
"fail")
self.assertNotEqual(before["dimensions"], after["dimensions"])
def test_fail_node_blocks_pass_even_with_other_pass_nodes(self):
out = nodes_to_dimension_inputs(
[_acc("a1", "gate_audit", "pass"),
_acc("a2", "gate_audit", "fail")], [])
self.assertEqual(out["dimensions"]["gate_audit"]["status"], "fail")
def test_unknown_node_means_dimension_unknown(self):
out = nodes_to_dimension_inputs(
[_acc("a1", "delivery_quality", "unknown")], [])
self.assertEqual(out["dimensions"]["delivery_quality"]["status"],
"unknown")
out2 = nodes_to_dimension_inputs(
[_acc("a1", "delivery_quality", "pass"),
_acc("a2", "delivery_quality", "unknown")], [])
self.assertEqual(out2["dimensions"]["delivery_quality"]["status"],
"unknown")
def test_key_fact_fail_blocks_pass(self):
out = nodes_to_dimension_inputs(
[_acc("a1", "delivery_quality", "pass")],
[_fact("f1", "delivery_quality", "fail")])
self.assertEqual(out["dimensions"]["delivery_quality"]["status"],
"fail")
def test_key_fact_unknown_blocks_pass(self):
out = nodes_to_dimension_inputs(
[_acc("a1", "delivery_quality", "pass")],
[_fact("f1", "delivery_quality", "unknown")])
self.assertEqual(out["dimensions"]["delivery_quality"]["status"],
"unknown")
def test_key_fact_alone_cannot_pass_dimension(self):
out = nodes_to_dimension_inputs(
[], [_fact("f1", "delivery_quality", "pass")])
self.assertEqual(out["dimensions"]["delivery_quality"]["status"],
"unknown")
def test_unmapped_nodes_do_not_affect_any_dimension(self):
out = nodes_to_dimension_inputs(
[_acc("a1", None, "pass")],
[_fact("f1", "bogus_dim", "pass")])
for dim in DIMENSION_IDS:
self.assertEqual(out["dimensions"][dim]["status"], "unknown")
self.assertIn("a1", out["unmapped_acceptance_node_ids"])
self.assertIn("f1", out["unmapped_key_fact_node_ids"])
def test_invalid_status_is_unknown_not_pass(self):
out = nodes_to_dimension_inputs(
[_acc("a1", "delivery_quality", "PASS")], [])
self.assertEqual(out["dimensions"]["delivery_quality"]["status"],
"unknown")
def test_dataclass_nodes_work(self):
out = nodes_to_dimension_inputs(
[AcceptanceNode(id="a1", title="t", acceptance_criteria=["c"],
status="pass", evidence_refs=["e"],
dimension="arch_contract")],
[KeyFactNode(id="f1", fact="f", status="pass", source="s",
dimension="arch_contract")])
self.assertEqual(out["dimensions"]["arch_contract"]["status"],
"pass")
def test_thresholds_default_unapproved_and_do_not_change_status(self):
nodes = [_acc("a1", "delivery_quality", "pass")]
default = nodes_to_dimension_inputs(nodes, [])
self.assertEqual(default["thresholds_source"],
"test_default_unapproved")
approved = nodes_to_dimension_inputs(
nodes, [],
thresholds={"x": {"value": 1, "source": "user_approved"}})
self.assertEqual(approved["thresholds_source"], "user_approved")
self.assertEqual(default["dimensions"], approved["dimensions"])
# 未知维传了已拍板阈值也不能变 pass。
unknown_approved = nodes_to_dimension_inputs(
[], [],
thresholds={"x": {"value": 1, "source": "user_approved"}})
self.assertEqual(
unknown_approved["dimensions"]["delivery_quality"]["status"],
"unknown")
def test_test_coverage_not_filled_by_coverage_metric(self):
out = nodes_to_dimension_inputs(
[], [], process_metrics={"test_count": 5000, "coverage": 0.99})
self.assertEqual(out["dimensions"]["test_coverage"]["status"],
"unknown")
self.assertIn("no_acceptance_node_mapped",
out["dimensions"]["test_coverage"]["missing_items"])
if __name__ == "__main__":
unittest.main()