fix(replay): guard single in-flight dispatch
Add a private in-flight helper for replay dispatch, block machine retargeting while an action is still pending on the previous session, and warn on duplicate in-flight entries for the same replay triplet. Freeze the Notepad runtime dialog success path and add integration coverage for single in-flight dispatch, watchdog late-report documentation, and the known concurrent-poll race as an xfail.
This commit is contained in:
@@ -391,6 +391,8 @@ class TestSetupActionsSkipPixelChange:
|
||||
class TestRuntimeDialogHandling:
|
||||
def test_handle_confirm_save_dialog_clicks_oui_via_server(self):
|
||||
exe = _make_executor_skeleton()
|
||||
exe._active_window_rect_for_dialog = MagicMock(return_value=None)
|
||||
exe._try_click_runtime_dialog_button_uia = MagicMock(return_value=None)
|
||||
exe._capture_screenshot_b64 = MagicMock(return_value="abc")
|
||||
exe._server_resolve_target = MagicMock(
|
||||
return_value={
|
||||
@@ -403,6 +405,7 @@ class TestRuntimeDialogHandling:
|
||||
)
|
||||
exe._find_text_on_screen = MagicMock(return_value=None)
|
||||
exe._click = MagicMock()
|
||||
exe._wait_until_title_changes = MagicMock(return_value="Enregistrer sous")
|
||||
|
||||
spec = ActionExecutorV1._match_known_runtime_dialog(
|
||||
"Confirmer l'enregistrement"
|
||||
@@ -418,6 +421,22 @@ class TestRuntimeDialogHandling:
|
||||
exe._server_resolve_target.assert_called_once()
|
||||
exe._click.assert_called_once_with((480, 810), "left")
|
||||
|
||||
def test_confirm_save_geometry_fallback_targets_yes_button_in_active_window(self):
|
||||
exe = _make_executor_skeleton()
|
||||
spec = ActionExecutorV1._match_known_runtime_dialog(
|
||||
"Confirmer l'enregistrement"
|
||||
)
|
||||
rect = {
|
||||
"left": 1000,
|
||||
"top": 600,
|
||||
"width": 520,
|
||||
"height": 200,
|
||||
}
|
||||
|
||||
pos = exe._runtime_dialog_button_geometry_fallback(spec, "Oui", rect)
|
||||
|
||||
assert pos == (1338, 764)
|
||||
|
||||
def test_runtime_dialog_before_pause_returns_skip_result(self):
|
||||
exe = _make_executor_skeleton()
|
||||
exe._check_and_pause_on_system_dialog = MagicMock(return_value=False)
|
||||
@@ -491,6 +510,60 @@ class TestRuntimeDialogHandling:
|
||||
assert res["actual_position"] == {"x_pct": 0.5, "y_pct": 0.5}
|
||||
exe._handle_known_runtime_dialog.assert_called_once()
|
||||
|
||||
def test_live_notepad_confirm_save_dialog_is_frozen_offline(self):
|
||||
exe = _make_executor_skeleton()
|
||||
exe._click = MagicMock()
|
||||
exe._quick_screenshot_hash = MagicMock(return_value="hash_before")
|
||||
exe._wait_for_screen_change = MagicMock(return_value=True)
|
||||
exe._capture_human_correction = MagicMock(return_value=[])
|
||||
exe._handle_known_runtime_dialog = MagicMock(
|
||||
return_value={
|
||||
"handled": True,
|
||||
"button_text": "Oui",
|
||||
"x_pct": 0.63,
|
||||
"y_pct": 0.76,
|
||||
"resolution_score": 0.9,
|
||||
"post_title": "http192.168.1.408765dossier.htmlid=.txt - Bloc-notes",
|
||||
}
|
||||
)
|
||||
|
||||
action = {
|
||||
"action_id": "act_raw_a8dbaaac",
|
||||
"type": "click",
|
||||
"x_pct": 0.5,
|
||||
"y_pct": 0.5,
|
||||
"expected_window_title": (
|
||||
"http192.168.1.408765dossier.htmlid=.txt - Bloc-notes"
|
||||
),
|
||||
}
|
||||
|
||||
titles = iter(
|
||||
[
|
||||
{"title": "Confirmer l'enregistrement"},
|
||||
{"title": "http192.168.1.408765dossier.htmlid=.txt - Bloc-notes"},
|
||||
]
|
||||
)
|
||||
|
||||
with patch("agent_v0.agent_v1.core.executor.time.sleep", lambda *_a, **_k: None):
|
||||
with patch(
|
||||
"agent_v0.agent_v1.window_info_crossplatform.get_active_window_info",
|
||||
side_effect=lambda: next(titles),
|
||||
):
|
||||
res = exe.execute_replay_action(action)
|
||||
|
||||
assert res["success"] is True
|
||||
assert res["warning"] == "runtime_dialog_handled_post_verify"
|
||||
assert res["action_id"] == "act_raw_a8dbaaac"
|
||||
assert res.get("needs_human") is not True
|
||||
assert "correction" not in res
|
||||
assert res["runtime_dialog"] == {
|
||||
"dialog_id": "confirm_save_overwrite",
|
||||
"dialog_title": "Confirmer l'enregistrement",
|
||||
"button_text": "Oui",
|
||||
}
|
||||
exe._handle_known_runtime_dialog.assert_called_once()
|
||||
exe._capture_human_correction.assert_not_called()
|
||||
|
||||
def test_post_verify_can_retry_same_runtime_dialog_before_recovery(self):
|
||||
exe = _make_executor_skeleton()
|
||||
exe._click = MagicMock()
|
||||
|
||||
Reference in New Issue
Block a user