import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) import app def _assert(cond: bool, msg: str) -> None: if not cond: raise AssertionError(msg) def test_prompt_uses_visible_rows_only() -> None: # If selected state contains stale hidden tags, prompt should still reflect visible-row selections only. row_defs = [ {"name": "r1", "label": "R1", "tags": ["solo", "female"], "tag_meta": {}}, {"name": "r2", "label": "R2", "tags": ["cub"], "tag_meta": {}}, ] payload = app._build_ui_payload( console_text="x", row_defs=row_defs, selected_tags=["solo", "rosalina_(mario)"], ) prompt_text = payload[2] selected_state = payload[3] _assert("rosalina \\(mario\\)" not in prompt_text, "stale hidden tag leaked into prompt") _assert("solo" in prompt_text, "visible selected tag missing from prompt") _assert("rosalina_(mario)" not in selected_state, "stale hidden tag leaked into selected state") def test_row_deduping() -> None: row_defs = [ { "name": "other_retrieved", "label": "Other (Retrieved)", "tags": ["cub", "expressions", "invalid_tag", "cub", "expressions"], "tag_meta": {}, } ] prompt_text, row_values_state, _, checkbox_updates = app._build_row_component_updates( row_defs=row_defs, selected_tags=["cub", "expressions"], max_rows=app.display_max_rows_default, ) _assert(prompt_text == "cub, expressions", "prompt should be deduped and ordered from row") _assert(row_values_state[0] == ["cub", "expressions"], "row selected values should be deduped") first_choices = checkbox_updates[0]["choices"] first_values = [v for _, v in first_choices] _assert(first_values == ["cub", "expressions", "invalid_tag"], "row choices should be deduped") def test_rebuild_ignores_stale_selected_state() -> None: row_defs = [ {"name": "selected_other", "label": "Selected (Other)", "tags": ["solo", "female", "anthro"], "tag_meta": {}}, {"name": "other_retrieved", "label": "Other (Retrieved)", "tags": ["cub", "expressions"], "tag_meta": {}}, ] # Simulate UI state where user has deselected anthro, but stale selected state still contains it. selected_state = ["solo", "female", "anthro", "cub"] row_values_state = [["solo", "female"], ["cub"]] out = app._rebuild_rows_from_selected( selected_state, row_defs, row_values_state, app.display_top_groups_default, app.display_top_tags_per_group_default, app.display_rank_top_k_default, ) prompt = out[1] selected_after = out[2] _assert("anthro" not in selected_after, "rebuild should not resurrect stale deselected tags") _assert("anthro" not in prompt, "prompt should not include stale deselected tags") _assert("solo" in prompt and "female" in prompt and "cub" in prompt, "rebuild should retain current row selections") def test_toggle_then_rebuild_does_not_resurrect_removed_tag() -> None: row_defs = [ {"name": "selected_other", "label": "Selected (Other)", "tags": ["solo", "anthro", "female"], "tag_meta": {}}, {"name": "other_retrieved", "label": "Other (Retrieved)", "tags": ["cub", "expressions"], "tag_meta": {}}, ] selected_state = ["solo", "anthro", "female", "cub"] row_values_state = [["solo", "anthro", "female"], ["cub"]] # User unchecks anthro in row 0. toggle_out = app._on_toggle_row( 0, ["solo", "female"], selected_state, False, row_defs, row_values_state, app.display_max_rows_default, ) selected_after_toggle = toggle_out[0] row_values_after_toggle = toggle_out[3] _assert("anthro" not in selected_after_toggle, "toggle should remove anthro from selected state") # Rebuild from current row values must preserve the user-toggle result. rebuild_out = app._rebuild_rows_from_selected( selected_after_toggle, row_defs, row_values_after_toggle, app.display_top_groups_default, app.display_top_tags_per_group_default, app.display_rank_top_k_default, ) prompt_after_rebuild = rebuild_out[1] selected_after_rebuild = rebuild_out[2] _assert("anthro" not in selected_after_rebuild, "rebuild should not resurrect deselected anthro") _assert("anthro" not in prompt_after_rebuild, "prompt should not contain deselected anthro after rebuild") _assert("solo" in prompt_after_rebuild and "female" in prompt_after_rebuild, "kept selections should remain") _assert("cub" in prompt_after_rebuild, "other retrieved selection should remain") def test_toggle_does_not_cross_activate_unrelated_row_tag() -> None: row_defs = [ {"name": "organization", "label": "Organization", "tags": ["pinup", "close-up"], "tag_meta": {}}, {"name": "color_markings", "label": "Color Markings", "tags": ["shoulder_markings", "black_markings"], "tag_meta": {}}, ] selected_state = [] row_values_state = [[], []] # User enables close-up in organization row. out = app._on_toggle_row( 0, ["close-up"], selected_state, False, row_defs, row_values_state, app.display_max_rows_default, ) selected_after = out[0] row_values_after = out[3] _assert("close-up" in selected_after, "close-up should be selected") _assert("shoulder_markings" not in selected_after, "unrelated row tag should not be auto-selected") _assert(row_values_after[0] == ["close-up"], "organization row values should include close-up only") _assert(row_values_after[1] == [], "color markings row should remain unselected") def test_shared_tag_mirrors_without_unrelated_cross_toggle() -> None: row_defs = [ {"name": "objects_props", "label": "Objects Props", "tags": ["holding_face", "holding_clothing"], "tag_meta": {}}, {"name": "expression_detail", "label": "Expression Detail", "tags": ["open_mouth", "closed_smile"], "tag_meta": {}}, {"name": "pose_action_detail", "label": "Pose Action Detail", "tags": ["holding_face", "walking"], "tag_meta": {}}, ] selected_state = [] row_values_state = [[], [], []] # Enable open_mouth; should not affect holding_face rows. out1 = app._on_toggle_row( 1, ["open_mouth"], selected_state, False, row_defs, row_values_state, app.display_max_rows_default, ) sel1 = out1[0] vals1 = out1[3] _assert("open_mouth" in sel1, "open_mouth should be selected") _assert("holding_face" not in sel1, "holding_face must remain unselected") _assert(vals1[0] == [], "objects props row should remain unselected") _assert(vals1[1] == ["open_mouth"], "expression row should select open_mouth") _assert(vals1[2] == [], "pose row should remain unselected") # Enable holding_face in objects row; should mirror only to pose row, not expression row. out2 = app._on_toggle_row( 0, ["holding_face"], sel1, True, row_defs, vals1, app.display_max_rows_default, ) sel2 = out2[0] vals2 = out2[3] _assert("holding_face" in sel2 and "open_mouth" in sel2, "both explicitly selected tags should be present") _assert(vals2[0] == ["holding_face"], "objects row should select holding_face") _assert(vals2[1] == ["open_mouth"], "expression row should keep open_mouth only") _assert(vals2[2] == ["holding_face"], "pose row should mirror holding_face") def main() -> None: test_prompt_uses_visible_rows_only() test_row_deduping() test_rebuild_ignores_stale_selected_state() test_toggle_then_rebuild_does_not_resurrect_removed_tag() test_toggle_does_not_cross_activate_unrelated_row_tag() test_shared_tag_mirrors_without_unrelated_cross_toggle() print("ui state smoke: ok") if __name__ == "__main__": main()