appsignal 3.9.3-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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +22 -19
  3. data/CHANGELOG.md +92 -0
  4. data/README.md +0 -1
  5. data/Rakefile +1 -1
  6. data/build_matrix.yml +10 -12
  7. data/gemfiles/webmachine1.gemfile +5 -4
  8. data/lib/appsignal/config.rb +4 -0
  9. data/lib/appsignal/environment.rb +6 -1
  10. data/lib/appsignal/helpers/instrumentation.rb +163 -1
  11. data/lib/appsignal/hooks/active_job.rb +1 -6
  12. data/lib/appsignal/integrations/padrino.rb +21 -25
  13. data/lib/appsignal/integrations/rake.rb +46 -12
  14. data/lib/appsignal/integrations/sidekiq.rb +1 -11
  15. data/lib/appsignal/integrations/webmachine.rb +15 -9
  16. data/lib/appsignal/rack/abstract_middleware.rb +49 -12
  17. data/lib/appsignal/rack/body_wrapper.rb +143 -0
  18. data/lib/appsignal/rack/generic_instrumentation.rb +5 -4
  19. data/lib/appsignal/rack/grape_middleware.rb +1 -1
  20. data/lib/appsignal/rack/hanami_middleware.rb +1 -1
  21. data/lib/appsignal/rack/instrumentation_middleware.rb +62 -0
  22. data/lib/appsignal/rack/rails_instrumentation.rb +1 -3
  23. data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -3
  24. data/lib/appsignal/rack/streaming_listener.rb +13 -59
  25. data/lib/appsignal/rack.rb +31 -0
  26. data/lib/appsignal/transaction.rb +50 -8
  27. data/lib/appsignal/version.rb +1 -1
  28. data/lib/appsignal.rb +3 -1
  29. data/spec/lib/appsignal/config_spec.rb +1 -0
  30. data/spec/lib/appsignal/hooks/rake_spec.rb +100 -17
  31. data/spec/lib/appsignal/integrations/padrino_spec.rb +181 -131
  32. data/spec/lib/appsignal/integrations/sinatra_spec.rb +10 -2
  33. data/spec/lib/appsignal/integrations/webmachine_spec.rb +65 -17
  34. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +96 -8
  35. data/spec/lib/appsignal/rack/body_wrapper_spec.rb +263 -0
  36. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +70 -17
  37. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +1 -1
  38. data/spec/lib/appsignal/rack/instrumentation_middleware_spec.rb +38 -0
  39. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +43 -120
  40. data/spec/lib/appsignal/transaction_spec.rb +163 -4
  41. data/spec/lib/appsignal_spec.rb +197 -6
  42. data/spec/support/mocks/dummy_app.rb +1 -1
  43. metadata +8 -4
  44. data/support/check_versions +0 -22
@@ -45,6 +45,11 @@ describe Appsignal::Rack::AbstractMiddleware do
45
45
  expect(last_transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
46
46
  end
47
47
 
48
+ it "wraps the response body in a BodyWrapper subclass" do
49
+ _status, _headers, body = make_request
50
+ expect(body).to be_kind_of(Appsignal::Rack::BodyWrapper)
51
+ end
52
+
48
53
  context "without an error" do
49
54
  before { make_request }
50
55
 
@@ -56,8 +61,20 @@ describe Appsignal::Rack::AbstractMiddleware do
56
61
  expect(last_transaction).to_not have_error
57
62
  end
58
63
 
59
- it "records an instrumentation event" do
60
- expect(last_transaction).to include_event(:name => "process.abstract")
64
+ context "without :instrument_event_name option set" do
65
+ let(:options) { {} }
66
+
67
+ it "does not record an instrumentation event" do
68
+ expect(last_transaction).to_not include_event
69
+ end
70
+ end
71
+
72
+ context "with :instrument_event_name option set" do
73
+ let(:options) { { :instrument_event_name => "event_name.category" } }
74
+
75
+ it "records an instrumentation event" do
76
+ expect(last_transaction).to include_event(:name => "event_name.category")
77
+ end
61
78
  end
62
79
 
63
80
  it "completes the transaction" do
@@ -66,8 +83,8 @@ describe Appsignal::Rack::AbstractMiddleware do
66
83
  .to be_kind_of(Appsignal::Transaction::NilTransaction)
67
84
  end
68
85
 
69
- context "when instrument_span_name option is nil" do
70
- let(:options) { { :instrument_span_name => nil } }
86
+ context "when instrument_event_name option is nil" do
87
+ let(:options) { { :instrument_event_name => nil } }
71
88
 
72
89
  it "does not record an instrumentation event" do
73
90
  expect(last_transaction).to_not include_events
@@ -148,21 +165,61 @@ describe Appsignal::Rack::AbstractMiddleware do
148
165
  end
149
166
 
150
167
  context "with appsignal.route env" do
168
+ before { env["appsignal.route"] = "POST /my-route" }
169
+
151
170
  it "reports the appsignal.route value as the action name" do
152
- env["appsignal.route"] = "POST /my-route"
153
171
  make_request
154
172
 
155
173
  expect(last_transaction).to have_action("POST /my-route")
156
174
  end
175
+
176
+ it "prints a deprecation warning" do
177
+ err_stream = std_stream
178
+ capture_std_streams(std_stream, err_stream) do
179
+ make_request
180
+ end
181
+
182
+ expect(err_stream.read).to include(
183
+ "Setting the action name with the request env 'appsignal.route' is deprecated."
184
+ )
185
+ end
186
+
187
+ it "logs a deprecation warning" do
188
+ logs = capture_logs { make_request }
189
+ expect(logs).to contains_log(
190
+ :warn,
191
+ "Setting the action name with the request env 'appsignal.route' is deprecated."
192
+ )
193
+ end
157
194
  end
158
195
 
159
196
  context "with appsignal.action env" do
160
- it "reports the appsignal.route value as the action name" do
161
- env["appsignal.action"] = "POST /my-action"
197
+ before { env["appsignal.action"] = "POST /my-action" }
198
+
199
+ it "reports the appsignal.action value as the action name" do
162
200
  make_request
163
201
 
164
202
  expect(last_transaction).to have_action("POST /my-action")
165
203
  end
204
+
205
+ it "prints a deprecation warning" do
206
+ err_stream = std_stream
207
+ capture_std_streams(std_stream, err_stream) do
208
+ make_request
209
+ end
210
+
211
+ expect(err_stream.read).to include(
212
+ "Setting the action name with the request env 'appsignal.action' is deprecated."
213
+ )
214
+ end
215
+
216
+ it "logs a deprecation warning" do
217
+ logs = capture_logs { make_request }
218
+ expect(logs).to contains_log(
219
+ :warn,
220
+ "Setting the action name with the request env 'appsignal.action' is deprecated."
221
+ )
222
+ end
166
223
  end
167
224
 
168
225
  describe "request metadata" do
@@ -274,8 +331,10 @@ describe Appsignal::Rack::AbstractMiddleware do
274
331
  end
275
332
 
276
333
  context "with parent instrumentation" do
334
+ let(:transaction) { http_request_transaction }
277
335
  before do
278
- env[Appsignal::Rack::APPSIGNAL_TRANSACTION] = http_request_transaction
336
+ env[Appsignal::Rack::APPSIGNAL_TRANSACTION] = transaction
337
+ set_current_transaction(transaction)
279
338
  end
280
339
 
281
340
  it "uses the existing transaction" do
@@ -284,6 +343,35 @@ describe Appsignal::Rack::AbstractMiddleware do
284
343
  expect { make_request }.to_not(change { created_transactions.count })
285
344
  end
286
345
 
346
+ it "wraps the response body in a BodyWrapper subclass" do
347
+ _status, _headers, body = make_request
348
+ expect(body).to be_kind_of(Appsignal::Rack::BodyWrapper)
349
+
350
+ body.to_ary
351
+ response_events =
352
+ last_transaction.to_h["events"].count do |event|
353
+ event["name"] == "process_response_body.rack"
354
+ end
355
+ expect(response_events).to eq(1)
356
+ end
357
+
358
+ context "when response body is already a BodyWrapper subclass" do
359
+ let(:body) { Appsignal::Rack::BodyWrapper.wrap(["hello!"], transaction) }
360
+ let(:app) { DummyApp.new { [200, {}, body] } }
361
+
362
+ it "doesn't wrap the body again" do
363
+ _status, _headers, body = make_request
364
+ expect(body).to eq(body)
365
+
366
+ body.to_ary
367
+ response_events =
368
+ last_transaction.to_h["events"].count do |event|
369
+ event["name"] == "process_response_body.rack"
370
+ end
371
+ expect(response_events).to eq(1)
372
+ end
373
+ end
374
+
287
375
  context "with error" do
288
376
  let(:app) { lambda { |_env| raise ExampleException, "error message" } }
289
377
 
@@ -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
@@ -1,28 +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
10
+
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
5
17
 
6
- before(:context) { start_agent }
7
- around { |example| keep_transactions { example.run } }
18
+ it "prints a deprecation warning to STDERR" do
19
+ capture_std_streams(std_stream, err_stream) do
20
+ Appsignal::Rack::GenericInstrumentation
21
+ end
8
22
 
9
- def make_request(env)
10
- middleware.call(env)
11
- end
23
+ expect(stderr).to include(
24
+ "appsignal WARNING: The constant Appsignal::Rack::GenericInstrumentation " \
25
+ "has been deprecated."
26
+ )
27
+ end
12
28
 
13
- context "without an exception" do
14
- it "reports a process_action.generic event" do
15
- make_request(env)
29
+ it "logs a warning" do
30
+ logs =
31
+ capture_logs do
32
+ silence do
33
+ Appsignal::Rack::GenericInstrumentation
34
+ end
35
+ end
16
36
 
17
- expect(last_transaction).to include_event("name" => "process_action.generic")
37
+ expect(logs).to contains_log(
38
+ :warn,
39
+ "The constant Appsignal::Rack::GenericInstrumentation has been deprecated."
40
+ )
18
41
  end
19
42
  end
20
43
 
21
- context "without action name metadata" do
22
- it "reports 'unknown' as the action name" do
23
- 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)
24
76
 
25
- expect(last_transaction).to have_action("unknown")
77
+ expect(last_transaction).to have_action("unknown")
78
+ end
26
79
  end
27
80
  end
28
81
  end
@@ -5,7 +5,7 @@ if DependencyHelper.grape_present?
5
5
  let(:err_stream) { std_stream }
6
6
  let(:stderr) { err_stream.read }
7
7
 
8
- it "returns the Probes constant calling the Minutely constant" do
8
+ it "returns the Rack::GrapeMiddleware constant calling the Grape::Middleware constant" do
9
9
  silence { expect(Appsignal::Grape::Middleware).to be(Appsignal::Rack::GrapeMiddleware) }
10
10
  end
11
11
 
@@ -0,0 +1,38 @@
1
+ describe Appsignal::Rack::InstrumentationMiddleware do
2
+ let(:app) { DummyApp.new }
3
+ let(:env) { Rack::MockRequest.env_for("/some/path") }
4
+ let(:middleware) { described_class.new(app, {}) }
5
+
6
+ before { start_agent }
7
+ around { |example| keep_transactions { example.run } }
8
+
9
+ def make_request(env)
10
+ middleware.call(env)
11
+ end
12
+
13
+ context "without an exception" do
14
+ it "reports a process_request_middleware.rack event" do
15
+ make_request(env)
16
+
17
+ expect(last_transaction).to include_event("name" => "process_request_middleware.rack")
18
+ end
19
+ end
20
+
21
+ context "with custom action name" do
22
+ let(:app) { DummyApp.new { |_env| Appsignal.set_action("MyAction") } }
23
+
24
+ it "reports the custom action name" do
25
+ make_request(env)
26
+
27
+ expect(last_transaction).to have_action("MyAction")
28
+ end
29
+ end
30
+
31
+ context "without action name metadata" do
32
+ it "reports no action name" do
33
+ make_request(env)
34
+
35
+ expect(last_transaction).to_not have_action
36
+ end
37
+ end
38
+ end