f4357a2e9b
Sprint 1 — Foundation (User-Vorgabe 2026-06-08): Foundation: - _base.py: BaseSpecialistAgent ABC + Pydantic Contract (AgentInput/AgentOutput/Finding/Recommendation/McCoverage/EscalationLog). - _base.lint_output(): Disclaimer-Linter verbietet "rechtssicher" / "garantiert" / "gesetzeskonform" — scrubbed inline + Log in notes. - _registry.py: AgentRegistry mit MC-Owner-Mapping (verhindert Doppel-Ownership). - _escalation.py: cascade(local → ovh). qwen2.5:7b default, OVH 120b als Stage-2 (deaktiviert wenn OVH_URL leer). - _rollup.py: deterministisches Dedup ähnlicher actions zu Recommendations mit related_finding_ids[]. - _evidence_vault.py: Pro-Run File-Vault für Playwright-Videos, Screenshots, CSV. SHA256 + manifest.json. DSR-tauglich (delete_run). Agenten: - ImpressumAgent v2 (impressum/agent.py + mcs.py) — konsolidiert v1-Pattern-Match + v2-LLM-MVP unter dem neuen Contract. 12 MCs. - CookiePolicyAgent v1 (cookie_policy/agent.py + mcs.py) — 12 MCs zu Cookie-Richtlinie-Vollständigkeit + KB-Layer für CMP-Vendor-Cross-Check. Tests: 25/25 grün (10 Impressum + 9 Vault + 6 Cookie-Policy). Roadmap: SSE-Test-Endpoint + Frontend-Tab → DSE/AGB-Agents → Cookie-Banner-Themen-Agent → Cross-Doc-Konsistenz-Agent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
144 lines
4.3 KiB
Python
144 lines
4.3 KiB
Python
"""Tests für Cookie-Policy-Agent."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
|
|
import pytest
|
|
|
|
from compliance.services.specialist_agents import (
|
|
REGISTRY,
|
|
AgentInput,
|
|
CookiePolicyAgent,
|
|
Severity,
|
|
)
|
|
|
|
|
|
FULL_POLICY = """Cookie-Richtlinie
|
|
|
|
Stand: 1. Juni 2026
|
|
|
|
Wir verwenden auf unserer Website verschiedene Cookies. Diese werden
|
|
in folgende Kategorien eingeteilt:
|
|
|
|
1. Essentielle Cookies (unbedingt erforderlich)
|
|
Zweck: Diese Cookies dienen der grundlegenden Funktion der Website.
|
|
Rechtsgrundlage: § 25 Abs. 2 TDDDG
|
|
Laufzeit: Session
|
|
|
|
2. Funktionale Cookies
|
|
Zweck: Speichern Ihre Präferenzen wie Sprache und Region.
|
|
Rechtsgrundlage: Art. 6 Abs. 1 lit. a DSGVO
|
|
Laufzeit: 30 Tage
|
|
|
|
3. Analytics-Cookies (Performance)
|
|
Drittanbieter: Google LLC, USA
|
|
Zweck: Nutzungsstatistiken erheben.
|
|
Laufzeit: 24 Monate
|
|
Cookies: _ga, _gid
|
|
Drittland: USA — Standardvertragsklauseln + Data Privacy Framework
|
|
|
|
4. Marketing-Cookies (Tracking)
|
|
Drittanbieter: Meta Platforms Inc., USA
|
|
Cookies: _fbp, _fbc
|
|
Laufzeit: 90 Tage
|
|
|
|
Sie können Ihre Cookie-Einstellungen jederzeit ändern über den Link
|
|
unten oder das Banner erneut öffnen.
|
|
|
|
Browser-Einstellungen: Auch in Chrome, Firefox, Safari und Edge
|
|
können Sie Cookies blockieren oder löschen.
|
|
|
|
Kontakt: datenschutz@example.com
|
|
Datenschutzbeauftragter: Max Mustermann
|
|
"""
|
|
|
|
|
|
GAPPY_POLICY = """Cookies
|
|
|
|
Wir verwenden Cookies um die Website zu betreiben.
|
|
Cookies werden so lange gespeichert wie nötig.
|
|
"""
|
|
|
|
|
|
def _run(coro):
|
|
return asyncio.get_event_loop().run_until_complete(coro)
|
|
|
|
|
|
def test_agent_is_registered():
|
|
agent = REGISTRY.get("cookie_policy")
|
|
assert agent is not None
|
|
assert agent.doc_type == "cookie"
|
|
|
|
|
|
def test_short_text_skipped(monkeypatch):
|
|
async def _no_cascade(*a, **kw): return None, []
|
|
monkeypatch.setattr(
|
|
"compliance.services.specialist_agents.cookie_policy.agent.cascade",
|
|
_no_cascade,
|
|
)
|
|
agent = CookiePolicyAgent()
|
|
out = _run(agent.evaluate(AgentInput(doc_type="cookie", text="x")))
|
|
assert out.mc_total > 0
|
|
assert all(c.status == "skipped" for c in out.mc_coverage)
|
|
|
|
|
|
def test_full_policy_has_few_high_findings(monkeypatch):
|
|
async def _no_cascade(*a, **kw): return None, []
|
|
monkeypatch.setattr(
|
|
"compliance.services.specialist_agents.cookie_policy.agent.cascade",
|
|
_no_cascade,
|
|
)
|
|
agent = CookiePolicyAgent()
|
|
out = _run(agent.evaluate(AgentInput(doc_type="cookie", text=FULL_POLICY)))
|
|
high = [f for f in out.findings if f.severity == Severity.HIGH.value]
|
|
assert not high, f"unexpected HIGH findings: {[f.field_id for f in high]}"
|
|
|
|
|
|
def test_gappy_policy_triggers_high(monkeypatch):
|
|
async def _no_cascade(*a, **kw): return None, []
|
|
monkeypatch.setattr(
|
|
"compliance.services.specialist_agents.cookie_policy.agent.cascade",
|
|
_no_cascade,
|
|
)
|
|
agent = CookiePolicyAgent()
|
|
out = _run(agent.evaluate(AgentInput(doc_type="cookie",
|
|
text=GAPPY_POLICY)))
|
|
field_ids = {f.field_id for f in out.findings}
|
|
# 4 Kategorien fehlen, Vendoren fehlen, Opt-Out fehlt, Tabelle fehlt
|
|
assert "categories_named" in field_ids
|
|
assert "vendor_recipients" in field_ids
|
|
assert "opt_out_mechanism" in field_ids
|
|
|
|
|
|
def test_cmp_vendor_cross_check_emits_finding(monkeypatch):
|
|
async def _no_cascade(*a, **kw): return None, []
|
|
monkeypatch.setattr(
|
|
"compliance.services.specialist_agents.cookie_policy.agent.cascade",
|
|
_no_cascade,
|
|
)
|
|
agent = CookiePolicyAgent()
|
|
out = _run(agent.evaluate(AgentInput(
|
|
doc_type="cookie", text=FULL_POLICY,
|
|
context={"cmp_vendors": [
|
|
{"name": "Hotjar"}, # NICHT in Policy
|
|
{"name": "Google LLC"}, # IN Policy
|
|
]},
|
|
)))
|
|
field_ids = {f.field_id for f in out.findings}
|
|
assert "vendor_consistency" in field_ids
|
|
cmp_f = next(f for f in out.findings
|
|
if f.field_id == "vendor_consistency")
|
|
assert "Hotjar" in cmp_f.evidence
|
|
assert "Google" not in cmp_f.evidence
|
|
|
|
|
|
def test_recommendations_are_built():
|
|
agent = CookiePolicyAgent()
|
|
out = _run(agent.evaluate(AgentInput(doc_type="cookie",
|
|
text=GAPPY_POLICY)))
|
|
assert out.recommendations
|
|
# Jede Recommendation hat mind. ein related_finding
|
|
for r in out.recommendations:
|
|
assert r.related_finding_ids
|