feat(control-pipeline): add Document Compliance Engine — scope detection + document requirements
New service: document_scope_resolver.py with 28 document rules covering: - Base (impressum, privacy_policy) - Tracking (cookie_banner, cookie_policy) - E-Commerce (AGB, withdrawal, shipping, pricing, payment) - Digital (digital_content_terms, no_withdrawal_notice) - SaaS (ToS, service_description, DPA, SLA) - AI (transparency_notice, automated_decisions) - Hardware (warranty, return, CE, safety) - Environmental (WEEE, battery disposal) - Marketplace (seller terms, ranking transparency) - Subscription (cancellation terms) API: POST /v1/document-compliance/required Input: company flags + jurisdiction → Output: required documents + assessment Includes confidence scoring, escalation detection (e.g. ecommerce without distance_selling flag), and reasoning. 19 tests covering all business model combinations including B2B-only exclusions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
190
control-pipeline/tests/test_document_compliance.py
Normal file
190
control-pipeline/tests/test_document_compliance.py
Normal file
@@ -0,0 +1,190 @@
|
||||
"""Tests for Document Scope Resolver — required documents per business model."""
|
||||
|
||||
import pytest
|
||||
from services.document_scope_resolver import resolve_required_documents
|
||||
|
||||
|
||||
class TestBasicWebsite:
|
||||
def test_only_website(self):
|
||||
result = resolve_required_documents({"has_website": True})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "impressum" in types
|
||||
assert "privacy_policy" in types
|
||||
assert "withdrawal_policy" not in types
|
||||
assert "terms_and_conditions" not in types
|
||||
|
||||
def test_website_with_tracking(self):
|
||||
result = resolve_required_documents({"has_website": True, "uses_tracking": True})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "cookie_banner" in types
|
||||
assert "cookie_policy" in types
|
||||
|
||||
def test_no_cookie_banner_without_tracking(self):
|
||||
result = resolve_required_documents({"has_website": True})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "cookie_banner" not in types
|
||||
|
||||
|
||||
class TestEcommerce:
|
||||
def test_webshop_physical(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_ecommerce": True,
|
||||
"sells_physical_products": True, "distance_selling": True,
|
||||
"has_checkout": True,
|
||||
})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "terms_and_conditions" in types
|
||||
assert "withdrawal_policy" in types
|
||||
assert "shipping_information" in types
|
||||
assert "warranty_information" in types
|
||||
assert "agb_checkout_summary" in types
|
||||
assert "pricing_transparency" in types
|
||||
assert "payment_terms" in types
|
||||
|
||||
def test_b2b_only_no_withdrawal(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_ecommerce": True,
|
||||
"sells_physical_products": True, "b2b_only": True,
|
||||
"has_checkout": True,
|
||||
})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "terms_and_conditions" in types
|
||||
assert "withdrawal_policy" not in types # B2B = kein Widerruf
|
||||
assert "return_policy" not in types
|
||||
|
||||
def test_digital_products(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_ecommerce": True,
|
||||
"sells_digital_products": True, "distance_selling": True,
|
||||
})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "digital_content_terms" in types
|
||||
assert "no_withdrawal_notice" in types
|
||||
|
||||
|
||||
class TestSaaS:
|
||||
def test_saas_basic(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_saas": True, "has_user_accounts": True,
|
||||
})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "terms_of_service" in types
|
||||
assert "data_processing_agreement" in types
|
||||
assert "service_description" in types
|
||||
|
||||
def test_saas_with_ai(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_saas": True, "uses_ai": True,
|
||||
})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "ai_transparency_notice" in types
|
||||
|
||||
def test_saas_with_automated_decisions(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_saas": True, "automated_decisions": True,
|
||||
})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "automated_decision_explanation" in types
|
||||
|
||||
|
||||
class TestHardware:
|
||||
def test_regulated_products(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_ecommerce": True,
|
||||
"sells_regulated_products": True,
|
||||
})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "ce_conformity_declaration" in types
|
||||
assert "product_safety_instructions" in types
|
||||
|
||||
def test_electronics_weee(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_ecommerce": True,
|
||||
"sells_electronics": True,
|
||||
})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "weee_information" in types
|
||||
|
||||
def test_battery_products(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "sells_physical_products": True,
|
||||
"contains_battery": True,
|
||||
})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "battery_disposal_information" in types
|
||||
|
||||
|
||||
class TestMarketplace:
|
||||
def test_marketplace(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "operates_marketplace": True,
|
||||
})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "marketplace_seller_terms" in types
|
||||
assert "marketplace_ranking_transparency" in types
|
||||
|
||||
|
||||
class TestSubscription:
|
||||
def test_subscription(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_saas": True, "subscription_model": True,
|
||||
})
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
assert "subscription_cancellation_terms" in types
|
||||
|
||||
|
||||
class TestAssessment:
|
||||
def test_high_confidence_full_flags(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_ecommerce": True,
|
||||
"uses_tracking": True, "sells_physical_products": True,
|
||||
"distance_selling": True,
|
||||
})
|
||||
assert result["assessment"]["confidence"] >= 0.85
|
||||
|
||||
def test_low_confidence_few_flags(self):
|
||||
result = resolve_required_documents({"has_website": True})
|
||||
assert result["assessment"]["confidence"] < 0.80
|
||||
|
||||
def test_escalation_ecommerce_without_distance_selling(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_ecommerce": True,
|
||||
})
|
||||
assert result["assessment"]["escalation_flag"] is True
|
||||
|
||||
def test_no_escalation_b2b_explicit(self):
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_ecommerce": True, "b2b_only": True,
|
||||
})
|
||||
assert result["assessment"]["escalation_flag"] is False
|
||||
|
||||
|
||||
class TestFullStack:
|
||||
def test_full_stack_worst_case(self):
|
||||
"""Enterprise with everything: website + webshop + SaaS + AI + hardware."""
|
||||
result = resolve_required_documents({
|
||||
"has_website": True, "has_ecommerce": True, "has_saas": True,
|
||||
"has_checkout": True, "distance_selling": True,
|
||||
"uses_tracking": True, "uses_ai": True,
|
||||
"sells_physical_products": True, "sells_regulated_products": True,
|
||||
"sells_electronics": True, "contains_battery": True,
|
||||
"subscription_model": True, "has_user_accounts": True,
|
||||
})
|
||||
assert result["total_required"] >= 15
|
||||
types = [d["document_type"] for d in result["required_documents"]]
|
||||
# Must have all base docs
|
||||
assert "impressum" in types
|
||||
assert "privacy_policy" in types
|
||||
assert "cookie_banner" in types
|
||||
# Must have ecommerce docs
|
||||
assert "terms_and_conditions" in types
|
||||
assert "withdrawal_policy" in types
|
||||
# Must have SaaS docs
|
||||
assert "terms_of_service" in types
|
||||
assert "data_processing_agreement" in types
|
||||
# Must have AI docs
|
||||
assert "ai_transparency_notice" in types
|
||||
# Must have hardware docs
|
||||
assert "ce_conformity_declaration" in types
|
||||
assert "weee_information" in types
|
||||
assert "battery_disposal_information" in types
|
||||
Reference in New Issue
Block a user