diff options
| author | Adam Malczewski <[email protected]> | 2026-03-31 23:10:45 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-03-31 23:10:45 +0900 |
| commit | 57c56daf5906442dacc15951c9b3405f89309839 (patch) | |
| tree | e76890119c0e47acb48f8585222b7a2f5e22df56 /spec/dispatch/tool/files/read_file_spec.rb | |
| parent | 25488d32336e05b69a41391cc7b5153478d3cc8a (diff) | |
| download | dispatch-tool-files-dev.tar.gz dispatch-tool-files-dev.zip | |
impdev
Diffstat (limited to 'spec/dispatch/tool/files/read_file_spec.rb')
| -rw-r--r-- | spec/dispatch/tool/files/read_file_spec.rb | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/spec/dispatch/tool/files/read_file_spec.rb b/spec/dispatch/tool/files/read_file_spec.rb new file mode 100644 index 0000000..01b7828 --- /dev/null +++ b/spec/dispatch/tool/files/read_file_spec.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +RSpec.describe "read_file tool" do + let(:worktree_path) { Dir.mktmpdir("read-file-test") } + let(:context) { { worktree_path: } } + let(:registry) { Dispatch::Tools::Registry.new } + + before { Dispatch::Tool::Files.register(registry) } + + after { FileUtils.remove_entry(worktree_path) } + + subject(:tool) { registry.get("read_file") } + + describe "reading a full file" do + it "returns file contents with line numbers" do + File.write(File.join(worktree_path, "hello.txt"), "line one\nline two\nline three\n") + + result = tool.call({ "path" => "hello.txt" }, context:) + + expect(result.success?).to be true + expect(result.output).to include("line one") + expect(result.output).to include("line two") + expect(result.output).to include("line three") + end + + it "prefixes each line with its line number" do + File.write(File.join(worktree_path, "numbered.txt"), "alpha\nbeta\ngamma\n") + + result = tool.call({ "path" => "numbered.txt" }, context:) + + expect(result.success?).to be true + lines = result.output.split("\n") + expect(lines[0]).to match(/\A\s*0.*alpha/) + expect(lines[1]).to match(/\A\s*1.*beta/) + expect(lines[2]).to match(/\A\s*2.*gamma/) + end + end + + describe "reading a line range" do + before do + content = (0..9).map { |i| "line #{i}" }.join("\n") + "\n" + File.write(File.join(worktree_path, "lines.txt"), content) + end + + it "returns only the specified line range (0-based)" do + result = tool.call({ "path" => "lines.txt", "start_line" => 2, "end_line" => 4 }, context:) + + expect(result.success?).to be true + expect(result.output).to include("line 2") + expect(result.output).to include("line 3") + expect(result.output).to include("line 4") + expect(result.output).not_to include("line 1") + expect(result.output).not_to include("line 5") + end + + it "reads from start_line to end of file when end_line is -1" do + result = tool.call({ "path" => "lines.txt", "start_line" => 8, "end_line" => -1 }, context:) + + expect(result.success?).to be true + expect(result.output).to include("line 8") + expect(result.output).to include("line 9") + expect(result.output).not_to include("line 7") + end + + it "reads from the beginning when only end_line is specified" do + result = tool.call({ "path" => "lines.txt", "end_line" => 1 }, context:) + + expect(result.success?).to be true + expect(result.output).to include("line 0") + expect(result.output).to include("line 1") + expect(result.output).not_to include("line 2") + end + end + + describe "error cases" do + it "returns failure when the file does not exist" do + result = tool.call({ "path" => "nonexistent.txt" }, context:) + + expect(result.failure?).to be true + expect(result.error).to match(/not found|does not exist/i) + end + + it "returns failure when the path escapes the sandbox" do + result = tool.call({ "path" => "../../../etc/passwd" }, context:) + + expect(result.failure?).to be true + expect(result.error).to match(/sandbox|outside/i) + end + + it "returns failure for a binary file" do + binary_path = File.join(worktree_path, "binary.bin") + File.write(binary_path, "Hello\x00World\x00Binary\x00Content") + + result = tool.call({ "path" => "binary.bin" }, context:) + + expect(result.failure?).to be true + expect(result.error).to match(/binary/i) + end + end + + describe "parameter validation" do + it "requires the path parameter" do + result = tool.call({}, context:) + + expect(result.failure?).to be true + end + end +end |
