from __future__ import annotations from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] APP = REPO_ROOT / "presidio_streamlit.py" SERIAL_PANEL = REPO_ROOT / "serial_review_panel_ui.py" RENDERER = REPO_ROOT / "replacement_decision_panel_ui.py" HELPER = REPO_ROOT / "replacement_decision.py" PLAN = REPO_ROOT / "REPLACE_LOGIC_UI_PLAN.md" CONTRACT_TEST = REPO_ROOT / "tests" / "test_replace_logic_ui_contract.py" STARTUP_PATCH = REPO_ROOT / "fix_streamlit_nested_expanders.py" def _normal_flow_text() -> str: return APP.read_text(encoding="utf-8") + "\n" + SERIAL_PANEL.read_text(encoding="utf-8") def _renderer_text() -> str: return RENDERER.read_text(encoding="utf-8") def test_replacement_helper_and_contract_assets_are_preserved_for_redesign(): assert HELPER.exists() assert PLAN.exists() assert CONTRACT_TEST.exists() assert RENDERER.exists() helper_text = HELPER.read_text(encoding="utf-8") plan_text = PLAN.read_text(encoding="utf-8") contract_text = CONTRACT_TEST.read_text(encoding="utf-8") assert "build_replacement_decision" in helper_text assert "build_replacement_audit" in helper_text assert "matching_occurrence_ids" in helper_text assert "staged decision preview only" in plan_text assert "existing review table remains source of truth and fallback" in plan_text assert "test_staged_vs_applied_state_contract_is_explicit_and_non_mutating" in contract_text def test_replacement_helper_panel_is_not_rendered_in_normal_scrub_flow(): app_text = APP.read_text(encoding="utf-8") serial_text = SERIAL_PANEL.read_text(encoding="utf-8") normal_flow = _normal_flow_text() assert "render_serial_review_panel(" in app_text assert "from replacement_decision_panel_ui import" not in app_text assert "from replacement_decision_panel_ui import" not in serial_text assert "render_replacement_decision_panel(" not in normal_flow assert "Replacement decision helper" not in normal_flow assert "replacement_decision_preview" not in normal_flow def test_serial_review_panel_remains_available_and_table_first(): normal_flow = _normal_flow_text() serial_text = SERIAL_PANEL.read_text(encoding="utf-8") assert "Stap voor stap controleren" in serial_text assert "expanded=False" in serial_text assert "Loop de gevonden gegevens één voor één na" in serial_text assert "de vervangtabel blijft leidend voor export" in serial_text assert "Filter voor stap-voor-stap controle" in serial_text assert "Serial review — experimentele reviewhulp" not in serial_text assert "table-first baseline" not in serial_text assert "no Scrub Key mutation" not in serial_text assert "no export blocking" not in serial_text assert "no reinsert behavior change" not in serial_text assert "render_serial_review_panel(" in normal_flow assert "include_side_by_side=False" in normal_flow assert "render_side_by_side_review_panel(" in normal_flow assert "replacement_editor" in normal_flow def test_parked_replacement_panel_still_documents_non_mutating_boundaries(): renderer_text = _renderer_text() for phrase in [ "staged decision preview only", "staged decision state is not applied state", "existing review table remains source of truth and fallback", "does not mutate review rows", "does not write edited_replacements_df", "does not apply replacements", "does not write Scrub Key mappings", "does not block export", "does not call export/download", "does not change reinsert behavior", "does not use fuzzy matching", "does not use real data", ]: assert phrase in renderer_text def test_normal_flow_does_not_call_export_download_scrub_key_or_reinsert_from_replacement_panel(): normal_flow = _normal_flow_text().lower() forbidden_calls = [ "render_replacement_decision_panel(", "replacement_decision_panel_ui", "scrub_key_to_json(", "build_scrub_key(", "validate_scrub_key(", "reinsert_from_scrub_key(", "reinsert_docx_bytes(", "reinsert_txt_bytes(", "replacement_decision_preview", ] for marker in forbidden_calls: assert marker.lower() not in normal_flow def test_no_automatic_replacement_or_editor_marking_features_were_added(): normal_flow = _normal_flow_text().lower() renderer_text = _renderer_text().lower() forbidden_normal_flow_markers = [ "automatic_replacement = true", "review_table_mutation = true", "st.session_state[\"replacement_editor", "click-to-mark", "advanced editor", "full-document marking", "unsafe_allow_html", ] for marker in forbidden_normal_flow_markers: assert marker not in normal_flow assert "automatic_replacement" in renderer_text assert "scrub_key_writes" in renderer_text assert "export_blocking" in renderer_text assert "reinsert_behavior_change" in renderer_text def test_no_startup_source_mutation_or_cloud_real_data_fixture_added(): rendered = _normal_flow_text() + "\n" + _renderer_text() startup_text = STARTUP_PATCH.read_text(encoding="utf-8") app_file_write_marker = "APP_FILE" + ".write_text" replace_once_marker = "replace" + "_once(" assert "replacement_decision_panel_ui" not in startup_text assert app_file_write_marker not in rendered assert replace_once_marker not in rendered assert "cloud services" in rendered.lower() assert "real data" in rendered.lower() forbidden_real_data_examples = [ "Jan " + "Jansen", "Piet " + "de " + "Vries", "123" + "456" + "782", ] for forbidden in forbidden_real_data_examples: assert forbidden not in rendered