"""Phase 3: scan_context → business_scope Normalisierung + Rechtsform-Gates. Rechts-Zuordnung vom Domain-Experten bestätigt (2026-06-10): e.K. ist registerpflichtig (Handelsregister-Finding) aber ohne gesonderte Vertretungsberechtigte; Verein umgekehrt; USt-IdNr fehlt → kein Finding; healthcare triggert NICHT regulated_profession. """ from __future__ import annotations import asyncio import pytest from compliance.services.specialist_agents import AgentInput from compliance.services.specialist_agents.impressum._classification import ( scan_context_to_scope, ) from compliance.services.specialist_agents.impressum.agent import ImpressumAgent def _scope(**kw) -> set[str]: return set(scan_context_to_scope(kw)) def test_gmbh_no_exclusions(): # GmbH → keine Ausschluss-Tokens → beide MCs anwendbar. s = _scope(legal_form="gmbh") assert "kein_handelsregister" not in s assert "keine_vertretung" not in s def test_einzelkaufmann_register_but_no_vertretung(): # e.K. registerpflichtig (kein Ausschluss) aber Inhaber genügt. s = _scope(legal_form="ek") assert "kein_handelsregister" not in s assert "keine_vertretung" in s def test_verein_vertretung_but_no_register(): # e.V. = Vereinsregister, NICHT Handelsregister → HR ausgeschlossen. s = _scope(legal_form="verein") assert "kein_handelsregister" in s assert "keine_vertretung" not in s def test_branche_tokens(): assert "ecommerce" in _scope(industry="ecommerce") assert "ecommerce" in _scope(direct_sales="yes") assert "b2c" in _scope(business_model="b2c") assert "b2c" in _scope(business_model="both") assert "automotive" in _scope(industry="automotive") assert "editorial" in _scope(industry="media") assert "insurance" in _scope(industry="insurance") assert "financial_services" in _scope(industry="banking") assert "public_authority" in _scope(legal_form="behoerde") assert "public_authority" in _scope(industry="public") def test_healthcare_does_not_imply_regulated_profession(): # Krankenhaus-GmbH ≠ Apotheke → industry allein triggert es nicht. assert "regulated_profession" not in _scope( industry="healthcare", legal_form="gmbh") def test_unknown_legal_form_no_exclusions(): # Unbekannte Rechtsform → keine Ausschluss-Tokens → MCs bleiben anwendbar. # (Das 4-Status-Modell INSUFFICIENT_EVIDENCE folgt in der nächsten Phase.) s = _scope(industry="ecommerce") # kein legal_form assert "kein_handelsregister" not in s assert "keine_vertretung" not in s # ── Agent-Verhalten mit den Gates ────────────────────────────────── IMPRESSUM_MINIMAL = ( "Angaben gemäß § 5 TMG\n\n" "Beispiel Firma\n" "Musterstraße 1\n" "12345 Berlin\n\n" "E-Mail: info@example.com\n" "Telefon: +49 30 1234567\n" "Mehr Informationen auf unserer Website.\n" ) @pytest.fixture(autouse=True) def _llm_offline(monkeypatch): async def _no_validate(*_a, **_kw): return {} monkeypatch.setattr( "compliance.services.specialist_agents.impressum.agent.validate_present", _no_validate, raising=False, ) def _finding_fields(legal_form: str) -> set[str]: agent = ImpressumAgent() out = asyncio.run(agent.evaluate(AgentInput( doc_type="impressum", text=IMPRESSUM_MINIMAL, business_scope=scan_context_to_scope({"legal_form": legal_form}), ))) return {f.field_id for f in out.findings} def test_einzelkaufmann_handelsregister_finding_no_vertretung(): fields = _finding_fields("ek") assert "handelsregister" in fields # registerpflichtig assert "vertretungsberechtigte" not in fields # Inhaber genügt def test_gmbh_both_findings(): fields = _finding_fields("gmbh") assert "handelsregister" in fields assert "vertretungsberechtigte" in fields def test_ust_id_absent_yields_no_finding(): # USt-IdNr fehlt im Text → optional → KEIN Finding (egal welche Rechtsform). assert "ust_id" not in _finding_fields("gmbh") assert "ust_id" not in _finding_fields("ek")