115 lines
3.6 KiB
Python
115 lines
3.6 KiB
Python
"""Tests for domain value objects: MitreId and ScoringWeights."""
|
|
|
|
import sys
|
|
import os
|
|
|
|
backend_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
if backend_dir not in sys.path:
|
|
sys.path.insert(0, backend_dir)
|
|
|
|
import pytest
|
|
|
|
from app.domain.value_objects.mitre_id import MitreId
|
|
from app.domain.value_objects.scoring_weights import ScoringWeights
|
|
|
|
|
|
# ── MitreId ──────────────────────────────────────────────────────────
|
|
|
|
|
|
class TestMitreId:
|
|
|
|
def test_valid_technique(self):
|
|
mid = MitreId("T1059")
|
|
assert mid.value == "T1059"
|
|
assert str(mid) == "T1059"
|
|
assert not mid.is_subtechnique
|
|
assert mid.parent_id is None
|
|
|
|
def test_valid_subtechnique(self):
|
|
mid = MitreId("T1059.001")
|
|
assert mid.value == "T1059.001"
|
|
assert mid.is_subtechnique
|
|
assert mid.parent_id == "T1059"
|
|
|
|
def test_invalid_empty_string(self):
|
|
with pytest.raises(ValueError, match="Invalid MITRE"):
|
|
MitreId("")
|
|
|
|
def test_invalid_no_prefix(self):
|
|
with pytest.raises(ValueError, match="Invalid MITRE"):
|
|
MitreId("1059")
|
|
|
|
def test_invalid_wrong_prefix(self):
|
|
with pytest.raises(ValueError, match="Invalid MITRE"):
|
|
MitreId("A1059")
|
|
|
|
def test_invalid_too_few_digits(self):
|
|
with pytest.raises(ValueError, match="Invalid MITRE"):
|
|
MitreId("T105")
|
|
|
|
def test_invalid_subtechnique_format(self):
|
|
with pytest.raises(ValueError, match="Invalid MITRE"):
|
|
MitreId("T1059.01") # needs 3 digits after dot
|
|
|
|
def test_invalid_trailing_garbage(self):
|
|
with pytest.raises(ValueError, match="Invalid MITRE"):
|
|
MitreId("T1059.001.002")
|
|
|
|
def test_equality_with_same_mitre_id(self):
|
|
assert MitreId("T1059") == MitreId("T1059")
|
|
|
|
def test_equality_with_string(self):
|
|
assert MitreId("T1059") == "T1059"
|
|
|
|
def test_inequality(self):
|
|
assert MitreId("T1059") != MitreId("T1060")
|
|
|
|
def test_hashable(self):
|
|
s = {MitreId("T1059"), MitreId("T1059"), MitreId("T1060")}
|
|
assert len(s) == 2
|
|
|
|
def test_immutable(self):
|
|
mid = MitreId("T1059")
|
|
with pytest.raises(AttributeError):
|
|
mid.value = "T1060"
|
|
|
|
|
|
# ── ScoringWeights ───────────────────────────────────────────────────
|
|
|
|
|
|
class TestScoringWeights:
|
|
|
|
def test_valid_default(self):
|
|
w = ScoringWeights.default()
|
|
assert w.tests == 40.0
|
|
assert w.detection_rules == 25.0
|
|
assert w.d3fend == 15.0
|
|
assert w.recency == 10.0
|
|
assert w.severity == 10.0
|
|
|
|
def test_valid_custom(self):
|
|
w = ScoringWeights(
|
|
tests=50, detection_rules=20, d3fend=10,
|
|
recency=10, severity=10,
|
|
)
|
|
assert w.tests == 50
|
|
|
|
def test_invalid_sum_not_100(self):
|
|
with pytest.raises(ValueError, match="sum to 100"):
|
|
ScoringWeights(
|
|
tests=50, detection_rules=20, d3fend=10,
|
|
recency=10, severity=5,
|
|
)
|
|
|
|
def test_invalid_negative_weight(self):
|
|
with pytest.raises(ValueError, match="non-negative"):
|
|
ScoringWeights(
|
|
tests=-10, detection_rules=40, d3fend=30,
|
|
recency=20, severity=20,
|
|
)
|
|
|
|
def test_immutable(self):
|
|
w = ScoringWeights.default()
|
|
with pytest.raises(AttributeError):
|
|
w.tests = 50
|