chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
"""Tests for runner-local timer tool dispatch."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from omnigent.runner.tool_dispatch import execute_tool
|
||||
|
||||
|
||||
class _TimerPostRecorder:
|
||||
"""
|
||||
``httpx.MockTransport`` handler that records timer wake POSTs.
|
||||
|
||||
The ``posts`` attribute stores dictionaries with ``url``,
|
||||
``method``, ``json``, and ``headers`` keys, e.g.
|
||||
``{"url": "/v1/sessions/...", "method": "POST"}``.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize an empty call log."""
|
||||
self.posts: list[dict[str, Any]] = []
|
||||
self.post_seen = asyncio.Event()
|
||||
|
||||
async def __call__(
|
||||
self,
|
||||
request: httpx.Request,
|
||||
) -> httpx.Response:
|
||||
"""
|
||||
Record a timer wake request and return an accepted response.
|
||||
|
||||
:param request: HTTPX request, e.g. POST to
|
||||
``"/v1/sessions/conv_x/events"``.
|
||||
:returns: HTTP 202 response matching the session event endpoint.
|
||||
"""
|
||||
self.posts.append(
|
||||
{
|
||||
"url": request.url.path,
|
||||
"method": request.method,
|
||||
"json": json.loads(request.content),
|
||||
"headers": dict(request.headers),
|
||||
}
|
||||
)
|
||||
self.post_seen.set()
|
||||
return httpx.Response(202, json={"queued": True})
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_timer_firing_posts_hidden_meta_message() -> None:
|
||||
"""
|
||||
Timer firings wake the agent but stay hidden from user-facing UI.
|
||||
|
||||
The timer POST must remain a ``role="user"`` message so the
|
||||
sessions event path starts or steers the next turn. Marking it
|
||||
``is_meta=True`` is what makes existing web/TUI transcript
|
||||
rendering skip the synthetic ``[System: timer ... fired]`` row.
|
||||
"""
|
||||
recorder = _TimerPostRecorder()
|
||||
transport = httpx.MockTransport(recorder)
|
||||
|
||||
async with httpx.AsyncClient(transport=transport, base_url="http://server") as server_client:
|
||||
output = await execute_tool(
|
||||
tool_name="sys_timer_set",
|
||||
arguments=json.dumps({"seconds": 0, "note": "check build"}),
|
||||
conversation_id="conv_parent",
|
||||
server_client=server_client,
|
||||
)
|
||||
|
||||
result = json.loads(output)
|
||||
assert result["status"] == "scheduled"
|
||||
assert isinstance(result["timer_id"], str)
|
||||
|
||||
await asyncio.wait_for(recorder.post_seen.wait(), timeout=1.0)
|
||||
|
||||
# A non-repeating timer should produce exactly one wake POST:
|
||||
# zero means the firing never reached AP, more than one means it
|
||||
# accidentally behaved like a repeating timer.
|
||||
assert len(recorder.posts) == 1
|
||||
post = recorder.posts[0]
|
||||
assert post["method"] == "POST"
|
||||
assert post["url"] == "/v1/sessions/conv_parent/events"
|
||||
payload = post["json"]
|
||||
assert payload == {
|
||||
"type": "message",
|
||||
"data": {
|
||||
"role": "user",
|
||||
"is_meta": True,
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": f"[System: timer {result['timer_id']} fired]\nnote: 'check build'",
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_timer_set_rejects_invalid_args_via_shared_validator() -> None:
|
||||
"""
|
||||
The runner dispatch path validates through the shared
|
||||
``validate_timer_set_args`` helper, so a bad ``seconds`` returns the
|
||||
same message the in-process builtin surfaces and starts no timer
|
||||
task (no wake POST is ever made).
|
||||
"""
|
||||
recorder = _TimerPostRecorder()
|
||||
transport = httpx.MockTransport(recorder)
|
||||
|
||||
async with httpx.AsyncClient(transport=transport, base_url="http://server") as server_client:
|
||||
output = await execute_tool(
|
||||
tool_name="sys_timer_set",
|
||||
arguments=json.dumps({"seconds": -1}),
|
||||
conversation_id="conv_parent",
|
||||
server_client=server_client,
|
||||
)
|
||||
|
||||
assert json.loads(output) == {"error": "seconds must be non-negative"}
|
||||
assert recorder.posts == []
|
||||
Reference in New Issue
Block a user