mirror of
https://github.com/Skyvern-AI/skyvern.git
synced 2026-04-29 04:00:13 +00:00
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Shuchang Zheng <wintonzheng0325@gmail.com>
127 lines
5.2 KiB
Python
127 lines
5.2 KiB
Python
from __future__ import annotations
|
|
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock, Mock
|
|
|
|
import pytest
|
|
|
|
from tests.unit_tests._stub_streaming import import_with_stubs
|
|
|
|
screencast = import_with_stubs(
|
|
"skyvern.forge.sdk.routes.streaming.screencast",
|
|
extra_stubs=["skyvern.forge.sdk.routes.streaming.screenshot"],
|
|
)
|
|
|
|
|
|
def _make_app(browser_manager=None, persistent_sessions_manager=None):
|
|
"""Build a fake app namespace to replace screencast.app (an AppHolder proxy)."""
|
|
return SimpleNamespace(
|
|
BROWSER_MANAGER=browser_manager or SimpleNamespace(),
|
|
PERSISTENT_SESSIONS_MANAGER=persistent_sessions_manager or SimpleNamespace(),
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_resolve_browser_state_for_workflow_run(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
expected_state = object()
|
|
fake_app = _make_app(
|
|
browser_manager=SimpleNamespace(get_for_workflow_run=Mock(return_value=expected_state), get_for_task=Mock()),
|
|
persistent_sessions_manager=SimpleNamespace(get_browser_state=AsyncMock()),
|
|
)
|
|
monkeypatch.setattr(screencast, "app", fake_app)
|
|
|
|
result = await screencast._resolve_browser_state("wr_123", "workflow_run")
|
|
|
|
assert result is expected_state
|
|
fake_app.BROWSER_MANAGER.get_for_workflow_run.assert_called_once_with("wr_123")
|
|
fake_app.BROWSER_MANAGER.get_for_task.assert_not_called()
|
|
fake_app.PERSISTENT_SESSIONS_MANAGER.get_browser_state.assert_not_awaited()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_resolve_browser_state_for_task(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
expected_state = object()
|
|
fake_app = _make_app(
|
|
browser_manager=SimpleNamespace(get_for_workflow_run=Mock(), get_for_task=Mock(return_value=expected_state)),
|
|
persistent_sessions_manager=SimpleNamespace(get_browser_state=AsyncMock()),
|
|
)
|
|
monkeypatch.setattr(screencast, "app", fake_app)
|
|
|
|
result = await screencast._resolve_browser_state("task_123", "task", workflow_run_id="wr_123")
|
|
|
|
assert result is expected_state
|
|
fake_app.BROWSER_MANAGER.get_for_task.assert_called_once_with("task_123", "wr_123")
|
|
fake_app.BROWSER_MANAGER.get_for_workflow_run.assert_not_called()
|
|
fake_app.PERSISTENT_SESSIONS_MANAGER.get_browser_state.assert_not_awaited()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_resolve_browser_state_for_browser_session(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
expected_state = object()
|
|
fake_app = _make_app(
|
|
browser_manager=SimpleNamespace(get_for_workflow_run=Mock(), get_for_task=Mock()),
|
|
persistent_sessions_manager=SimpleNamespace(get_browser_state=AsyncMock(return_value=expected_state)),
|
|
)
|
|
monkeypatch.setattr(screencast, "app", fake_app)
|
|
|
|
result = await screencast._resolve_browser_state("bs_123", "browser_session")
|
|
|
|
assert result is expected_state
|
|
fake_app.PERSISTENT_SESSIONS_MANAGER.get_browser_state.assert_awaited_once_with("bs_123")
|
|
fake_app.BROWSER_MANAGER.get_for_workflow_run.assert_not_called()
|
|
fake_app.BROWSER_MANAGER.get_for_task.assert_not_called()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_resolve_browser_state_unknown_entity_type(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
fake_app = _make_app(
|
|
browser_manager=SimpleNamespace(get_for_workflow_run=Mock(), get_for_task=Mock()),
|
|
persistent_sessions_manager=SimpleNamespace(get_browser_state=AsyncMock()),
|
|
)
|
|
monkeypatch.setattr(screencast, "app", fake_app)
|
|
|
|
result = await screencast._resolve_browser_state("id_123", "unknown")
|
|
|
|
assert result is None
|
|
fake_app.BROWSER_MANAGER.get_for_workflow_run.assert_not_called()
|
|
fake_app.BROWSER_MANAGER.get_for_task.assert_not_called()
|
|
fake_app.PERSISTENT_SESSIONS_MANAGER.get_browser_state.assert_not_awaited()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_wait_for_browser_state_returns_when_working_page_is_available(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
browser_state = SimpleNamespace(get_working_page=AsyncMock(return_value=object()))
|
|
resolve_mock = AsyncMock(return_value=browser_state)
|
|
sleep_mock = AsyncMock()
|
|
monkeypatch.setattr(screencast, "_resolve_browser_state", resolve_mock)
|
|
monkeypatch.setattr(screencast.asyncio, "sleep", sleep_mock)
|
|
|
|
result = await screencast.wait_for_browser_state("wr_123", "workflow_run", timeout=1, poll_interval=0.1)
|
|
|
|
assert result is browser_state
|
|
resolve_mock.assert_awaited_once_with("wr_123", "workflow_run", None)
|
|
browser_state.get_working_page.assert_awaited_once()
|
|
sleep_mock.assert_not_awaited()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_wait_for_browser_state_returns_none_on_timeout(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
browser_state = SimpleNamespace(get_working_page=AsyncMock(return_value=None))
|
|
resolve_mock = AsyncMock(return_value=browser_state)
|
|
sleep_mock = AsyncMock()
|
|
monkeypatch.setattr(screencast, "_resolve_browser_state", resolve_mock)
|
|
monkeypatch.setattr(screencast.asyncio, "sleep", sleep_mock)
|
|
|
|
result = await screencast.wait_for_browser_state(
|
|
"bs_123",
|
|
"browser_session",
|
|
timeout=0.3,
|
|
poll_interval=0.1,
|
|
)
|
|
|
|
assert result is None
|
|
assert resolve_mock.await_count == 3
|
|
assert browser_state.get_working_page.await_count == 3
|
|
assert sleep_mock.await_count == 3
|