feat: add compliance modules 2-5 (dashboard, security templates, process manager, evidence collector)
All checks were successful
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 32s
CI/CD / test-python-backend-compliance (push) Successful in 34s
CI/CD / test-python-document-crawler (push) Successful in 23s
CI/CD / test-python-dsms-gateway (push) Successful in 21s
CI/CD / validate-canonical-controls (push) Successful in 11s
CI/CD / Deploy (push) Successful in 2s
All checks were successful
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 32s
CI/CD / test-python-backend-compliance (push) Successful in 34s
CI/CD / test-python-document-crawler (push) Successful in 23s
CI/CD / test-python-dsms-gateway (push) Successful in 21s
CI/CD / validate-canonical-controls (push) Successful in 11s
CI/CD / Deploy (push) Successful in 2s
Module 2: Extended Compliance Dashboard with roadmap, module-status, next-actions, snapshots, score-history Module 3: 7 German security document templates (IT-Sicherheitskonzept, Datenschutz, Backup, Logging, Incident-Response, Zugriff, Risikomanagement) Module 4: Compliance Process Manager with CRUD, complete/skip/seed, ~50 seed tasks, 3-tab UI Module 5: Evidence Collector Extended with automated checks, control-mapping, coverage report, 4-tab UI Also includes: canonical control library enhancements (verification method, categories, dedup), control generator improvements, RAG client extensions 52 tests pass, frontend builds clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
175
backend-compliance/tests/test_security_templates.py
Normal file
175
backend-compliance/tests/test_security_templates.py
Normal file
@@ -0,0 +1,175 @@
|
||||
"""Tests for security document templates (Module 3)."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import MagicMock, patch
|
||||
from fastapi.testclient import TestClient
|
||||
from fastapi import FastAPI
|
||||
from datetime import datetime
|
||||
|
||||
from compliance.api.legal_template_routes import router
|
||||
from classroom_engine.database import get_db
|
||||
from compliance.api.tenant_utils import get_tenant_id
|
||||
|
||||
DEFAULT_TENANT_ID = "9282a473-5c95-4b3a-bf78-0ecc0ec71d3e"
|
||||
|
||||
# =============================================================================
|
||||
# Test App Setup
|
||||
# =============================================================================
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
|
||||
mock_db = MagicMock()
|
||||
|
||||
|
||||
def override_get_db():
|
||||
yield mock_db
|
||||
|
||||
|
||||
def override_tenant():
|
||||
return DEFAULT_TENANT_ID
|
||||
|
||||
|
||||
app.dependency_overrides[get_db] = override_get_db
|
||||
app.dependency_overrides[get_tenant_id] = override_tenant
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
SECURITY_TEMPLATE_TYPES = [
|
||||
"it_security_concept",
|
||||
"data_protection_concept",
|
||||
"backup_recovery_concept",
|
||||
"logging_concept",
|
||||
"incident_response_plan",
|
||||
"access_control_concept",
|
||||
"risk_management_concept",
|
||||
]
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Helpers
|
||||
# =============================================================================
|
||||
|
||||
def make_template_row(doc_type, title="Test Template", content="# Test"):
|
||||
row = MagicMock()
|
||||
row._mapping = {
|
||||
"id": "tmpl-001",
|
||||
"tenant_id": DEFAULT_TENANT_ID,
|
||||
"document_type": doc_type,
|
||||
"title": title,
|
||||
"description": f"Test {doc_type}",
|
||||
"content": content,
|
||||
"placeholders": ["COMPANY_NAME", "ISB_NAME"],
|
||||
"language": "de",
|
||||
"jurisdiction": "DE",
|
||||
"status": "published",
|
||||
"license_id": None,
|
||||
"license_name": None,
|
||||
"source_name": None,
|
||||
"inspiration_sources": [],
|
||||
"created_at": datetime(2026, 3, 14),
|
||||
"updated_at": datetime(2026, 3, 14),
|
||||
}
|
||||
return row
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Tests
|
||||
# =============================================================================
|
||||
|
||||
class TestSecurityTemplateTypes:
|
||||
"""Verify the 7 security template types are accepted by the API."""
|
||||
|
||||
def test_all_security_types_in_valid_set(self):
|
||||
"""All 7 security template types are in VALID_DOCUMENT_TYPES."""
|
||||
from compliance.api.legal_template_routes import VALID_DOCUMENT_TYPES
|
||||
|
||||
for doc_type in SECURITY_TEMPLATE_TYPES:
|
||||
assert doc_type in VALID_DOCUMENT_TYPES, (
|
||||
f"{doc_type} not in VALID_DOCUMENT_TYPES"
|
||||
)
|
||||
|
||||
def test_security_template_count(self):
|
||||
"""There are exactly 7 security template types."""
|
||||
assert len(SECURITY_TEMPLATE_TYPES) == 7
|
||||
|
||||
def test_create_security_template_accepted(self):
|
||||
"""Creating a template with a security type is accepted (not 400)."""
|
||||
insert_row = MagicMock()
|
||||
insert_row._mapping = {
|
||||
"id": "new-tmpl",
|
||||
"tenant_id": DEFAULT_TENANT_ID,
|
||||
"document_type": "it_security_concept",
|
||||
"title": "IT-Sicherheitskonzept",
|
||||
"description": "Test",
|
||||
"content": "# IT-Sicherheitskonzept",
|
||||
"placeholders": [],
|
||||
"language": "de",
|
||||
"jurisdiction": "DE",
|
||||
"status": "draft",
|
||||
"license_id": None,
|
||||
"license_name": None,
|
||||
"source_name": None,
|
||||
"inspiration_sources": [],
|
||||
"created_at": datetime(2026, 3, 14),
|
||||
"updated_at": datetime(2026, 3, 14),
|
||||
}
|
||||
mock_db.execute.return_value.fetchone.return_value = insert_row
|
||||
mock_db.commit = MagicMock()
|
||||
|
||||
resp = client.post("/legal-templates", json={
|
||||
"document_type": "it_security_concept",
|
||||
"title": "IT-Sicherheitskonzept",
|
||||
"content": "# IT-Sicherheitskonzept\n\n## 1. Managementzusammenfassung",
|
||||
"language": "de",
|
||||
"jurisdiction": "DE",
|
||||
})
|
||||
# Should NOT be 400 (invalid type)
|
||||
assert resp.status_code != 400 or "Invalid document_type" not in resp.text
|
||||
|
||||
def test_invalid_type_rejected(self):
|
||||
"""A non-existent template type is rejected with 400."""
|
||||
resp = client.post("/legal-templates", json={
|
||||
"document_type": "nonexistent_type",
|
||||
"title": "Test",
|
||||
"content": "# Test",
|
||||
})
|
||||
assert resp.status_code == 400
|
||||
assert "Invalid document_type" in resp.json()["detail"]
|
||||
|
||||
|
||||
class TestSecurityTemplateFilter:
|
||||
"""Verify filtering templates by security document types."""
|
||||
|
||||
def test_filter_by_security_type(self):
|
||||
"""GET /legal-templates?document_type=it_security_concept returns matching templates."""
|
||||
row = make_template_row("it_security_concept", "IT-Sicherheitskonzept")
|
||||
mock_db.execute.return_value.fetchall.return_value = [row]
|
||||
|
||||
resp = client.get("/legal-templates?document_type=it_security_concept")
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert "templates" in data or isinstance(data, list)
|
||||
|
||||
|
||||
class TestSecurityTemplatePlaceholders:
|
||||
"""Verify placeholder structure for security templates."""
|
||||
|
||||
def test_common_placeholders_present(self):
|
||||
"""Security templates should use standard placeholders."""
|
||||
common_placeholders = [
|
||||
"COMPANY_NAME", "GF_NAME", "ISB_NAME",
|
||||
"DOCUMENT_VERSION", "VERSION_DATE", "NEXT_REVIEW_DATE",
|
||||
]
|
||||
row = make_template_row(
|
||||
"it_security_concept",
|
||||
content="# IT-Sicherheitskonzept\n{{COMPANY_NAME}} {{ISB_NAME}}"
|
||||
)
|
||||
row._mapping["placeholders"] = common_placeholders
|
||||
mock_db.execute.return_value.fetchone.return_value = row
|
||||
|
||||
# Verify the mock has all expected placeholders
|
||||
assert all(
|
||||
p in row._mapping["placeholders"]
|
||||
for p in ["COMPANY_NAME", "GF_NAME", "ISB_NAME"]
|
||||
)
|
||||
Reference in New Issue
Block a user