Ctrl K
ComunidadGuías…

test/events/test_tool_events.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Copyright (c) 2026, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
#
# SPDX-License-Identifier: Apache-2.0

import json
from unittest.mock import patch

from ag2.events import (
    ClientToolCallEvent,
    ToolCallEvent,
    ToolErrorEvent,
)


class TestClientToolCallEventFromCall:
    """from_call must link back to the original call via parent_id."""

    def test_parent_id_matches_original_id(self) -> None:
        original = ToolCallEvent(name="search", arguments='{"q": "test"}')
        client_call = ClientToolCallEvent.from_call(original)
        assert client_call.id == original.id

    def test_name_and_arguments_preserved(self) -> None:
        original = ToolCallEvent(name="calc", arguments='{"x": 1}')
        client_call = ClientToolCallEvent.from_call(original)
        assert client_call.name == original.name
        assert client_call.arguments == original.arguments


class TestToolErrorEventContent:
    """Traceback must be stored in result.parts[0] via from_call."""

    def test_content_contains_original_error(self) -> None:
        call = ToolCallEvent(name="test_tool")
        try:
            raise ValueError("test error message")
        except Exception as e:
            event = ToolErrorEvent.from_call(call, e)

        traceback_text = event.result.parts[0].content  # type: ignore[union-attr]
        assert "ValueError" in traceback_text
        assert "test error message" in traceback_text

    def test_content_does_not_return_none_type(self) -> None:
        call = ToolCallEvent(name="t")
        try:
            raise RuntimeError("something broke")
        except Exception as e:
            event = ToolErrorEvent.from_call(call, e)

        assert "NoneType" not in event.result.parts[0].content  # type: ignore[union-attr]


class TestSerializedArgumentsCache:
    """serialized_arguments must cache even when the result is an empty dict."""

    def test_empty_dict_is_cached(self) -> None:
        tc = ToolCallEvent(name="tool", arguments="{}")
        with patch.object(json, "loads", wraps=json.loads) as mock_loads:
            result = tc.serialized_arguments
            _ = tc.serialized_arguments
            assert mock_loads.call_count == 1
            assert result == {}

    def test_non_empty_dict_is_cached(self) -> None:
        tc = ToolCallEvent(name="tool", arguments='{"key": "value"}')
        with patch.object(json, "loads", wraps=json.loads) as mock_loads:
            result = tc.serialized_arguments
            _ = tc.serialized_arguments
            assert mock_loads.call_count == 1
            assert result == {"key": "value"}


class TestSerializedArgumentsEmptyInput:
    """serialized_arguments must handle empty string and None without crashing."""

    def test_empty_string_returns_empty_dict(self) -> None:
        tc = ToolCallEvent(name="tool", arguments="")
        assert tc.serialized_arguments == {}

    def test_none_returns_empty_dict(self) -> None:
        tc = ToolCallEvent(name="tool", arguments=None)
        assert tc.serialized_arguments == {}

    def test_setter_updates_cache(self) -> None:
        tc = ToolCallEvent(name="tool", arguments='{"a": 1}')

        with patch.object(json, "loads", wraps=json.loads) as mock_loads:
            assert tc.serialized_arguments == {"a": 1}

            tc.serialized_arguments = {"b": 2}
            assert tc.serialized_arguments == {"b": 2}

            assert mock_loads.call_count == 1