feat(founding-wizard): Per-Person IP-Assignment + Prefill + E2E-Tests
CI / loc-budget (push) Failing after 20s
CI / detect-changes (push) Successful in 12s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / validate-canonical-controls (push) Successful in 19s
CI / nodejs-build (push) Successful in 3m17s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-backend (push) Successful in 43s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped

Wizard unterstuetzt jetzt 2-4 Gesellschafter mit individuellem IP-Bereich:
- Pro Gruender ein IP-Assignment-Vertrag (z.B. Benjamin: Compliance+RAG;
  Sharang: Security+Infrastruktur). Pro GF ein eigener Dienstvertrag.
- Step 1: Prefill-Button aus Unternehmensprofil + Felder Registergericht
  und HRB-Nr.
- Step 2: Rollen-Dropdown (CEO/CTO/CFO/COO/CPO/GF/Sonstige) statt freie
  Texteingabe, IP-Bereiche-Textarea pro Person.

Backend:
- generate_documents() iteriert pro Person fuer PER_PERSON_DOCS.
- _build_person_context() injiziert ASSIGNOR_*, GF_*, IP_LIST_DETAILS
  aus person.ip_areas.
- base_context() propagiert basics.register_court und basics.hrb_number.

Tests:
- 30/30 Pytest gruen (6 neue: Per-Person-Context, Slug-Helper,
  Registergericht-Propagation).
- 4 neue Playwright-E2E-Specs (hermetisch via route.fulfill, mit
  Console-/Page-Error-Traps): kompletter 8-Step-Flow, Prefill-Fehlerpfad,
  Step-Navigation/Reset, Rollen-Dropdown + IP-Areas.
- Spec setzt 'bp-sdk-cookie-consent' im addInitScript damit der
  CookieBannerOverlay nicht die Wizard-Buttons ueberlagert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-21 18:49:10 +02:00
parent 138d9068c4
commit 7335f64f4f
7 changed files with 677 additions and 17 deletions
@@ -227,6 +227,70 @@ class TestMarkdownToDocx:
assert result[:4] == b"PK\x03\x04"
class TestPerPersonContext:
"""Tests fuer per-person Context-Building (IP-Assignment, GF-Vertrag)."""
def test_build_person_context_ip_areas_as_list(self):
from compliance.api.founding_wizard_routes import _build_person_context
base = {"COMPANY_NAME": "X GmbH"}
person = {
"name": "Benjamin Bönisch",
"geburtsdatum": "1980-01-01",
"adresse": "Test 1",
"internal_role": "CEO",
"ip_areas": ["Compliance-Engine", "RAG-Pipeline"],
}
ctx = _build_person_context(base, person, "ip_assignment_agreement")
assert ctx["ASSIGNOR_NAME"] == "Benjamin Bönisch"
assert "Compliance-Engine" in ctx["IP_LIST_DETAILS"]
assert "RAG-Pipeline" in ctx["IP_LIST_DETAILS"]
# Two distinct persons should yield distinct IP_LIST_DETAILS
person2 = {**person, "name": "Sharang", "ip_areas": ["Security", "Infrastruktur"]}
ctx2 = _build_person_context(base, person2, "ip_assignment_agreement")
assert ctx["IP_LIST_DETAILS"] != ctx2["IP_LIST_DETAILS"]
assert "Security" in ctx2["IP_LIST_DETAILS"]
def test_build_person_context_fallback_when_no_ip_areas(self):
"""Wenn keine ip_areas gesetzt sind, behaelt der Context den Default aus base."""
from compliance.api.founding_wizard_routes import _build_person_context
base = {"COMPANY_NAME": "X GmbH", "IP_LIST_DETAILS": "- Default IP"}
person = {"name": "Foo", "ip_areas": []}
ctx = _build_person_context(base, person, "ip_assignment_agreement")
assert ctx["IP_LIST_DETAILS"] == "- Default IP"
def test_safe_slug_handles_special_chars(self):
from compliance.api.founding_wizard_routes import _safe_slug
assert _safe_slug("Benjamin Bönisch") == "Benjamin_B_nisch"
assert _safe_slug("Sharang Parnerkar") == "Sharang_Parnerkar"
assert _safe_slug("") == "Person"
assert _safe_slug(" ") == "Person"
def test_per_person_docs_set_contains_expected(self):
from compliance.api.founding_wizard_routes import PER_PERSON_DOCS
assert "ip_assignment_agreement" in PER_PERSON_DOCS
assert "managing_director_employment_contract" in PER_PERSON_DOCS
# Satzung etc. duerfen NICHT per-person sein:
assert "articles_of_association" not in PER_PERSON_DOCS
assert "sha" not in PER_PERSON_DOCS
class TestBasicsRegisterCourt:
def test_register_court_propagates(self):
state = TestWizardToContext()._basic_state()
state["basics"]["register_court"] = "Amtsgericht Stuttgart"
state["basics"]["hrb_number"] = "HRB 12345"
ctx = base_context(state)
assert ctx["REGISTER_COURT"] == "Amtsgericht Stuttgart"
assert ctx["COMPANY_REGISTRY_COURT"] == "Amtsgericht Stuttgart"
assert ctx["HRB_NUMBER"] == "HRB 12345"
assert ctx["HAS_HRB"] is True
def test_register_court_default_when_missing(self):
ctx = base_context(TestWizardToContext()._basic_state())
assert "[zuständiges Amtsgericht]" in ctx["REGISTER_COURT"]
assert ctx["HAS_HRB"] is False
class TestEndToEndRendering:
"""Test mit echtem Template-aehnlichen Markdown + 2-Mann GmbH Daten."""