Block 7.1-7.2 from masterplan: - 26 action_types with German aliases + phase mapping - Negative obligation patterns (exclude, prevent, enforce) - Container detection (11 composite objects that must not become atomic) - Evidence detection (14 indicators + "X dokumentieren" pattern) - Framework reference detection (OWASP, NIST, BSI, CSA, ISO patterns) - classify_obligation() routes to: atomic, composite, evidence, framework_container - build_canonical_key() for deterministic dedup - 36 tests covering all classification functions Also: merge_key bug fix in _process_pass0b_control() Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
151 lines
5.4 KiB
Python
151 lines
5.4 KiB
Python
"""Tests for Control Ontology — action classification, evidence/container detection."""
|
|
|
|
import pytest
|
|
from services.control_ontology import (
|
|
classify_action, classify_obligation, is_container, is_evidence,
|
|
is_framework_reference, build_canonical_key, get_phase,
|
|
)
|
|
|
|
|
|
class TestClassifyAction:
|
|
def test_implement(self):
|
|
assert classify_action("implementieren") == "implement"
|
|
assert classify_action("umsetzen") == "implement"
|
|
assert classify_action("einführen") == "implement"
|
|
assert classify_action("etablieren") == "implement"
|
|
|
|
def test_monitor(self):
|
|
assert classify_action("überwachen") == "monitor"
|
|
|
|
def test_test(self):
|
|
assert classify_action("testen") == "test"
|
|
|
|
def test_prevent(self):
|
|
assert classify_action("verhindern") == "prevent"
|
|
|
|
def test_exclude(self):
|
|
assert classify_action("nicht zulassen") == "exclude"
|
|
|
|
def test_negative_pattern(self):
|
|
assert classify_action("dürfen nicht verwendet werden") == "exclude"
|
|
assert classify_action("darf nicht wiederverwendet werden") == "prevent"
|
|
|
|
def test_compound_picks_first(self):
|
|
# Compound text — should pick the dominant action
|
|
result = classify_action("identifizieren und bewerten")
|
|
assert result in ("identify", "assess")
|
|
|
|
def test_schulen(self):
|
|
assert classify_action("schulen") == "train"
|
|
|
|
def test_melden(self):
|
|
assert classify_action("melden") == "report"
|
|
|
|
|
|
class TestIsContainer:
|
|
def test_session_management(self):
|
|
assert is_container("Sichere Sitzungsverwaltung muss umgesetzt werden")
|
|
|
|
def test_token_protection(self):
|
|
assert is_container("Token-Schutz muss umgesetzt werden")
|
|
|
|
def test_risk_management(self):
|
|
assert is_container("Umfassendes Risikomanagement einrichten")
|
|
|
|
def test_not_container(self):
|
|
assert not is_container("Rate-Limiting für API-Endpunkte konfigurieren")
|
|
assert not is_container("MFA für privilegierte Accounts aktivieren")
|
|
|
|
|
|
class TestIsEvidence:
|
|
def test_sbom_nachweis(self):
|
|
assert is_evidence("Ein SBOM-Nachweis muss vorliegen")
|
|
|
|
def test_screenshot(self):
|
|
assert is_evidence("Ein Screenshot der MFA-Konfiguration ist vorzulegen")
|
|
|
|
def test_auditbericht(self):
|
|
assert is_evidence("Ein Auditbericht zur Zugriffskontrolle muss vorhanden sein")
|
|
|
|
def test_tests_dokumentieren(self):
|
|
assert is_evidence("Tests dokumentieren")
|
|
assert is_evidence("Tests des Verfahrens dokumentiert")
|
|
|
|
def test_massnahmen_dokumentieren(self):
|
|
assert is_evidence("Ergriffene Maßnahmen dokumentieren")
|
|
|
|
def test_not_evidence_standalone_doc_duty(self):
|
|
# VVT führen is a standalone documentation duty, not evidence
|
|
assert not is_evidence("Verarbeitungsverzeichnis führen")
|
|
|
|
def test_not_evidence_implement(self):
|
|
assert not is_evidence("Rate-Limiting implementieren")
|
|
assert not is_evidence("MFA für privilegierte Accounts aktivieren")
|
|
|
|
|
|
class TestIsFrameworkReference:
|
|
def test_owasp_asvs(self):
|
|
assert is_framework_reference("OWASP ASVS V3 Session Management umsetzen")
|
|
|
|
def test_nist(self):
|
|
assert is_framework_reference("NIST SP 800-53 IA-Anforderungen implementieren")
|
|
|
|
def test_not_framework_specific(self):
|
|
assert not is_framework_reference("Rate-Limiting konfigurieren")
|
|
assert not is_framework_reference("MFA aktivieren")
|
|
|
|
|
|
class TestClassifyObligation:
|
|
def test_atomic(self):
|
|
result = classify_obligation("Rate-Limiting für API-Endpunkte konfigurieren", "konfigurieren")
|
|
assert result["routing"] == "atomic"
|
|
assert result["action_type"] == "configure"
|
|
|
|
def test_evidence_routed(self):
|
|
result = classify_obligation("Ein SBOM-Nachweis muss vorliegen")
|
|
assert result["routing"] == "evidence"
|
|
|
|
def test_container_routed(self):
|
|
result = classify_obligation("Sichere Sitzungsverwaltung muss umgesetzt werden")
|
|
assert result["routing"] == "composite"
|
|
|
|
def test_framework_routed(self):
|
|
result = classify_obligation("OWASP ASVS V3 umsetzen", "umsetzen")
|
|
assert result["routing"] == "framework_container"
|
|
|
|
def test_negative_obligation(self):
|
|
result = classify_obligation("Sensible Daten dürfen nicht in URLs übertragen werden")
|
|
assert result["routing"] == "atomic"
|
|
assert result["action_type"] == "exclude"
|
|
|
|
|
|
class TestBuildCanonicalKey:
|
|
def test_minimal(self):
|
|
key = build_canonical_key("implement", "api_rate_limiting")
|
|
assert key == "implement:api_rate_limiting"
|
|
|
|
def test_with_phase(self):
|
|
key = build_canonical_key("implement", "api_rate_limiting", phase="implementation")
|
|
assert key == "implement:api_rate_limiting:implementation"
|
|
|
|
def test_full(self):
|
|
key = build_canonical_key("implement", "api_rate_limiting", "implementation", "api_endpoints")
|
|
assert key == "implement:api_rate_limiting:implementation:api_endpoints"
|
|
|
|
|
|
class TestGetPhase:
|
|
def test_implement(self):
|
|
assert get_phase("implement") == "implementation"
|
|
|
|
def test_monitor(self):
|
|
assert get_phase("monitor") == "monitoring"
|
|
|
|
def test_test(self):
|
|
assert get_phase("test") == "testing"
|
|
|
|
def test_document(self):
|
|
assert get_phase("document") == "evidence"
|
|
|
|
def test_unknown(self):
|
|
assert get_phase("unknown_action") == "implementation"
|