File size: 5,869 Bytes
f02a571
 
 
 
 
 
 
 
 
429a15d
 
 
f02a571
 
429a15d
 
 
f02a571
 
 
 
 
 
429a15d
 
 
 
 
 
 
 
 
f02a571
429a15d
 
 
 
 
 
f02a571
429a15d
 
f02a571
 
429a15d
f02a571
 
429a15d
 
 
 
 
f02a571
 
429a15d
 
 
f02a571
5ae85c9
 
853ce51
 
5ae85c9
 
 
 
 
 
 
 
 
 
 
 
f02a571
853ce51
429a15d
 
f02a571
429a15d
f02a571
 
 
429a15d
 
 
 
 
 
 
 
 
f02a571
 
 
 
429a15d
 
f02a571
 
429a15d
 
f02a571
 
 
 
 
 
429a15d
f02a571
 
429a15d
f02a571
 
429a15d
 
f02a571
 
429a15d
 
 
 
 
 
 
 
f02a571
429a15d
 
f02a571
429a15d
 
 
 
f02a571
 
429a15d
70877b7
f02a571
 
70877b7
 
 
f02a571
70877b7
 
429a15d
 
f02a571
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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