"""Unit tests for the shared ``display_condition`` evaluator used by every Feishu-backed flow. Pins the schema variations + operator coverage + the two answer-sourcing modes (strict for widget-id-keyed answers, relaxed for the dict-flow backends that don't thread widget-id keys). """ from app.integrations.feishu_form_visibility import ( evaluate_display_conditions, is_widget_visible, ) def _widget(wid: str, **kwargs): base = {"id": wid, "name": kwargs.pop("name", wid), "type": "input"} base.update(kwargs) return base def test_no_condition_returns_visible(): widgets = [_widget("a")] assert evaluate_display_conditions(widgets, {}) == {"a": True} def test_form1_conditions_array_equal_match(): widgets = [ _widget("trigger"), _widget("gated", display_condition={ "conditions": [{"widget_id": "trigger", "operator": "equal", "value": "yes"}], }), ] assert evaluate_display_conditions(widgets, {"trigger": "yes"})["gated"] is True assert evaluate_display_conditions(widgets, {"trigger": "no"})["gated"] is False def test_form1_or_operator(): widgets = [_widget("gated", display_condition={ "conditions": [ {"widget_id": "a", "operator": "equal", "value": "x"}, {"widget_id": "b", "operator": "equal", "value": "y"}, ], "operator": "OR", })] assert evaluate_display_conditions(widgets, {"a": "x", "b": "z"})["gated"] is True assert evaluate_display_conditions(widgets, {"a": "q", "b": "y"})["gated"] is True assert evaluate_display_conditions(widgets, {"a": "q", "b": "z"})["gated"] is False def test_form2_condition_groups_or_between(): widgets = [_widget("gated", display_condition={ "condition_groups": [ {"conditions": [{"widget_id": "a", "operator": "equal", "value": "x"}]}, {"conditions": [{"widget_id": "b", "operator": "equal", "value": "y"}]}, ], })] assert evaluate_display_conditions(widgets, {"a": "x"})["gated"] is True assert evaluate_display_conditions(widgets, {"b": "y"})["gated"] is True assert evaluate_display_conditions(widgets, {})["gated"] is False # strict def test_form3_bare_single_condition(): widgets = [_widget("gated", display_condition={ "widget_id": "trig", "operator": "equal", "value": "ok", })] assert evaluate_display_conditions(widgets, {"trig": "ok"})["gated"] is True assert evaluate_display_conditions(widgets, {"trig": "no"})["gated"] is False def test_form4_bare_list_at_root(): widgets = [_widget("gated", display_condition=[ {"widget_id": "a", "operator": "equal", "value": "x"}, {"widget_id": "b", "operator": "equal", "value": "y"}, ])] assert evaluate_display_conditions(widgets, {"a": "x", "b": "y"})["gated"] is True assert evaluate_display_conditions(widgets, {"a": "x", "b": "z"})["gated"] is False def test_not_equal_operator(): widgets = [_widget("gated", display_condition={ "conditions": [{"widget_id": "t", "operator": "not_equal", "value": "x"}], })] assert evaluate_display_conditions(widgets, {"t": "y"})["gated"] is True assert evaluate_display_conditions(widgets, {"t": "x"})["gated"] is False def test_in_operator_with_array_actual(): widgets = [_widget("gated", display_condition={ "conditions": [{"widget_id": "t", "operator": "in", "value": "x"}], })] assert evaluate_display_conditions(widgets, {"t": ["x", "y"]})["gated"] is True assert evaluate_display_conditions(widgets, {"t": ["a", "b"]})["gated"] is False def test_string_coerce_radio_machine_key(): widgets = [_widget("gated", display_condition={ "conditions": [{"widget_id": "t", "operator": "equal", "value": "m28s4lec-3oa7yirug6o-0"}], })] assert evaluate_display_conditions(widgets, {"t": "m28s4lec-3oa7yirug6o-0"})["gated"] is True def test_unknown_operator_fails_open(): widgets = [_widget("gated", display_condition={ "conditions": [{"widget_id": "t", "operator": "future_op", "value": "x"}], })] assert evaluate_display_conditions(widgets, {"t": "anything"})["gated"] is True def test_unknown_schema_fails_open(): widgets = [_widget("gated", display_condition={"weird": "shape", "no": "conditions"})] assert evaluate_display_conditions(widgets, {"t": "x"})["gated"] is True def test_strict_mode_hides_when_trigger_missing(): """Default (assume_visible_when_answers_unsourced=False) — used by the frontend create form and ApprovalService. A conditional widget whose trigger answer is missing stays hidden (don't ask the user for the buyback photo before they answer the buyback radio). """ widgets = [_widget("gated", display_condition={ "conditions": [{"widget_id": "trigger", "operator": "equal", "value": "yes"}], })] assert evaluate_display_conditions(widgets, {})["gated"] is False def test_relaxed_mode_shows_when_trigger_unsourced(): """Relaxed mode (assume_visible_when_answers_unsourced=True) — used by the dict-flow backends (cloud_scope / DNS / domain / VPN / announcement / AI tool / SSO) that don't thread widget-id-keyed answers. When the trigger is genuinely unsourced from the caller's keying scheme, the evaluator falls open to visible rather than hiding the widget by accident. """ widgets = [_widget("gated", display_condition={ "conditions": [{"widget_id": "trigger", "operator": "equal", "value": "yes"}], })] assert evaluate_display_conditions( widgets, {}, assume_visible_when_answers_unsourced=True, )["gated"] is True def test_relaxed_mode_still_evaluates_when_trigger_present(): """The relaxed flag only kicks in for missing-trigger cases — if the trigger IS in the answer map, the evaluator runs the gate normally.""" widgets = [_widget("gated", display_condition={ "conditions": [{"widget_id": "trigger", "operator": "equal", "value": "yes"}], })] answers = {"trigger": "no"} assert evaluate_display_conditions( widgets, answers, assume_visible_when_answers_unsourced=True, )["gated"] is False def test_is_widget_visible_helper(): widget = _widget("gated", display_condition={ "conditions": [{"widget_id": "t", "operator": "equal", "value": "yes"}], }) assert is_widget_visible(widget, {"t": "yes"}) is True assert is_widget_visible(widget, {"t": "no"}) is False # No display_condition → always visible. assert is_widget_visible(_widget("a"), {}) is True def test_widget_without_id_is_skipped(): widgets = [{"name": "headless", "type": "text", "display_condition": None}] result = evaluate_display_conditions(widgets, {}) # No id → not in the result map. assert result == {} # ── Real Feishu wire shape (laptop_replacement 2026-05-12) ────────── # # Reverse-engineered from the live ``approval.asset_template_cache`` # row on prod TiDB. Pins the schema so a future evaluator regression # would fail loudly instead of silently fail-opening and showing # every conditional widget to the user (the exact bug the 2026-05-13 # audit caught). _FEISHU_REAL_WIRE_SHAPE = { "conditional": "OR", "conditions": [ { "conditional": "AND", "conditions": None, "expressions": [ { "compare_type": "is", "source_widget": { "custom_id": "", "id": "widget17288965866710001", }, "standard_value": "m28se01b-z05xuezwbv8-0", }, ], }, ], "expressions": None, } def test_feishu_real_wire_shape_match_hides_widget_when_not_picked(): widgets = [ _widget("widget17288965866710001", name="是否回购"), _widget("buyback_photo", name="对公转账截图", display_condition=_FEISHU_REAL_WIRE_SHAPE), ] # buyback widget answered "No" key (NOT the Yes machine key). assert evaluate_display_conditions( widgets, {"widget17288965866710001": "m28se01b-z05xuezwbv8-1"}, )["buyback_photo"] is False def test_feishu_real_wire_shape_match_shows_widget_when_picked(): widgets = [ _widget("widget17288965866710001", name="是否回购"), _widget("buyback_photo", name="对公转账截图", display_condition=_FEISHU_REAL_WIRE_SHAPE), ] assert evaluate_display_conditions( widgets, {"widget17288965866710001": "m28se01b-z05xuezwbv8-0"}, )["buyback_photo"] is True def test_feishu_real_wire_shape_compare_type_not(): """`Please explain the reason.` is gated with ``compare_type: "not"`` against the non-standard-model option key. The widget shows when the model is NOT non_standard (i.e. for the standard models).""" not_shape = { "conditional": "OR", "conditions": [ { "conditional": "AND", "conditions": None, "expressions": [ { "compare_type": "not", "source_widget": {"id": "model_widget"}, "standard_value": "non_standard_key", }, ], }, ], "expressions": None, } widgets = [ _widget("model_widget", name="申请电脑机型"), _widget("reason", name="Please explain the reason.", display_condition=not_shape), ] # Standard model → NOT non_standard → reason visible. assert evaluate_display_conditions( widgets, {"model_widget": "macbook_air_key"}, )["reason"] is True # Non-standard model → NOT non_standard is False → reason hidden. assert evaluate_display_conditions( widgets, {"model_widget": "non_standard_key"}, )["reason"] is False def test_feishu_real_wire_shape_text_widget_gated(): """Text widgets (description blocks like ``说明 4``) carry display_condition too — gated when the trigger answer doesn't match. Verifies the evaluator returns the visibility map for text widgets the same way as input widgets; the renderer is responsible for skipping render when visibility is False.""" widgets = [ _widget("buyback", name="是否回购"), _widget("notice", name="说明 4", type="text", display_condition={ "conditional": "OR", "conditions": [{ "conditional": "AND", "conditions": None, "expressions": [{ "compare_type": "is", "source_widget": {"id": "buyback"}, "standard_value": "yes_key", }], }], }), ] assert evaluate_display_conditions(widgets, {"buyback": "yes_key"})["notice"] is True assert evaluate_display_conditions(widgets, {"buyback": "no_key"})["notice"] is False def test_feishu_real_wire_shape_nested_or_groups(): """Outer ``conditional: OR`` with multiple condition groups — widget shows when ANY group matches.""" or_shape = { "conditional": "OR", "conditions": [ { "conditional": "AND", "conditions": None, "expressions": [{ "compare_type": "is", "source_widget": {"id": "a"}, "standard_value": "x", }], }, { "conditional": "AND", "conditions": None, "expressions": [{ "compare_type": "is", "source_widget": {"id": "b"}, "standard_value": "y", }], }, ], "expressions": None, } widgets = [ _widget("a"), _widget("b"), _widget("gated", display_condition=or_shape), ] assert evaluate_display_conditions(widgets, {"a": "x"})["gated"] is True assert evaluate_display_conditions(widgets, {"b": "y"})["gated"] is True assert evaluate_display_conditions(widgets, {"a": "x", "b": "y"})["gated"] is True assert evaluate_display_conditions(widgets, {"a": "p", "b": "q"})["gated"] is False def test_feishu_real_wire_shape_inner_and_multiple_expressions(): """Single condition group with ``conditional: AND`` + multiple expressions — all must match.""" and_shape = { "conditional": "OR", "conditions": [{ "conditional": "AND", "conditions": None, "expressions": [ {"compare_type": "is", "source_widget": {"id": "a"}, "standard_value": "x"}, {"compare_type": "is", "source_widget": {"id": "b"}, "standard_value": "y"}, ], }], "expressions": None, } widgets = [ _widget("a"), _widget("b"), _widget("gated", display_condition=and_shape), ] assert evaluate_display_conditions(widgets, {"a": "x", "b": "y"})["gated"] is True assert evaluate_display_conditions(widgets, {"a": "x", "b": "z"})["gated"] is False assert evaluate_display_conditions(widgets, {"a": "p", "b": "y"})["gated"] is False