appsignal 2.8.0.alpha.1 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  require "appsignal/cli"
2
2
 
3
- describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
3
+ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_input do
4
4
  include CLIHelpers
5
5
 
6
6
  class DiagnosticsReportEndpoint
@@ -27,6 +27,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
27
27
  let(:gem_path) { Bundler::CLI::Common.select_spec("appsignal").full_gem_path.strip }
28
28
  let(:received_report) { DiagnosticsReportEndpoint.received_report }
29
29
  let(:process_user) { Etc.getpwuid(Process.uid).name }
30
+ let(:process_group) { Etc.getgrgid(Process.gid).name }
30
31
  before(:context) { Appsignal.stop }
31
32
  before do
32
33
  # Clear previous reports
@@ -51,13 +52,22 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
51
52
  before :api_stub => true do
52
53
  stub_api_request config, "auth"
53
54
  end
54
- before :report => true do
55
- send_diagnostics_report
56
- stub_diagnostics_report_request.to_rack(DiagnosticsReportEndpoint)
55
+ before(:send_report => :yes_cli_input) do
56
+ accept_prompt_to_send_diagnostics_report
57
+ capture_diagnatics_report_request
58
+ end
59
+ before(:send_report => :no_cli_input) { dont_accept_prompt_to_send_diagnostics_report }
60
+ before(:send_report => :yes_cli_option) do
61
+ options[:send_report] = true
62
+ capture_diagnatics_report_request
57
63
  end
58
- before(:report => false) { dont_send_diagnostics_report }
64
+ before(:send_report => :no_cli_option) { options[:send_report] = false }
59
65
  after { Appsignal.config = nil }
60
66
 
67
+ def capture_diagnatics_report_request
68
+ stub_diagnostics_report_request.to_rack(DiagnosticsReportEndpoint)
69
+ end
70
+
61
71
  def run
62
72
  run_within_dir project_fixture_path
63
73
  end
@@ -82,11 +92,11 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
82
92
  )
83
93
  end
84
94
 
85
- def send_diagnostics_report
95
+ def accept_prompt_to_send_diagnostics_report
86
96
  add_cli_input "y"
87
97
  end
88
98
 
89
- def dont_send_diagnostics_report
99
+ def dont_accept_prompt_to_send_diagnostics_report
90
100
  add_cli_input "n"
91
101
  end
92
102
 
@@ -94,22 +104,28 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
94
104
  run
95
105
  expect(output).to include \
96
106
  "AppSignal diagnose",
97
- "http://docs.appsignal.com/",
107
+ "https://docs.appsignal.com/",
98
108
  "support@appsignal.com"
99
109
  end
100
110
 
111
+ it "logs to the log file" do
112
+ run
113
+ log_contents = File.read(config.log_file_path)
114
+ expect(log_contents).to contains_log :info, "Starting AppSignal diagnose"
115
+ end
116
+
101
117
  describe "report" do
102
118
  context "when user wants to send report" do
103
119
  it "sends report" do
104
120
  run
105
121
  expect(output).to include "Diagnostics report",
106
- "Send diagnostics report to AppSignal? (Y/n): ",
107
- "Please email us at support@appsignal.com with the following\n support token."
122
+ "Send diagnostics report to AppSignal? (Y/n): "
108
123
  end
109
124
 
110
125
  it "outputs the support token from the server" do
111
126
  run
112
127
  expect(output).to include "Your support token: my_support_token"
128
+ expect(output).to include "View this report: https://appsignal.com/diagnose/my_support_token"
113
129
  end
114
130
 
115
131
  context "when server response is invalid" do
@@ -138,7 +154,25 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
138
154
  end
139
155
  end
140
156
 
141
- context "when user doesn't want to send report", :report => false do
157
+ context "when user uses the --send-report option", :send_report => :yes_cli_option do
158
+ it "sends the report without prompting" do
159
+ run
160
+ expect(output).to include "Diagnostics report",
161
+ "Confirmed sending report using --send-report option.",
162
+ "Transmitting diagnostics report"
163
+ end
164
+ end
165
+
166
+ context "when user uses the --no-send-report option", :send_report => :no_cli_option do
167
+ it "does not send the report" do
168
+ run
169
+ expect(output).to include "Diagnostics report",
170
+ "Not sending report. (Specified with the --no-send-report option.)",
171
+ "Not sending diagnostics information to AppSignal."
172
+ end
173
+ end
174
+
175
+ context "when user doesn't want to send report", :send_report => :no_cli_input do
142
176
  it "does not send report" do
143
177
  run
144
178
  expect(output).to include "Diagnostics report",
@@ -155,8 +189,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
155
189
  expect(output).to include \
156
190
  "Gem version: #{Appsignal::VERSION}",
157
191
  "Agent version: #{Appsignal::Extension.agent_version}",
158
- "Agent architecture: #{Appsignal::System.installed_agent_architecture}",
159
- "Gem install path: #{gem_path}"
192
+ "Agent architecture: #{Appsignal::System.installed_agent_architecture}"
160
193
  end
161
194
 
162
195
  it "transmits version numbers in report" do
@@ -166,7 +199,6 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
166
199
  "package_version" => Appsignal::VERSION,
167
200
  "agent_version" => Appsignal::Extension.agent_version,
168
201
  "agent_architecture" => Appsignal::System.installed_agent_architecture,
169
- "package_install_path" => gem_path,
170
202
  "extension_loaded" => true
171
203
  }
172
204
  )
@@ -205,14 +237,12 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
205
237
  end
206
238
 
207
239
  describe "agent diagnostics" do
240
+ let(:working_directory_stat) { File.stat("/tmp/appsignal") }
241
+
208
242
  it "starts the agent in diagnose mode and outputs the report" do
209
243
  run
210
- expect(output).to include \
211
- "Agent diagnostics",
212
- " Extension config: valid",
213
- " Agent config: valid",
214
- " Agent logger: started",
215
- " Agent lock path: writable"
244
+ working_directory_stat = File.stat("/tmp/appsignal")
245
+ expect_valid_agent_diagnostics_report(output, working_directory_stat)
216
246
  end
217
247
 
218
248
  it "adds the agent diagnostics to the report" do
@@ -223,8 +253,17 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
223
253
  },
224
254
  "agent" => {
225
255
  "boot" => { "started" => { "result" => true } },
256
+ "host" => {
257
+ "uid" => { "result" => Process.uid },
258
+ "gid" => { "result" => Process.gid }
259
+ },
226
260
  "config" => { "valid" => { "result" => true } },
227
261
  "logger" => { "started" => { "result" => true } },
262
+ "working_directory_stat" => {
263
+ "uid" => { "result" => working_directory_stat.uid },
264
+ "gid" => { "result" => working_directory_stat.gid },
265
+ "mode" => { "result" => working_directory_stat.mode }
266
+ },
228
267
  "lock_path" => { "created" => { "result" => true } }
229
268
  }
230
269
  )
@@ -239,13 +278,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
239
278
  it "force starts the agent in diagnose mode and outputs a log" do
240
279
  run
241
280
  expect(output).to include("active: false")
242
- expect(output).to include \
243
- "Agent diagnostics",
244
- " Extension config: valid",
245
- " Agent started: started",
246
- " Agent config: valid",
247
- " Agent logger: started",
248
- " Agent lock path: writable"
281
+ expect_valid_agent_diagnostics_report(output, working_directory_stat)
249
282
  end
250
283
  end
251
284
 
@@ -323,11 +356,12 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
323
356
  it "prints the tests, but shows a dash `-` for missed results" do
324
357
  expect(output).to include \
325
358
  "Agent diagnostics",
326
- " Extension config: invalid",
327
- " Agent started: -",
328
- " Agent config: -",
329
- " Agent logger: -",
330
- " Agent lock path: -"
359
+ " Extension tests\n Configuration: invalid",
360
+ " Agent tests",
361
+ " Started: -",
362
+ " Configuration: -",
363
+ " Logger: -",
364
+ " Lock path: -"
331
365
  end
332
366
 
333
367
  it "adds the output to the report" do
@@ -356,8 +390,8 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
356
390
  it "prints the error and output" do
357
391
  expect(output).to include \
358
392
  "Agent diagnostics",
359
- " Extension config: valid",
360
- " Agent started: not started\n Error: some-error"
393
+ " Extension tests\n Configuration: valid",
394
+ " Agent tests\n Started: not started\n Error: some-error"
361
395
  end
362
396
 
363
397
  it "adds the agent report to the diagnostics report" do
@@ -382,8 +416,8 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
382
416
  run
383
417
  expect(output).to include \
384
418
  "Agent diagnostics",
385
- " Extension config: valid",
386
- " Agent config: invalid\n Output: some output"
419
+ " Extension tests\n Configuration: valid",
420
+ " Configuration: invalid\n Output: some output"
387
421
  end
388
422
  end
389
423
  end
@@ -531,31 +565,147 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
531
565
 
532
566
  it "outputs a warning that no config is loaded" do
533
567
  expect(output).to include \
534
- "Environment: \n Warning: No environment set, no config loaded!",
568
+ "Environment: \"\"\n",
569
+ " Warning: No environment set, no config loaded!",
535
570
  " appsignal diagnose --environment=production"
536
571
  end
537
572
 
538
573
  it "outputs config defaults" do
539
574
  expect(output).to include("Configuration")
540
- Appsignal::Config::DEFAULT_CONFIG.each do |key, value|
541
- expect(output).to include("#{key}: #{value}")
542
- end
575
+ expect_config_to_be_printed(Appsignal::Config::DEFAULT_CONFIG)
576
+ end
577
+
578
+ it "transmits validation in report" do
579
+ default_config = hash_with_string_keys(Appsignal::Config::DEFAULT_CONFIG)
580
+ expect(received_report["config"]).to eq(
581
+ "options" => default_config.merge("env" => ""),
582
+ "sources" => {
583
+ "default" => default_config,
584
+ "system" => {},
585
+ "initial" => { "env" => "" },
586
+ "file" => {},
587
+ "env" => {}
588
+ }
589
+ )
543
590
  end
544
591
  end
545
592
 
546
593
  context "with configured environment" do
547
- before { run }
594
+ describe "environment" do
595
+ it "outputs environment" do
596
+ run
597
+ expect(output).to include(%(Environment: "production"))
598
+ end
548
599
 
549
- it "outputs environment" do
550
- expect(output).to include("Environment: production")
600
+ context "when the source is a single source" do
601
+ before { run }
602
+
603
+ it "outputs the label source after the value" do
604
+ expect(output).to include(
605
+ %(Environment: "#{Appsignal.config.env}" (Loaded from: initial)\n)
606
+ )
607
+ end
608
+ end
609
+
610
+ context "when the source is multiple sources" do
611
+ let(:options) { { :environment => "development" } }
612
+ before do
613
+ ENV["APPSIGNAL_APP_ENV"] = "production"
614
+ config.instance_variable_set(:@env, ENV["APPSIGNAL_APP_ENV"])
615
+ stub_api_request(config, "auth").to_return(:status => 200)
616
+ capture_diagnatics_report_request
617
+ run
618
+ end
619
+
620
+ it "outputs a list of sources with their values" do
621
+ expect(output).to include(
622
+ %( Environment: "production"\n) +
623
+ %( Sources:\n) +
624
+ %( initial: "development"\n) +
625
+ %( env: "production"\n)
626
+ )
627
+ end
628
+ end
551
629
  end
552
630
 
553
631
  it "outputs configuration" do
632
+ run
554
633
  expect(output).to include("Configuration")
555
- Appsignal.config.config_hash.each do |key, value|
556
- expect(output).to include("#{key}: #{value}")
634
+ expect_config_to_be_printed(Appsignal.config.config_hash)
635
+ end
636
+
637
+ describe "option sources" do
638
+ context "when the source is a single source" do
639
+ before { run }
640
+
641
+ it "outputs the label source after the value" do
642
+ expect(output).to include(
643
+ %(push_api_key: "#{Appsignal.config[:push_api_key]}" (Loaded from: file)\n)
644
+ )
645
+ end
646
+
647
+ context "when the source is only default" do
648
+ it "does not print a source" do
649
+ expect(output).to include("debug: #{Appsignal.config[:debug]}\n")
650
+ end
651
+ end
652
+ end
653
+
654
+ context "when the source is multiple sources" do
655
+ before do
656
+ ENV["APPSIGNAL_APP_NAME"] = "MyApp"
657
+ config[:name] = ENV["APPSIGNAL_APP_NAME"]
658
+ stub_api_request(config, "auth").to_return(:status => 200)
659
+ capture_diagnatics_report_request
660
+ run
661
+ end
662
+
663
+ if DependencyHelper.rails_present?
664
+ it "outputs a list of sources with their values" do
665
+ expect(output).to include(
666
+ %( name: "MyApp"\n) +
667
+ %( Sources:\n) +
668
+ %( initial: "MyApp"\n) +
669
+ %( file: "TestApp"\n) +
670
+ %( env: "MyApp"\n)
671
+ )
672
+ end
673
+ else
674
+ it "outputs a list of sources with their values" do
675
+ expect(output).to include(
676
+ %( name: "MyApp"\n) +
677
+ %( Sources:\n) +
678
+ %( file: "TestApp"\n) +
679
+ %( env: "MyApp"\n)
680
+ )
681
+ end
682
+ end
557
683
  end
558
684
  end
685
+
686
+ it "transmits config in report" do
687
+ run
688
+ additional_initial_config = {}
689
+ if DependencyHelper.rails_present?
690
+ additional_initial_config = {
691
+ :name => "MyApp",
692
+ :log_path => File.join(Rails.root, "log")
693
+ }
694
+ end
695
+ final_config = { "env" => "production" }
696
+ .merge(additional_initial_config)
697
+ .merge(config.config_hash)
698
+ expect(received_report["config"]).to match(
699
+ "options" => hash_with_string_keys(final_config),
700
+ "sources" => {
701
+ "default" => hash_with_string_keys(Appsignal::Config::DEFAULT_CONFIG),
702
+ "system" => {},
703
+ "initial" => hash_with_string_keys(config.initial_config.merge(additional_initial_config)),
704
+ "file" => hash_with_string_keys(config.file_config),
705
+ "env" => {}
706
+ }
707
+ )
708
+ end
559
709
  end
560
710
 
561
711
  context "with unconfigured environment" do
@@ -563,14 +713,42 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
563
713
  before { run_within_dir tmp_dir }
564
714
 
565
715
  it "outputs environment" do
566
- expect(output).to include("Environment: foobar")
716
+ expect(output).to include(%(Environment: "foobar"))
567
717
  end
568
718
 
569
719
  it "outputs config defaults" do
570
720
  expect(output).to include("Configuration")
571
- Appsignal::Config::DEFAULT_CONFIG.each do |key, value|
572
- expect(output).to include("#{key}: #{value}")
573
- end
721
+ expect_config_to_be_printed(Appsignal::Config::DEFAULT_CONFIG)
722
+ end
723
+
724
+ it "transmits config in report" do
725
+ expect(received_report["config"]).to match(
726
+ "options" => hash_with_string_keys(config.config_hash).merge("env" => "foobar"),
727
+ "sources" => {
728
+ "default" => hash_with_string_keys(Appsignal::Config::DEFAULT_CONFIG),
729
+ "system" => {},
730
+ "initial" => hash_with_string_keys(config.initial_config),
731
+ "file" => hash_with_string_keys(config.file_config),
732
+ "env" => {}
733
+ }
734
+ )
735
+ end
736
+ end
737
+
738
+ def expect_config_to_be_printed(config)
739
+ nil_options = config.select { |_, v| v.nil? }
740
+ nil_options.each_key do |key|
741
+ expect(output).to include(%(#{key}: nil))
742
+ end
743
+ string_options = config.select { |_, v| v.is_a?(String) }
744
+ string_options.each do |key, value|
745
+ expect(output).to include(%(#{key}: "#{value}"))
746
+ end
747
+ other_options = config.select do |k, _|
748
+ !string_options.key?(k) && !nil_options.key?(k)
749
+ end
750
+ other_options.each do |key, value|
751
+ expect(output).to include(%(#{key}: #{value}))
574
752
  end
575
753
  end
576
754
  end
@@ -639,6 +817,8 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
639
817
  end
640
818
 
641
819
  describe "paths" do
820
+ let(:config) { Appsignal::Config.new(root_path, "production") }
821
+ let(:root_path) { tmp_dir }
642
822
  let(:system_tmp_dir) { Appsignal::Config.system_tmp_dir }
643
823
  before do
644
824
  FileUtils.mkdir_p(root_path)
@@ -647,82 +827,220 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
647
827
  after { FileUtils.rm_rf([root_path, system_tmp_dir]) }
648
828
 
649
829
  describe "report" do
650
- let(:root_path) { tmp_dir }
651
-
652
830
  it "adds paths to the report" do
653
- run
654
- expect(received_report["paths"].keys)
655
- .to match_array(%w[root_path working_dir log_dir_path log_file_path])
831
+ run_within_dir root_path
832
+ expect(received_report["paths"].keys).to match_array(
833
+ %w[
834
+ package_install_path root_path working_dir log_dir_path
835
+ ext/install.log ext/mkmf.log appsignal.log
836
+ ]
837
+ )
656
838
  end
657
- end
658
839
 
659
- context "when a directory is not configured" do
660
- let(:root_path) { File.join(tmp_dir, "writable_path") }
661
- let(:config) { Appsignal::Config.new(root_path, "production", :log_file => nil) }
662
- before do
663
- FileUtils.mkdir_p(File.join(root_path, "log"), :mode => 0o555)
664
- FileUtils.chmod(0o555, system_tmp_dir)
665
- run_within_dir root_path
840
+ describe "working_dir" do
841
+ before { run_within_dir root_path }
842
+
843
+ it "outputs current path" do
844
+ expect(output).to include %(Current working directory\n Path: "#{tmp_dir}")
845
+ end
846
+
847
+ it "transmits path data in report" do
848
+ expect(received_report["paths"]["working_dir"]).to match(
849
+ "path" => tmp_dir,
850
+ "exists" => true,
851
+ "type" => "directory",
852
+ "mode" => kind_of(String),
853
+ "writable" => boolean,
854
+ "ownership" => {
855
+ "uid" => kind_of(Integer),
856
+ "user" => kind_of(String),
857
+ "gid" => kind_of(Integer),
858
+ "group" => kind_of(String)
859
+ }
860
+ )
861
+ end
666
862
  end
667
863
 
668
- it "outputs unconfigured directory" do
669
- expect(output).to include %(log_file_path: ""\n Configured?: false)
864
+ describe "root_path" do
865
+ before { run_within_dir root_path }
866
+
867
+ it "outputs root path" do
868
+ expect(output).to include %(Root path\n Path: "#{root_path}")
869
+ end
870
+
871
+ it "transmits path data in report" do
872
+ expect(received_report["paths"]["root_path"]).to match(
873
+ "path" => root_path,
874
+ "exists" => true,
875
+ "type" => "directory",
876
+ "mode" => kind_of(String),
877
+ "writable" => boolean,
878
+ "ownership" => {
879
+ "uid" => kind_of(Integer),
880
+ "user" => kind_of(String),
881
+ "gid" => kind_of(Integer),
882
+ "group" => kind_of(String)
883
+ }
884
+ )
885
+ end
670
886
  end
671
887
 
672
- it "transmits path data in report" do
673
- expect(received_report["paths"]["log_file_path"]).to eq(
674
- "path" => nil,
675
- "configured" => false,
676
- "exists" => false,
677
- "writable" => false
678
- )
888
+ describe "package_install_path" do
889
+ before { run_within_dir root_path }
890
+
891
+ it "outputs gem install path" do
892
+ expect(output).to match %(AppSignal gem path\n Path: "#{gem_path}")
893
+ end
894
+
895
+ it "transmits path data in report" do
896
+ expect(received_report["paths"]["package_install_path"]).to match(
897
+ "path" => gem_path,
898
+ "exists" => true,
899
+ "type" => "directory",
900
+ "mode" => kind_of(String),
901
+ "writable" => boolean,
902
+ "ownership" => {
903
+ "uid" => kind_of(Integer),
904
+ "user" => kind_of(String),
905
+ "gid" => kind_of(Integer),
906
+ "group" => kind_of(String)
907
+ }
908
+ )
909
+ end
910
+ end
911
+
912
+ describe "log_dir_path" do
913
+ before { run_within_dir root_path }
914
+
915
+ it "outputs log directory path" do
916
+ expect(output).to match %(Log directory\n Path: "#{system_tmp_dir}")
917
+ end
918
+
919
+ it "transmits path data in report" do
920
+ expect(received_report["paths"]["log_dir_path"]).to match(
921
+ "path" => system_tmp_dir,
922
+ "exists" => true,
923
+ "type" => "directory",
924
+ "mode" => kind_of(String),
925
+ "writable" => boolean,
926
+ "ownership" => {
927
+ "uid" => kind_of(Integer),
928
+ "user" => kind_of(String),
929
+ "gid" => kind_of(Integer),
930
+ "group" => kind_of(String)
931
+ }
932
+ )
933
+ end
679
934
  end
680
935
  end
681
936
 
682
937
  context "when a directory does not exist" do
683
938
  let(:root_path) { tmp_dir }
684
939
  let(:execution_path) { File.join(tmp_dir, "not_existing_dir") }
685
- let(:config) { Appsignal::Config.new(execution_path, "production") }
940
+ let(:config) do
941
+ silence(:allowed => ["Push api key not set after loading config"]) do
942
+ Appsignal::Config.new(execution_path, "production")
943
+ end
944
+ end
686
945
  before do
687
946
  allow(Dir).to receive(:pwd).and_return(execution_path)
688
947
  run_within_dir tmp_dir
689
948
  end
690
949
 
691
950
  it "outputs not existing path" do
692
- expect(output).to include %(root_path: "#{execution_path}"\n Exists?: false)
951
+ expect(output).to include %(Root path\n Path: "#{execution_path}"\n Exists?: false)
693
952
  end
694
953
 
695
954
  it "transmits path data in report" do
696
955
  expect(received_report["paths"]["root_path"]).to eq(
697
956
  "path" => execution_path,
698
- "configured" => true,
699
- "exists" => false,
700
- "writable" => false
957
+ "exists" => false
701
958
  )
702
959
  end
703
960
  end
704
961
 
705
- describe "ownership" do
706
- let(:config) { Appsignal::Config.new(root_path, "production") }
962
+ context "when not writable" do
963
+ let(:root_path) { File.join(tmp_dir, "not_writable_path") }
964
+ before do
965
+ FileUtils.chmod(0o555, root_path)
966
+ run_within_dir root_path
967
+ end
968
+
969
+ it "outputs not writable root path" do
970
+ expect(output).to include %(Root path\n Path: "#{root_path}"\n Writable?: false)
971
+ end
972
+
973
+ it "transmits path data in report" do
974
+ expect(received_report["paths"]["root_path"]).to eq(
975
+ "path" => root_path,
976
+ "exists" => true,
977
+ "type" => "directory",
978
+ "mode" => "40555",
979
+ "writable" => false,
980
+ "ownership" => {
981
+ "uid" => Process.uid,
982
+ "user" => process_user,
983
+ "gid" => Process.gid,
984
+ "group" => process_group
985
+ }
986
+ )
987
+ end
988
+ end
989
+
990
+ context "when writable" do
991
+ let(:root_path) { File.join(tmp_dir, "writable_path") }
992
+ before do
993
+ FileUtils.chmod(0o755, root_path)
994
+ run_within_dir root_path
995
+ end
996
+
997
+ it "outputs writable root path" do
998
+ expect(output).to include %(Root path\n Path: "#{root_path}"\n Writable?: true)
999
+ end
707
1000
 
1001
+ it "transmits path data in report" do
1002
+ expect(received_report["paths"]["root_path"]).to eq(
1003
+ "path" => root_path,
1004
+ "exists" => true,
1005
+ "type" => "directory",
1006
+ "mode" => "40755",
1007
+ "writable" => true,
1008
+ "ownership" => {
1009
+ "uid" => Process.uid,
1010
+ "user" => process_user,
1011
+ "gid" => Process.gid,
1012
+ "group" => process_group
1013
+ }
1014
+ )
1015
+ end
1016
+ end
1017
+
1018
+ describe "ownership" do
708
1019
  context "when a directory is owned by the current user" do
709
1020
  let(:root_path) { File.join(tmp_dir, "owned_path") }
710
1021
  before { run_within_dir root_path }
711
1022
 
712
1023
  it "outputs ownership" do
713
1024
  expect(output).to include \
714
- %(root_path: "#{root_path}"\n Writable?: true\n ) \
1025
+ %(Root path\n Path: "#{root_path}"\n Writable?: true\n ) \
715
1026
  "Ownership?: true (file: #{process_user}:#{Process.uid}, "\
716
1027
  "process: #{process_user}:#{Process.uid})"
717
1028
  end
718
1029
 
719
1030
  it "transmits path data in report" do
1031
+ mode = ENV["RUNNING_IN_CI"] ? "40775" : "40755"
720
1032
  expect(received_report["paths"]["root_path"]).to eq(
721
1033
  "path" => root_path,
722
- "configured" => true,
723
1034
  "exists" => true,
1035
+ "type" => "directory",
1036
+ "mode" => mode,
724
1037
  "writable" => true,
725
- "ownership" => { "uid" => Process.uid, "user" => process_user }
1038
+ "ownership" => {
1039
+ "uid" => Process.uid,
1040
+ "user" => process_user,
1041
+ "gid" => Process.gid,
1042
+ "group" => process_group
1043
+ }
726
1044
  )
727
1045
  end
728
1046
  end
@@ -738,228 +1056,80 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
738
1056
 
739
1057
  it "outputs no ownership" do
740
1058
  expect(output).to include \
741
- %(root_path: "#{root_path}"\n Writable?: true\n ) \
1059
+ %(Root path\n Path: "#{root_path}"\n Writable?: true\n ) \
742
1060
  "Ownership?: false (file: root:0, process: #{process_user}:#{Process.uid})"
743
1061
  end
744
1062
  end
745
1063
  end
746
-
747
- describe "working_dir" do
748
- let(:root_path) { tmp_dir }
749
- let(:config) { Appsignal::Config.new(root_path, "production") }
750
- before { run_within_dir root_path }
751
-
752
- it "outputs current path" do
753
- expect(output).to include %(working_dir: "#{tmp_dir}"\n Writable?: true)
754
- end
755
-
756
- it "transmits path data in report" do
757
- expect(received_report["paths"]["working_dir"]).to eq(
758
- "path" => tmp_dir,
759
- "configured" => true,
760
- "exists" => true,
761
- "writable" => true,
762
- "ownership" => { "uid" => Process.uid, "user" => process_user }
763
- )
764
- end
765
- end
766
-
767
- describe "root_path" do
768
- let(:system_tmp_log_file) { File.join(system_tmp_dir, "appsignal.log") }
769
- let(:config) { Appsignal::Config.new(root_path, "production") }
770
-
771
- context "when not writable" do
772
- let(:root_path) { File.join(tmp_dir, "not_writable_path") }
773
- before do
774
- FileUtils.chmod(0o555, root_path)
775
- run_within_dir root_path
776
- end
777
-
778
- it "outputs not writable root path" do
779
- expect(output).to include %(root_path: "#{root_path}"\n Writable?: false)
780
- end
781
-
782
- it "log files fall back on system tmp directory" do
783
- expect(output).to include \
784
- %(log_dir_path: "#{system_tmp_dir}"\n Writable?: true),
785
- %(log_file_path: "#{system_tmp_log_file}"\n Exists?: false)
786
- end
787
-
788
- it "transmits path data in report" do
789
- expect(received_report["paths"]["root_path"]).to eq(
790
- "path" => root_path,
791
- "configured" => true,
792
- "exists" => true,
793
- "writable" => false,
794
- "ownership" => { "uid" => Process.uid, "user" => process_user }
795
- )
796
- end
797
- end
798
-
799
- context "when writable" do
800
- let(:root_path) { File.join(tmp_dir, "writable_path") }
801
-
802
- context "without log dir" do
803
- before do
804
- FileUtils.chmod(0o777, root_path)
805
- run_within_dir root_path
806
- end
807
-
808
- it "outputs writable root path" do
809
- expect(output).to include %(root_path: "#{root_path}"\n Writable?: true)
810
- end
811
-
812
- it "log files fall back on system tmp directory" do
813
- expect(output).to include \
814
- %(log_dir_path: "#{system_tmp_dir}"\n Writable?: true),
815
- %(log_file_path: "#{system_tmp_log_file}"\n Exists?: false)
816
- end
817
-
818
- it "transmits path data in report" do
819
- expect(received_report["paths"]["root_path"]).to eq(
820
- "path" => root_path,
821
- "configured" => true,
822
- "exists" => true,
823
- "writable" => true,
824
- "ownership" => { "uid" => Process.uid, "user" => process_user }
825
- )
826
- end
827
- end
828
-
829
- context "with log dir" do
830
- let(:log_dir) { File.join(root_path, "log") }
831
- let(:log_file) { File.join(log_dir, "appsignal.log") }
832
- before { FileUtils.mkdir_p(log_dir) }
833
-
834
- context "when not writable" do
835
- before do
836
- FileUtils.chmod(0o444, log_dir)
837
- run_within_dir root_path
838
- end
839
-
840
- it "log files fall back on system tmp directory" do
841
- expect(output).to include \
842
- %(log_dir_path: "#{system_tmp_dir}"\n Writable?: true),
843
- %(log_file_path: "#{system_tmp_log_file}"\n Exists?: false)
844
- end
845
-
846
- it "transmits path data in report" do
847
- expect(received_report["paths"]["log_dir_path"]).to be_kind_of(Hash)
848
- expect(received_report["paths"]["log_file_path"]).to be_kind_of(Hash)
849
- end
850
- end
851
-
852
- context "when writable" do
853
- context "without log file" do
854
- before { run_within_dir root_path }
855
-
856
- it "outputs writable but without log file" do
857
- expect(output).to include \
858
- %(root_path: "#{root_path}"\n Writable?: true),
859
- %(log_dir_path: "#{log_dir}"\n Writable?: true),
860
- %(log_file_path: "#{log_file}"\n Exists?: false)
861
- end
862
-
863
- it "transmits path data in report" do
864
- expect(received_report["paths"]["log_dir_path"]).to be_kind_of(Hash)
865
- expect(received_report["paths"]["log_file_path"]).to be_kind_of(Hash)
866
- end
867
- end
868
-
869
- context "with log file" do
870
- context "when writable" do
871
- before do
872
- FileUtils.touch(log_file)
873
- run_within_dir root_path
874
- end
875
-
876
- it "lists log file as writable" do
877
- expect(output).to include %(log_file_path: "#{log_file}"\n Writable?: true)
878
- end
879
-
880
- it "transmits path data in report" do
881
- expect(received_report["paths"]["log_dir_path"]).to be_kind_of(Hash)
882
- expect(received_report["paths"]["log_file_path"]).to be_kind_of(Hash)
883
- end
884
- end
885
-
886
- context "when not writable" do
887
- before do
888
- FileUtils.touch(log_file)
889
- FileUtils.chmod(0o444, log_file)
890
- run_within_dir root_path
891
- end
892
-
893
- it "lists log file as not writable" do
894
- expect(output).to include %(log_file_path: "#{log_file}"\n Writable?: false)
895
- end
896
-
897
- it "transmits path data in report" do
898
- expect(received_report["paths"]["log_dir_path"]).to be_kind_of(Hash)
899
- expect(received_report["paths"]["log_file_path"]).to be_kind_of(Hash)
900
- end
901
- end
902
- end
903
- end
904
- end
905
- end
906
- end
907
1064
  end
908
1065
 
909
- describe "logs" do
910
- shared_examples "ext log file" do |log_file|
911
- let(:ext_path) { File.join(gem_path, "ext") }
912
- let(:log_path) { File.join(ext_path, log_file) }
913
- before do
914
- FileUtils.mkdir_p ext_path
915
- allow(cli).to receive(:gem_path).and_return(gem_path)
916
- end
917
- after { FileUtils.rm_rf ext_path }
1066
+ describe "files" do
1067
+ shared_examples "diagnose file" do |shared_example_options|
1068
+ let(:parent_directory) { File.join(tmp_dir, "diagnose_files") }
1069
+ let(:file_path) { File.join(parent_directory, filename) }
1070
+ before { FileUtils.mkdir_p File.dirname(file_path) }
1071
+ after { FileUtils.rm_rf parent_directory }
918
1072
 
919
1073
  context "when file exists" do
920
- let(:gem_path) { File.join(tmp_dir, "gem") }
921
- let(:log_content) do
922
- [
923
- "log line 1",
924
- "log line 2"
925
- ]
1074
+ let(:contents) do
1075
+ [].tap do |lines|
1076
+ (1..12).each do |i|
1077
+ lines << "log line #{i}"
1078
+ end
1079
+ end
926
1080
  end
927
1081
  before do
928
- File.open log_path, "a" do |f|
929
- log_content.each do |line|
1082
+ File.open file_path, "a" do |f|
1083
+ contents.each do |line|
930
1084
  f.puts line
931
1085
  end
932
1086
  end
933
1087
  run
934
1088
  end
935
1089
 
936
- it "outputs install.log" do
937
- expect(output).to include(%(Path: "#{log_path}"))
938
- expect(output).to include(*log_content)
1090
+ it "outputs file location and content" do
1091
+ expect(output).to include(
1092
+ %(Path: "#{file_path}"),
1093
+ "Contents (last 10 lines):"
1094
+ )
1095
+ expect(output).to include(*contents.last(10).join("\n"))
1096
+ expect(output).to_not include(*contents.first(2).join("\n"))
939
1097
  end
940
1098
 
941
- it "transmits log data in report" do
942
- expect(received_report["logs"][File.join("ext", log_file)]).to eq(
943
- "path" => log_path,
1099
+ it "transmits file data in report" do
1100
+ expect(received_report["paths"][filename]).to match(
1101
+ "path" => file_path,
944
1102
  "exists" => true,
945
- "content" => log_content
1103
+ "type" => "file",
1104
+ "mode" => kind_of(String),
1105
+ "writable" => boolean,
1106
+ "ownership" => {
1107
+ "uid" => kind_of(Integer),
1108
+ "user" => kind_of(String),
1109
+ "gid" => kind_of(Integer),
1110
+ "group" => kind_of(String)
1111
+ },
1112
+ "content" => contents
946
1113
  )
947
1114
  end
948
-
949
- after { FileUtils.rm_rf(gem_path) }
950
1115
  end
951
1116
 
952
1117
  context "when file does not exist" do
953
- let(:gem_path) { File.join(tmp_dir, "gem_without_log_files") }
954
- before { run }
1118
+ before do
1119
+ if shared_example_options && shared_example_options[:stub_not_exists]
1120
+ allow(File).to receive(:exist?).and_call_original
1121
+ expect(File).to receive(:exist?).with(file_path).and_return(false)
1122
+ end
1123
+ run
1124
+ end
955
1125
 
956
- it "outputs install.log" do
957
- expect(output).to include %(Path: "#{log_path}"\n File not found.)
1126
+ it "outputs file does not exists" do
1127
+ expect(output).to include %(Path: "#{file_path}"\n Exists?: false)
958
1128
  end
959
1129
 
960
- it "transmits log data in report" do
961
- expect(received_report["logs"][File.join("ext", log_file)]).to eq(
962
- "path" => log_path,
1130
+ it "transmits file data in report" do
1131
+ expect(received_report["paths"][filename]).to eq(
1132
+ "path" => file_path,
963
1133
  "exists" => false
964
1134
  )
965
1135
  end
@@ -967,7 +1137,15 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
967
1137
  end
968
1138
 
969
1139
  describe "install.log" do
970
- it_behaves_like "ext log file", "install.log"
1140
+ it_behaves_like "diagnose file" do
1141
+ let(:filename) { File.join("ext", "install.log") }
1142
+ before do
1143
+ expect(Bundler::CLI::Common).to receive(:select_spec)
1144
+ .with("appsignal")
1145
+ .at_least(:once)
1146
+ .and_return(double(:full_gem_path => parent_directory))
1147
+ end
1148
+ end
971
1149
 
972
1150
  it "outputs header" do
973
1151
  run
@@ -976,13 +1154,55 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
976
1154
  end
977
1155
 
978
1156
  describe "mkmf.log" do
979
- it_behaves_like "ext log file", "mkmf.log"
1157
+ it_behaves_like "diagnose file" do
1158
+ let(:filename) { File.join("ext", "mkmf.log") }
1159
+ before do
1160
+ expect(Bundler::CLI::Common).to receive(:select_spec)
1161
+ .with("appsignal")
1162
+ .at_least(:once)
1163
+ .and_return(double(:full_gem_path => parent_directory))
1164
+ end
1165
+ end
980
1166
 
981
1167
  it "outputs header" do
982
1168
  run
983
1169
  expect(output).to include("Makefile install log")
984
1170
  end
985
1171
  end
1172
+
1173
+ describe "appsignal.log" do
1174
+ it_behaves_like "diagnose file", :stub_not_exists => true do
1175
+ let(:filename) { "appsignal.log" }
1176
+ before do
1177
+ ENV["APPSIGNAL_LOG"] = "stdout"
1178
+ expect_any_instance_of(Appsignal::Config).to receive(:log_file_path).at_least(:once).and_return(file_path)
1179
+ end
1180
+ end
1181
+
1182
+ it "outputs header" do
1183
+ run
1184
+ expect(output).to include("AppSignal log")
1185
+ end
1186
+ end
986
1187
  end
987
1188
  end
1189
+
1190
+ def expect_valid_agent_diagnostics_report(output, working_directory_stat)
1191
+ expect(output).to include \
1192
+ "Agent diagnostics",
1193
+ " Extension tests\n Configuration: valid",
1194
+ " Started: started",
1195
+ " Process user id: #{Process.uid}",
1196
+ " Process user group id: #{Process.gid}\n" \
1197
+ " Configuration: valid",
1198
+ " Logger: started",
1199
+ " Working directory user id: #{working_directory_stat.uid}",
1200
+ " Working directory user group id: #{working_directory_stat.gid}",
1201
+ " Working directory permissions: #{working_directory_stat.mode}",
1202
+ " Lock path: writable"
1203
+ end
1204
+
1205
+ def hash_with_string_keys(hash)
1206
+ Hash[hash.map { |key, value| [key.to_s, value] }]
1207
+ end
988
1208
  end