strix/tests/integration/helpers.py
2026-04-01 13:49:30 -07:00

88 lines
2.3 KiB
Python

import inspect
from pytest_check import check
from . import console as ui
class Browser:
def __init__(self, agent_id, agent_state=None):
self._agent_id = agent_id
self._agent_state = agent_state
def _call(self, action, **kwargs):
import asyncio
from strix.tools.browser.browser_actions import browser_action
from strix.tools.context import set_current_agent_id
from .conftest import _bg_loop
async def _run():
set_current_agent_id(self._agent_id)
return await browser_action(
agent_state=self._agent_state,
action=action,
**kwargs,
)
future = asyncio.run_coroutine_threadsafe(_run(), _bg_loop)
result = future.result(timeout=120)
if "screenshot" in result:
result["screenshot"] = "[Image]"
return result
def __getattr__(self, action):
def call(**kwargs):
result = self._call(action, **kwargs)
if "error" in result:
Fail(result).error(result["error"])
return result
return call
def act_parallel(tasks):
from concurrent.futures import ThreadPoolExecutor
def _run_one(browser, kwargs):
action = kwargs.pop("action")
return getattr(browser, action)(**kwargs)
with ThreadPoolExecutor(max_workers=len(tasks)) as pool:
futures = [pool.submit(_run_one, b, dict(kw)) for b, kw in tasks]
return [f.result(timeout=120) for f in futures]
def _caller_test_name():
for frame in inspect.stack():
if frame.function.startswith("test_"):
return frame.function
return "unknown"
class Fail:
def __init__(self, result=None):
self._result = result
self._name = _caller_test_name()
def expected(self, value):
self._expected = value
return self
def got(self, value):
self._emit(f"expected {self._expected!r}, got {value!r}")
def error(self, msg):
self._emit(msg)
def _emit(self, reason):
ui.log_error(f" \\[{self._name}] {reason}")
ui.record_failure(
self._name,
self._name,
reason,
self._result,
)
with check:
check.fail(f"[{self._name}] {reason}")