Files
pbf/tests/test_standard_display_review_gate.py

147 lines
7.3 KiB
Python

"""B2.6:显示组和 PRIMARY 证据必须由独立人审,且审核通过不等于执行授权。"""
from __future__ import annotations
import copy
import importlib.util
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[1]
SCRIPT = ROOT / "tools/provider-pipeline/standard_display_review_gate.py"
spec = importlib.util.spec_from_file_location("review_gate_module", SCRIPT)
gate = importlib.util.module_from_spec(spec)
assert spec and spec.loader
spec.loader.exec_module(gate)
HASHARGS = dict(queue_sha256="a" * 64, groups_sha256="b" * 64, style_sha256="c" * 64)
def fixtures():
queue = {
"schemaVersion": "navsea-feature-identity-scope-audit/v1",
"providerId": "unknown-provider", "datasetId": "test-v1",
"reviewQueue": [
{
"visualObjectTypeId": "VO-1", "assignmentStatus": "LAYER_ONLY",
"standardLayer": "reviewed_category", "primaryReviewStatus": "PENDING_REVIEW",
"componentCandidates": [
{"componentIndex": 0, "drawingRole": "line", "styleLayerId": "layer-A"},
{"componentIndex": 1, "drawingRole": "text", "styleLayerId": "layer-B"},
],
},
{
"visualObjectTypeId": "VO-2", "assignmentStatus": "UNKNOWN",
"standardLayer": None, "primaryReviewStatus": "PENDING_REVIEW",
"componentCandidates": [{"componentIndex": 0, "drawingRole": "icon", "styleLayerId": "layer-C"}],
},
],
}
groups = {
"schemaVersion": gate.GROUP_SCHEMA, "approvalStatus": "OBSERVED_NOT_APPROVED",
"groupIds": ["display-A", "display-B"],
}
draft = gate.generate_review_draft(queue, groups, **HASHARGS)
return queue, groups, draft
def review_both(draft):
std = draft["standardLayerDecisions"][0]
std.update(status="REVIEWED", displayGroupId="display-A", reviewer="real-reviewer",
evidenceRefs=["audit/signed-standard-review"])
obj = draft["visualObjectDecisions"][0]
obj.update(status="REVIEWED", reviewedStandardLayer="reviewed_category",
primaryComponentIndex=0, componentParentLinks=[
{"componentIndex": 1, "parentComponentIndex": 0}
], reviewer="real-reviewer", evidenceRefs=["audit/signed-primary-review"])
class StandardDisplayReviewGateTests(unittest.TestCase):
def test_pending_draft_is_not_executable_and_keeps_unknown(self):
queue, groups, draft = fixtures()
self.assertEqual(len(draft["standardLayerDecisions"]), 1)
self.assertEqual(len(draft["visualObjectDecisions"]), 2)
result = gate.validate_review_draft(draft, queue, groups, **HASHARGS)
self.assertEqual(result["status"], "PASS_PENDING_REVIEW")
self.assertEqual(result["runtimeExecutionAuthorizedCount"], 0)
self.assertEqual(result["visualObjectStatusCounts"], {"PENDING": 2})
def test_unapproved_host_groups_rejects_review(self):
queue, groups, draft = fixtures()
review_both(draft)
out = gate.validate_review_draft(draft, queue, groups, **HASHARGS)
self.assertEqual(out["status"], "FAIL_CLOSED")
self.assertTrue(any("未正式批准" in error for error in out["validationErrors"]))
def test_explicit_reviewed_records_structurally_valid_but_never_runtime_authorized(self):
queue, groups, draft = fixtures()
groups["approvalStatus"] = "APPROVED"
review_both(draft)
result = gate.validate_review_draft(draft, queue, groups, **HASHARGS)
self.assertEqual(result["status"], "PASS_PENDING_REVIEW")
self.assertEqual(result["reviewedObjectCount"], 1)
self.assertEqual(result["runtimeExecutionAuthorizedCount"], 0)
self.assertEqual(result["globalIdentityAuthorizedCount"], 0)
def test_incomplete_or_double_parent_component_links_fail_closed(self):
queue, groups, draft = fixtures()
groups["approvalStatus"] = "APPROVED"
review_both(draft)
first = draft["visualObjectDecisions"][0]
first["componentParentLinks"] = []
self.assertEqual(gate.validate_review_draft(draft, queue, groups, **HASHARGS)["status"], "FAIL_CLOSED")
first["componentParentLinks"] = [
{"componentIndex": 1, "parentComponentIndex": 0},
{"componentIndex": 1, "parentComponentIndex": 0},
]
self.assertEqual(gate.validate_review_draft(draft, queue, groups, **HASHARGS)["status"], "FAIL_CLOSED")
def test_unknown_classification_cannot_be_promoted_through_review(self):
queue, groups, draft = fixtures()
groups["approvalStatus"] = "APPROVED"
review_both(draft)
second = draft["visualObjectDecisions"][1]
second.update(status="REVIEWED", reviewedStandardLayer="reviewed_category",
primaryComponentIndex=0, componentParentLinks=[],
reviewer="not-authorized", evidenceRefs=["ticket/123"])
out = gate.validate_review_draft(draft, queue, groups, **HASHARGS)
self.assertEqual(out["status"], "FAIL_CLOSED")
self.assertTrue(any("UNKNOWN/AMBIGUOUS" in err for err in out["validationErrors"]))
def test_binding_digest_and_extra_or_missing_review_ids_fail_closed(self):
queue, groups, draft = fixtures()
with self.assertRaisesRegex(ValueError, "指纹"):
gate.validate_review_draft(draft, queue, groups, **{**HASHARGS, "style_sha256": "wrong"})
draft["visualObjectDecisions"].pop()
with self.assertRaisesRegex(ValueError, "缺少"):
gate.validate_review_draft(draft, queue, groups, **HASHARGS)
draft = gate.generate_review_draft(queue, groups, **HASHARGS)
draft["standardLayerDecisions"].append(copy.deepcopy(draft["standardLayerDecisions"][0]))
with self.assertRaisesRegex(ValueError, "重复"):
gate.validate_review_draft(draft, queue, groups, **HASHARGS)
def test_pending_row_cannot_smuggle_parent_or_group(self):
queue, groups, draft = fixtures()
draft["visualObjectDecisions"][0]["primaryComponentIndex"] = 0
result = gate.validate_review_draft(draft, queue, groups, **HASHARGS)
self.assertEqual(result["status"], "FAIL_CLOSED")
draft = gate.generate_review_draft(queue, groups, **HASHARGS)
draft["standardLayerDecisions"][0]["displayGroupId"] = "display-A"
result = gate.validate_review_draft(draft, queue, groups, **HASHARGS)
self.assertEqual(result["status"], "FAIL_CLOSED")
def test_cross_provider_visual_types_have_no_implied_same_semantics(self):
queue_a, groups, draft_a = fixtures()
queue_b = copy.deepcopy(queue_a)
queue_b["providerId"] = "independent-provider"
draft_b = gate.generate_review_draft(queue_b, groups, **HASHARGS)
self.assertEqual(draft_a["visualObjectDecisions"], draft_b["visualObjectDecisions"])
self.assertNotEqual(draft_a["binding"]["providerId"], draft_b["binding"]["providerId"])
with self.assertRaisesRegex(ValueError, "来源身份"):
gate.validate_review_draft(draft_a, queue_b, groups, **HASHARGS)
result = gate.validate_review_draft(draft_b, queue_b, groups, **HASHARGS)
self.assertEqual(result["runtimeExecutionAuthorizedCount"], 0)
if __name__ == "__main__":
unittest.main()