appsignal 3.9.2-java → 3.10.0-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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +3138 -0
  3. data/.rubocop.yml +28 -20
  4. data/.rubocop_todo.yml +7 -33
  5. data/CHANGELOG.md +130 -0
  6. data/README.md +0 -1
  7. data/Rakefile +80 -65
  8. data/appsignal.gemspec +1 -1
  9. data/build_matrix.yml +112 -184
  10. data/ext/base.rb +1 -1
  11. data/gemfiles/hanami-2.1.gemfile +7 -0
  12. data/gemfiles/webmachine1.gemfile +5 -4
  13. data/lib/appsignal/cli/diagnose.rb +1 -1
  14. data/lib/appsignal/config.rb +5 -1
  15. data/lib/appsignal/demo.rb +0 -1
  16. data/lib/appsignal/environment.rb +11 -2
  17. data/lib/appsignal/extension/jruby.rb +1 -1
  18. data/lib/appsignal/helpers/instrumentation.rb +164 -2
  19. data/lib/appsignal/hooks/active_job.rb +1 -6
  20. data/lib/appsignal/integrations/grape.rb +19 -47
  21. data/lib/appsignal/integrations/hanami.rb +8 -7
  22. data/lib/appsignal/integrations/padrino.rb +51 -52
  23. data/lib/appsignal/integrations/railtie.rb +0 -3
  24. data/lib/appsignal/integrations/rake.rb +46 -12
  25. data/lib/appsignal/integrations/sidekiq.rb +1 -11
  26. data/lib/appsignal/integrations/sinatra.rb +0 -1
  27. data/lib/appsignal/integrations/webmachine.rb +15 -9
  28. data/lib/appsignal/probes/gvl.rb +24 -2
  29. data/lib/appsignal/probes/sidekiq.rb +1 -1
  30. data/lib/appsignal/probes.rb +1 -1
  31. data/lib/appsignal/rack/abstract_middleware.rb +104 -33
  32. data/lib/appsignal/rack/body_wrapper.rb +143 -0
  33. data/lib/appsignal/rack/event_handler.rb +12 -3
  34. data/lib/appsignal/rack/generic_instrumentation.rb +5 -4
  35. data/lib/appsignal/rack/grape_middleware.rb +40 -0
  36. data/lib/appsignal/rack/hanami_middleware.rb +2 -12
  37. data/lib/appsignal/rack/instrumentation_middleware.rb +62 -0
  38. data/lib/appsignal/rack/rails_instrumentation.rb +14 -57
  39. data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -3
  40. data/lib/appsignal/rack/streaming_listener.rb +13 -59
  41. data/lib/appsignal/rack.rb +31 -0
  42. data/lib/appsignal/transaction.rb +50 -8
  43. data/lib/appsignal/utils/integration_memory_logger.rb +78 -0
  44. data/lib/appsignal/utils.rb +1 -0
  45. data/lib/appsignal/version.rb +1 -1
  46. data/lib/appsignal.rb +36 -33
  47. data/spec/.rubocop.yml +1 -1
  48. data/spec/lib/appsignal/cli/diagnose_spec.rb +1 -1
  49. data/spec/lib/appsignal/cli/install_spec.rb +3 -3
  50. data/spec/lib/appsignal/config_spec.rb +8 -5
  51. data/spec/lib/appsignal/demo_spec.rb +38 -41
  52. data/spec/lib/appsignal/hooks/action_cable_spec.rb +86 -167
  53. data/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb +8 -20
  54. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +38 -84
  55. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +16 -37
  56. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +4 -4
  57. data/spec/lib/appsignal/hooks/activejob_spec.rb +111 -200
  58. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +54 -91
  59. data/spec/lib/appsignal/hooks/dry_monitor_spec.rb +14 -32
  60. data/spec/lib/appsignal/hooks/excon_spec.rb +8 -12
  61. data/spec/lib/appsignal/hooks/net_http_spec.rb +7 -42
  62. data/spec/lib/appsignal/hooks/rake_spec.rb +107 -34
  63. data/spec/lib/appsignal/hooks/redis_client_spec.rb +18 -30
  64. data/spec/lib/appsignal/hooks/redis_spec.rb +10 -16
  65. data/spec/lib/appsignal/hooks/resque_spec.rb +42 -62
  66. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +33 -74
  67. data/spec/lib/appsignal/integrations/hanami_spec.rb +79 -21
  68. data/spec/lib/appsignal/integrations/http_spec.rb +12 -20
  69. data/spec/lib/appsignal/integrations/net_http_spec.rb +33 -0
  70. data/spec/lib/appsignal/integrations/object_spec.rb +29 -36
  71. data/spec/lib/appsignal/integrations/padrino_spec.rb +190 -163
  72. data/spec/lib/appsignal/integrations/que_spec.rb +43 -70
  73. data/spec/lib/appsignal/integrations/railtie_spec.rb +26 -67
  74. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +86 -160
  75. data/spec/lib/appsignal/integrations/sinatra_spec.rb +10 -3
  76. data/spec/lib/appsignal/integrations/webmachine_spec.rb +77 -40
  77. data/spec/lib/appsignal/probes/gvl_spec.rb +80 -3
  78. data/spec/lib/appsignal/probes_spec.rb +7 -4
  79. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +302 -105
  80. data/spec/lib/appsignal/rack/body_wrapper_spec.rb +263 -0
  81. data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -78
  82. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +70 -27
  83. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +234 -0
  84. data/spec/lib/appsignal/rack/hanami_middleware_spec.rb +2 -16
  85. data/spec/lib/appsignal/rack/instrumentation_middleware_spec.rb +38 -0
  86. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +67 -131
  87. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +36 -44
  88. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +44 -139
  89. data/spec/lib/appsignal/transaction_spec.rb +239 -94
  90. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +163 -0
  91. data/spec/lib/appsignal_spec.rb +556 -344
  92. data/spec/support/helpers/dependency_helper.rb +6 -1
  93. data/spec/support/helpers/std_streams_helper.rb +1 -1
  94. data/spec/support/helpers/transaction_helpers.rb +8 -0
  95. data/spec/support/matchers/transaction.rb +185 -0
  96. data/spec/support/mocks/dummy_app.rb +20 -0
  97. data/spec/support/shared_examples/instrument.rb +17 -12
  98. data/spec/support/testing.rb +18 -9
  99. metadata +20 -11
  100. data/.semaphore/semaphore.yml +0 -2347
  101. data/script/lint_git +0 -22
  102. data/spec/lib/appsignal/integrations/grape_spec.rb +0 -239
  103. data/spec/support/matchers/be_completed.rb +0 -5
  104. data/support/check_versions +0 -22
  105. /data/gemfiles/{hanami.gemfile → hanami-2.0.gemfile} +0 -0
@@ -0,0 +1,263 @@
1
+ describe Appsignal::Rack::BodyWrapper do
2
+ let(:transaction) { http_request_transaction }
3
+ before do
4
+ start_agent
5
+ set_current_transaction(transaction)
6
+ end
7
+
8
+ describe "with a body that supports all possible features" do
9
+ it "reduces the supported methods to just each()" do
10
+ # which is the safest thing to do, since the body is likely broken
11
+ fake_body = double(
12
+ :each => nil,
13
+ :call => nil,
14
+ :to_ary => [],
15
+ :to_path => "/tmp/foo.bin",
16
+ :close => nil
17
+ )
18
+
19
+ wrapped = described_class.wrap(fake_body, transaction)
20
+ expect(wrapped).to respond_to(:each)
21
+ expect(wrapped).to_not respond_to(:to_ary)
22
+ expect(wrapped).to_not respond_to(:call)
23
+ expect(wrapped).to respond_to(:close)
24
+ end
25
+ end
26
+
27
+ describe "with a body only supporting each()" do
28
+ it "wraps with appropriate class" do
29
+ fake_body = double(:each => nil)
30
+
31
+ wrapped = described_class.wrap(fake_body, transaction)
32
+ expect(wrapped).to respond_to(:each)
33
+ expect(wrapped).to_not respond_to(:to_ary)
34
+ expect(wrapped).to_not respond_to(:call)
35
+ expect(wrapped).to respond_to(:close)
36
+ end
37
+
38
+ it "reads out the body in full using each" do
39
+ fake_body = double
40
+ expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
41
+
42
+ wrapped = described_class.wrap(fake_body, transaction)
43
+ expect { |b| wrapped.each(&b) }.to yield_successive_args("a", "b", "c")
44
+
45
+ expect(transaction).to include_event(
46
+ "name" => "process_response_body.rack",
47
+ "title" => "Process Rack response body (#each)"
48
+ )
49
+ end
50
+
51
+ it "returns an Enumerator if each() gets called without a block" do
52
+ fake_body = double
53
+ expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
54
+
55
+ wrapped = described_class.wrap(fake_body, transaction)
56
+ enum = wrapped.each
57
+ expect(enum).to be_kind_of(Enumerator)
58
+ expect { |b| enum.each(&b) }.to yield_successive_args("a", "b", "c")
59
+
60
+ expect(transaction).to_not include_event("name" => "process_response_body.rack")
61
+ end
62
+
63
+ it "sets the exception raised inside each() on the transaction" do
64
+ fake_body = double
65
+ expect(fake_body).to receive(:each).once.and_raise(ExampleException, "error message")
66
+
67
+ wrapped = described_class.wrap(fake_body, transaction)
68
+ expect do
69
+ expect { |b| wrapped.each(&b) }.to yield_control
70
+ end.to raise_error(ExampleException, "error message")
71
+
72
+ expect(transaction).to have_error("ExampleException", "error message")
73
+ end
74
+
75
+ it "closes the body and tracks an instrumentation event when it gets closed" do
76
+ fake_body = double(:close => nil)
77
+ expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
78
+
79
+ wrapped = described_class.wrap(fake_body, transaction)
80
+ expect { |b| wrapped.each(&b) }.to yield_successive_args("a", "b", "c")
81
+ wrapped.close
82
+
83
+ expect(transaction).to include_event("name" => "close_response_body.rack")
84
+ end
85
+ end
86
+
87
+ describe "with a body supporting both each() and call" do
88
+ it "wraps with the wrapper that conceals call() and exposes each" do
89
+ fake_body = double
90
+ allow(fake_body).to receive(:each)
91
+ allow(fake_body).to receive(:call)
92
+
93
+ wrapped = described_class.wrap(fake_body, transaction)
94
+ expect(wrapped).to respond_to(:each)
95
+ expect(wrapped).to_not respond_to(:to_ary)
96
+ expect(wrapped).to_not respond_to(:call)
97
+ expect(wrapped).to_not respond_to(:to_path)
98
+ expect(wrapped).to respond_to(:close)
99
+ end
100
+ end
101
+
102
+ describe "with a body supporting both to_ary and each" do
103
+ let(:fake_body) { double(:each => nil, :to_ary => []) }
104
+
105
+ it "wraps with appropriate class" do
106
+ wrapped = described_class.wrap(fake_body, transaction)
107
+ expect(wrapped).to respond_to(:each)
108
+ expect(wrapped).to respond_to(:to_ary)
109
+ expect(wrapped).to_not respond_to(:call)
110
+ expect(wrapped).to_not respond_to(:to_path)
111
+ expect(wrapped).to respond_to(:close)
112
+ end
113
+
114
+ it "reads out the body in full using each" do
115
+ expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
116
+
117
+ wrapped = described_class.wrap(fake_body, transaction)
118
+ expect { |b| wrapped.each(&b) }.to yield_successive_args("a", "b", "c")
119
+
120
+ expect(transaction).to include_event(
121
+ "name" => "process_response_body.rack",
122
+ "title" => "Process Rack response body (#each)"
123
+ )
124
+ end
125
+
126
+ it "sets the exception raised inside each() into the Appsignal transaction" do
127
+ expect(fake_body).to receive(:each).once.and_raise(ExampleException, "error message")
128
+
129
+ wrapped = described_class.wrap(fake_body, transaction)
130
+ expect do
131
+ expect { |b| wrapped.each(&b) }.to yield_control
132
+ end.to raise_error(ExampleException, "error message")
133
+
134
+ expect(transaction).to have_error("ExampleException", "error message")
135
+ end
136
+
137
+ it "reads out the body in full using to_ary" do
138
+ expect(fake_body).to receive(:to_ary).and_return(["one", "two", "three"])
139
+
140
+ wrapped = described_class.wrap(fake_body, transaction)
141
+ expect(wrapped.to_ary).to eq(["one", "two", "three"])
142
+
143
+ expect(transaction).to include_event(
144
+ "name" => "process_response_body.rack",
145
+ "title" => "Process Rack response body (#to_ary)"
146
+ )
147
+ end
148
+
149
+ it "sends the exception raised inside to_ary() into the Appsignal and closes transaction" do
150
+ fake_body = double
151
+ allow(fake_body).to receive(:each)
152
+ expect(fake_body).to receive(:to_ary).once.and_raise(ExampleException, "error message")
153
+ expect(fake_body).to_not receive(:close) # Per spec we expect the body has closed itself
154
+
155
+ wrapped = described_class.wrap(fake_body, transaction)
156
+ expect do
157
+ wrapped.to_ary
158
+ end.to raise_error(ExampleException, "error message")
159
+
160
+ expect(transaction).to have_error("ExampleException", "error message")
161
+ end
162
+ end
163
+
164
+ describe "with a body supporting both to_path and each" do
165
+ let(:fake_body) { double(:each => nil, :to_path => nil) }
166
+
167
+ it "wraps with appropriate class" do
168
+ wrapped = described_class.wrap(fake_body, transaction)
169
+ expect(wrapped).to respond_to(:each)
170
+ expect(wrapped).to_not respond_to(:to_ary)
171
+ expect(wrapped).to_not respond_to(:call)
172
+ expect(wrapped).to respond_to(:to_path)
173
+ expect(wrapped).to respond_to(:close)
174
+ end
175
+
176
+ it "reads out the body in full using each()" do
177
+ expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
178
+
179
+ wrapped = described_class.wrap(fake_body, transaction)
180
+ expect { |b| wrapped.each(&b) }.to yield_successive_args("a", "b", "c")
181
+
182
+ expect(transaction).to include_event(
183
+ "name" => "process_response_body.rack",
184
+ "title" => "Process Rack response body (#each)"
185
+ )
186
+ end
187
+
188
+ it "sets the exception raised inside each() into the Appsignal transaction" do
189
+ expect(fake_body).to receive(:each).once.and_raise(ExampleException, "error message")
190
+
191
+ wrapped = described_class.wrap(fake_body, transaction)
192
+ expect do
193
+ expect { |b| wrapped.each(&b) }.to yield_control
194
+ end.to raise_error(ExampleException, "error message")
195
+
196
+ expect(transaction).to have_error("ExampleException", "error message")
197
+ end
198
+
199
+ it "sets the exception raised inside to_path() into the Appsignal transaction" do
200
+ allow(fake_body).to receive(:to_path).once.and_raise(ExampleException, "error message")
201
+
202
+ wrapped = described_class.wrap(fake_body, transaction)
203
+ expect do
204
+ wrapped.to_path
205
+ end.to raise_error(ExampleException, "error message")
206
+
207
+ expect(transaction).to have_error("ExampleException", "error message")
208
+ end
209
+
210
+ it "exposes to_path to the sender" do
211
+ allow(fake_body).to receive(:to_path).and_return("/tmp/file.bin")
212
+
213
+ wrapped = described_class.wrap(fake_body, transaction)
214
+ expect(wrapped.to_path).to eq("/tmp/file.bin")
215
+
216
+ expect(transaction).to include_event(
217
+ "name" => "process_response_body.rack",
218
+ "title" => "Process Rack response body (#to_path)"
219
+ )
220
+ end
221
+ end
222
+
223
+ describe "with a body only supporting call()" do
224
+ let(:fake_body) { double(:call => nil) }
225
+
226
+ it "wraps with appropriate class" do
227
+ wrapped = described_class.wrap(fake_body, transaction)
228
+ expect(wrapped).to_not respond_to(:each)
229
+ expect(wrapped).to_not respond_to(:to_ary)
230
+ expect(wrapped).to respond_to(:call)
231
+ expect(wrapped).to_not respond_to(:to_path)
232
+ expect(wrapped).to respond_to(:close)
233
+ end
234
+
235
+ it "passes the stream into the call() of the body" do
236
+ fake_rack_stream = double("stream")
237
+ expect(fake_body).to receive(:call).with(fake_rack_stream)
238
+
239
+ wrapped = described_class.wrap(fake_body, transaction)
240
+ wrapped.call(fake_rack_stream)
241
+
242
+ expect(transaction).to include_event(
243
+ "name" => "process_response_body.rack",
244
+ "title" => "Process Rack response body (#call)"
245
+ )
246
+ end
247
+
248
+ it "sets the exception raised inside call() into the Appsignal transaction" do
249
+ fake_rack_stream = double
250
+ allow(fake_body).to receive(:call)
251
+ .with(fake_rack_stream)
252
+ .and_raise(ExampleException, "error message")
253
+
254
+ wrapped = described_class.wrap(fake_body, transaction)
255
+
256
+ expect do
257
+ wrapped.call(fake_rack_stream)
258
+ end.to raise_error(ExampleException, "error message")
259
+
260
+ expect(transaction).to have_error("ExampleException", "error message")
261
+ end
262
+ end
263
+ end
@@ -22,17 +22,19 @@ describe Appsignal::Rack::EventHandler do
22
22
  event_handler_instance.on_start(request, response)
23
23
  end
24
24
 
25
+ def on_error(error)
26
+ event_handler_instance.on_error(request, response, error)
27
+ end
28
+
25
29
  describe "#on_start" do
26
30
  it "creates a new transaction" do
27
31
  expect { on_start }.to change { created_transactions.length }.by(1)
28
32
 
29
33
  transaction = last_transaction
30
- expect(transaction.to_h).to include(
31
- "id" => kind_of(String),
32
- "namespace" => Appsignal::Transaction::HTTP_REQUEST
33
- )
34
+ expect(transaction).to have_id
35
+ expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
34
36
 
35
- expect(Appsignal::Transaction.current).to eq(last_transaction)
37
+ expect(Appsignal::Transaction.current).to eq(transaction)
36
38
  end
37
39
 
38
40
  context "when the handler is nested in another EventHandler" do
@@ -119,21 +121,11 @@ describe Appsignal::Rack::EventHandler do
119
121
  end
120
122
 
121
123
  describe "#on_error" do
122
- def on_error(error)
123
- event_handler_instance.on_error(request, response, error)
124
- end
125
-
126
124
  it "reports the error" do
127
125
  on_start
128
126
  on_error(ExampleStandardError.new("the error"))
129
127
 
130
- expect(last_transaction.to_h).to include(
131
- "error" => {
132
- "name" => "ExampleStandardError",
133
- "message" => "the error",
134
- "backtrace" => kind_of(String)
135
- }
136
- )
128
+ expect(last_transaction).to have_error("ExampleStandardError", "the error")
137
129
  end
138
130
 
139
131
  context "when the handler is nested in another EventHandler" do
@@ -141,7 +133,7 @@ describe Appsignal::Rack::EventHandler do
141
133
  on_start
142
134
  described_class.new.on_error(request, response, ExampleStandardError.new("the error"))
143
135
 
144
- expect(last_transaction.to_h).to include("error" => nil)
136
+ expect(last_transaction).to_not have_error
145
137
  end
146
138
  end
147
139
 
@@ -174,11 +166,9 @@ describe Appsignal::Rack::EventHandler do
174
166
 
175
167
  on_finish
176
168
 
177
- expect(last_transaction.to_h).to include(
178
- "action" => nil,
179
- "sample_data" => {},
180
- "events" => []
181
- )
169
+ expect(last_transaction).to_not have_action
170
+ expect(last_transaction).to_not include_events
171
+ expect(last_transaction).to include("sample_data" => {})
182
172
  expect(last_transaction).to_not be_completed
183
173
  end
184
174
 
@@ -186,18 +176,12 @@ describe Appsignal::Rack::EventHandler do
186
176
  on_start
187
177
  on_finish
188
178
 
189
- expect(last_transaction.to_h).to include(
190
- # The action is not set on purpose, as we can't set a normalized route
191
- # It requires the app to set an action name
192
- "action" => nil,
193
- "sample_data" => hash_including(
194
- "environment" => {
195
- "REQUEST_METHOD" => "GET",
196
- "PATH_INFO" => "/path"
197
- }
198
- )
179
+ expect(last_transaction).to_not have_action
180
+ expect(last_transaction).to include_environment(
181
+ "REQUEST_METHOD" => "GET",
182
+ "PATH_INFO" => "/path"
199
183
  )
200
- expect(last_transaction.ext.queue_start).to eq(queue_start_time)
184
+ expect(last_transaction).to have_queue_start(queue_start_time)
201
185
  expect(last_transaction).to be_completed
202
186
  end
203
187
 
@@ -206,18 +190,14 @@ describe Appsignal::Rack::EventHandler do
206
190
  on_start
207
191
  on_finish(request, nil)
208
192
 
209
- expect(last_transaction.to_h).to include(
210
- # The action is not set on purpose, as we can't set a normalized route
211
- # It requires the app to set an action name
212
- "action" => nil,
213
- "sample_data" => hash_including(
214
- "environment" => {
215
- "REQUEST_METHOD" => "GET",
216
- "PATH_INFO" => "/path"
217
- }
218
- )
193
+ # The action is not set on purpose, as we can't set a normalized route
194
+ # It requires the app to set an action name
195
+ expect(last_transaction).to_not have_action
196
+ expect(last_transaction).to include_environment(
197
+ "REQUEST_METHOD" => "GET",
198
+ "PATH_INFO" => "/path"
219
199
  )
220
- expect(last_transaction.ext.queue_start).to eq(queue_start_time)
200
+ expect(last_transaction).to have_queue_start(queue_start_time)
221
201
  expect(last_transaction).to be_completed
222
202
  end
223
203
 
@@ -225,7 +205,7 @@ describe Appsignal::Rack::EventHandler do
225
205
  on_start
226
206
  on_finish(request, nil)
227
207
 
228
- expect(last_transaction.to_h.dig("sample_data", "tags")).to_not have_key("response_status")
208
+ expect(last_transaction).to_not include_tags("response_status" => anything)
229
209
  end
230
210
 
231
211
  it "does not report a response_status counter metric" do
@@ -235,6 +215,25 @@ describe Appsignal::Rack::EventHandler do
235
215
  on_start
236
216
  on_finish(request, nil)
237
217
  end
218
+
219
+ context "with an error previously recorded by on_error" do
220
+ it "sets response status 500 as a tag" do
221
+ on_start
222
+ on_error(ExampleStandardError.new("the error"))
223
+ on_finish(request, nil)
224
+
225
+ expect(last_transaction).to include_tags("response_status" => 500)
226
+ end
227
+
228
+ it "increments the response status counter for response status 500" do
229
+ expect(Appsignal).to receive(:increment_counter)
230
+ .with(:response_status, 1, :status => 500, :namespace => :web)
231
+
232
+ on_start
233
+ on_error(ExampleStandardError.new("the error"))
234
+ on_finish(request, nil)
235
+ end
236
+ end
238
237
  end
239
238
 
240
239
  context "with error inside on_finish handler" do
@@ -260,12 +259,10 @@ describe Appsignal::Rack::EventHandler do
260
259
  on_start
261
260
  described_class.new.on_finish(request, response)
262
261
 
263
- expect(last_transaction.to_h).to include(
264
- "action" => nil,
265
- "metadata" => {},
266
- "sample_data" => {},
267
- "events" => []
268
- )
262
+ expect(last_transaction).to_not have_action
263
+ expect(last_transaction).to_not include_metadata
264
+ expect(last_transaction).to_not include_events
265
+ expect(last_transaction.to_h).to include("sample_data" => {})
269
266
  expect(last_transaction).to_not be_completed
270
267
  end
271
268
  end
@@ -275,44 +272,50 @@ describe Appsignal::Rack::EventHandler do
275
272
  last_transaction.set_action("My action")
276
273
  on_finish
277
274
 
278
- expect(last_transaction.to_h).to include(
279
- "action" => "My action"
280
- )
275
+ expect(last_transaction).to have_action("My action")
281
276
  end
282
277
 
283
278
  it "finishes the process_request.rack event" do
284
279
  on_start
285
280
  on_finish
286
281
 
287
- expect(last_transaction.to_h).to include(
288
- "events" => [
289
- hash_including(
290
- "name" => "process_request.rack",
291
- "title" => "",
292
- "body" => "",
293
- "body_format" => Appsignal::EventFormatter::DEFAULT
294
- )
295
- ]
296
- )
282
+ expect(last_transaction).to include_event("name" => "process_request.rack")
297
283
  end
298
284
 
299
- it "sets the response status as a tag" do
300
- on_start
301
- on_finish
285
+ context "with response" do
286
+ it "sets the response status as a tag" do
287
+ on_start
288
+ on_finish
302
289
 
303
- expect(last_transaction.to_h).to include(
304
- "sample_data" => hash_including(
305
- "tags" => { "response_status" => 200 }
306
- )
307
- )
308
- end
290
+ expect(last_transaction).to include_tags("response_status" => 200)
291
+ end
309
292
 
310
- it "increments the response status counter for response status" do
311
- expect(Appsignal).to receive(:increment_counter)
312
- .with(:response_status, 1, :status => 200, :namespace => :web)
293
+ it "increments the response status counter for response status" do
294
+ expect(Appsignal).to receive(:increment_counter)
295
+ .with(:response_status, 1, :status => 200, :namespace => :web)
313
296
 
314
- on_start
315
- on_finish
297
+ on_start
298
+ on_finish
299
+ end
300
+
301
+ context "with an error previously recorded by on_error" do
302
+ it "sets response status from the response as a tag" do
303
+ on_start
304
+ on_error(ExampleStandardError.new("the error"))
305
+ on_finish
306
+
307
+ expect(last_transaction).to include_tags("response_status" => 200)
308
+ end
309
+
310
+ it "increments the response status counter based on the response" do
311
+ expect(Appsignal).to receive(:increment_counter)
312
+ .with(:response_status, 1, :status => 200, :namespace => :web)
313
+
314
+ on_start
315
+ on_error(ExampleStandardError.new("the error"))
316
+ on_finish
317
+ end
318
+ end
316
319
  end
317
320
 
318
321
  it "logs an error in case of an error" do
@@ -1,38 +1,81 @@
1
- describe Appsignal::Rack::GenericInstrumentation do
2
- let(:app) { double(:call => true) }
3
- let(:env) { Rack::MockRequest.env_for("/some/path") }
4
- let(:middleware) { Appsignal::Rack::GenericInstrumentation.new(app, {}) }
1
+ describe "Appsignal::Rack::GenericInstrumentation" do
2
+ describe "Appsignal::Rack::GenericInstrumentation constant" do
3
+ let(:err_stream) { std_stream }
4
+ let(:stderr) { err_stream.read }
5
+ before do
6
+ if Appsignal::Rack.const_defined?(:GenericInstrumentation)
7
+ hide_const "Appsignal::Rack::GenericInstrumentation"
8
+ end
9
+ end
5
10
 
6
- before(:context) { start_agent }
7
- around { |example| keep_transactions { example.run } }
11
+ it "returns the Rack::GenericInstrumentation constant" do
12
+ silence do
13
+ expect(Appsignal::Rack::GenericInstrumentation)
14
+ .to be(Appsignal::Rack::GenericInstrumentationAlias)
15
+ end
16
+ end
8
17
 
9
- def make_request(env)
10
- middleware.call(env)
11
- end
18
+ it "prints a deprecation warning to STDERR" do
19
+ capture_std_streams(std_stream, err_stream) do
20
+ Appsignal::Rack::GenericInstrumentation
21
+ end
12
22
 
13
- context "without an exception" do
14
- it "reports a process_action.generic event" do
15
- make_request(env)
16
-
17
- expect(last_transaction.to_h).to include(
18
- "events" => [
19
- hash_including(
20
- "body" => "",
21
- "body_format" => Appsignal::EventFormatter::DEFAULT,
22
- "count" => 1,
23
- "name" => "process_action.generic",
24
- "title" => ""
25
- )
26
- ]
23
+ expect(stderr).to include(
24
+ "appsignal WARNING: The constant Appsignal::Rack::GenericInstrumentation " \
25
+ "has been deprecated."
26
+ )
27
+ end
28
+
29
+ it "logs a warning" do
30
+ logs =
31
+ capture_logs do
32
+ silence do
33
+ Appsignal::Rack::GenericInstrumentation
34
+ end
35
+ end
36
+
37
+ expect(logs).to contains_log(
38
+ :warn,
39
+ "The constant Appsignal::Rack::GenericInstrumentation has been deprecated."
27
40
  )
28
41
  end
29
42
  end
30
43
 
31
- context "without action name metadata" do
32
- it "reports 'unknown' as the action name" do
33
- make_request(env)
44
+ describe "middleware" do
45
+ let(:app) { double(:call => true) }
46
+ let(:env) { Rack::MockRequest.env_for("/some/path") }
47
+ let(:middleware) { Appsignal::Rack::GenericInstrumentation.new(app, {}) }
48
+
49
+ before(:context) { start_agent }
50
+ around { |example| keep_transactions { example.run } }
51
+
52
+ def make_request(env)
53
+ middleware.call(env)
54
+ end
55
+
56
+ context "without an exception" do
57
+ it "reports a process_action.generic event" do
58
+ make_request(env)
59
+
60
+ expect(last_transaction).to include_event("name" => "process_action.generic")
61
+ end
62
+ end
63
+
64
+ context "with action name env" do
65
+ it "reports the appsignal.action env as the action name" do
66
+ env["appsignal.action"] = "MyAction"
67
+ make_request(env)
68
+
69
+ expect(last_transaction).to have_action("MyAction")
70
+ end
71
+ end
72
+
73
+ context "without action name metadata" do
74
+ it "reports 'unknown' as the action name" do
75
+ make_request(env)
34
76
 
35
- expect(last_transaction.to_h).to include("action" => "unknown")
77
+ expect(last_transaction).to have_action("unknown")
78
+ end
36
79
  end
37
80
  end
38
81
  end