from pathlib import Path from flow_session import FlowSession from recorder.base import RawUserEvent from diagnostics.capture_validator import validate_capture from diagnostics.replay_preflight import build_replay_preflight def test_session_always_creates_raw_event_journal(tmp_path: Path) -> None: session = FlowSession(tmp_path, flow_name="contract", surface="browser", dump_fn=None) session.manager.start() session.manager.status = "recording" event = RawUserEvent(kind="click", surface="browser", target={"role": "button", "name": "Go"}) session.journal.append(event, channel="semantic") assert (tmp_path / "journal" / "raw_events.jsonl").exists() def test_validator_flags_oversized_click_target(tmp_path: Path) -> None: import json actions = tmp_path / "actions" actions.mkdir() (tmp_path / "elements").mkdir() (tmp_path / "observations").mkdir() (tmp_path / "journal").mkdir() (tmp_path / "journal" / "raw_events.jsonl").write_text("{}\n", encoding="utf-8") (actions / "a.json").write_text( json.dumps({ "id": "a", "sequence": 1, "action_type": "click", "target_snapshot": {"rect": {"width": 1200, "height": 5000}}, "evidence": {"eventual_observation_id": "obs"}, }), encoding="utf-8", ) result = validate_capture(tmp_path) assert any(issue["code"] == "CLICK_TARGET_OVERSIZED" for issue in result["issues"]) def test_validator_flags_missing_desktop_input_and_generic_targets(tmp_path: Path) -> None: import json (tmp_path / "actions").mkdir() (tmp_path / "elements").mkdir() (tmp_path / "observations").mkdir() (tmp_path / "journal").mkdir() journal = { "event": { "kind": "typing_activity", "surface": "windows", "target": {"name": "searchedit", "control_type": "TreeControl"}, } } (tmp_path / "journal" / "raw_events.jsonl").write_text( json.dumps(journal) + "\n", encoding="utf-8" ) rows = [ { "id": "a1", "sequence": 1, "surface": "windows", "action_type": "click", "target_snapshot": { "name": "search_result", "control_type": "TreeControl", "bounds": {"width": 600, "height": 500}, }, "evidence": {"eventual_observation_id": "obs"}, }, { "id": "a2", "sequence": 2, "surface": "windows", "action_type": "press_key", "target_snapshot": { "name": "WeCom", "control_type": "WindowControl", "bounds": {"width": 1440, "height": 900}, }, "input": {"key": "Enter"}, "evidence": {"eventual_observation_id": "obs"}, }, ] for row in rows: (tmp_path / "actions" / f"{row['id']}.json").write_text( json.dumps(row), encoding="utf-8" ) codes = {issue["code"] for issue in validate_capture(tmp_path)["issues"]} assert "CLICK_TARGET_GENERIC_CONTAINER" in codes assert "KEY_TARGET_ROOT_WINDOW" in codes assert "TEXT_INPUT_TRANSACTION_MISSING" in codes replay = build_replay_preflight(tmp_path) assert replay["ready"] is False assert replay["quality_gate"]["passed"] is False assert replay["summary"]["quality_blockers"] >= 1 def test_validator_flags_missing_production_locator_and_raw_link(tmp_path: Path) -> None: import json actions = tmp_path / "actions" elements = tmp_path / "elements" actions.mkdir() elements.mkdir() (tmp_path / "observations").mkdir() (tmp_path / "journal").mkdir() (tmp_path / "journal" / "raw_events.jsonl").write_text("{}\n", encoding="utf-8") (elements / "el.json").write_text( json.dumps({"id": "el", "primary_locator": None}), encoding="utf-8" ) (actions / "a.json").write_text( json.dumps({ "id": "a", "sequence": 1, "surface": "browser", "action_type": "click", "target_element_id": "el", "target_snapshot": {"role": "link", "rect": {"width": 20, "height": 20}}, "raw_event": {}, "evidence": {"eventual_observation_id": "obs"}, }), encoding="utf-8", ) result = validate_capture(tmp_path) codes = {issue["code"] for issue in result["issues"]} assert "ACTION_PRODUCTION_LOCATOR_MISSING" in codes assert "ACTION_RAW_EVENT_LINK_MISSING" in codes def test_validator_flags_graphic_leaf_low_stability_and_split_fill(tmp_path: Path) -> None: import json actions = tmp_path / "actions" elements = tmp_path / "elements" actions.mkdir() elements.mkdir() (tmp_path / "observations").mkdir() (tmp_path / "journal").mkdir() (tmp_path / "journal" / "raw_events.jsonl").write_text("{}\n", encoding="utf-8") locator = { "strategy": "css", "expression": "css:.hash", "actual_count": 1, "unique": True, "production_eligible": True, "stability_score": 0.32, "validation": "live", } (elements / "el.json").write_text( json.dumps({"id": "el", "primary_locator": locator}), encoding="utf-8" ) rows = [ { "id": "a1", "sequence": 1, "surface": "browser", "action_type": "fill", "target_element_id": "el", "target_snapshot": {"tag": "input"}, "input": {"text": "a"}, "raw_event": {"meta": {"event_id": "doc:1", "source_ts_ms": 1000}}, "evidence": {"eventual_observation_id": "obs", "locator_unique": True}, }, { "id": "a2", "sequence": 2, "surface": "browser", "action_type": "fill", "target_element_id": "el", "target_snapshot": {"tag": "input"}, "input": {"text": "ai"}, "raw_event": {"meta": {"event_id": "doc:2", "source_ts_ms": 1100}}, "evidence": {"eventual_observation_id": "obs", "locator_unique": True}, }, { "id": "a3", "sequence": 3, "surface": "browser", "action_type": "click", "target_element_id": "el", "target_snapshot": {"tag": "path"}, "raw_event": {"meta": {"event_id": "doc:3"}}, "evidence": {"eventual_observation_id": "obs", "locator_unique": True}, }, ] for row in rows: (actions / f"{row['id']}.json").write_text(json.dumps(row), encoding="utf-8") codes = {issue["code"] for issue in validate_capture(tmp_path)["issues"]} assert "DUPLICATE_FILL_TRANSACTION" in codes assert "GRAPHIC_LEAF_TARGET" in codes assert "LOW_STABILITY_LOCATOR" in codes def test_replay_preflight_is_honest_about_event_time_validation(tmp_path: Path) -> None: import json (tmp_path / "actions").mkdir() (tmp_path / "elements").mkdir() (tmp_path / "actions" / "a.json").write_text(json.dumps({ "id": "a", "sequence": 1, "action_type": "click", "target_element_id": "el", "raw_event_ids": ["doc:1"], "raw_event": {"meta": {"page_id": "page", "document_id": "doc"}}, }), encoding="utf-8") (tmp_path / "elements" / "el.json").write_text(json.dumps({ "id": "el", "logical_name": "go", "primary_locator": { "strategy": "role", "expression": "role:button,Go", "actual_count": 1, "unique": True, "production_eligible": True, "stability_score": 0.95, "validation": "live", }, }), encoding="utf-8") report = build_replay_preflight(tmp_path) assert report["mode"] == "static_non_mutating" assert report["summary"]["pass"] == 1 assert report["actions"][0]["verification"] == "event_time_unique" def test_replay_preflight_rejects_unique_root_window_click(tmp_path: Path) -> None: import json (tmp_path / "actions").mkdir() (tmp_path / "elements").mkdir() (tmp_path / "actions" / "a.json").write_text(json.dumps({ "id": "a", "sequence": 1, "surface": "windows", "action_type": "click", "target_element_id": "root", "target_snapshot": { "control_type": "WindowControl", "name": "WeCom", "bounds": {"width": 1440, "height": 900}, }, }), encoding="utf-8") (tmp_path / "elements" / "root.json").write_text(json.dumps({ "id": "root", "logical_name": "WeCom", "primary_locator": { "strategy": "window_name_type", "expression": "window:WeCom", "actual_count": 1, "unique": True, "production_eligible": True, "stability_score": 0.9, "validation": "snapshot_only", }, }), encoding="utf-8") report = build_replay_preflight(tmp_path) assert report["summary"]["fail"] == 1 assert report["actions"][0]["reason"] == "oversized_or_root_window_click_target"