appsignal 2.0.3 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,17 +1,22 @@
1
1
  require "appsignal/cli"
2
2
 
3
- describe Appsignal::CLI::Diagnose do
3
+ describe Appsignal::CLI::Diagnose, :api_stub => true do
4
4
  describe ".run" do
5
- let(:out_stream) { StringIO.new }
5
+ let(:out_stream) { std_stream }
6
+ let(:output) { out_stream.read }
6
7
  let(:config) { project_fixture_config }
7
8
  let(:cli) { described_class }
8
- let(:output) { out_stream.string }
9
+ let(:options) { { :environment => config.env } }
10
+ before(:all) { Appsignal.stop }
9
11
  before do
10
- ENV["APPSIGNAL_APP_ENV"] = "production"
12
+ if DependencyHelper.rails_present?
13
+ allow(Rails).to receive(:root).and_return(Pathname.new(config.root_path))
14
+ end
15
+ end
16
+ before :api_stub => true do
11
17
  stub_api_request config, "auth"
12
18
  end
13
19
  after { Appsignal.config = nil }
14
- around { |example| capture_stdout(out_stream) { example.run } }
15
20
 
16
21
  def run
17
22
  run_within_dir project_fixture_path
@@ -19,7 +24,7 @@ describe Appsignal::CLI::Diagnose do
19
24
 
20
25
  def run_within_dir(chdir)
21
26
  Dir.chdir chdir do
22
- cli.run
27
+ capture_stdout(out_stream) { cli.run(options) }
23
28
  end
24
29
  end
25
30
 
@@ -31,13 +36,48 @@ describe Appsignal::CLI::Diagnose do
31
36
  "support@appsignal.com"
32
37
  end
33
38
 
34
- it "outputs version numbers" do
35
- run
36
- gem_path = Bundler::CLI::Common.select_spec("appsignal").full_gem_path.strip
37
- expect(output).to include \
38
- "Gem version: #{Appsignal::VERSION}",
39
- "Agent version: #{Appsignal::Extension.agent_version}",
40
- "Gem install path: #{gem_path}"
39
+ describe "agent information" do
40
+ it "outputs version numbers" do
41
+ run
42
+ gem_path = Bundler::CLI::Common.select_spec("appsignal").full_gem_path.strip
43
+ expect(output).to include \
44
+ "Gem version: #{Appsignal::VERSION}",
45
+ "Agent version: #{Appsignal::Extension.agent_version}",
46
+ "Gem install path: #{gem_path}"
47
+ end
48
+
49
+ context "with extension" do
50
+ it "outputs extension is loaded" do
51
+ run
52
+ expect(output).to include "Extension loaded: yes"
53
+ end
54
+
55
+ it "starts the agent in diagnose mode and outputs a log" do
56
+ run
57
+ expect(output).to include \
58
+ "Agent diagnostics:",
59
+ "Running agent in diagnose mode",
60
+ "Valid config present",
61
+ "Logger initialized successfully",
62
+ "Lock path is writable",
63
+ "Agent diagnose finished"
64
+ end
65
+ end
66
+
67
+ context "without extension" do
68
+ before do
69
+ # When the extension isn't loaded the Appsignal.start operation exits
70
+ # early and doesn't load the configuration.
71
+ # Happens when the extension wasn't installed properly.
72
+ Appsignal.extension_loaded = false
73
+ run
74
+ end
75
+ after { Appsignal.extension_loaded = true }
76
+
77
+ it "outputs extension is not loaded" do
78
+ expect(output).to include "Extension loaded: no"
79
+ end
80
+ end
41
81
  end
42
82
 
43
83
  describe "host information" do
@@ -50,6 +90,26 @@ describe Appsignal::CLI::Diagnose do
50
90
  "Ruby version: #{RbConfig::CONFIG["RUBY_VERSION_NAME"]}"
51
91
  end
52
92
 
93
+ describe "root user detection" do
94
+ context "when not root user" do
95
+ it "prints no" do
96
+ run
97
+ expect(output).to include "root user: no"
98
+ end
99
+ end
100
+
101
+ context "when root user" do
102
+ before do
103
+ allow(Process).to receive(:uid).and_return(0)
104
+ run
105
+ end
106
+
107
+ it "prints yes, with warning" do
108
+ expect(output).to include "root user: yes (not recommended)"
109
+ end
110
+ end
111
+ end
112
+
53
113
  describe "Heroku detection" do
54
114
  context "when not on Heroku" do
55
115
  before { recognize_as_container(:none) { run } }
@@ -89,42 +149,20 @@ describe Appsignal::CLI::Diagnose do
89
149
  end
90
150
 
91
151
  describe "configuration" do
92
- context "without extension" do
93
- before do
94
- # When the extension isn't loaded the Appsignal.start operation exits
95
- # early and doesn't load the configuration.
96
- # Happens when the extension wasn't installed properly.
97
- Appsignal.extension_loaded = false
98
- run
99
- end
100
- after { Appsignal.extension_loaded = true }
101
-
102
- it "outputs an error" do
103
- expect(output).to include \
104
- "Error: No config found!\nCould not start AppSignal."
105
- end
106
-
107
- it "outputs as much as it can" do
108
- expect(output).to include \
109
- "AppSignal agent\n Gem version: #{Appsignal::VERSION}",
110
- "Host information\n Architecture: ",
111
- %(Extension install log\n Path: "),
112
- %(Makefile install log\n Path: ")
113
- end
114
- end
115
-
116
152
  context "without environment" do
117
- let(:config) { project_fixture_config("") }
153
+ let(:config) { project_fixture_config(nil) }
154
+ let(:options) { {} }
118
155
  before do
119
- ENV["APPSIGNAL_APP_ENV"] = ""
120
- recognize_as_container(:none) { run }
156
+ ENV.delete("RAILS_ENV") # From spec_helper
157
+ ENV.delete("RACK_ENV")
158
+ recognize_as_container(:none) { run_within_dir tmp_dir }
121
159
  end
122
160
 
123
161
  it "outputs a warning that no config is loaded" do
124
162
  expect(output).to_not include "Error"
125
163
  expect(output).to include \
126
164
  "Environment: \n Warning: No environment set, no config loaded!",
127
- " APPSIGNAL_APP_ENV=production appsignal diagnose"
165
+ " appsignal diagnose --environment=production"
128
166
  end
129
167
 
130
168
  it "outputs config defaults" do
@@ -153,10 +191,7 @@ describe Appsignal::CLI::Diagnose do
153
191
 
154
192
  context "with unconfigured environment" do
155
193
  let(:config) { project_fixture_config("foobar") }
156
- before do
157
- ENV["APPSIGNAL_APP_ENV"] = "foobar"
158
- recognize_as_container(:none) { run }
159
- end
194
+ before { recognize_as_container(:none) { run_within_dir tmp_dir } }
160
195
 
161
196
  it "outputs environment" do
162
197
  expect(output).to include("Environment: foobar")
@@ -172,7 +207,7 @@ describe Appsignal::CLI::Diagnose do
172
207
  end
173
208
  end
174
209
 
175
- describe "API key validation" do
210
+ describe "API key validation", :api_stub => false do
176
211
  context "with valid key" do
177
212
  before do
178
213
  stub_api_request(config, "auth").to_return(:status => 200)
@@ -208,71 +243,185 @@ describe Appsignal::CLI::Diagnose do
208
243
  end
209
244
 
210
245
  describe "paths" do
211
- before { FileUtils.mkdir_p(root_path) }
246
+ let(:system_tmp_dir) { Appsignal::Config::SYSTEM_TMP_DIR }
247
+ before do
248
+ FileUtils.mkdir_p(root_path)
249
+ FileUtils.mkdir_p(system_tmp_dir)
250
+ end
251
+ after { FileUtils.rm_rf([root_path, system_tmp_dir]) }
212
252
 
213
- context "when a directory is writable" do
253
+ context "when a directory is not configured" do
214
254
  let(:root_path) { File.join(tmp_dir, "writable_path") }
215
- let(:log_file) { File.join(root_path, "appsignal.log") }
216
- let(:config) { Appsignal::Config.new(root_path, "production") }
255
+ let(:config) { Appsignal::Config.new(root_path, "production", :log_file => nil) }
256
+ before do
257
+ FileUtils.mkdir_p(File.join(root_path, "log"), :mode => 0555)
258
+ FileUtils.chmod(0555, system_tmp_dir)
259
+ run_within_dir root_path
260
+ end
261
+
262
+ it "outputs unconfigured directory" do
263
+ expect(output).to include %(log_file_path: ""\n - Configured?: no)
264
+ end
265
+ end
266
+
267
+ context "when a directory does not exist" do
268
+ let(:root_path) { tmp_dir }
269
+ let(:execution_path) { File.join(tmp_dir, "not_existing_dir") }
270
+ let(:config) { Appsignal::Config.new(execution_path, "production") }
271
+ before do
272
+ allow(Dir).to receive(:pwd).and_return(execution_path)
273
+ run_within_dir tmp_dir
274
+ end
217
275
 
218
- context "without log file" do
276
+ it "outputs not existing path" do
277
+ expect(output).to include %(root_path: "#{execution_path}"\n - Exists?: no)
278
+ end
279
+ end
280
+
281
+ describe "ownership" do
282
+ context "when a directory is owned by the current user" do
283
+ let(:root_path) { File.join(tmp_dir, "owned_path") }
284
+ let(:config) { Appsignal::Config.new(root_path, "production") }
285
+ let(:process_user) { Etc.getpwuid(Process.uid).name }
219
286
  before { run_within_dir root_path }
220
287
 
221
- it "outputs writable" do
288
+ it "outputs ownership" do
222
289
  expect(output).to include \
223
- "Required paths",
224
- %(root_path: "#{root_path}" - Writable),
225
- %(log_file_path: "#{log_file}" - Does not exist)
290
+ %(root_path: "#{root_path}"\n - Writable?: yes\n ) \
291
+ "- Ownership?: yes (file: #{process_user}:#{Process.uid}, "\
292
+ "process: #{process_user}:#{Process.uid})"
226
293
  end
227
294
  end
228
295
 
229
- context "with log file" do
230
- context "when writable" do
231
- before do
232
- FileUtils.touch(log_file)
233
- run_within_dir root_path
234
- end
296
+ context "when a directory is not owned by the current user" do
297
+ let(:root_path) { File.join(tmp_dir, "not_owned_path") }
298
+ let(:config) { Appsignal::Config.new(root_path, "production") }
299
+ let(:process_user) { Etc.getpwuid(Process.uid).name }
300
+ before do
301
+ stat = File.stat(root_path)
302
+ allow(stat).to receive(:uid).and_return(0)
303
+ allow(File).to receive(:stat).and_return(stat)
304
+ run_within_dir root_path
305
+ end
235
306
 
236
- it "lists log file as writable" do
237
- expect(output).to include \
238
- %(root_path: "#{root_path}" - Writable),
239
- %(log_file_path: "#{File.join(root_path, "appsignal.log")}" - Writable)
240
- end
307
+ it "outputs no ownership" do
308
+ expect(output).to include \
309
+ %(root_path: "#{root_path}"\n - Writable?: yes\n ) \
310
+ "- Ownership?: no (file: root:0, process: #{process_user}:#{Process.uid})"
241
311
  end
312
+ end
313
+ end
242
314
 
243
- context "when not writable" do
315
+ describe "current_path" do
316
+ let(:root_path) { tmp_dir }
317
+ let(:config) { Appsignal::Config.new(root_path, "production") }
318
+ before { run_within_dir root_path }
319
+
320
+ it "outputs current path" do
321
+ expect(output).to include %(current_path: "#{tmp_dir}"\n - Writable?: yes)
322
+ end
323
+ end
324
+
325
+ describe "root_path" do
326
+ let(:system_tmp_log_file) { File.join(system_tmp_dir, "appsignal.log") }
327
+ context "when not writable" do
328
+ let(:root_path) { File.join(tmp_dir, "not_writable_path") }
329
+ let(:config) { Appsignal::Config.new(root_path, "production") }
330
+ before do
331
+ FileUtils.chmod(0555, root_path)
332
+ run_within_dir root_path
333
+ end
334
+
335
+ it "outputs not writable root path" do
336
+ expect(output).to include %(root_path: "#{root_path}"\n - Writable?: no)
337
+ end
338
+
339
+ it "log files fall back on system tmp directory" do
340
+ expect(output).to include \
341
+ %(log_dir_path: "#{system_tmp_dir}"\n - Writable?: yes)
342
+ %(log_file_path: "#{system_tmp_log_file}"\n - Exist?: false)
343
+ end
344
+ end
345
+
346
+ context "when writable" do
347
+ let(:root_path) { File.join(tmp_dir, "writable_path") }
348
+ let(:config) { Appsignal::Config.new(root_path, "production") }
349
+
350
+ context "without log dir" do
244
351
  before do
245
- FileUtils.touch(log_file)
246
- FileUtils.chmod(0444, log_file)
352
+ FileUtils.chmod(0777, root_path)
247
353
  run_within_dir root_path
248
354
  end
249
355
 
250
- it "lists log file as not writable" do
356
+ it "outputs writable root path" do
357
+ expect(output).to include %(root_path: "#{root_path}"\n - Writable?: yes)
358
+ end
359
+
360
+ it "log files fall back on system tmp directory" do
251
361
  expect(output).to include \
252
- %(root_path: "#{root_path}" - Writable),
253
- %(log_file_path: "#{File.join(root_path, "appsignal.log")}" - Not writable)
362
+ %(log_dir_path: "#{system_tmp_dir}"\n - Writable?: yes),
363
+ %(log_file_path: "#{system_tmp_log_file}"\n - Exists?: no)
254
364
  end
255
365
  end
256
- end
257
- end
258
366
 
259
- context "when a directory is not writable" do
260
- let(:root_path) { File.join(tmp_dir, "not_writable_path") }
261
- let(:config) { Appsignal::Config.new(root_path, "production") }
262
- before do
263
- FileUtils.chmod(0555, root_path)
264
- run_within_dir root_path
265
- end
367
+ context "with log dir" do
368
+ let(:log_dir) { File.join(root_path, "log") }
369
+ let(:log_file) { File.join(log_dir, "appsignal.log") }
370
+ before { FileUtils.mkdir_p(log_dir) }
371
+
372
+ context "when not writable" do
373
+ before do
374
+ FileUtils.chmod(0444, log_dir)
375
+ run_within_dir root_path
376
+ end
377
+
378
+ it "log files fall back on system tmp directory" do
379
+ expect(output).to include \
380
+ %(log_dir_path: "#{system_tmp_dir}"\n - Writable?: yes),
381
+ %(log_file_path: "#{system_tmp_log_file}"\n - Exists?: no)
382
+ end
383
+ end
266
384
 
267
- it "outputs not writable" do
268
- expect(output).to include \
269
- "Required paths",
270
- %(root_path: "#{root_path}" - Not writable),
271
- %(log_file_path: "" - Not writable)
385
+ context "when writable" do
386
+ context "without log file" do
387
+ before { run_within_dir root_path }
388
+
389
+ it "outputs writable but without log file" do
390
+ expect(output).to include \
391
+ %(root_path: "#{root_path}"\n - Writable?: yes),
392
+ %(log_dir_path: "#{log_dir}"\n - Writable?: yes),
393
+ %(log_file_path: "#{log_file}"\n - Exists?: no)
394
+ end
395
+ end
396
+
397
+ context "with log file" do
398
+ context "when writable" do
399
+ before do
400
+ FileUtils.touch(log_file)
401
+ run_within_dir root_path
402
+ end
403
+
404
+ it "lists log file as writable" do
405
+ expect(output).to include %(log_file_path: "#{log_file}"\n - Writable?: yes)
406
+ end
407
+ end
408
+
409
+ context "when not writable" do
410
+ before do
411
+ FileUtils.touch(log_file)
412
+ FileUtils.chmod(0444, log_file)
413
+ run_within_dir root_path
414
+ end
415
+
416
+ it "lists log file as not writable" do
417
+ expect(output).to include %(log_file_path: "#{log_file}"\n - Writable?: no)
418
+ end
419
+ end
420
+ end
421
+ end
422
+ end
272
423
  end
273
424
  end
274
-
275
- after { FileUtils.rm_rf(root_path) }
276
425
  end
277
426
 
278
427
  describe "logs" do
@@ -0,0 +1,99 @@
1
+ require "appsignal/cli/helpers"
2
+
3
+ describe Appsignal::CLI::Helpers do
4
+ include CLIHelpers
5
+
6
+ let(:out_stream) { std_stream }
7
+ let(:output) { out_stream.read }
8
+ let(:cli) do
9
+ Class.new do
10
+ extend Appsignal::CLI::Helpers
11
+ end
12
+ end
13
+ before do
14
+ # Speed up tests
15
+ allow(cli).to receive(:sleep)
16
+ end
17
+ around do |example|
18
+ original_stdin = $stdin
19
+ $stdin = StringIO.new
20
+ example.run
21
+ $stdin = original_stdin
22
+ end
23
+
24
+ describe ".colorize" do
25
+ subject { cli.send(:colorize, "text", :green) }
26
+
27
+ context "on windows" do
28
+ before { allow(Gem).to receive(:win_platform?).and_return(true) }
29
+
30
+ it "outputs plain string" do
31
+ expect(subject).to eq "text"
32
+ end
33
+ end
34
+
35
+ context "not on windows" do
36
+ before { allow(Gem).to receive(:win_platform?).and_return(false) }
37
+
38
+ it "wraps text in color tags" do
39
+ expect(subject).to eq "\e[32mtext\e[0m"
40
+ end
41
+ end
42
+ end
43
+
44
+ describe ".periods" do
45
+ it "prints three periods" do
46
+ capture_stdout(out_stream) { cli.send :periods }
47
+ expect(output).to include("...")
48
+ end
49
+ end
50
+
51
+ describe ".press_any_key" do
52
+ before do
53
+ set_input "a" # a as in any
54
+ end
55
+
56
+ it "continues after press" do
57
+ capture_stdout(out_stream) { cli.send :press_any_key }
58
+ expect(output).to include("Press any key")
59
+ end
60
+ end
61
+
62
+ describe ".yes_or_no" do
63
+ def yes_or_no
64
+ capture_stdout(out_stream) { cli.send(:yes_or_no, "yes or no?: ") }
65
+ end
66
+
67
+ it "takes yes for an answer" do
68
+ set_input ""
69
+ set_input "nonsense"
70
+ set_input "y"
71
+ prepare_input
72
+
73
+ expect(yes_or_no).to be_true
74
+ end
75
+
76
+ it "takes no for an answer" do
77
+ set_input ""
78
+ set_input "nonsense"
79
+ set_input "n"
80
+ prepare_input
81
+
82
+ expect(yes_or_no).to be_false
83
+ end
84
+ end
85
+
86
+ describe ".required_input" do
87
+ def required_input
88
+ capture_stdout(out_stream) { cli.send(:required_input, "provide: ") }
89
+ end
90
+
91
+ it "collects required input" do
92
+ set_input ""
93
+ set_input "value"
94
+ prepare_input
95
+
96
+ expect(required_input).to eq("value")
97
+ end
98
+ end
99
+ end