Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 35 additions & 16 deletions backend/tests/test_tool_output_budget_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@

from __future__ import annotations

import contextlib
import json
import os
import pathlib
import tempfile
from types import SimpleNamespace

Expand Down Expand Up @@ -66,6 +68,27 @@ def _make_request(tool_name: str = "remote_executor", tool_call_id: str = "tc-1"
)


@contextlib.contextmanager
def _unwritable_outputs_path():
"""Yield an ``outputs_path`` that ``os.makedirs`` cannot create, on any platform.

The parent component is a regular file, so creating a directory below it
fails with an ``OSError`` subclass everywhere (``NotADirectoryError`` on
POSIX, ``FileNotFoundError`` on Windows) and nothing is written outside the
temporary directory.

This deliberately avoids expressing "unwritable" as a magic absolute path.
``/nonexistent/...`` was creatable by root in the CI container, and its
replacement ``/dev/null/...`` relies on ``/dev/null`` being a character
device, which is only true on POSIX -- on Windows it is an ordinary
relative path that ``os.makedirs`` happily creates at the drive root.
"""
with tempfile.TemporaryDirectory() as tmpdir:
blocker = pathlib.Path(tmpdir) / "not-a-directory"
blocker.touch()
yield os.path.join(blocker, "outputs")


def _tm(content: str = "ok", name: str = "tool", tool_call_id: str = "tc-1") -> ToolMessage:
return ToolMessage(content=content, name=name, tool_call_id=tool_call_id)

Expand Down Expand Up @@ -160,20 +183,15 @@ def test_writes_file_and_returns_virtual_path(self):
assert f.read() == "full content here"

def test_returns_none_on_invalid_path(self):
# ``/dev/null`` is a character device on both Linux and macOS, so
# ``os.makedirs`` cannot create any subdirectory under it for any
# user (including root). The previously-used ``/nonexistent/...``
# path was silently created by ``mkdir -p`` when the test process
# ran as root inside the CI container, which made this test fail
# in CI independently of the externalization logic under test.
path = _externalize(
"data",
tool_name="test",
tool_call_id="tc-1",
outputs_path="/dev/null/cannot-mkdir-here",
storage_subdir=".tool-results",
)
assert path is None
with _unwritable_outputs_path() as outputs_path:
path = _externalize(
"data",
tool_name="test",
tool_call_id="tc-1",
outputs_path=outputs_path,
storage_subdir=".tool-results",
)
assert path is None

def test_txt_extension_for_unknown_tool(self):
with tempfile.TemporaryDirectory() as tmpdir:
Expand Down Expand Up @@ -710,9 +728,10 @@ def test_fallback_when_disk_write_fails(self):
mw = ToolOutputBudgetMiddleware(config=config)
content = "x" * 500
msg = _tm(content, name="tool")
req = _make_request(outputs_path="/dev/null/cannot-mkdir-here")

result = mw.wrap_tool_call(req, lambda _: msg)
with _unwritable_outputs_path() as outputs_path:
req = _make_request(outputs_path=outputs_path)
result = mw.wrap_tool_call(req, lambda _: msg)

assert isinstance(result, ToolMessage)
assert "omitted from tool output" in result.content
Expand Down
Loading