using Test using YiemAgent using YiemAgent.toolRegistry using YiemAgent.type # Path to the real tools directory TOOLS_DIR = joinpath(@__DIR__, "..", "src", "tools") @testset "loadTools" begin # ------------------------------------------------------------------ # # 1. loadTools throws on non-existent directory # # ------------------------------------------------------------------ # @test_throws ArgumentError loadTools("/nonexistent/dir/that/does/not/exist") # ------------------------------------------------------------------ # # 2. loadTools throws if a .jl file does not define getTool() # # Must run BEFORE any other loadTools call (getTool binding # # persists in module scope after include()). # # ------------------------------------------------------------------ # bad_dir = mktempdir() write(joinpath(bad_dir, "noTool.jl"), "x = 42\n") @test_throws ArgumentError loadTools(bad_dir) # ------------------------------------------------------------------ # # 3. loadTools loads actual tool files from src/tools/ # # ------------------------------------------------------------------ # loaded = loadTools(TOOLS_DIR) @test !isempty(loaded) @test length(loaded) == 3 names = [k for k in keys(loaded)] @test "getTime" in names @test "getWeather" in names @test "writeTool" in names # ------------------------------------------------------------------ # # 4. loadTools returns tools sorted alphabetically by filename # # (getTime.jl < getWeather.jl < writeTool.jl) # # because 'T' < 'W' in ASCII # # ------------------------------------------------------------------ # @test collect(keys(loaded))[1] == "getTime" @test collect(keys(loaded))[2] == "getWeather" @test collect(keys(loaded))[3] == "writeTool" # ------------------------------------------------------------------ # # 5. Verify loaded tool fields are correct # # ------------------------------------------------------------------ # # getTime time_tool = loaded["getTime"] @test time_tool.name == "getTime" @test time_tool.label == "Time Lookup" @test time_tool.validateRequiredArgs !== nothing @test time_tool.parallelToolExecute == false @test time_tool.inputSchema["required"] == Any[] # getWeather weather = loaded["getWeather"] @test weather.name == "getWeather" @test weather.label == "Weather Lookup" @test weather.execute !== nothing @test weather.parallelToolExecute == false @test weather.inputSchema["required"] == ["city"] # writeTool wt = loaded["writeTool"] @test wt.name == "writeTool" @test wt.label == "Create Tool" @test wt.execute !== nothing @test "name" in wt.inputSchema["required"] @test "executeCode" in wt.inputSchema["required"] # ------------------------------------------------------------------ # # 6. Tool execution returns valid results # # ------------------------------------------------------------------ # sig = nothing op = x -> x # no-op partial result callback # execute getTime result_t = time_tool.execute("call-1", Dict{String,Any}("city" => "Tokyo"), sig, op) @test result_t isa agentToolResult @test result_t.content[1] isa textContent @test occursin("Tokyo", result_t.content[1].text) # execute getTime with timezone result_tz = time_tool.execute("call-2", Dict{String,Any}("timezone" => "America/New_York"), sig, op) @test result_tz isa agentToolResult @test occursin("America/New_York", result_tz.content[1].text) # execute getWeather result_w = weather.execute("call-3", Dict{String,Any}("city" => "Bangkok"), sig, op) @test result_w isa agentToolResult @test result_w.content[1] isa textContent @test occursin("Bangkok", result_w.content[1].text) # execute getWeather with units result_w2 = weather.execute("call-4", Dict{String,Any}("city" => "London", "units" => "fahrenheit"), sig, op) @test occursin("72°F", result_w2.content[1].text) # ------------------------------------------------------------------ # # 7. getTools / registerTool / clearTools # # ------------------------------------------------------------------ # registry_tools = getTools() @test !isempty(registry_tools) @test any(t -> t.name == "getTime", registry_tools) @test any(t -> t.name == "getWeather", registry_tools) clearTools() @test isempty(getTools()) test_tool = agentTool( name = "manualTool", label = "Manual Tool", description = "Registered manually", inputSchema = Dict{String,Any}("type" => "object", "properties" => Dict{String,Any}(), "required" => Any[]), execute = (toolCallId, args, signal, onPartialResult) -> agentToolResult([textContent("manual")], Dict{Any,Any}(), nothing, false), prepareArguments = nothing, validateRequiredArgs = nothing, parallelToolExecute = true ) registerTool(test_tool) reg = getTools() @test any(t -> t.name == "manualTool", reg) @test count(t -> t.name == "manualTool", reg) == 1 @test reg[1].parallelToolExecute == true # ------------------------------------------------------------------ # # 8. getTools returns deep copy (mutations don't affect registry) # # ------------------------------------------------------------------ # copy1 = getTools() copy2 = getTools() @test copy1 !== copy2 empty!(copy1) @test !isempty(getTools()) end