diff options
Diffstat (limited to 'spec/dispatch/tools/registry_spec.rb')
| -rw-r--r-- | spec/dispatch/tools/registry_spec.rb | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/spec/dispatch/tools/registry_spec.rb b/spec/dispatch/tools/registry_spec.rb new file mode 100644 index 0000000..fc7332c --- /dev/null +++ b/spec/dispatch/tools/registry_spec.rb @@ -0,0 +1,227 @@ +# frozen_string_literal: true + +RSpec.describe Dispatch::Tools::Registry do + let(:read_file_tool) do + Dispatch::Tools::Definition.new( + name: "read_file", + description: "Read the contents of a file", + parameters: { + type: "object", + properties: { + path: { type: "string" } + }, + required: ["path"] + } + ) { |params, _context| Dispatch::Tools::Result.success(output: "contents of #{params[:path]}") } + end + + let(:write_file_tool) do + Dispatch::Tools::Definition.new( + name: "write_file", + description: "Write contents to a file", + parameters: { + type: "object", + properties: { + path: { type: "string" }, + content: { type: "string" } + }, + required: %w[path content] + } + ) { |_params, _context| Dispatch::Tools::Result.success(output: "written") } + end + + let(:delete_file_tool) do + Dispatch::Tools::Definition.new( + name: "delete_file", + description: "Delete a file", + parameters: { + type: "object", + properties: { + path: { type: "string" } + }, + required: ["path"] + } + ) { |_params, _context| Dispatch::Tools::Result.success(output: "deleted") } + end + + let(:registry) { described_class.new } + + describe "#register" do + it "adds a tool definition to the registry" do + registry.register(read_file_tool) + + expect(registry.has?("read_file")).to be true + end + + it "returns self for chaining" do + result = registry.register(read_file_tool) + + expect(result).to be(registry) + end + + it "supports chaining multiple registrations" do + registry.register(read_file_tool).register(write_file_tool) + + expect(registry.has?("read_file")).to be true + expect(registry.has?("write_file")).to be true + end + + it "raises DuplicateToolError when registering a tool with the same name" do + registry.register(read_file_tool) + + duplicate_tool = Dispatch::Tools::Definition.new( + name: "read_file", + description: "Another read file", + parameters: { type: "object", properties: {}, required: [] } + ) { |_params, _context| Dispatch::Tools::Result.success(output: "dupe") } + + expect { registry.register(duplicate_tool) }.to raise_error(Dispatch::Tools::DuplicateToolError) + end + end + + describe "#get" do + before { registry.register(read_file_tool) } + + it "returns the tool definition by name" do + tool = registry.get("read_file") + + expect(tool).to be(read_file_tool) + end + + it "raises ToolNotFoundError for unknown tool name" do + expect { registry.get("nonexistent") }.to raise_error(Dispatch::Tools::ToolNotFoundError) + end + end + + describe "#has?" do + it "returns true when the tool is registered" do + registry.register(read_file_tool) + + expect(registry.has?("read_file")).to be true + end + + it "returns false when the tool is not registered" do + expect(registry.has?("read_file")).to be false + end + end + + describe "#tools" do + it "returns an empty array when no tools are registered" do + expect(registry.tools).to eq([]) + end + + it "returns all registered tool definitions" do + registry.register(read_file_tool).register(write_file_tool) + + expect(registry.tools).to contain_exactly(read_file_tool, write_file_tool) + end + end + + describe "#tool_names" do + it "returns an empty array when no tools are registered" do + expect(registry.tool_names).to eq([]) + end + + it "returns all registered tool names as strings" do + registry.register(read_file_tool).register(write_file_tool) + + expect(registry.tool_names).to contain_exactly("read_file", "write_file") + end + end + + describe "#to_a" do + it "returns an empty array when no tools are registered" do + expect(registry.to_a).to eq([]) + end + + it "returns an array of hashes with name, description, and parameters" do + registry.register(read_file_tool) + + result = registry.to_a + + expect(result).to be_an(Array) + expect(result.size).to eq(1) + expect(result.first).to eq({ + name: "read_file", + description: "Read the contents of a file", + parameters: { + type: "object", + properties: { + path: { type: "string" } + }, + required: ["path"] + } + }) + end + + it "returns plain hashes, not structs" do + registry.register(read_file_tool) + + registry.to_a.each do |entry| + expect(entry).to be_a(Hash) + end + end + + it "includes all registered tools" do + registry.register(read_file_tool).register(write_file_tool) + + names = registry.to_a.map { |h| h[:name] } + + expect(names).to contain_exactly("read_file", "write_file") + end + end + + describe "#subset" do + before do + registry.register(read_file_tool).register(write_file_tool).register(delete_file_tool) + end + + it "returns a new Registry containing only the specified tools" do + sub = registry.subset("read_file", "write_file") + + expect(sub).to be_a(described_class) + expect(sub.tool_names).to contain_exactly("read_file", "write_file") + end + + it "does not include tools not specified" do + sub = registry.subset("read_file") + + expect(sub.has?("write_file")).to be false + expect(sub.has?("delete_file")).to be false + end + + it "returns a different registry instance" do + sub = registry.subset("read_file") + + expect(sub).not_to be(registry) + end + + it "raises ToolNotFoundError when a requested name is not found" do + expect { registry.subset("read_file", "nonexistent") }.to raise_error(Dispatch::Tools::ToolNotFoundError) + end + end + + describe "#size" do + it "returns 0 for an empty registry" do + expect(registry.size).to eq(0) + end + + it "returns the number of registered tools" do + registry.register(read_file_tool).register(write_file_tool) + + expect(registry.size).to eq(2) + end + end + + describe "#empty?" do + it "returns true when no tools are registered" do + expect(registry.empty?).to be true + end + + it "returns false when tools are registered" do + registry.register(read_file_tool) + + expect(registry.empty?).to be false + end + end +end |
