appsignal 3.9.2 → 3.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +3135 -0
  3. data/.rubocop.yml +28 -20
  4. data/.rubocop_todo.yml +7 -33
  5. data/CHANGELOG.md +38 -0
  6. data/Rakefile +79 -64
  7. data/appsignal.gemspec +1 -1
  8. data/build_matrix.yml +109 -179
  9. data/ext/base.rb +1 -1
  10. data/gemfiles/hanami-2.1.gemfile +7 -0
  11. data/lib/appsignal/cli/diagnose.rb +1 -1
  12. data/lib/appsignal/config.rb +1 -1
  13. data/lib/appsignal/demo.rb +0 -1
  14. data/lib/appsignal/environment.rb +5 -1
  15. data/lib/appsignal/extension/jruby.rb +1 -1
  16. data/lib/appsignal/helpers/instrumentation.rb +1 -1
  17. data/lib/appsignal/integrations/grape.rb +19 -47
  18. data/lib/appsignal/integrations/hanami.rb +8 -7
  19. data/lib/appsignal/integrations/padrino.rb +46 -43
  20. data/lib/appsignal/integrations/railtie.rb +0 -3
  21. data/lib/appsignal/integrations/sinatra.rb +0 -1
  22. data/lib/appsignal/probes/gvl.rb +24 -2
  23. data/lib/appsignal/probes/sidekiq.rb +1 -1
  24. data/lib/appsignal/probes.rb +1 -1
  25. data/lib/appsignal/rack/abstract_middleware.rb +62 -28
  26. data/lib/appsignal/rack/event_handler.rb +12 -3
  27. data/lib/appsignal/rack/grape_middleware.rb +40 -0
  28. data/lib/appsignal/rack/hanami_middleware.rb +1 -11
  29. data/lib/appsignal/rack/rails_instrumentation.rb +14 -55
  30. data/lib/appsignal/utils/integration_memory_logger.rb +78 -0
  31. data/lib/appsignal/utils.rb +1 -0
  32. data/lib/appsignal/version.rb +1 -1
  33. data/lib/appsignal.rb +34 -33
  34. data/spec/.rubocop.yml +1 -1
  35. data/spec/lib/appsignal/cli/diagnose_spec.rb +1 -1
  36. data/spec/lib/appsignal/cli/install_spec.rb +3 -3
  37. data/spec/lib/appsignal/config_spec.rb +7 -5
  38. data/spec/lib/appsignal/demo_spec.rb +38 -41
  39. data/spec/lib/appsignal/hooks/action_cable_spec.rb +86 -167
  40. data/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb +8 -20
  41. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +38 -84
  42. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +16 -37
  43. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +4 -4
  44. data/spec/lib/appsignal/hooks/activejob_spec.rb +111 -200
  45. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +54 -91
  46. data/spec/lib/appsignal/hooks/dry_monitor_spec.rb +14 -32
  47. data/spec/lib/appsignal/hooks/excon_spec.rb +8 -12
  48. data/spec/lib/appsignal/hooks/net_http_spec.rb +7 -42
  49. data/spec/lib/appsignal/hooks/rake_spec.rb +9 -19
  50. data/spec/lib/appsignal/hooks/redis_client_spec.rb +18 -30
  51. data/spec/lib/appsignal/hooks/redis_spec.rb +10 -16
  52. data/spec/lib/appsignal/hooks/resque_spec.rb +42 -62
  53. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +33 -74
  54. data/spec/lib/appsignal/integrations/hanami_spec.rb +79 -21
  55. data/spec/lib/appsignal/integrations/http_spec.rb +12 -20
  56. data/spec/lib/appsignal/integrations/net_http_spec.rb +33 -0
  57. data/spec/lib/appsignal/integrations/object_spec.rb +29 -36
  58. data/spec/lib/appsignal/integrations/padrino_spec.rb +47 -70
  59. data/spec/lib/appsignal/integrations/que_spec.rb +43 -70
  60. data/spec/lib/appsignal/integrations/railtie_spec.rb +26 -67
  61. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +86 -160
  62. data/spec/lib/appsignal/integrations/sinatra_spec.rb +0 -1
  63. data/spec/lib/appsignal/integrations/webmachine_spec.rb +28 -39
  64. data/spec/lib/appsignal/probes/gvl_spec.rb +80 -3
  65. data/spec/lib/appsignal/probes_spec.rb +7 -4
  66. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +215 -106
  67. data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -78
  68. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +2 -12
  69. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +234 -0
  70. data/spec/lib/appsignal/rack/hanami_middleware_spec.rb +2 -16
  71. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +67 -131
  72. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +36 -44
  73. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +68 -86
  74. data/spec/lib/appsignal/transaction_spec.rb +76 -90
  75. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +163 -0
  76. data/spec/lib/appsignal_spec.rb +363 -342
  77. data/spec/support/helpers/dependency_helper.rb +6 -1
  78. data/spec/support/helpers/std_streams_helper.rb +1 -1
  79. data/spec/support/helpers/transaction_helpers.rb +8 -0
  80. data/spec/support/matchers/transaction.rb +185 -0
  81. data/spec/support/mocks/dummy_app.rb +20 -0
  82. data/spec/support/shared_examples/instrument.rb +17 -12
  83. data/spec/support/testing.rb +18 -9
  84. metadata +15 -10
  85. data/.semaphore/semaphore.yml +0 -2347
  86. data/script/lint_git +0 -22
  87. data/spec/lib/appsignal/integrations/grape_spec.rb +0 -239
  88. data/spec/support/matchers/be_completed.rb +0 -5
  89. /data/gemfiles/{hanami.gemfile → hanami-2.0.gemfile} +0 -0
@@ -3,11 +3,6 @@ if DependencyHelper.rails_present?
3
3
  class MockController; end
4
4
 
5
5
  let(:log) { StringIO.new }
6
- before do
7
- start_agent
8
- Appsignal.internal_logger = test_logger(log)
9
- end
10
-
11
6
  let(:transaction) do
12
7
  Appsignal::Transaction.new(
13
8
  "transaction_id",
@@ -15,7 +10,7 @@ if DependencyHelper.rails_present?
15
10
  Rack::Request.new(env)
16
11
  )
17
12
  end
18
- let(:app) { double(:call => true) }
13
+ let(:app) { DummyApp.new }
19
14
  let(:params) do
20
15
  {
21
16
  "controller" => "blog_posts",
@@ -25,9 +20,8 @@ if DependencyHelper.rails_present?
25
20
  "password" => "super secret"
26
21
  }
27
22
  end
28
- let(:env_extra) { {} }
29
23
  let(:env) do
30
- http_request_env_with_data({
24
+ http_request_env_with_data(
31
25
  :params => params,
32
26
  :with_queue_start => true,
33
27
  "action_dispatch.request_id" => "request_id123",
@@ -36,160 +30,102 @@ if DependencyHelper.rails_present?
36
30
  :class => MockController,
37
31
  :action_name => "index"
38
32
  )
39
- }.merge(env_extra))
33
+ )
40
34
  end
41
35
  let(:middleware) { Appsignal::Rack::RailsInstrumentation.new(app, {}) }
42
36
  around { |example| keep_transactions { example.run } }
43
37
  before do
38
+ start_agent
39
+ Appsignal.internal_logger = test_logger(log)
44
40
  env[Appsignal::Rack::APPSIGNAL_TRANSACTION] = transaction
45
41
  end
46
42
 
47
- describe "#call" do
48
- before do
49
- allow(middleware).to receive(:raw_payload).and_return({})
50
- end
51
-
52
- context "when appsignal is active" do
53
- before { allow(Appsignal).to receive(:active?).and_return(true) }
54
-
55
- it "calls with monitoring" do
56
- expect(middleware).to receive(:call_with_appsignal_monitoring).with(env)
57
- end
58
- end
43
+ def make_request
44
+ middleware.call(env)
45
+ last_transaction&._sample
46
+ end
59
47
 
60
- context "when appsignal is not active" do
61
- before { allow(Appsignal).to receive(:active?).and_return(false) }
48
+ def make_request_with_error(error_class, error_message)
49
+ expect { make_request }.to raise_error(error_class, error_message)
50
+ end
62
51
 
63
- it "does not call with monitoring" do
64
- expect(middleware).to_not receive(:call_with_appsignal_monitoring)
65
- end
52
+ context "with a request that doesn't raise an error" do
53
+ before { make_request }
66
54
 
67
- it "calls the app" do
68
- expect(app).to receive(:call).with(env)
69
- end
55
+ it "calls the next middleware in the stack" do
56
+ expect(app).to be_called
70
57
  end
71
58
 
72
- after { middleware.call(env) }
59
+ it "does not instrument an event" do
60
+ expect(last_transaction).to_not include_events
61
+ end
73
62
  end
74
63
 
75
- describe "#call_with_appsignal_monitoring" do
76
- def run
77
- middleware.call(env)
78
- last_transaction.complete # Manually close transaction to set sample data
64
+ context "with a request that raises an error" do
65
+ let(:app) do
66
+ DummyApp.new { |_env| raise ExampleException, "error message" }
67
+ end
68
+ before do
69
+ make_request_with_error(ExampleException, "error message")
79
70
  end
80
71
 
81
- it "calls the wrapped app" do
82
- expect { run }.to_not(change { created_transactions.length })
83
- expect(app).to have_received(:call).with(env)
72
+ it "calls the next middleware in the stack" do
73
+ expect(app).to be_called
84
74
  end
85
75
 
86
- it "sets request metadata on the transaction" do
87
- run
88
-
89
- expect(last_transaction.to_h).to include(
90
- "namespace" => Appsignal::Transaction::HTTP_REQUEST,
91
- "action" => "MockController#index",
92
- "metadata" => hash_including(
93
- "method" => "GET",
94
- "path" => "/blog"
95
- ),
96
- "sample_data" => hash_including(
97
- "tags" => { "request_id" => "request_id123" }
98
- )
99
- )
76
+ it "reports the error on the transaction" do
77
+ expect(last_transaction).to have_error("ExampleException", "error message")
100
78
  end
79
+ end
101
80
 
102
- it "reports Rails filter parameters" do
103
- run
81
+ it "sets the controller action as the action name" do
82
+ make_request
104
83
 
105
- expect(last_transaction.to_h).to include(
106
- "sample_data" => hash_including(
107
- "params" => params.merge(
108
- "my_custom_param" => "[FILTERED]",
109
- "password" => "[FILTERED]"
110
- )
111
- )
112
- )
113
- end
84
+ expect(last_transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
85
+ expect(last_transaction).to have_action("MockController#index")
86
+ end
114
87
 
115
- context "with custom params" do
116
- let(:app) do
117
- lambda do |env|
118
- env[Appsignal::Rack::APPSIGNAL_TRANSACTION].set_params("custom_param" => "yes")
119
- end
120
- end
121
-
122
- it "allows custom params to be set" do
123
- run
124
-
125
- expect(last_transaction.to_h).to include(
126
- "sample_data" => hash_including(
127
- "params" => {
128
- "custom_param" => "yes"
129
- }
130
- )
131
- )
132
- end
133
- end
88
+ it "sets request metadata on the transaction" do
89
+ make_request
134
90
 
135
- context "with an invalid HTTP request method" do
136
- let(:env_extra) { { :request_method => "FOO", "REQUEST_METHOD" => "FOO" } }
91
+ expect(last_transaction).to include_metadata(
92
+ "method" => "GET",
93
+ "path" => "/blog"
94
+ )
95
+ expect(last_transaction).to include_tags("request_id" => "request_id123")
96
+ end
137
97
 
138
- it "does not store the HTTP request method" do
139
- run
98
+ it "reports Rails filter parameters" do
99
+ make_request
140
100
 
141
- transaction_hash = last_transaction.to_h
142
- expect(transaction_hash["metadata"]).to_not have_key("method")
143
- expect(log_contents(log))
144
- .to contains_log(:error, "Unable to report HTTP request method: '")
145
- end
146
- end
101
+ expect(last_transaction).to include_params(
102
+ "controller" => "blog_posts",
103
+ "action" => "show",
104
+ "id" => "1",
105
+ "my_custom_param" => "[FILTERED]",
106
+ "password" => "[FILTERED]"
107
+ )
108
+ end
147
109
 
148
- context "with an exception" do
149
- let(:error) { ExampleException.new("ExampleException message") }
150
- let(:app) do
151
- double.tap do |d|
152
- allow(d).to receive(:call).and_raise(error)
153
- end
154
- end
155
-
156
- it "records the exception" do
157
- expect { run }.to raise_error(error)
158
-
159
- transaction_hash = last_transaction.to_h
160
- expect(transaction_hash["error"]).to include(
161
- "name" => "ExampleException",
162
- "message" => "ExampleException message",
163
- "backtrace" => kind_of(String)
164
- )
165
- end
166
- end
110
+ context "with an invalid HTTP request method" do
111
+ it "does not store the invalid HTTP request method" do
112
+ env[:request_method] = "FOO"
113
+ env["REQUEST_METHOD"] = "FOO"
114
+ make_request
167
115
 
168
- context "with a request path that's not a route" do
169
- let(:env_extra) do
170
- {
171
- :path => "/unknown-route",
172
- "action_controller.instance" => nil
173
- }
174
- end
175
-
176
- it "doesn't set an action name" do
177
- run
178
-
179
- expect(last_transaction.to_h).to include(
180
- "action" => nil
181
- )
182
- end
116
+ expect(last_transaction).to_not include_metadata("method" => anything)
117
+ expect(log_contents(log))
118
+ .to contains_log(:error, "Unable to report HTTP request method: '")
183
119
  end
184
120
  end
185
121
 
186
- describe "#fetch_request_id" do
187
- subject { middleware.fetch_request_id(env) }
188
-
189
- let(:env) { { "action_dispatch.request_id" => "id" } }
122
+ context "with a request path that's not a route" do
123
+ it "doesn't set an action name" do
124
+ env[:path] = "/unknown-route"
125
+ env["action_controller.instance"] = nil
126
+ make_request
190
127
 
191
- it "returns the action dispatch id" do
192
- is_expected.to eq "id"
128
+ expect(last_transaction).to_not have_action
193
129
  end
194
130
  end
195
131
  end
@@ -2,11 +2,11 @@ if DependencyHelper.sinatra_present?
2
2
  require "appsignal/integrations/sinatra"
3
3
 
4
4
  module SinatraRequestHelpers
5
- def make_request(env)
5
+ def make_request
6
6
  middleware.call(env)
7
7
  end
8
8
 
9
- def make_request_with_error(env, error)
9
+ def make_request_with_error(error)
10
10
  expect { middleware.call(env) }.to raise_error(error)
11
11
  end
12
12
  end
@@ -30,7 +30,7 @@ if DependencyHelper.sinatra_present?
30
30
  before { allow(middleware).to receive(:raw_payload).and_return({}) }
31
31
 
32
32
  it "doesn't instrument requests" do
33
- expect { make_request(env) }.to_not(change { created_transactions.count })
33
+ expect { make_request }.to_not(change { created_transactions.count })
34
34
  end
35
35
  end
36
36
 
@@ -100,54 +100,48 @@ if DependencyHelper.sinatra_present?
100
100
  before { allow(Appsignal).to receive(:active?).and_return(false) }
101
101
 
102
102
  it "does not instrument requests" do
103
- expect { make_request(env) }.to_not(change { created_transactions.count })
103
+ expect { make_request }.to_not(change { created_transactions.count })
104
104
  end
105
105
 
106
106
  it "calls the next middleware in the stack" do
107
- make_request(env)
107
+ make_request
108
108
 
109
109
  expect(app).to have_received(:call).with(env)
110
110
  end
111
111
  end
112
112
 
113
113
  context "when appsignal is active" do
114
- context "without an exception" do
114
+ context "without an error" do
115
+ it "creates a transaction for the request" do
116
+ expect { make_request }.to(change { created_transactions.count }.by(1))
117
+
118
+ expect(last_transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
119
+ end
120
+
115
121
  it "reports a process_action.sinatra event" do
116
- make_request(env)
117
-
118
- expect(last_transaction.to_h).to include(
119
- "events" => [
120
- hash_including(
121
- "body" => "",
122
- "body_format" => Appsignal::EventFormatter::DEFAULT,
123
- "count" => 1,
124
- "name" => "process_action.sinatra",
125
- "title" => ""
126
- )
127
- ]
128
- )
122
+ make_request
123
+
124
+ expect(last_transaction).to include_event("name" => "process_action.sinatra")
129
125
  end
130
126
  end
131
127
 
132
128
  context "with an error in sinatra.error" do
133
129
  let(:error) { ExampleException.new("error message") }
134
- before do
135
- env["sinatra.error"] = error
130
+ before { env["sinatra.error"] = error }
131
+
132
+ it "creates a transaction for the request" do
133
+ expect { make_request }.to(change { created_transactions.count }.by(1))
134
+
135
+ expect(last_transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
136
136
  end
137
137
 
138
138
  context "when raise_errors is off" do
139
139
  let(:settings) { double(:raise_errors => false) }
140
140
 
141
- it "record the error" do
142
- expect { make_request(env) }
143
- .to(change { created_transactions.count }.by(1))
141
+ it "records the error" do
142
+ make_request
144
143
 
145
- expect(last_transaction.to_h).to include(
146
- "error" => hash_including(
147
- "name" => "ExampleException",
148
- "message" => "error message"
149
- )
150
- )
144
+ expect(last_transaction).to have_error("ExampleException", "error message")
151
145
  end
152
146
  end
153
147
 
@@ -155,10 +149,9 @@ if DependencyHelper.sinatra_present?
155
149
  let(:settings) { double(:raise_errors => true) }
156
150
 
157
151
  it "does not record the error" do
158
- expect { make_request(env) }
159
- .to(change { created_transactions.count }.by(1))
152
+ make_request
160
153
 
161
- expect(last_transaction.to_h).to include("error" => nil)
154
+ expect(last_transaction).to_not have_error
162
155
  end
163
156
  end
164
157
 
@@ -171,19 +164,18 @@ if DependencyHelper.sinatra_present?
171
164
  end
172
165
 
173
166
  it "does not record the error" do
174
- expect { make_request(env) }
175
- .to(change { created_transactions.count }.by(1))
167
+ make_request
176
168
 
177
- expect(last_transaction.to_h).to include("error" => nil)
169
+ expect(last_transaction).to_not have_error
178
170
  end
179
171
  end
180
172
  end
181
173
 
182
174
  describe "action name" do
183
175
  it "sets the action to the request method and path" do
184
- make_request(env)
176
+ make_request
185
177
 
186
- expect(last_transaction.to_h).to include("action" => "GET /path")
178
+ expect(last_transaction).to have_action("GET /path")
187
179
  end
188
180
 
189
181
  context "without 'sinatra.route' env" do
@@ -192,9 +184,9 @@ if DependencyHelper.sinatra_present?
192
184
  end
193
185
 
194
186
  it "doesn't set an action name" do
195
- make_request(env)
187
+ make_request
196
188
 
197
- expect(last_transaction.to_h).to include("action" => nil)
189
+ expect(last_transaction).to_not have_action
198
190
  end
199
191
  end
200
192
 
@@ -202,9 +194,9 @@ if DependencyHelper.sinatra_present?
202
194
  before { env["SCRIPT_NAME"] = "/api" }
203
195
 
204
196
  it "sets the action name with an application prefix path" do
205
- make_request(env)
197
+ make_request
206
198
 
207
- expect(last_transaction.to_h).to include("action" => "GET /api/path")
199
+ expect(last_transaction).to have_action("GET /api/path")
208
200
  end
209
201
 
210
202
  context "without 'sinatra.route' env" do
@@ -213,9 +205,9 @@ if DependencyHelper.sinatra_present?
213
205
  end
214
206
 
215
207
  it "doesn't set an action name" do
216
- make_request(env)
208
+ make_request
217
209
 
218
- expect(last_transaction.to_h).to include("action" => nil)
210
+ expect(last_transaction).to_not have_action
219
211
  end
220
212
  end
221
213
  end
@@ -1,8 +1,6 @@
1
1
  require "appsignal/rack/streaming_listener"
2
2
 
3
3
  describe Appsignal::Rack::StreamingListener do
4
- before(:context) { start_agent }
5
- let(:headers) { {} }
6
4
  let(:env) do
7
5
  {
8
6
  "rack.input" => StringIO.new,
@@ -11,114 +9,96 @@ describe Appsignal::Rack::StreamingListener do
11
9
  "QUERY_STRING" => "param=something"
12
10
  }
13
11
  end
14
- let(:app) { double(:call => [200, headers, "body"]) }
12
+ let(:app) { DummyApp.new }
15
13
  let(:listener) { Appsignal::Rack::StreamingListener.new(app, {}) }
14
+ before(:context) { start_agent }
15
+ around { |example| keep_transactions { example.run } }
16
16
 
17
17
  describe "#call" do
18
- context "when Appsignal is active" do
19
- before { allow(Appsignal).to receive(:active?).and_return(true) }
20
-
21
- it "should call `call_with_appsignal_monitoring`" do
22
- expect(listener).to receive(:call_with_appsignal_monitoring)
23
- end
24
- end
25
-
26
18
  context "when Appsignal is not active" do
27
19
  before { allow(Appsignal).to receive(:active?).and_return(false) }
28
20
 
29
- it "should not call `call_with_appsignal_monitoring`" do
30
- expect(listener).to_not receive(:call_with_appsignal_monitoring)
21
+ it "does not create a transaction" do
22
+ expect do
23
+ listener.call(env)
24
+ end.to_not(change { created_transactions.count })
31
25
  end
32
- end
33
26
 
34
- after { listener.call(env) }
35
- end
36
-
37
- describe "#call_with_appsignal_monitoring" do
38
- let!(:transaction) do
39
- Appsignal::Transaction.create(
40
- SecureRandom.uuid,
41
- Appsignal::Transaction::HTTP_REQUEST,
42
- ::Rack::Request.new(env)
43
- )
44
- end
45
- let(:wrapper) { Appsignal::StreamWrapper.new("body", transaction) }
46
- let(:raw_payload) { { :foo => :bar } }
47
-
48
- before do
49
- allow(SecureRandom).to receive(:uuid).and_return("123")
50
- allow(listener).to receive(:raw_payload).and_return(raw_payload)
51
- allow(Appsignal::Transaction).to receive(:create).and_return(transaction)
52
- end
27
+ it "calls the app" do
28
+ listener.call(env)
53
29
 
54
- it "should create a transaction" do
55
- expect(Appsignal::Transaction).to receive(:create)
56
- .with("123", Appsignal::Transaction::HTTP_REQUEST, instance_of(Rack::Request))
57
- .and_return(transaction)
58
-
59
- listener.call_with_appsignal_monitoring(env)
30
+ expect(app).to be_called
31
+ end
60
32
  end
61
33
 
62
- it "should instrument the call" do
63
- expect(Appsignal).to receive(:instrument)
64
- .with("process_action.rack")
65
- .and_yield
34
+ context "when Appsignal is active" do
35
+ before { allow(Appsignal).to receive(:active?).and_return(true) }
66
36
 
67
- listener.call_with_appsignal_monitoring(env)
68
- end
37
+ let(:wrapper) { Appsignal::StreamWrapper.new("body", transaction) }
38
+ let(:raw_payload) { { :foo => :bar } }
39
+ before { allow(listener).to receive(:raw_payload).and_return(raw_payload) }
69
40
 
70
- it "should add `appsignal.action` to the transaction" do
71
- allow(Appsignal).to receive(:instrument).and_yield
41
+ it "creates a transaction" do
42
+ expect do
43
+ listener.call(env)
44
+ end.to(change { created_transactions.count }.by(1))
45
+ end
72
46
 
73
- env["appsignal.action"] = "Action"
47
+ it "instruments the call" do
48
+ listener.call(env)
74
49
 
75
- expect(transaction).to receive(:set_action_if_nil).with("Action")
50
+ expect(last_transaction).to include_event("name" => "process_action.rack")
51
+ end
76
52
 
77
- listener.call_with_appsignal_monitoring(env)
78
- end
53
+ it "set `appsignal.action` to the action name" do
54
+ env["appsignal.action"] = "Action"
79
55
 
80
- it "should add the path, method and queue start to the transaction" do
81
- allow(Appsignal).to receive(:instrument).and_yield
56
+ listener.call(env)
82
57
 
83
- expect(transaction).to receive(:set_metadata).with("path", "/homepage")
84
- expect(transaction).to receive(:set_metadata).with("method", "GET")
85
- expect(transaction).to receive(:set_http_or_background_queue_start)
58
+ expect(last_transaction).to have_action("Action")
59
+ end
86
60
 
87
- listener.call_with_appsignal_monitoring(env)
88
- end
61
+ it "adds the path, method and queue start to the transaction" do
62
+ listener.call(env)
89
63
 
90
- context "with an exception in the instrumentation call" do
91
- let(:error) { ExampleException }
64
+ expect(last_transaction).to include_metadata(
65
+ "path" => "/homepage",
66
+ "method" => "GET"
67
+ )
68
+ expect(last_transaction).to have_queue_start
69
+ end
92
70
 
93
- it "should add the exception to the transaction" do
94
- allow(app).to receive(:call).and_raise(error)
71
+ context "with an exception in the instrumentation call" do
72
+ let(:error) { ExampleException.new("error message") }
73
+ let(:app) { DummyApp.new { raise error } }
95
74
 
96
- expect(transaction).to receive(:set_error).with(error)
75
+ it "adds the exception to the transaction" do
76
+ expect do
77
+ listener.call(env)
78
+ end.to raise_error(error)
97
79
 
98
- expect do
99
- listener.call_with_appsignal_monitoring(env)
100
- end.to raise_error(error)
80
+ expect(last_transaction).to have_error("ExampleException", "error message")
81
+ end
101
82
  end
102
- end
103
83
 
104
- it "should wrap the body in a wrapper" do
105
- expect(Appsignal::StreamWrapper).to receive(:new)
106
- .with("body", transaction)
107
- .and_return(wrapper)
84
+ it "wraps the body in a wrapper" do
85
+ _, _, body = listener.call(env)
108
86
 
109
- body = listener.call_with_appsignal_monitoring(env)[2]
110
-
111
- expect(body).to be_a(Appsignal::StreamWrapper)
87
+ expect(body).to be_a(Appsignal::StreamWrapper)
88
+ end
112
89
  end
113
90
  end
114
91
  end
115
92
 
116
93
  describe Appsignal::StreamWrapper do
117
- let(:stream) { double }
118
- let(:transaction) do
119
- Appsignal::Transaction.create(SecureRandom.uuid, Appsignal::Transaction::HTTP_REQUEST, {})
120
- end
94
+ let(:stream) { double }
95
+ let(:transaction) { http_request_transaction }
121
96
  let(:wrapper) { Appsignal::StreamWrapper.new(stream, transaction) }
97
+ before do
98
+ start_agent
99
+ set_current_transaction(transaction)
100
+ end
101
+ around { |example| keep_transactions { example.run } }
122
102
 
123
103
  describe "#each" do
124
104
  it "calls the original stream" do
@@ -128,14 +108,14 @@ describe Appsignal::StreamWrapper do
128
108
  end
129
109
 
130
110
  context "when #each raises an error" do
131
- let(:error) { ExampleException }
111
+ let(:error) { ExampleException.new("error message") }
132
112
 
133
113
  it "records the exception" do
134
114
  allow(stream).to receive(:each).and_raise(error)
135
115
 
136
- expect(transaction).to receive(:set_error).with(error)
137
-
138
116
  expect { wrapper.send(:each) }.to raise_error(error)
117
+
118
+ expect(transaction).to have_error("ExampleException", "error message")
139
119
  end
140
120
  end
141
121
  end
@@ -143,21 +123,23 @@ describe Appsignal::StreamWrapper do
143
123
  describe "#close" do
144
124
  it "closes the original stream and completes the transaction" do
145
125
  expect(stream).to receive(:close)
146
- expect(Appsignal::Transaction).to receive(:complete_current!)
147
126
 
148
127
  wrapper.close
128
+
129
+ expect(current_transaction?).to be_falsy
130
+ expect(transaction).to be_completed
149
131
  end
150
132
 
151
133
  context "when #close raises an error" do
152
- let(:error) { ExampleException }
134
+ let(:error) { ExampleException.new("error message") }
153
135
 
154
136
  it "records the exception and completes the transaction" do
155
137
  allow(stream).to receive(:close).and_raise(error)
156
138
 
157
- expect(transaction).to receive(:set_error).with(error)
158
- expect(transaction).to receive(:complete)
159
-
160
139
  expect { wrapper.send(:close) }.to raise_error(error)
140
+
141
+ expect(transaction).to have_error("ExampleException", "error message")
142
+ expect(transaction).to be_completed
161
143
  end
162
144
  end
163
145
  end