defmodule AiDebug.MCPToolsTest do use ExUnit.Case, async: true alias AiDebug.MCPTools describe "all_tools/0" do test "returns all LiveView debugging tools" do tools = MCPTools.all_tools() assert is_list(tools) assert length(tools) > 0 # Check for essential tools tool_names = Enum.map(tools, & &1.name) assert "list_liveviews" in tool_names assert "inspect_liveview" in tool_names assert "get_liveview_components" in tool_names assert "update_liveview_assigns" in tool_names assert "monitor_liveview" in tool_names assert "start_liveview_recording" in tool_names end test "all tools have required fields" do tools = MCPTools.all_tools() Enum.each(tools, fn tool -> assert Map.has_key?(tool, :name) assert Map.has_key?(tool, :description) assert Map.has_key?(tool, :input_schema) # Validate input schema schema = tool.input_schema assert schema.type == "object" assert Map.has_key?(schema, :properties) end) end end describe "execute_tool/2" do test "list_liveviews returns list" do result = MCPTools.execute_tool("list_liveviews", %{}) assert is_list(result) end test "unknown tool returns error" do result = MCPTools.execute_tool("unknown_tool", %{}) assert {:error, "Unknown tool: unknown_tool"} = result end test "get_liveview_stats with valid module" do # This would normally require a running LiveView result = MCPTools.execute_tool("get_liveview_stats", %{"module" => "Elixir.String"}) assert is_map(result) assert Map.has_key?(result, :events) assert Map.has_key?(result, :errors) end end describe "tool parameter validation" do test "handles PID string conversion" do # Test the PID resolution logic pid_string = "#PID<0.123.0>" # This would be called internally by execute_tool # In a real test, we'd need a live PID result = MCPTools.execute_tool("inspect_liveview", %{"pid" => pid_string}) assert match?({:error, _}, result) end test "handles module string conversion" do # Module resolution should work result = MCPTools.execute_tool("get_liveview_stats", %{"module" => "String"}) assert is_map(result) end end describe "recording tools" do test "start_liveview_recording validates parameters" do # Without a valid target result = MCPTools.execute_tool("start_liveview_recording", %{"target" => "invalid"}) assert result == {:error, "Module invalid not found"} end test "recording options are properly parsed" do # This tests the option building logic args = %{ "target" => "NonExistentModule", # Using a non-existent module "include_state" => true, "include_dom" => false, "max_events" => 100 } # Should return error for non-existent module result = MCPTools.execute_tool("start_liveview_recording", args) assert result == {:error, "Module NonExistentModule not found"} end end describe "HEEX annotation tools" do test "get_heex_annotations with HTML" do html = """
Content
""" result = MCPTools.execute_tool("get_heex_annotations", %{"html" => html}) assert is_map(result) assert Map.has_key?(result, :components) assert Map.has_key?(result, :files) assert Map.has_key?(result, :stats) end end describe "monitoring tools" do test "monitor_liveview with options" do args = %{ "module" => "String", "log_level" => "debug" } # Should return success even without PubSub in test result = MCPTools.execute_tool("monitor_liveview", args) assert result == {:ok, %{monitoring: :String}} end end describe "tool schema validation" do test "validates required parameters" do tools = MCPTools.all_tools() # Check that tools with required params have them listed Enum.each(tools, fn tool -> schema = tool.input_schema if Map.has_key?(schema, :required) do assert is_list(schema.required) # All required params should be in properties Enum.each(schema.required, fn param -> assert Map.has_key?(schema.properties, String.to_atom(param)) end) end end) end test "parameter types are valid" do tools = MCPTools.all_tools() valid_types = ["string", "object", "boolean", "integer", "number", "array"] Enum.each(tools, fn tool -> Enum.each(tool.input_schema.properties, fn {_name, prop} -> assert prop.type in valid_types # If enum is present, it should be a list if Map.has_key?(prop, :enum) do assert is_list(prop.enum) end end) end) end end end