Files
odysseus/tests/test_web_fetch_batch.py
T

103 lines
3.5 KiB
Python

import asyncio
import json
from src.agent_tools.web_tools import WebFetchTool
from src.search import content as content_mod
from src.tool_schemas import FUNCTION_TOOL_SCHEMAS, function_call_to_tool_block
from src.tool_execution import _active_workspace
def test_web_fetch_batch_is_concurrent_bounded_and_source_separated(monkeypatch):
def fake_fetch(url, **_kwargs):
return {
"content": (f"evidence for {url} " * 1000),
"title": url.rsplit("/", 1)[-1],
"error": "",
}
monkeypatch.setattr(content_mod, "fetch_webpage_content", fake_fetch)
result = asyncio.run(WebFetchTool().execute(json.dumps({
"urls": [
"https://example.com/a",
"https://example.com/b",
],
}), {}))
assert result["exit_code"] == 0
assert result["successful"] == 2
assert result["requested"] == 2
assert "## URL 1: https://example.com/a" in result["output"]
assert "## URL 2: https://example.com/b" in result["output"]
assert "[...batch item truncated]" in result["output"]
def test_web_fetch_batch_rejects_empty_or_oversized_lists():
tool = WebFetchTool()
empty = asyncio.run(tool.execute('{"urls": []}', {}))
oversized = asyncio.run(tool.execute(json.dumps({
"urls": [f"https://example.com/{index}" for index in range(13)],
}), {}))
assert empty["exit_code"] == 1
assert "non-empty" in empty["error"]
assert oversized["exit_code"] == 1
assert "at most 12" in oversized["error"]
def test_web_fetch_batch_reads_local_workspace_text_files(tmp_path):
(tmp_path / "one.json").write_text('{"one": 1}')
(tmp_path / "two.json").write_text('{"two": 2}')
token = _active_workspace.set(str(tmp_path))
try:
result = asyncio.run(WebFetchTool().execute(json.dumps({
"urls": [
"/workspace/one.json",
"file:///workspace/two.json",
],
}), {}))
finally:
_active_workspace.reset(token)
assert result["exit_code"] == 0
assert result["successful"] == 2
assert '"one": 1' in result["output"]
assert '"two": 2' in result["output"]
assert "Read the local workspace path with read_file" in result["output"]
def test_web_fetch_does_not_decode_local_html_or_binary_media(tmp_path):
token = _active_workspace.set(str(tmp_path))
try:
html = asyncio.run(WebFetchTool().execute("/workspace/page.html", {}))
video = asyncio.run(WebFetchTool().execute("/workspace/video.mp4", {}))
finally:
_active_workspace.reset(token)
assert html["exit_code"] == 1
assert "private_browser" in html["error"]
assert video["exit_code"] == 1
assert "inspect_media" in video["error"]
def test_web_fetch_schema_and_native_parser_accept_urls_batch():
schema = next(
item["function"] for item in FUNCTION_TOOL_SCHEMAS
if item["function"]["name"] == "web_fetch"
)
assert schema["parameters"]["properties"]["urls"]["maxItems"] == 12
block = function_call_to_tool_block(
"web_fetch",
json.dumps({"urls": ["https://example.com/a", "https://example.com/b"]}),
)
assert block is not None
assert block.tool_type == "web_fetch"
def test_web_fetch_parser_normalizes_empty_label_batch_pairs_only():
block = function_call_to_tool_block(
"web_fetch",
json.dumps({"urls": [["https://example.com/a", ""]]}),
)
assert block is not None
assert json.loads(block.content)["urls"] == ["https://example.com/a"]