mirror of
https://github.com/Skyvern-AI/skyvern.git
synced 2026-04-28 11:40:32 +00:00
94 lines
3.6 KiB
Python
94 lines
3.6 KiB
Python
"""Tests for phantom script_block entry prevention (SKY-8443).
|
|
|
|
When _generate_pending_script_for_block fires after an early block completion,
|
|
generate_workflow_script_python_code must NOT create script_block DB entries
|
|
for blocks that haven't executed yet (no actions, no task_id). Creating such
|
|
entries causes a permanent stuck state where subsequent runs think all blocks
|
|
are cached but the Python file only has code for the first block.
|
|
"""
|
|
|
|
|
|
def test_unexecuted_block_detected_by_guard():
|
|
"""A block with no actions and no task_id should be identified as unexecuted."""
|
|
# Simulates a block in workflow_blocks that has a workflow_run_block entry
|
|
# (pre-created in queued state) but hasn't actually run yet
|
|
unexecuted_block = {
|
|
"block_type": "navigation",
|
|
"label": "block_1",
|
|
"task_id": "", # Empty — no task created yet
|
|
"workflow_run_block_id": "wrb_123",
|
|
}
|
|
actions_by_task: dict = {}
|
|
|
|
task_id = unexecuted_block.get("task_id", "")
|
|
block_actions = actions_by_task.get(task_id, [])
|
|
|
|
# This is the guard condition from generate_script.py
|
|
should_skip = not block_actions and not task_id
|
|
assert should_skip, "Block with no actions and no task_id should be skipped"
|
|
|
|
|
|
def test_executed_block_with_actions_passes_guard():
|
|
"""A block with a task_id and actions should NOT be skipped."""
|
|
executed_block = {
|
|
"block_type": "navigation",
|
|
"label": "block_1",
|
|
"task_id": "tsk_456",
|
|
"workflow_run_block_id": "wrb_123",
|
|
}
|
|
actions_by_task = {
|
|
"tsk_456": [{"action_type": "click", "element_id": "AAAB"}],
|
|
}
|
|
|
|
task_id = executed_block.get("task_id", "")
|
|
block_actions = actions_by_task.get(task_id, [])
|
|
|
|
should_skip = not block_actions and not task_id
|
|
assert not should_skip, "Block with task_id and actions should NOT be skipped"
|
|
|
|
|
|
def test_executed_block_with_task_id_but_no_actions_passes_guard():
|
|
"""A block with a task_id but zero actions should NOT be skipped.
|
|
This is a valid scenario — the block executed but completed immediately."""
|
|
executed_empty_block = {
|
|
"block_type": "navigation",
|
|
"label": "block_1",
|
|
"task_id": "tsk_789",
|
|
"workflow_run_block_id": "wrb_123",
|
|
}
|
|
actions_by_task: dict = {}
|
|
|
|
task_id = executed_empty_block.get("task_id", "")
|
|
block_actions = actions_by_task.get(task_id, [])
|
|
|
|
should_skip = not block_actions and not task_id
|
|
assert not should_skip, "Block with task_id (even without actions) should NOT be skipped"
|
|
|
|
|
|
def test_progressive_caching_scenario():
|
|
"""Simulate the exact scenario from SKY-8443: login completes first,
|
|
block_1 and block_2 haven't executed yet. Only login should produce
|
|
a script_block entry."""
|
|
blocks = [
|
|
{"block_type": "login", "label": "login", "task_id": "tsk_001"},
|
|
{"block_type": "navigation", "label": "block_1", "task_id": ""}, # Not executed
|
|
{"block_type": "file_download", "label": "block_2", "task_id": ""}, # Not executed
|
|
]
|
|
actions_by_task = {
|
|
"tsk_001": [
|
|
{"action_type": "input_text", "element_id": "AAA1"},
|
|
{"action_type": "click", "element_id": "AAA2"},
|
|
],
|
|
}
|
|
|
|
blocks_that_should_get_entries = []
|
|
for block in blocks:
|
|
task_id = block.get("task_id", "")
|
|
block_actions = actions_by_task.get(task_id, [])
|
|
if not block_actions and not task_id:
|
|
continue # Skip — unexecuted
|
|
blocks_that_should_get_entries.append(block["label"])
|
|
|
|
assert blocks_that_should_get_entries == ["login"], (
|
|
"Only login should get a script_block entry; block_1 and block_2 haven't executed"
|
|
)
|