Compare commits
1 Commits
main
...
fix/qa-307
| Author | SHA1 | Date | |
|---|---|---|---|
| f08c9de7a0 |
@ -15,7 +15,6 @@ The wrappers are ~40 LOC of glue. The full delivery behavior
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import json
|
import json
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
@ -29,24 +28,22 @@ def _require_workspace_id(monkeypatch):
|
|||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
def _run(coro):
|
|
||||||
return asyncio.get_event_loop().run_until_complete(coro)
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# tool_inbox_peek
|
# tool_inbox_peek
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class TestToolInboxPeek:
|
class TestToolInboxPeek:
|
||||||
def test_returns_not_enabled_when_state_none(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_returns_not_enabled_when_state_none(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
with patch("inbox.get_state", return_value=None):
|
with patch("inbox.get_state", return_value=None):
|
||||||
out = _run(a2a_tools.tool_inbox_peek())
|
out = await a2a_tools.tool_inbox_peek()
|
||||||
assert "not enabled" in out
|
assert "not enabled" in out
|
||||||
|
|
||||||
def test_returns_json_array_of_messages(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_returns_json_array_of_messages(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
msg1 = MagicMock()
|
msg1 = MagicMock()
|
||||||
@ -58,20 +55,21 @@ class TestToolInboxPeek:
|
|||||||
fake_state.peek.return_value = [msg1, msg2]
|
fake_state.peek.return_value = [msg1, msg2]
|
||||||
|
|
||||||
with patch("inbox.get_state", return_value=fake_state):
|
with patch("inbox.get_state", return_value=fake_state):
|
||||||
out = _run(a2a_tools.tool_inbox_peek(limit=5))
|
out = await a2a_tools.tool_inbox_peek(limit=5)
|
||||||
# peek limit is forwarded
|
# peek limit is forwarded
|
||||||
fake_state.peek.assert_called_once_with(limit=5)
|
fake_state.peek.assert_called_once_with(limit=5)
|
||||||
parsed = json.loads(out)
|
parsed = json.loads(out)
|
||||||
assert len(parsed) == 2
|
assert len(parsed) == 2
|
||||||
assert parsed[0]["activity_id"] == "a1"
|
assert parsed[0]["activity_id"] == "a1"
|
||||||
|
|
||||||
def test_non_int_limit_falls_back_to_10(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_non_int_limit_falls_back_to_10(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
fake_state = MagicMock()
|
fake_state = MagicMock()
|
||||||
fake_state.peek.return_value = []
|
fake_state.peek.return_value = []
|
||||||
with patch("inbox.get_state", return_value=fake_state):
|
with patch("inbox.get_state", return_value=fake_state):
|
||||||
_run(a2a_tools.tool_inbox_peek(limit="garbage")) # type: ignore[arg-type]
|
await a2a_tools.tool_inbox_peek(limit="garbage") # type: ignore[arg-type]
|
||||||
fake_state.peek.assert_called_once_with(limit=10)
|
fake_state.peek.assert_called_once_with(limit=10)
|
||||||
|
|
||||||
|
|
||||||
@ -81,49 +79,54 @@ class TestToolInboxPeek:
|
|||||||
|
|
||||||
|
|
||||||
class TestToolInboxPop:
|
class TestToolInboxPop:
|
||||||
def test_returns_not_enabled_when_state_none(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_returns_not_enabled_when_state_none(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
with patch("inbox.get_state", return_value=None):
|
with patch("inbox.get_state", return_value=None):
|
||||||
out = _run(a2a_tools.tool_inbox_pop("act-1"))
|
out = await a2a_tools.tool_inbox_pop("act-1")
|
||||||
assert "not enabled" in out
|
assert "not enabled" in out
|
||||||
|
|
||||||
def test_rejects_empty_activity_id(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_rejects_empty_activity_id(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
fake_state = MagicMock()
|
fake_state = MagicMock()
|
||||||
with patch("inbox.get_state", return_value=fake_state):
|
with patch("inbox.get_state", return_value=fake_state):
|
||||||
out = _run(a2a_tools.tool_inbox_pop(""))
|
out = await a2a_tools.tool_inbox_pop("")
|
||||||
assert "activity_id is required" in out
|
assert "activity_id is required" in out
|
||||||
fake_state.pop.assert_not_called()
|
fake_state.pop.assert_not_called()
|
||||||
|
|
||||||
def test_rejects_non_str_activity_id(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_rejects_non_str_activity_id(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
fake_state = MagicMock()
|
fake_state = MagicMock()
|
||||||
with patch("inbox.get_state", return_value=fake_state):
|
with patch("inbox.get_state", return_value=fake_state):
|
||||||
out = _run(a2a_tools.tool_inbox_pop(123)) # type: ignore[arg-type]
|
out = await a2a_tools.tool_inbox_pop(123) # type: ignore[arg-type]
|
||||||
assert "activity_id is required" in out
|
assert "activity_id is required" in out
|
||||||
fake_state.pop.assert_not_called()
|
fake_state.pop.assert_not_called()
|
||||||
|
|
||||||
def test_returns_removed_true_when_popped(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_returns_removed_true_when_popped(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
fake_state = MagicMock()
|
fake_state = MagicMock()
|
||||||
fake_state.pop.return_value = MagicMock() # truthy = something was removed
|
fake_state.pop.return_value = MagicMock() # truthy = something was removed
|
||||||
with patch("inbox.get_state", return_value=fake_state):
|
with patch("inbox.get_state", return_value=fake_state):
|
||||||
out = _run(a2a_tools.tool_inbox_pop("act-7"))
|
out = await a2a_tools.tool_inbox_pop("act-7")
|
||||||
parsed = json.loads(out)
|
parsed = json.loads(out)
|
||||||
assert parsed == {"removed": True, "activity_id": "act-7"}
|
assert parsed == {"removed": True, "activity_id": "act-7"}
|
||||||
fake_state.pop.assert_called_once_with("act-7")
|
fake_state.pop.assert_called_once_with("act-7")
|
||||||
|
|
||||||
def test_returns_removed_false_when_unknown(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_returns_removed_false_when_unknown(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
fake_state = MagicMock()
|
fake_state = MagicMock()
|
||||||
fake_state.pop.return_value = None
|
fake_state.pop.return_value = None
|
||||||
with patch("inbox.get_state", return_value=fake_state):
|
with patch("inbox.get_state", return_value=fake_state):
|
||||||
out = _run(a2a_tools.tool_inbox_pop("act-missing"))
|
out = await a2a_tools.tool_inbox_pop("act-missing")
|
||||||
parsed = json.loads(out)
|
parsed = json.loads(out)
|
||||||
assert parsed == {"removed": False, "activity_id": "act-missing"}
|
assert parsed == {"removed": False, "activity_id": "act-missing"}
|
||||||
|
|
||||||
@ -134,25 +137,28 @@ class TestToolInboxPop:
|
|||||||
|
|
||||||
|
|
||||||
class TestToolWaitForMessage:
|
class TestToolWaitForMessage:
|
||||||
def test_returns_not_enabled_when_state_none(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_returns_not_enabled_when_state_none(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
with patch("inbox.get_state", return_value=None):
|
with patch("inbox.get_state", return_value=None):
|
||||||
out = _run(a2a_tools.tool_wait_for_message(timeout_secs=1.0))
|
out = await a2a_tools.tool_wait_for_message(timeout_secs=1.0)
|
||||||
assert "not enabled" in out
|
assert "not enabled" in out
|
||||||
|
|
||||||
def test_timeout_payload_when_no_message(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_timeout_payload_when_no_message(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
fake_state = MagicMock()
|
fake_state = MagicMock()
|
||||||
fake_state.wait.return_value = None
|
fake_state.wait.return_value = None
|
||||||
with patch("inbox.get_state", return_value=fake_state):
|
with patch("inbox.get_state", return_value=fake_state):
|
||||||
out = _run(a2a_tools.tool_wait_for_message(timeout_secs=0.1))
|
out = await a2a_tools.tool_wait_for_message(timeout_secs=0.1)
|
||||||
parsed = json.loads(out)
|
parsed = json.loads(out)
|
||||||
assert parsed["timeout"] is True
|
assert parsed["timeout"] is True
|
||||||
assert parsed["timeout_secs"] == 0.1
|
assert parsed["timeout_secs"] == 0.1
|
||||||
|
|
||||||
def test_returns_message_when_delivered(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_returns_message_when_delivered(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
msg = MagicMock()
|
msg = MagicMock()
|
||||||
@ -160,37 +166,40 @@ class TestToolWaitForMessage:
|
|||||||
fake_state = MagicMock()
|
fake_state = MagicMock()
|
||||||
fake_state.wait.return_value = msg
|
fake_state.wait.return_value = msg
|
||||||
with patch("inbox.get_state", return_value=fake_state):
|
with patch("inbox.get_state", return_value=fake_state):
|
||||||
out = _run(a2a_tools.tool_wait_for_message(timeout_secs=2.0))
|
out = await a2a_tools.tool_wait_for_message(timeout_secs=2.0)
|
||||||
parsed = json.loads(out)
|
parsed = json.loads(out)
|
||||||
assert parsed["activity_id"] == "a-9"
|
assert parsed["activity_id"] == "a-9"
|
||||||
|
|
||||||
def test_timeout_clamped_to_300(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_timeout_clamped_to_300(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
fake_state = MagicMock()
|
fake_state = MagicMock()
|
||||||
fake_state.wait.return_value = None
|
fake_state.wait.return_value = None
|
||||||
with patch("inbox.get_state", return_value=fake_state):
|
with patch("inbox.get_state", return_value=fake_state):
|
||||||
_run(a2a_tools.tool_wait_for_message(timeout_secs=99999))
|
await a2a_tools.tool_wait_for_message(timeout_secs=99999)
|
||||||
# Whatever wait was called with, it must not exceed 300
|
# Whatever wait was called with, it must not exceed 300
|
||||||
passed = fake_state.wait.call_args.args[0]
|
passed = fake_state.wait.call_args.args[0]
|
||||||
assert passed == 300.0
|
assert passed == 300.0
|
||||||
|
|
||||||
def test_timeout_clamped_to_zero_floor(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_timeout_clamped_to_zero_floor(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
fake_state = MagicMock()
|
fake_state = MagicMock()
|
||||||
fake_state.wait.return_value = None
|
fake_state.wait.return_value = None
|
||||||
with patch("inbox.get_state", return_value=fake_state):
|
with patch("inbox.get_state", return_value=fake_state):
|
||||||
_run(a2a_tools.tool_wait_for_message(timeout_secs=-5))
|
await a2a_tools.tool_wait_for_message(timeout_secs=-5)
|
||||||
passed = fake_state.wait.call_args.args[0]
|
passed = fake_state.wait.call_args.args[0]
|
||||||
assert passed == 0.0
|
assert passed == 0.0
|
||||||
|
|
||||||
def test_non_numeric_timeout_falls_back_to_60(self):
|
@pytest.mark.asyncio
|
||||||
|
async def test_non_numeric_timeout_falls_back_to_60(self):
|
||||||
import a2a_tools
|
import a2a_tools
|
||||||
|
|
||||||
fake_state = MagicMock()
|
fake_state = MagicMock()
|
||||||
fake_state.wait.return_value = None
|
fake_state.wait.return_value = None
|
||||||
with patch("inbox.get_state", return_value=fake_state):
|
with patch("inbox.get_state", return_value=fake_state):
|
||||||
_run(a2a_tools.tool_wait_for_message(timeout_secs="garbage")) # type: ignore[arg-type]
|
await a2a_tools.tool_wait_for_message(timeout_secs="garbage") # type: ignore[arg-type]
|
||||||
passed = fake_state.wait.call_args.args[0]
|
passed = fake_state.wait.call_args.args[0]
|
||||||
assert passed == 60.0
|
assert passed == 60.0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user