using Test using YiemAgent using YiemAgent.toolRegistry using YiemAgent.type # ------------------------------------------------------------------ # # loadTools() unit tests # # ------------------------------------------------------------------ # @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 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 tool files that define getTool() # # ------------------------------------------------------------------ # tmpdir = mktempdir() # Create a valid tool file (must use bare type names — include() places file in toolRegistry scope) valid_tool_echo = """ function getTool()::agentTool return agentTool( name = "testEcho", label = "Echo Test", description = "Echoes the input argument", inputSchema = Dict{String,Any}( "type" => "object", "properties" => Dict("message" => Dict("type" => "string")), "required" => Any["message"] ), execute = (toolCallId, args, signal, onPartialResult) -> begin return agentToolResult( [textContent("echo: " * string(args["message"]))], Dict{Any,Any}(), nothing, false ) end, prepareArguments = nothing, validateRequiredArgs = nothing, parallelToolExecute = false ) end """ write(joinpath(tmpdir, "getEcho.jl"), valid_tool_echo) loaded = loadTools(tmpdir) @test !isempty(loaded) @test length(loaded) >= 1 names = [t.name for t in loaded] @test "testEcho" in names # Check agentTool fields echo_tool = filter(t -> t.name == "testEcho", loaded) @test !isempty(echo_tool) @test echo_tool[1].label == "Echo Test" @test echo_tool[1].description == "Echoes the input argument" @test echo_tool[1].parallelToolExecute == false @test echo_tool[1].execute !== nothing # ------------------------------------------------------------------ # # 4. loadTools returns tools sorted alphabetically # # ------------------------------------------------------------------ # sorted_dir = mktempdir() tool_a = """ function getTool()::agentTool return agentTool( name = "alphaTool", label = "Alpha Tool", description = "First tool", inputSchema = Dict{String,Any}("type" => "object", "properties" => Dict{String,Any}(), "required" => Any[]), execute = (toolCallId, args, signal, onPartialResult) -> agentToolResult([textContent("alpha")], Dict{Any,Any}(), nothing, false), prepareArguments = nothing, validateRequiredArgs = nothing, parallelToolExecute = false ) end """ tool_m = """ function getTool()::agentTool return agentTool( name = "midTool", label = "Mid Tool", description = "Middle tool", inputSchema = Dict{String,Any}("type" => "object", "properties" => Dict{String,Any}(), "required" => Any[]), execute = (toolCallId, args, signal, onPartialResult) -> agentToolResult([textContent("mid")], Dict{Any,Any}(), nothing, false), prepareArguments = nothing, validateRequiredArgs = nothing, parallelToolExecute = false ) end """ tool_z = """ function getTool()::agentTool return agentTool( name = "zuluTool", label = "Zulu Tool", description = "Last tool", inputSchema = Dict{String,Any}("type" => "object", "properties" => Dict{String,Any}(), "required" => Any[]), execute = (toolCallId, args, signal, onPartialResult) -> agentToolResult([textContent("zulu")], Dict{Any,Any}(), nothing, false), prepareArguments = nothing, validateRequiredArgs = nothing, parallelToolExecute = false ) end """ write(joinpath(sorted_dir, "zTool.jl"), tool_z) write(joinpath(sorted_dir, "aTool.jl"), tool_a) write(joinpath(sorted_dir, "mTool.jl"), tool_m) loaded_sorted = loadTools(sorted_dir) # loadTools returns only tools loaded from the directory, in file-sorted order @test length(loaded_sorted) == 3 @test loaded_sorted[1].name == "alphaTool" @test loaded_sorted[2].name == "midTool" @test loaded_sorted[3].name == "zuluTool" # ------------------------------------------------------------------ # # 5. getTools returns a deep copy (mutations don't affect registry) # # ------------------------------------------------------------------ # registry_tools = getTools() @test !isempty(registry_tools) orig_count = length(registry_tools) # Clear and add a new tool via registerTool clearTools() registry_after_clear = getTools() @test isempty(registry_after_clear) # ------------------------------------------------------------------ # # 6. registerTool adds to global registry # # ------------------------------------------------------------------ # 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 # parallelToolExecute flag manual_entry = filter(t -> t.name == "manualTool", reg) @test manual_entry[1].parallelToolExecute == true # ------------------------------------------------------------------ # # 7. getTools returns deep copy # # ------------------------------------------------------------------ # copy1 = getTools() copy2 = getTools() @test copy1 !== copy2 empty!(copy1) @test !isempty(getTools()) end