summaryrefslogtreecommitdiff
path: root/spec/bundler/commands/doctor_spec.rb
blob: 860b638f06e19af9d36f061218e86bd16cdaaf47 (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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# frozen_string_literal: true

require "find"
require "stringio"
require "bundler/cli"
require "bundler/cli/doctor"

RSpec.describe "bundle doctor" do
  before(:each) do
    install_gemfile <<-G
      source "#{file_uri_for(gem_repo1)}"
      gem "rack"
    G

    @stdout = StringIO.new

    [:error, :warn].each do |method|
      allow(Bundler.ui).to receive(method).and_wrap_original do |m, message|
        m.call message
        @stdout.puts message
      end
    end
  end

  it "succeeds on a sane installation" do
    bundle :doctor
  end

  context "when all files in home are readable/writable" do
    before(:each) do
      stat = double("stat")
      unwritable_file = double("file")
      allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
      allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [unwritable_file] }
      allow(File).to receive(:exist?).and_call_original
      allow(File).to receive(:exist?).with(unwritable_file).and_return(true)
      allow(File).to receive(:stat).with(unwritable_file) { stat }
      allow(stat).to receive(:uid) { Process.uid }
      allow(File).to receive(:writable?).with(unwritable_file) { true }
      allow(File).to receive(:readable?).with(unwritable_file) { true }
    end

    it "exits with no message if the installed gem has no C extensions" do
      expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
      expect(@stdout.string).to be_empty
    end

    it "exits with no message if the installed gem's C extension dylib breakage is fine" do
      doctor = Bundler::CLI::Doctor.new({})
      expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
      expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/lib/libSystem.dylib"]
      allow(File).to receive(:exist?).with("/usr/lib/libSystem.dylib").and_return(true)
      expect { doctor.run }.not_to(raise_error, @stdout.string)
      expect(@stdout.string).to be_empty
    end

    it "exits with a message if one of the linked libraries is missing" do
      doctor = Bundler::CLI::Doctor.new({})
      expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
      expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"]
      allow(File).to receive(:exist?).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(false)
      expect { doctor.run }.to raise_error(Bundler::ProductionError, strip_whitespace(<<-E).strip), @stdout.string
        The following gems are missing OS dependencies:
         * bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
         * rack: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
      E
    end
  end

  context "when home contains broken symlinks" do
    before(:each) do
      @broken_symlink = double("file")
      allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
      allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [@broken_symlink] }
      allow(File).to receive(:exist?).and_call_original
      allow(File).to receive(:exist?).with(@broken_symlink) { false }
    end

    it "exits with an error if home contains files that are not readable/writable" do
      expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
      expect(@stdout.string).to include(
        "Broken links exist in the Bundler home. Please report them to the offending gem's upstream repo. These files are:\n - #{@broken_symlink}"
      )
      expect(@stdout.string).not_to include("No issues")
    end
  end

  context "when home contains files that are not readable/writable" do
    before(:each) do
      @stat = double("stat")
      @unwritable_file = double("file")
      allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
      allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [@unwritable_file] }
      allow(File).to receive(:exist?).and_call_original
      allow(File).to receive(:exist?).with(@unwritable_file) { true }
      allow(File).to receive(:stat).with(@unwritable_file) { @stat }
    end

    it "exits with an error if home contains files that are not readable/writable" do
      allow(@stat).to receive(:uid) { Process.uid }
      allow(File).to receive(:writable?).with(@unwritable_file) { false }
      allow(File).to receive(:readable?).with(@unwritable_file) { false }
      expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
      expect(@stdout.string).to include(
        "Files exist in the Bundler home that are not readable/writable by the current user. These files are:\n - #{@unwritable_file}"
      )
      expect(@stdout.string).not_to include("No issues")
    end

    context "when home contains files that are not owned by the current process", :permissions do
      before(:each) do
        allow(@stat).to receive(:uid) { 0o0000 }
      end

      it "exits with an error if home contains files that are not readable/writable and are not owned by the current user" do
        allow(File).to receive(:writable?).with(@unwritable_file) { false }
        allow(File).to receive(:readable?).with(@unwritable_file) { false }
        expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
        expect(@stdout.string).to include(
          "Files exist in the Bundler home that are owned by another user, and are not readable/writable. These files are:\n - #{@unwritable_file}"
        )
        expect(@stdout.string).not_to include("No issues")
      end

      it "exits with a warning if home contains files that are read/write but not owned by current user" do
        allow(File).to receive(:writable?).with(@unwritable_file) { true }
        allow(File).to receive(:readable?).with(@unwritable_file) { true }
        expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
        expect(@stdout.string).to include(
          "Files exist in the Bundler home that are owned by another user, but are still readable/writable. These files are:\n - #{@unwritable_file}"
        )
        expect(@stdout.string).not_to include("No issues")
      end
    end
  end

  context "when home contains filesname with special characters" do
    it "escape filename before command execute" do
      doctor = Bundler::CLI::Doctor.new({})
      expect(doctor).to receive(:`).with("/usr/bin/otool -L \\$\\(date\\)\\ \\\"\\'\\\\.bundle").and_return("dummy string")
      doctor.dylibs_darwin('$(date) "\'\.bundle')
      expect(doctor).to receive(:`).with("/usr/bin/ldd \\$\\(date\\)\\ \\\"\\'\\\\.bundle").and_return("dummy string")
      doctor.dylibs_ldd('$(date) "\'\.bundle')
    end
  end
end