"""Tests for VVT routes and schemas (vvt_routes.py, vvt_models.py).""" import pytest from unittest.mock import MagicMock, patch from datetime import datetime, date import uuid from compliance.api.schemas import ( VVTActivityCreate, VVTActivityUpdate, VVTOrganizationUpdate, VVTStatsResponse, ) from compliance.api.vvt_routes import _activity_to_response, _log_audit from compliance.db.vvt_models import VVTActivityDB, VVTOrganizationDB, VVTAuditLogDB # ============================================================================= # Schema Tests # ============================================================================= class TestVVTActivityCreate: def test_default_values(self): req = VVTActivityCreate(vvt_id="VVT-001", name="Test Verarbeitung") assert req.vvt_id == "VVT-001" assert req.name == "Test Verarbeitung" assert req.status == "DRAFT" assert req.protection_level == "MEDIUM" assert req.dpia_required is False assert req.purposes == [] assert req.legal_bases == [] def test_full_values(self): req = VVTActivityCreate( vvt_id="VVT-002", name="Gehaltsabrechnung", description="Verarbeitung von Gehaltsabrechnungsdaten", purposes=["Vertragserfuellung"], legal_bases=["Art. 6 Abs. 1b DSGVO"], data_subject_categories=["Mitarbeiter"], personal_data_categories=["Bankdaten", "Steuer-ID"], status="APPROVED", dpia_required=False, ) assert req.vvt_id == "VVT-002" assert req.status == "APPROVED" assert len(req.purposes) == 1 assert len(req.personal_data_categories) == 2 def test_serialization(self): req = VVTActivityCreate(vvt_id="VVT-003", name="Test") data = req.model_dump() assert data["vvt_id"] == "VVT-003" assert isinstance(data["purposes"], list) assert isinstance(data["retention_period"], dict) class TestVVTActivityUpdate: def test_partial_update(self): req = VVTActivityUpdate(status="APPROVED") data = req.model_dump(exclude_none=True) assert data == {"status": "APPROVED"} def test_empty_update(self): req = VVTActivityUpdate() data = req.model_dump(exclude_none=True) assert data == {} def test_multi_field_update(self): req = VVTActivityUpdate( name="Updated Name", dpia_required=True, protection_level="HIGH", ) data = req.model_dump(exclude_none=True) assert data["name"] == "Updated Name" assert data["dpia_required"] is True assert data["protection_level"] == "HIGH" class TestVVTOrganizationUpdate: def test_defaults(self): req = VVTOrganizationUpdate() data = req.model_dump(exclude_none=True) assert data == {} def test_partial_update(self): req = VVTOrganizationUpdate( organization_name="BreakPilot GmbH", dpo_name="Max Mustermann", ) data = req.model_dump(exclude_none=True) assert data["organization_name"] == "BreakPilot GmbH" assert data["dpo_name"] == "Max Mustermann" class TestVVTStatsResponse: def test_stats_response(self): stats = VVTStatsResponse( total=5, by_status={"DRAFT": 3, "APPROVED": 2}, by_business_function={"HR": 2, "IT": 3}, dpia_required_count=1, third_country_count=0, draft_count=3, approved_count=2, ) assert stats.total == 5 assert stats.by_status["DRAFT"] == 3 assert stats.dpia_required_count == 1 # ============================================================================= # DB Model Tests # ============================================================================= class TestVVTModels: def test_activity_defaults(self): act = VVTActivityDB() assert act.status is None or act.status == 'DRAFT' assert act.dpia_required is False or act.dpia_required is None def test_activity_repr(self): act = VVTActivityDB() act.vvt_id = "VVT-001" act.name = "Test" assert "VVT-001" in repr(act) def test_organization_repr(self): org = VVTOrganizationDB() org.organization_name = "Test GmbH" assert "Test GmbH" in repr(org) def test_audit_log_repr(self): log = VVTAuditLogDB() log.action = "CREATE" log.entity_type = "activity" assert "CREATE" in repr(log) # ============================================================================= # Helper Function Tests # ============================================================================= class TestActivityToResponse: def _make_activity(self, **kwargs) -> VVTActivityDB: act = VVTActivityDB() act.id = uuid.uuid4() act.vvt_id = kwargs.get("vvt_id", "VVT-001") act.name = kwargs.get("name", "Test") act.description = kwargs.get("description", None) act.purposes = kwargs.get("purposes", []) act.legal_bases = kwargs.get("legal_bases", []) act.data_subject_categories = kwargs.get("data_subject_categories", []) act.personal_data_categories = kwargs.get("personal_data_categories", []) act.recipient_categories = kwargs.get("recipient_categories", []) act.third_country_transfers = kwargs.get("third_country_transfers", []) act.retention_period = kwargs.get("retention_period", {}) act.tom_description = kwargs.get("tom_description", None) act.business_function = kwargs.get("business_function", None) act.systems = kwargs.get("systems", []) act.deployment_model = kwargs.get("deployment_model", None) act.data_sources = kwargs.get("data_sources", []) act.data_flows = kwargs.get("data_flows", []) act.protection_level = kwargs.get("protection_level", "MEDIUM") act.dpia_required = kwargs.get("dpia_required", False) act.structured_toms = kwargs.get("structured_toms", {}) act.status = kwargs.get("status", "DRAFT") act.responsible = kwargs.get("responsible", None) act.owner = kwargs.get("owner", None) act.created_at = datetime.utcnow() act.updated_at = None return act def test_basic_conversion(self): act = self._make_activity(vvt_id="VVT-001", name="Kundendaten") response = _activity_to_response(act) assert response.vvt_id == "VVT-001" assert response.name == "Kundendaten" assert response.status == "DRAFT" assert response.protection_level == "MEDIUM" def test_null_lists_become_empty(self): act = self._make_activity() act.purposes = None act.legal_bases = None response = _activity_to_response(act) assert response.purposes == [] assert response.legal_bases == [] def test_null_dicts_become_empty(self): act = self._make_activity() act.retention_period = None act.structured_toms = None response = _activity_to_response(act) assert response.retention_period == {} assert response.structured_toms == {} class TestLogAudit: def test_creates_audit_entry(self): mock_db = MagicMock() act_id = uuid.uuid4() _log_audit( db=mock_db, action="CREATE", entity_type="activity", entity_id=act_id, changed_by="test_user", new_values={"name": "Test"}, ) mock_db.add.assert_called_once() added = mock_db.add.call_args[0][0] assert added.action == "CREATE" assert added.entity_type == "activity" assert added.entity_id == act_id def test_defaults_changed_by(self): mock_db = MagicMock() _log_audit(mock_db, "DELETE", "activity") added = mock_db.add.call_args[0][0] assert added.changed_by == "system"