"""Tests for the tool registry.""" import pytest from cortex_ai.tools import ToolError, ToolRegistry, default_registry, registry_from_names, tool def test_default_registry_exposes_expected_tools(): reg = default_registry() assert set(reg.names()) == {"calculate", "cortex_identity", "current_time", "text_stats"} assert len(reg) == 4 def test_openai_schema_shape(): reg = default_registry() schemas = {s["function"]["name"]: s for s in reg.to_openai_schemas()} calc = schemas["calculate"] assert calc["type"] == "function" assert calc["function"]["parameters"]["type"] == "object" assert calc["function"]["parameters"]["properties"]["expression"]["type"] == "string" assert calc["function"]["parameters"]["required"] == ["expression"] def test_calculate_returns_the_right_value(): reg = default_registry() assert reg.call("calculate", {"expression": "2*(3+4)"}) == "14" assert reg.call("calculate", {"expression": "10/4"}) == "2.5" def test_calculate_rejects_non_arithmetic_input(): reg = default_registry() # A rejected expression surfaces as an ERROR string, not an exception: the # model must be able to read the failure and react to it. assert reg.call("calculate", {"expression": "__import__('os')"}).startswith("ERROR:") assert reg.call("calculate", {"expression": "9**9**9"}).startswith("ERROR:") assert "exponentiation" in reg.call("calculate", {"expression": "9**9**9"}) assert reg.call("calculate", {"expression": "1/0"}).startswith("ERROR:") def test_calculate_rejects_oversized_input(): reg = default_registry() assert reg.call("calculate", {"expression": "1+" * 200 + "1"}).startswith("ERROR:") def test_arguments_may_be_a_json_string(): reg = default_registry() assert reg.call("calculate", '{"expression": "3+4"}') == "7" def test_unknown_tool_raises(): reg = default_registry() with pytest.raises(ToolError, match="unknown tool"): reg.call("does_not_exist", {}) def test_missing_required_argument_raises(): reg = default_registry() with pytest.raises(ToolError, match="missing required argument"): reg.call("calculate", {}) def test_unknown_argument_raises(): reg = default_registry() with pytest.raises(ToolError, match="unknown argument"): reg.call("calculate", {"expression": "1+1", "bogus": 1}) def test_invalid_json_arguments_raise(): reg = default_registry() with pytest.raises(ToolError, match="not valid JSON"): reg.call("calculate", "{not json") def test_registry_from_names_restricts_and_orders(): reg = registry_from_names(["text_stats", "calculate"]) assert reg.names() == ["calculate", "text_stats"] def test_registry_from_empty_names_returns_everything(): assert len(registry_from_names([])) == 4 def test_cortex_identity_tool_reports_project_attribution(): from cortex_ai.identity import IDENTITY_STATEMENT assert default_registry().call("cortex_identity", {}) == IDENTITY_STATEMENT def test_custom_tool_via_decorator(): @tool(description="Ajoute deux entiers.") def ajouter(a: int, b: int) -> str: return str(a + b) reg = ToolRegistry([ajouter]) assert reg.names() == ["ajouter"] assert reg.call("ajouter", {"a": 2, "b": 3}) == "5" assert reg.to_openai_schemas()[0]["function"]["parameters"]["required"] == ["a", "b"] def test_tool_returning_a_dict_is_json_encoded(): reg = default_registry() out = reg.call("text_stats", {"text": "un deux trois"}) assert '"mots": 3' in out