appsignal 2.5.0.alpha.1-java

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.
Files changed (211) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.rspec +4 -0
  4. data/.rubocop.yml +66 -0
  5. data/.rubocop_todo.yml +124 -0
  6. data/.travis.yml +72 -0
  7. data/.yardopts +8 -0
  8. data/CHANGELOG.md +639 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE +20 -0
  11. data/README.md +264 -0
  12. data/Rakefile +214 -0
  13. data/appsignal.gemspec +42 -0
  14. data/benchmark.rake +77 -0
  15. data/bin/appsignal +13 -0
  16. data/ext/Rakefile +27 -0
  17. data/ext/agent.yml +64 -0
  18. data/ext/appsignal_extension.c +692 -0
  19. data/ext/base.rb +79 -0
  20. data/ext/extconf.rb +35 -0
  21. data/gemfiles/capistrano2.gemfile +7 -0
  22. data/gemfiles/capistrano3.gemfile +7 -0
  23. data/gemfiles/grape.gemfile +7 -0
  24. data/gemfiles/no_dependencies.gemfile +5 -0
  25. data/gemfiles/padrino.gemfile +7 -0
  26. data/gemfiles/que.gemfile +5 -0
  27. data/gemfiles/rails-3.2.gemfile +6 -0
  28. data/gemfiles/rails-4.0.gemfile +6 -0
  29. data/gemfiles/rails-4.1.gemfile +6 -0
  30. data/gemfiles/rails-4.2.gemfile +10 -0
  31. data/gemfiles/rails-5.0.gemfile +5 -0
  32. data/gemfiles/rails-5.1.gemfile +5 -0
  33. data/gemfiles/resque.gemfile +12 -0
  34. data/gemfiles/sequel-435.gemfile +11 -0
  35. data/gemfiles/sequel.gemfile +11 -0
  36. data/gemfiles/sinatra.gemfile +6 -0
  37. data/gemfiles/webmachine.gemfile +5 -0
  38. data/lib/appsignal.rb +804 -0
  39. data/lib/appsignal/auth_check.rb +65 -0
  40. data/lib/appsignal/capistrano.rb +10 -0
  41. data/lib/appsignal/cli.rb +108 -0
  42. data/lib/appsignal/cli/demo.rb +63 -0
  43. data/lib/appsignal/cli/diagnose.rb +500 -0
  44. data/lib/appsignal/cli/helpers.rb +72 -0
  45. data/lib/appsignal/cli/install.rb +277 -0
  46. data/lib/appsignal/cli/notify_of_deploy.rb +113 -0
  47. data/lib/appsignal/config.rb +287 -0
  48. data/lib/appsignal/demo.rb +107 -0
  49. data/lib/appsignal/event_formatter.rb +74 -0
  50. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +24 -0
  51. data/lib/appsignal/event_formatter/active_record/instantiation_formatter.rb +14 -0
  52. data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +14 -0
  53. data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +32 -0
  54. data/lib/appsignal/event_formatter/faraday/request_formatter.rb +19 -0
  55. data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +89 -0
  56. data/lib/appsignal/event_formatter/moped/query_formatter.rb +80 -0
  57. data/lib/appsignal/extension.rb +63 -0
  58. data/lib/appsignal/extension/jruby.rb +460 -0
  59. data/lib/appsignal/garbage_collection_profiler.rb +48 -0
  60. data/lib/appsignal/hooks.rb +105 -0
  61. data/lib/appsignal/hooks/action_cable.rb +113 -0
  62. data/lib/appsignal/hooks/active_support_notifications.rb +52 -0
  63. data/lib/appsignal/hooks/celluloid.rb +30 -0
  64. data/lib/appsignal/hooks/data_mapper.rb +18 -0
  65. data/lib/appsignal/hooks/delayed_job.rb +19 -0
  66. data/lib/appsignal/hooks/mongo_ruby_driver.rb +21 -0
  67. data/lib/appsignal/hooks/net_http.rb +29 -0
  68. data/lib/appsignal/hooks/passenger.rb +22 -0
  69. data/lib/appsignal/hooks/puma.rb +35 -0
  70. data/lib/appsignal/hooks/que.rb +21 -0
  71. data/lib/appsignal/hooks/rake.rb +39 -0
  72. data/lib/appsignal/hooks/redis.rb +30 -0
  73. data/lib/appsignal/hooks/sequel.rb +60 -0
  74. data/lib/appsignal/hooks/shoryuken.rb +43 -0
  75. data/lib/appsignal/hooks/sidekiq.rb +144 -0
  76. data/lib/appsignal/hooks/unicorn.rb +40 -0
  77. data/lib/appsignal/hooks/webmachine.rb +23 -0
  78. data/lib/appsignal/integrations/capistrano/appsignal.cap +39 -0
  79. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +52 -0
  80. data/lib/appsignal/integrations/data_mapper.rb +33 -0
  81. data/lib/appsignal/integrations/delayed_job_plugin.rb +54 -0
  82. data/lib/appsignal/integrations/grape.rb +53 -0
  83. data/lib/appsignal/integrations/mongo_ruby_driver.rb +55 -0
  84. data/lib/appsignal/integrations/object.rb +35 -0
  85. data/lib/appsignal/integrations/padrino.rb +84 -0
  86. data/lib/appsignal/integrations/que.rb +43 -0
  87. data/lib/appsignal/integrations/railtie.rb +41 -0
  88. data/lib/appsignal/integrations/rake.rb +2 -0
  89. data/lib/appsignal/integrations/resque.rb +20 -0
  90. data/lib/appsignal/integrations/resque_active_job.rb +30 -0
  91. data/lib/appsignal/integrations/sinatra.rb +17 -0
  92. data/lib/appsignal/integrations/webmachine.rb +38 -0
  93. data/lib/appsignal/js_exception_transaction.rb +54 -0
  94. data/lib/appsignal/marker.rb +63 -0
  95. data/lib/appsignal/minutely.rb +42 -0
  96. data/lib/appsignal/rack/generic_instrumentation.rb +49 -0
  97. data/lib/appsignal/rack/js_exception_catcher.rb +70 -0
  98. data/lib/appsignal/rack/rails_instrumentation.rb +51 -0
  99. data/lib/appsignal/rack/sinatra_instrumentation.rb +99 -0
  100. data/lib/appsignal/rack/streaming_listener.rb +73 -0
  101. data/lib/appsignal/system.rb +81 -0
  102. data/lib/appsignal/transaction.rb +498 -0
  103. data/lib/appsignal/transmitter.rb +107 -0
  104. data/lib/appsignal/utils.rb +127 -0
  105. data/lib/appsignal/utils/params_sanitizer.rb +59 -0
  106. data/lib/appsignal/utils/query_params_sanitizer.rb +55 -0
  107. data/lib/appsignal/version.rb +3 -0
  108. data/lib/sequel/extensions/appsignal_integration.rb +3 -0
  109. data/resources/appsignal.yml.erb +39 -0
  110. data/resources/cacert.pem +3866 -0
  111. data/spec/.rubocop.yml +7 -0
  112. data/spec/lib/appsignal/auth_check_spec.rb +80 -0
  113. data/spec/lib/appsignal/capistrano2_spec.rb +224 -0
  114. data/spec/lib/appsignal/capistrano3_spec.rb +237 -0
  115. data/spec/lib/appsignal/cli/demo_spec.rb +67 -0
  116. data/spec/lib/appsignal/cli/diagnose_spec.rb +988 -0
  117. data/spec/lib/appsignal/cli/helpers_spec.rb +171 -0
  118. data/spec/lib/appsignal/cli/install_spec.rb +632 -0
  119. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +168 -0
  120. data/spec/lib/appsignal/cli_spec.rb +56 -0
  121. data/spec/lib/appsignal/config_spec.rb +637 -0
  122. data/spec/lib/appsignal/demo_spec.rb +87 -0
  123. data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +44 -0
  124. data/spec/lib/appsignal/event_formatter/active_record/instantiation_formatter_spec.rb +21 -0
  125. data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +21 -0
  126. data/spec/lib/appsignal/event_formatter/elastic_search/search_formatter_spec.rb +52 -0
  127. data/spec/lib/appsignal/event_formatter/faraday/request_formatter_spec.rb +21 -0
  128. data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +113 -0
  129. data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +112 -0
  130. data/spec/lib/appsignal/event_formatter_spec.rb +100 -0
  131. data/spec/lib/appsignal/extension/jruby_spec.rb +43 -0
  132. data/spec/lib/appsignal/extension_spec.rb +137 -0
  133. data/spec/lib/appsignal/garbage_collection_profiler_spec.rb +66 -0
  134. data/spec/lib/appsignal/hooks/action_cable_spec.rb +370 -0
  135. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +92 -0
  136. data/spec/lib/appsignal/hooks/celluloid_spec.rb +35 -0
  137. data/spec/lib/appsignal/hooks/data_mapper_spec.rb +39 -0
  138. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +358 -0
  139. data/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +44 -0
  140. data/spec/lib/appsignal/hooks/net_http_spec.rb +53 -0
  141. data/spec/lib/appsignal/hooks/passenger_spec.rb +30 -0
  142. data/spec/lib/appsignal/hooks/puma_spec.rb +80 -0
  143. data/spec/lib/appsignal/hooks/que_spec.rb +19 -0
  144. data/spec/lib/appsignal/hooks/rake_spec.rb +73 -0
  145. data/spec/lib/appsignal/hooks/redis_spec.rb +55 -0
  146. data/spec/lib/appsignal/hooks/sequel_spec.rb +46 -0
  147. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +192 -0
  148. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +419 -0
  149. data/spec/lib/appsignal/hooks/unicorn_spec.rb +52 -0
  150. data/spec/lib/appsignal/hooks/webmachine_spec.rb +35 -0
  151. data/spec/lib/appsignal/hooks_spec.rb +195 -0
  152. data/spec/lib/appsignal/integrations/data_mapper_spec.rb +65 -0
  153. data/spec/lib/appsignal/integrations/grape_spec.rb +225 -0
  154. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +127 -0
  155. data/spec/lib/appsignal/integrations/object_spec.rb +249 -0
  156. data/spec/lib/appsignal/integrations/padrino_spec.rb +323 -0
  157. data/spec/lib/appsignal/integrations/que_spec.rb +174 -0
  158. data/spec/lib/appsignal/integrations/railtie_spec.rb +129 -0
  159. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +83 -0
  160. data/spec/lib/appsignal/integrations/resque_spec.rb +92 -0
  161. data/spec/lib/appsignal/integrations/sinatra_spec.rb +73 -0
  162. data/spec/lib/appsignal/integrations/webmachine_spec.rb +69 -0
  163. data/spec/lib/appsignal/js_exception_transaction_spec.rb +128 -0
  164. data/spec/lib/appsignal/marker_spec.rb +51 -0
  165. data/spec/lib/appsignal/minutely_spec.rb +50 -0
  166. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +90 -0
  167. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +147 -0
  168. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +117 -0
  169. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +213 -0
  170. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +161 -0
  171. data/spec/lib/appsignal/system_spec.rb +131 -0
  172. data/spec/lib/appsignal/transaction_spec.rb +1146 -0
  173. data/spec/lib/appsignal/transmitter_spec.rb +152 -0
  174. data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +136 -0
  175. data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +192 -0
  176. data/spec/lib/appsignal/utils_spec.rb +150 -0
  177. data/spec/lib/appsignal_spec.rb +1049 -0
  178. data/spec/spec_helper.rb +116 -0
  179. data/spec/support/fixtures/containers/cgroups/docker +14 -0
  180. data/spec/support/fixtures/containers/cgroups/docker_systemd +8 -0
  181. data/spec/support/fixtures/containers/cgroups/lxc +10 -0
  182. data/spec/support/fixtures/containers/cgroups/no_permission +0 -0
  183. data/spec/support/fixtures/containers/cgroups/none +1 -0
  184. data/spec/support/fixtures/generated_config.yml +24 -0
  185. data/spec/support/fixtures/uploaded_file.txt +0 -0
  186. data/spec/support/helpers/api_request_helper.rb +19 -0
  187. data/spec/support/helpers/cli_helpers.rb +26 -0
  188. data/spec/support/helpers/config_helpers.rb +21 -0
  189. data/spec/support/helpers/dependency_helper.rb +73 -0
  190. data/spec/support/helpers/directory_helper.rb +27 -0
  191. data/spec/support/helpers/env_helpers.rb +33 -0
  192. data/spec/support/helpers/example_exception.rb +13 -0
  193. data/spec/support/helpers/example_standard_error.rb +13 -0
  194. data/spec/support/helpers/log_helpers.rb +22 -0
  195. data/spec/support/helpers/std_streams_helper.rb +66 -0
  196. data/spec/support/helpers/system_helpers.rb +8 -0
  197. data/spec/support/helpers/time_helpers.rb +11 -0
  198. data/spec/support/helpers/transaction_helpers.rb +37 -0
  199. data/spec/support/matchers/contains_log.rb +7 -0
  200. data/spec/support/mocks/fake_gc_profiler.rb +19 -0
  201. data/spec/support/mocks/mock_extension.rb +6 -0
  202. data/spec/support/project_fixture/config/application.rb +0 -0
  203. data/spec/support/project_fixture/config/appsignal.yml +32 -0
  204. data/spec/support/project_fixture/config/environments/development.rb +0 -0
  205. data/spec/support/project_fixture/config/environments/production.rb +0 -0
  206. data/spec/support/project_fixture/config/environments/test.rb +0 -0
  207. data/spec/support/project_fixture/log/.gitkeep +0 -0
  208. data/spec/support/rails/my_app.rb +6 -0
  209. data/spec/support/shared_examples/instrument.rb +43 -0
  210. data/spec/support/stubs/delayed_job.rb +0 -0
  211. metadata +483 -0
@@ -0,0 +1,67 @@
1
+ require "appsignal/cli"
2
+
3
+ describe Appsignal::CLI::Demo do
4
+ include CLIHelpers
5
+
6
+ let(:options) { {} }
7
+ let(:out_stream) { std_stream }
8
+ let(:output) { out_stream.read }
9
+ before(:context) { Appsignal.stop }
10
+ before do
11
+ ENV.delete("APPSIGNAL_APP_ENV")
12
+ ENV.delete("RAILS_ENV")
13
+ ENV.delete("RACK_ENV")
14
+ stub_api_request config, "auth"
15
+ end
16
+ after { Appsignal.config = nil }
17
+
18
+ def run
19
+ run_within_dir project_fixture_path
20
+ end
21
+
22
+ def run_within_dir(chdir)
23
+ Dir.chdir chdir do
24
+ capture_stdout(out_stream) { run_cli("demo", options) }
25
+ end
26
+ end
27
+
28
+ context "without configuration" do
29
+ let(:config) { Appsignal::Config.new("development", tmp_dir) }
30
+
31
+ it "returns an error" do
32
+ expect { run_within_dir tmp_dir }.to raise_error(SystemExit)
33
+
34
+ expect(output).to include("Error: Unable to start the AppSignal agent")
35
+ end
36
+ end
37
+
38
+ context "with configuration" do
39
+ let(:config) { project_fixture_config }
40
+ before do
41
+ # Ignore sleeps to speed up the test
42
+ allow(Appsignal::Demo).to receive(:sleep)
43
+ end
44
+
45
+ context "without environment" do
46
+ it "returns an error" do
47
+ expect { run_within_dir tmp_dir }.to raise_error(SystemExit)
48
+
49
+ expect(output).to include("Error: Unable to start the AppSignal agent")
50
+ end
51
+ end
52
+
53
+ context "with environment" do
54
+ let(:options) { { :environment => "development" } }
55
+
56
+ it "calls Appsignal::Demo transmitter" do
57
+ expect(Appsignal::Demo).to receive(:transmit).and_return(true)
58
+ run
59
+ end
60
+
61
+ it "outputs message" do
62
+ run
63
+ expect(output).to include("Demonstration sample data sent!")
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,988 @@
1
+ require "appsignal/cli"
2
+
3
+ describe Appsignal::CLI::Diagnose, :api_stub => true, :report => true do
4
+ include CLIHelpers
5
+
6
+ class DiagnosticsReportEndpoint
7
+ class << self
8
+ attr_reader :received_report
9
+
10
+ def clear_report!
11
+ @received_report = nil
12
+ end
13
+
14
+ def call(env)
15
+ @received_report = JSON.parse(env["rack.input"].read)["diagnose"]
16
+ [200, {}, [JSON.generate(:token => "my_support_token")]]
17
+ end
18
+ end
19
+ end
20
+
21
+ describe ".run" do
22
+ let(:out_stream) { std_stream }
23
+ let(:output) { out_stream.read }
24
+ let(:config) { project_fixture_config }
25
+ let(:cli) { described_class }
26
+ let(:options) { { :environment => config.env } }
27
+ let(:gem_path) { Bundler::CLI::Common.select_spec("appsignal").full_gem_path.strip }
28
+ let(:received_report) { DiagnosticsReportEndpoint.received_report }
29
+ let(:process_user) { Etc.getpwuid(Process.uid).name }
30
+ before(:context) { Appsignal.stop }
31
+ before do
32
+ # Clear previous reports
33
+ DiagnosticsReportEndpoint.clear_report!
34
+ if cli.instance_variable_defined? :@data
35
+ # Because this is saved on the class rather than an instance of the
36
+ # class we need to clear it like this in case a certain test doesn't
37
+ # generate a report.
38
+ cli.remove_instance_variable :@data
39
+ end
40
+
41
+ if DependencyHelper.rails_present?
42
+ allow(Rails).to receive(:root).and_return(Pathname.new(config.root_path))
43
+ end
44
+ end
45
+ around do |example|
46
+ original_stdin = $stdin
47
+ $stdin = StringIO.new
48
+ example.run
49
+ $stdin = original_stdin
50
+ end
51
+ before :api_stub => true do
52
+ stub_api_request config, "auth"
53
+ end
54
+ before :report => true do
55
+ send_diagnostics_report
56
+ stub_diagnostics_report_request.to_rack(DiagnosticsReportEndpoint)
57
+ end
58
+ before(:report => false) { dont_send_diagnostics_report }
59
+ after { Appsignal.config = nil }
60
+
61
+ def run
62
+ run_within_dir project_fixture_path
63
+ end
64
+
65
+ def run_within_dir(chdir)
66
+ prepare_input
67
+ Dir.chdir chdir do
68
+ capture_stdout(out_stream) { cli.run(options) }
69
+ end
70
+ end
71
+
72
+ def stub_diagnostics_report_request
73
+ stub_request(:post, "https://appsignal.com/diag").with(
74
+ :query => {
75
+ :api_key => config[:push_api_key],
76
+ :environment => config.env,
77
+ :gem_version => Appsignal::VERSION,
78
+ :hostname => config[:hostname],
79
+ :name => config[:name]
80
+ },
81
+ :headers => { "Content-Type" => "application/json; charset=UTF-8" }
82
+ )
83
+ end
84
+
85
+ def send_diagnostics_report
86
+ set_input "y"
87
+ end
88
+
89
+ def dont_send_diagnostics_report
90
+ set_input "n"
91
+ end
92
+
93
+ it "outputs header and support text" do
94
+ run
95
+ expect(output).to include \
96
+ "AppSignal diagnose",
97
+ "http://docs.appsignal.com/",
98
+ "support@appsignal.com"
99
+ end
100
+
101
+ describe "report" do
102
+ context "when user wants to send report" do
103
+ it "sends report" do
104
+ run
105
+ 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."
108
+ end
109
+
110
+ it "outputs the support token from the server" do
111
+ run
112
+ expect(output).to include "Your support token: my_support_token"
113
+ end
114
+
115
+ context "when server response is invalid" do
116
+ before do
117
+ stub_diagnostics_report_request
118
+ .to_return(:status => 200, :body => %({ foo: "Invalid json", a: }))
119
+ run
120
+ end
121
+
122
+ it "outputs the server response in full" do
123
+ expect(output).to include "Error: Couldn't decode server response.",
124
+ %({ foo: "Invalid json", a: })
125
+ end
126
+ end
127
+
128
+ context "when server returns an error" do
129
+ before do
130
+ stub_diagnostics_report_request
131
+ .to_return(:status => 500, :body => "report: server error")
132
+ run
133
+ end
134
+
135
+ it "outputs the server response in full" do
136
+ expect(output).to include "report: server error"
137
+ end
138
+ end
139
+ end
140
+
141
+ context "when user doesn't want to send report", :report => false do
142
+ it "does not send report" do
143
+ run
144
+ expect(output).to include "Diagnostics report",
145
+ "Send diagnostics report to AppSignal? (Y/n): ",
146
+ "Not sending diagnostics information to AppSignal."
147
+ end
148
+ end
149
+ end
150
+
151
+ describe "agent information" do
152
+ before { run }
153
+
154
+ it "outputs version numbers" do
155
+ expect(output).to include \
156
+ "Gem version: #{Appsignal::VERSION}",
157
+ "Agent version: #{Appsignal::Extension.agent_version}",
158
+ "Agent architecture: #{Appsignal::System.installed_agent_architecture}",
159
+ "Gem install path: #{gem_path}"
160
+ end
161
+
162
+ it "transmits version numbers in report" do
163
+ expect(received_report).to include(
164
+ "library" => {
165
+ "language" => "ruby",
166
+ "package_version" => Appsignal::VERSION,
167
+ "agent_version" => Appsignal::Extension.agent_version,
168
+ "agent_architecture" => Appsignal::System.installed_agent_architecture,
169
+ "package_install_path" => gem_path,
170
+ "extension_loaded" => true
171
+ }
172
+ )
173
+ end
174
+
175
+ context "with extension" do
176
+ before { run }
177
+
178
+ it "outputs extension is loaded" do
179
+ expect(output).to include "Extension loaded: true"
180
+ end
181
+
182
+ it "transmits extension_loaded: true in report" do
183
+ expect(received_report["library"]["extension_loaded"]).to eq(true)
184
+ end
185
+ end
186
+
187
+ context "without extension" do
188
+ before do
189
+ # When the extension isn't loaded the Appsignal.start operation exits
190
+ # early and doesn't load the configuration.
191
+ # Happens when the extension wasn't installed properly.
192
+ Appsignal.extension_loaded = false
193
+ run
194
+ end
195
+ after { Appsignal.extension_loaded = true }
196
+
197
+ it "outputs extension is not loaded" do
198
+ expect(output).to include "Extension loaded: false"
199
+ end
200
+
201
+ it "transmits extension_loaded: false in report" do
202
+ expect(received_report["library"]["extension_loaded"]).to eq(false)
203
+ end
204
+ end
205
+ end
206
+
207
+ describe "agent diagnostics" do
208
+ it "starts the agent in diagnose mode and outputs the report" do
209
+ 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"
216
+ end
217
+
218
+ it "adds the agent diagnostics to the report" do
219
+ run
220
+ expect(received_report["agent"]).to eq(
221
+ "extension" => {
222
+ "config" => { "valid" => { "result" => true } }
223
+ },
224
+ "agent" => {
225
+ "boot" => { "started" => { "result" => true } },
226
+ "config" => { "valid" => { "result" => true } },
227
+ "logger" => { "started" => { "result" => true } },
228
+ "lock_path" => { "created" => { "result" => true } }
229
+ }
230
+ )
231
+ end
232
+
233
+ context "when user config has active: false" do
234
+ before do
235
+ # ENV is leading so easiest to set in test to force user config with active: false
236
+ ENV["APPSIGNAL_ACTIVE"] = "false"
237
+ end
238
+
239
+ it "force starts the agent in diagnose mode and outputs a log" do
240
+ run
241
+ 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"
249
+ end
250
+ end
251
+
252
+ context "when the extention returns invalid JSON" do
253
+ before do
254
+ expect(Appsignal::Extension).to receive(:diagnose).and_return("invalid agent\njson")
255
+ run
256
+ end
257
+
258
+ it "prints a JSON parse error and prints the returned value" do
259
+ expect(output).to include \
260
+ "Agent diagnostics",
261
+ " Error while parsing agent diagnostics report:",
262
+ " Output: invalid agent\njson"
263
+ expect(output).to match(/Error:( \d+:)? unexpected token at 'invalid agent\njson'/)
264
+ end
265
+
266
+ it "adds the output to the report" do
267
+ expect(received_report["agent"]["error"])
268
+ .to match(/unexpected token at 'invalid agent\njson'/)
269
+ expect(received_report["agent"]["output"]).to eq(["invalid agent", "json"])
270
+ end
271
+ end
272
+
273
+ context "when the extension is not loaded" do
274
+ before do
275
+ DiagnosticsReportEndpoint.clear_report!
276
+ expect(Appsignal).to receive(:extension_loaded?).and_return(false)
277
+ run
278
+ end
279
+
280
+ it "prints a warning" do
281
+ expect(output).to include \
282
+ "Agent diagnostics",
283
+ " Extension is not loaded. No agent report created."
284
+ end
285
+
286
+ it "adds the output to the report" do
287
+ expect(received_report["agent"]).to be_nil
288
+ end
289
+ end
290
+
291
+ context "when the report contains an error" do
292
+ let(:agent_report) do
293
+ { "error" => "fatal error" }
294
+ end
295
+ before do
296
+ expect(Appsignal::Extension).to receive(:diagnose).and_return(JSON.generate(agent_report))
297
+ run
298
+ end
299
+
300
+ it "prints an error for the entire report" do
301
+ expect(output).to include "Agent diagnostics\n Error: fatal error"
302
+ end
303
+
304
+ it "adds the error to the report" do
305
+ expect(received_report["agent"]).to eq(agent_report)
306
+ end
307
+ end
308
+
309
+ context "when the report is incomplete (agent failed to start)" do
310
+ let(:agent_report) do
311
+ {
312
+ "extension" => {
313
+ "config" => { "valid" => { "result" => false } }
314
+ }
315
+ # missing agent section
316
+ }
317
+ end
318
+ before do
319
+ expect(Appsignal::Extension).to receive(:diagnose).and_return(JSON.generate(agent_report))
320
+ run
321
+ end
322
+
323
+ it "prints the tests, but shows a dash `-` for missed results" do
324
+ expect(output).to include \
325
+ "Agent diagnostics",
326
+ " Extension config: invalid",
327
+ " Agent started: -",
328
+ " Agent config: -",
329
+ " Agent logger: -",
330
+ " Agent lock path: -"
331
+ end
332
+
333
+ it "adds the output to the report" do
334
+ expect(received_report["agent"]).to eq(agent_report)
335
+ end
336
+ end
337
+
338
+ context "when a test contains an error" do
339
+ let(:agent_report) do
340
+ {
341
+ "extension" => {
342
+ "config" => { "valid" => { "result" => true } }
343
+ },
344
+ "agent" => {
345
+ "boot" => {
346
+ "started" => { "result" => false, "error" => "some-error" }
347
+ }
348
+ }
349
+ }
350
+ end
351
+ before do
352
+ expect(Appsignal::Extension).to receive(:diagnose).and_return(JSON.generate(agent_report))
353
+ run
354
+ end
355
+
356
+ it "prints the error and output" do
357
+ expect(output).to include \
358
+ "Agent diagnostics",
359
+ " Extension config: valid",
360
+ " Agent started: not started\n Error: some-error"
361
+ end
362
+
363
+ it "adds the agent report to the diagnostics report" do
364
+ expect(received_report["agent"]).to eq(agent_report)
365
+ end
366
+ end
367
+
368
+ context "when a test contains command output" do
369
+ let(:agent_report) do
370
+ {
371
+ "extension" => {
372
+ "config" => { "valid" => { "result" => true } }
373
+ },
374
+ "agent" => {
375
+ "config" => { "valid" => { "result" => false, "output" => "some output" } }
376
+ }
377
+ }
378
+ end
379
+
380
+ it "prints the command output" do
381
+ expect(Appsignal::Extension).to receive(:diagnose).and_return(JSON.generate(agent_report))
382
+ run
383
+ expect(output).to include \
384
+ "Agent diagnostics",
385
+ " Extension config: valid",
386
+ " Agent config: invalid\n Output: some output"
387
+ end
388
+ end
389
+ end
390
+
391
+ describe "host information" do
392
+ let(:rbconfig) { RbConfig::CONFIG }
393
+ let(:language_version) { "#{rbconfig["ruby_version"]}-p#{rbconfig["PATCHLEVEL"]}" }
394
+
395
+ it "outputs host information" do
396
+ run
397
+ expect(output).to include \
398
+ "Host information",
399
+ "Architecture: #{rbconfig["host_cpu"]}",
400
+ "Operating System: #{rbconfig["host_os"]}",
401
+ "Ruby version: #{language_version}"
402
+ end
403
+
404
+ context "when on Microsoft Windows" do
405
+ before do
406
+ expect(RbConfig::CONFIG).to receive(:[]).with("host_os").and_return("mingw32")
407
+ expect(RbConfig::CONFIG).to receive(:[]).at_least(:once).and_call_original
408
+ expect(Gem).to receive(:win_platform?).and_return(true)
409
+ run
410
+ end
411
+
412
+ it "adds the arch to the report" do
413
+ expect(received_report["host"]["os"]).to eq("mingw32")
414
+ end
415
+
416
+ it "prints warning that Microsoft Windows is not supported" do
417
+ expect(output).to match(/Operating System: .+ \(Microsoft Windows is not supported\.\)/)
418
+ end
419
+ end
420
+
421
+ it "transmits host information in report" do
422
+ run
423
+ host_report = received_report["host"]
424
+ host_report.delete("running_in_container") # Tested elsewhere
425
+ expect(host_report).to eq(
426
+ "architecture" => rbconfig["host_cpu"],
427
+ "os" => rbconfig["host_os"],
428
+ "language_version" => language_version,
429
+ "heroku" => false,
430
+ "root" => false
431
+ )
432
+ end
433
+
434
+ describe "root user detection" do
435
+ context "when not root user" do
436
+ it "outputs false" do
437
+ run
438
+ expect(output).to include "root user: false"
439
+ end
440
+
441
+ it "transmits root: false in report" do
442
+ run
443
+ expect(received_report["host"]["root"]).to eq(false)
444
+ end
445
+ end
446
+
447
+ context "when root user" do
448
+ before do
449
+ allow(Process).to receive(:uid).and_return(0)
450
+ run
451
+ end
452
+
453
+ it "outputs true, with warning" do
454
+ expect(output).to include "root user: true (not recommended)"
455
+ end
456
+
457
+ it "transmits root: true in report" do
458
+ expect(received_report["host"]["root"]).to eq(true)
459
+ end
460
+ end
461
+ end
462
+
463
+ describe "Heroku detection" do
464
+ context "when not on Heroku" do
465
+ before { run }
466
+
467
+ it "does not output Heroku detection" do
468
+ expect(output).to_not include("Heroku:")
469
+ end
470
+
471
+ it "transmits heroku: false in report" do
472
+ expect(received_report["host"]["heroku"]).to eq(false)
473
+ end
474
+ end
475
+
476
+ context "when on Heroku" do
477
+ before { recognize_as_heroku { run } }
478
+
479
+ it "outputs Heroku detection" do
480
+ expect(output).to include("Heroku: true")
481
+ end
482
+
483
+ it "transmits heroku: true in report" do
484
+ expect(received_report["host"]["heroku"]).to eq(true)
485
+ end
486
+ end
487
+ end
488
+
489
+ describe "container detection" do
490
+ context "when not in container" do
491
+ before do
492
+ allow(Appsignal::Extension).to receive(:running_in_container?).and_return(false)
493
+ run
494
+ end
495
+
496
+ it "outputs: false" do
497
+ expect(output).to include("Running in container: false")
498
+ end
499
+
500
+ it "transmits running_in_container: false in report" do
501
+ expect(received_report["host"]["running_in_container"]).to eq(false)
502
+ end
503
+ end
504
+
505
+ context "when in container" do
506
+ before do
507
+ allow(Appsignal::Extension).to receive(:running_in_container?).and_return(true)
508
+ run
509
+ end
510
+
511
+ it "outputs: true" do
512
+ expect(output).to include("Running in container: true")
513
+ end
514
+
515
+ it "transmits running_in_container: true in report" do
516
+ expect(received_report["host"]["running_in_container"]).to eq(true)
517
+ end
518
+ end
519
+ end
520
+ end
521
+
522
+ describe "configuration" do
523
+ context "without environment" do
524
+ let(:config) { project_fixture_config(nil) }
525
+ let(:options) { {} }
526
+ before do
527
+ ENV.delete("RAILS_ENV") # From spec_helper
528
+ ENV.delete("RACK_ENV")
529
+ run_within_dir tmp_dir
530
+ end
531
+
532
+ it "outputs a warning that no config is loaded" do
533
+ expect(output).to include \
534
+ "Environment: \n Warning: No environment set, no config loaded!",
535
+ " appsignal diagnose --environment=production"
536
+ end
537
+
538
+ it "outputs config defaults" do
539
+ expect(output).to include("Configuration")
540
+ Appsignal::Config::DEFAULT_CONFIG.each do |key, value|
541
+ expect(output).to include("#{key}: #{value}")
542
+ end
543
+ end
544
+ end
545
+
546
+ context "with configured environment" do
547
+ before { run }
548
+
549
+ it "outputs environment" do
550
+ expect(output).to include("Environment: production")
551
+ end
552
+
553
+ it "outputs configuration" do
554
+ expect(output).to include("Configuration")
555
+ Appsignal.config.config_hash.each do |key, value|
556
+ expect(output).to include("#{key}: #{value}")
557
+ end
558
+ end
559
+ end
560
+
561
+ context "with unconfigured environment" do
562
+ let(:config) { project_fixture_config("foobar") }
563
+ before { run_within_dir tmp_dir }
564
+
565
+ it "outputs environment" do
566
+ expect(output).to include("Environment: foobar")
567
+ end
568
+
569
+ it "outputs config defaults" do
570
+ expect(output).to include("Configuration")
571
+ Appsignal::Config::DEFAULT_CONFIG.each do |key, value|
572
+ expect(output).to include("#{key}: #{value}")
573
+ end
574
+ end
575
+ end
576
+ end
577
+
578
+ describe "API key validation", :api_stub => false do
579
+ context "with valid key" do
580
+ before do
581
+ stub_api_request(config, "auth").to_return(:status => 200)
582
+ run
583
+ end
584
+
585
+ it "outputs valid" do
586
+ expect(output).to include "Validation",
587
+ "Validating Push API key: valid"
588
+ end
589
+
590
+ it "transmits validation in report" do
591
+ expect(received_report).to include(
592
+ "validation" => {
593
+ "push_api_key" => "valid"
594
+ }
595
+ )
596
+ end
597
+ end
598
+
599
+ context "with invalid key" do
600
+ before do
601
+ stub_api_request(config, "auth").to_return(:status => 401)
602
+ run
603
+ end
604
+
605
+ it "outputs invalid" do
606
+ expect(output).to include "Validation",
607
+ "Validating Push API key: invalid"
608
+ end
609
+
610
+ it "transmits validation in report" do
611
+ expect(received_report).to include(
612
+ "validation" => {
613
+ "push_api_key" => "invalid"
614
+ }
615
+ )
616
+ end
617
+ end
618
+
619
+ context "with invalid key" do
620
+ before do
621
+ stub_api_request(config, "auth").to_return(:status => 500)
622
+ run
623
+ end
624
+
625
+ it "outputs failure with status code" do
626
+ expect(output).to include "Validation",
627
+ "Validating Push API key: Failed with status 500\n" +
628
+ %("Could not confirm authorization: 500")
629
+ end
630
+
631
+ it "transmits validation in report" do
632
+ expect(received_report).to include(
633
+ "validation" => {
634
+ "push_api_key" => %(Failed with status 500\n\"Could not confirm authorization: 500")
635
+ }
636
+ )
637
+ end
638
+ end
639
+ end
640
+
641
+ describe "paths" do
642
+ let(:system_tmp_dir) { Appsignal::Config.system_tmp_dir }
643
+ before do
644
+ FileUtils.mkdir_p(root_path)
645
+ FileUtils.mkdir_p(system_tmp_dir)
646
+ end
647
+ after { FileUtils.rm_rf([root_path, system_tmp_dir]) }
648
+
649
+ describe "report" do
650
+ let(:root_path) { tmp_dir }
651
+
652
+ 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])
656
+ end
657
+ end
658
+
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
666
+ end
667
+
668
+ it "outputs unconfigured directory" do
669
+ expect(output).to include %(log_file_path: ""\n Configured?: false)
670
+ end
671
+
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
+ )
679
+ end
680
+ end
681
+
682
+ context "when a directory does not exist" do
683
+ let(:root_path) { tmp_dir }
684
+ let(:execution_path) { File.join(tmp_dir, "not_existing_dir") }
685
+ let(:config) { Appsignal::Config.new(execution_path, "production") }
686
+ before do
687
+ allow(Dir).to receive(:pwd).and_return(execution_path)
688
+ run_within_dir tmp_dir
689
+ end
690
+
691
+ it "outputs not existing path" do
692
+ expect(output).to include %(root_path: "#{execution_path}"\n Exists?: false)
693
+ end
694
+
695
+ it "transmits path data in report" do
696
+ expect(received_report["paths"]["root_path"]).to eq(
697
+ "path" => execution_path,
698
+ "configured" => true,
699
+ "exists" => false,
700
+ "writable" => false
701
+ )
702
+ end
703
+ end
704
+
705
+ describe "ownership" do
706
+ let(:config) { Appsignal::Config.new(root_path, "production") }
707
+
708
+ context "when a directory is owned by the current user" do
709
+ let(:root_path) { File.join(tmp_dir, "owned_path") }
710
+ before { run_within_dir root_path }
711
+
712
+ it "outputs ownership" do
713
+ expect(output).to include \
714
+ %(root_path: "#{root_path}"\n Writable?: true\n ) \
715
+ "Ownership?: true (file: #{process_user}:#{Process.uid}, "\
716
+ "process: #{process_user}:#{Process.uid})"
717
+ end
718
+
719
+ it "transmits path data in report" do
720
+ expect(received_report["paths"]["root_path"]).to eq(
721
+ "path" => root_path,
722
+ "configured" => true,
723
+ "exists" => true,
724
+ "writable" => true,
725
+ "ownership" => { "uid" => Process.uid, "user" => process_user }
726
+ )
727
+ end
728
+ end
729
+
730
+ context "when a directory is not owned by the current user" do
731
+ let(:root_path) { File.join(tmp_dir, "not_owned_path") }
732
+ before do
733
+ stat = File.stat(root_path)
734
+ allow(stat).to receive(:uid).and_return(0)
735
+ allow(File).to receive(:stat).and_return(stat)
736
+ run_within_dir root_path
737
+ end
738
+
739
+ it "outputs no ownership" do
740
+ expect(output).to include \
741
+ %(root_path: "#{root_path}"\n Writable?: true\n ) \
742
+ "Ownership?: false (file: root:0, process: #{process_user}:#{Process.uid})"
743
+ end
744
+ end
745
+ 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
+ end
908
+
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 }
918
+
919
+ 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
+ ]
926
+ end
927
+ before do
928
+ File.open log_path, "a" do |f|
929
+ log_content.each do |line|
930
+ f.puts line
931
+ end
932
+ end
933
+ run
934
+ end
935
+
936
+ it "outputs install.log" do
937
+ expect(output).to include(%(Path: "#{log_path}"))
938
+ expect(output).to include(*log_content)
939
+ end
940
+
941
+ it "transmits log data in report" do
942
+ expect(received_report["logs"][File.join("ext", log_file)]).to eq(
943
+ "path" => log_path,
944
+ "exists" => true,
945
+ "content" => log_content
946
+ )
947
+ end
948
+
949
+ after { FileUtils.rm_rf(gem_path) }
950
+ end
951
+
952
+ context "when file does not exist" do
953
+ let(:gem_path) { File.join(tmp_dir, "gem_without_log_files") }
954
+ before { run }
955
+
956
+ it "outputs install.log" do
957
+ expect(output).to include %(Path: "#{log_path}"\n File not found.)
958
+ end
959
+
960
+ it "transmits log data in report" do
961
+ expect(received_report["logs"][File.join("ext", log_file)]).to eq(
962
+ "path" => log_path,
963
+ "exists" => false
964
+ )
965
+ end
966
+ end
967
+ end
968
+
969
+ describe "install.log" do
970
+ it_behaves_like "ext log file", "install.log"
971
+
972
+ it "outputs header" do
973
+ run
974
+ expect(output).to include("Extension install log")
975
+ end
976
+ end
977
+
978
+ describe "mkmf.log" do
979
+ it_behaves_like "ext log file", "mkmf.log"
980
+
981
+ it "outputs header" do
982
+ run
983
+ expect(output).to include("Makefile install log")
984
+ end
985
+ end
986
+ end
987
+ end
988
+ end