summaryrefslogtreecommitdiffhomepage
path: root/spec/dispatch/tool/files/write_file_spec.rb
blob: 8b7bacb494a550e03834804ac713faabf018f94d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# frozen_string_literal: true

RSpec.describe "write_file tool" do
  let(:worktree_path) { Dir.mktmpdir("write-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("write_file") }

  describe "writing a new file" do
    it "creates a new file with the given content" do
      result = tool.call({ "path" => "new_file.txt", "content" => "Hello, World!" }, context:)

      expect(result.success?).to be true
      expect(File.read(File.join(worktree_path, "new_file.txt"))).to eq("Hello, World!")
    end

    it "returns a confirmation message with path and byte count" do
      result = tool.call({ "path" => "output.txt", "content" => "12345" }, context:)

      expect(result.success?).to be true
      expect(result.output).to include("output.txt")
      expect(result.output).to match(/5\b.*bytes?/i)
    end
  end

  describe "overwriting an existing file" do
    it "replaces the entire content of an existing file" do
      file_path = File.join(worktree_path, "existing.txt")
      File.write(file_path, "old content")

      result = tool.call({ "path" => "existing.txt", "content" => "new content" }, context:)

      expect(result.success?).to be true
      expect(File.read(file_path)).to eq("new content")
    end
  end

  describe "creating parent directories" do
    it "creates intermediate directories as needed" do
      result = tool.call({ "path" => "a/b/c/deep.txt", "content" => "deep" }, context:)

      expect(result.success?).to be true
      expect(File.read(File.join(worktree_path, "a/b/c/deep.txt"))).to eq("deep")
    end
  end

  describe "error cases" do
    it "returns failure when the path escapes the sandbox" do
      result = tool.call({ "path" => "../../../tmp/evil.txt", "content" => "bad" }, context:)

      expect(result.failure?).to be true
      expect(result.error).to match(/sandbox|outside/i)
    end
  end

  describe "parameter validation" do
    it "requires the path parameter" do
      result = tool.call({ "content" => "data" }, context:)

      expect(result.failure?).to be true
    end

    it "requires the content parameter" do
      result = tool.call({ "path" => "file.txt" }, context:)

      expect(result.failure?).to be true
    end
  end
end