"""Unit-Tests fuer den Founding-Wizard Service. Testet: - Template-Renderer mit verschiedenen Inputs - Wizard-State -> Context Mapping - Markdown -> DOCX Konvertierung """ from __future__ import annotations import pytest from compliance.services.founding_wizard.template_renderer import ( find_undefined_placeholders, render_template, ) from compliance.services.founding_wizard.wizard_to_context import base_context class TestTemplateRenderer: def test_simple_variable_substitution(self): result = render_template("Hallo {{NAME}}!", {"NAME": "Welt"}) assert result == "Hallo Welt!" def test_missing_variable_placeholder(self): result = render_template("Hallo {{NAME}}!", {}) assert "[NAME fehlt]" in result def test_if_block_truthy(self): result = render_template( "Start {{#IF FLAG}}drin{{/IF}} Ende", {"FLAG": True} ) assert result == "Start drin Ende" def test_if_block_falsy(self): result = render_template( "Start {{#IF FLAG}}drin{{/IF}} Ende", {"FLAG": False} ) assert result == "Start Ende" def test_if_not_block(self): result = render_template( "{{#IF NOT FLAG}}negiert{{/IF}}", {"FLAG": False} ) assert "negiert" in result def test_truthy_int(self): result = render_template("{{#IF N}}yes{{/IF}}", {"N": 5}) assert "yes" in result def test_falsy_zero(self): result = render_template("{{#IF N}}yes{{/IF}}", {"N": 0}) assert "yes" not in result def test_truthy_list(self): result = render_template("{{#IF L}}yes{{/IF}}", {"L": [1, 2]}) assert "yes" in result def test_falsy_empty_list(self): result = render_template("{{#IF L}}yes{{/IF}}", {"L": []}) assert "yes" not in result def test_nested_if_blocks(self): template = "{{#IF A}}A-on{{#IF B}}+B-on{{/IF}}{{/IF}}" result = render_template(template, {"A": True, "B": True}) assert result == "A-on+B-on" def test_find_undefined_placeholders(self): template = "{{X}} {{Y}} {{#IF Z}}.{{/IF}}" undefined = find_undefined_placeholders(template, {"X": "1"}) assert "Y" in undefined assert "Z" in undefined assert "X" not in undefined class TestWizardToContext: def _basic_state(self) -> dict: return { "basics": { "company_name": "Test GmbH", "legal_form": "GmbH", "company_seat": "Stuttgart", "company_address": "Königstraße 1, 70173 Stuttgart", "company_purpose_description": "Test purpose", "company_purpose_bullets": ["a) Test", "b) Test 2"], "industry": "SaaS", "business_year": "Kalenderjahr", "has_research_focus": True, }, "capital": { "stammkapital_eur": 25000, "einlage_method": "Geld", "einlage_quote_initial_pct": 50, "has_sacheinlage": False, }, "gesellschafter": [ { "id": "g1", "anteil_nr": 1, "name": "Benjamin Bönisch", "geburtsdatum": "1980-01-01", "adresse": "Test 1", "nennbetrag_eur": 12500, "is_geschaeftsfuehrer": True, "internal_role": "CEO", "rolle": "founder", "has_academic_background": False, }, { "id": "g2", "anteil_nr": 2, "name": "Sharang Parnerkar", "geburtsdatum": "1985-05-15", "adresse": "Test 2", "nennbetrag_eur": 12500, "is_geschaeftsfuehrer": True, "internal_role": "CTO", "rolle": "founder", "has_academic_background": False, }, ], "notar": { "notary_name": "Dr. Notar", "notary_place": "Stuttgart", "notarial_date": "2026-06-01", }, "sha": { "has_sha": True, "vesting_months": 48, "cliff_months": 12, "drag_along_threshold_pct": 75, "tag_along_threshold_pct": 20, "reserved_matters_majority_pct": 75, "has_beirat": False, "has_texas_shootout": False, "has_ceo_designation": False, }, } def test_basics_in_context(self): ctx = base_context(self._basic_state()) assert ctx["COMPANY_NAME"] == "Test GmbH" assert ctx["COMPANY_LEGAL_FORM"] == "GmbH" assert ctx["COMPANY_SEAT"] == "Stuttgart" assert ctx["STAMMKAPITAL_EUR"] == "25.000" def test_num_gf_2_man(self): ctx = base_context(self._basic_state()) assert ctx["NUM_GF"] == 2 assert ctx["NUM_GF_TEXT"] == "zwei" assert ctx["IS_MULTI_GF"] is True assert ctx["NUM_GF_IS_2"] is True assert ctx["NUM_GF_GT_2"] is False def test_parties_list_format(self): ctx = base_context(self._basic_state()) plist = ctx["PARTIES_LIST"] assert "Benjamin Bönisch" in plist assert "Sharang Parnerkar" in plist assert "a)" in plist assert "b)" in plist def test_flags_default(self): ctx = base_context(self._basic_state()) assert ctx["HAS_SHA"] is True assert ctx["HAS_RESEARCH_FOCUS"] is True assert ctx["HAS_ACADEMIC_FOUNDER"] is False assert ctx["HAS_BEIRAT"] is False def test_academic_flag_detection(self): state = self._basic_state() state["gesellschafter"][0]["has_academic_background"] = True ctx = base_context(state) assert ctx["HAS_ACADEMIC_FOUNDER"] is True class TestMarkdownToDocx: def test_basic_conversion(self): from compliance.services.founding_wizard.markdown_to_docx import markdown_to_docx_bytes md = "# Titel\n\nDas ist ein Absatz.\n\n## Unterthema\n\n- Punkt 1\n- Punkt 2" result = markdown_to_docx_bytes(md) assert isinstance(result, bytes) # DOCX is a ZIP file starting with PK\x03\x04 assert result[:4] == b"PK\x03\x04" assert len(result) > 1000 # reasonable size def test_table_conversion(self): from compliance.services.founding_wizard.markdown_to_docx import markdown_to_docx_bytes md = "| A | B |\n| --- | --- |\n| 1 | 2 |\n| 3 | 4 |" result = markdown_to_docx_bytes(md) assert result[:4] == b"PK\x03\x04" def test_bold_italic(self): from compliance.services.founding_wizard.markdown_to_docx import markdown_to_docx_bytes md = "Das ist **fett** und _kursiv_ und `code`." result = markdown_to_docx_bytes(md) assert result[:4] == b"PK\x03\x04" class TestEndToEndRendering: """Test mit echtem Template-aehnlichen Markdown + 2-Mann GmbH Daten.""" def test_minimum_satzung_render(self): template = """# Satzung der {{COMPANY_NAME}} ## § 1 Firma (1) Die Gesellschaft führt die Firma {{COMPANY_NAME}}. (2) Sitz ist {{COMPANY_SEAT}}. {{#IF HAS_SHA}} ## § 5 SHA-Verweis Es gilt das SHA. {{/IF}} {{#IF NOT HAS_SHA}} ## § 5 Hinweis Kein SHA vereinbart. {{/IF}} """ ctx = base_context(TestWizardToContext()._basic_state()) result = render_template(template, ctx) assert "Test GmbH" in result assert "Stuttgart" in result assert "§ 5 SHA-Verweis" in result assert "Kein SHA vereinbart" not in result