bone_2026/tests/test_violations.py

144 lines
5.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Тесты единого словаря типов нарушений.
Словарь обслуживает три места сразу: инференс (коды в отчёте), SR-отчёт
(числовые коды) и веб-интерфейс (подписи). Тесты фиксируют, что все коды
описаны, а устаревшие значения внешних источников приводятся к канону.
"""
import sys
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from src.dxa import excel_labels # noqa: E402
from src.dxa.violations import ( # noqa: E402
ARTIFACT,
AXIS_DEVIATION,
INCOMPLETE_ANATOMY,
LABELING_ERROR,
LEGACY_ALIASES,
MOTION,
POSITIONING,
ROI_INCORRECT,
ROTATION,
TABLE_TYPES,
UNSPECIFIED,
VIOLATION_LABELS,
VIOLATION_NOTES,
VIOLATION_SCOPE,
VIOLATION_SR_CODES,
VIOLATION_TYPES,
canon_type,
catalogue,
label,
reason,
sr_code,
)
class TestCanonType:
"""Устаревшие значения из внешних источников приводятся к канону."""
@pytest.mark.parametrize("legacy, expected", [
("artifact_motion", MOTION),
("artifact_other", ARTIFACT),
("quality_violation_detected", UNSPECIFIED),
("roi_error", ROI_INCORRECT),
("position_error", POSITIONING),
("incomplete_view", INCOMPLETE_ANATOMY),
])
def test_legacy_aliases(self, legacy, expected):
assert canon_type(legacy) == expected
@pytest.mark.parametrize("value", ["", None, "correct", "none"])
def test_empty_values(self, value):
assert canon_type(value) == ""
def test_canonical_codes_pass_through(self):
for code in VIOLATION_TYPES:
assert canon_type(code) == code
def test_case_and_spaces_are_normalised(self):
assert canon_type(" ROTATION ") == ROTATION
def test_unknown_value_falls_back_to_unspecified(self):
assert canon_type("что_то_новое") == UNSPECIFIED
def test_every_alias_target_is_a_known_code(self):
for target in LEGACY_ALIASES.values():
assert target == "" or target in VIOLATION_TYPES
class TestLabels:
def test_label_for_known_code(self):
assert label(ROTATION) == VIOLATION_LABELS[ROTATION]
def test_label_for_empty_code_uses_default(self):
assert label("") == "Корректно"
assert label("", empty="—") == "—"
def test_label_accepts_legacy_value(self):
assert label("artifact_motion") == VIOLATION_LABELS[MOTION]
def test_reason_for_unknown_code_is_unspecified_note(self):
assert reason("нечто") == VIOLATION_NOTES[UNSPECIFIED]
def test_reason_for_empty_code_is_empty(self):
assert reason("") == ""
class TestSrCode:
def test_acceptable_code_for_good_image(self):
numeric, english, completion = sr_code("")
assert numeric == "113001" and completion == "FINAL"
assert "acceptable" in english
def test_warning_code_for_violation(self):
numeric, english, completion = sr_code(ROTATION)
assert numeric == VIOLATION_SR_CODES[ROTATION][0] and completion == "WARNING"
def test_unknown_code_falls_back_to_unspecified(self):
assert sr_code("нечто") == (*VIOLATION_SR_CODES[UNSPECIFIED], "WARNING")
class TestCatalogue:
def test_every_code_is_described(self):
entries = catalogue()
assert [e["code"] for e in entries] == list(VIOLATION_TYPES)
for entry in entries:
assert entry["label"] and entry["note"]
assert entry["scope"] in ("spine", "hip", "any")
assert entry["source"] in ("expert_table", "condition_doctor", "system")
def test_table_codes_are_marked_as_expert_table(self):
by_code = {e["code"]: e for e in catalogue()}
for code in TABLE_TYPES:
assert by_code[code]["source"] == "expert_table"
assert by_code[UNSPECIFIED]["source"] == "system"
def test_every_code_has_sr_code(self):
for code in VIOLATION_TYPES:
assert code in VIOLATION_SR_CODES
def test_every_code_has_label_and_note(self):
for code in VIOLATION_TYPES:
assert code in VIOLATION_LABELS
assert code in VIOLATION_NOTES
assert code in VIOLATION_SCOPE
class TestAgreementWithAnnotationTable:
"""Словарь таблицы и общий словарь не должны разойтись."""
def test_table_types_match_table_criteria(self):
expected = set(excel_labels.SPINE_CRITERIA) | set(excel_labels.HIP_CRITERIA)
assert set(TABLE_TYPES) == expected
def test_excel_labels_reexports_same_codes(self):
assert excel_labels.UNSPECIFIED == UNSPECIFIED
assert excel_labels.ARTIFACT == ARTIFACT
assert excel_labels.AXIS_DEVIATION == AXIS_DEVIATION
assert set(excel_labels.VIOLATION_TYPES) >= set(TABLE_TYPES) | {UNSPECIFIED}