appsignal 3.9.3 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +22 -19
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +180 -0
  5. data/Gemfile +1 -0
  6. data/README.md +0 -1
  7. data/Rakefile +1 -1
  8. data/benchmark.rake +99 -42
  9. data/build_matrix.yml +10 -12
  10. data/gemfiles/webmachine1.gemfile +5 -4
  11. data/lib/appsignal/cli/demo.rb +0 -1
  12. data/lib/appsignal/config.rb +57 -97
  13. data/lib/appsignal/demo.rb +15 -20
  14. data/lib/appsignal/environment.rb +6 -1
  15. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
  16. data/lib/appsignal/event_formatter.rb +3 -2
  17. data/lib/appsignal/helpers/instrumentation.rb +490 -16
  18. data/lib/appsignal/hooks/action_cable.rb +21 -16
  19. data/lib/appsignal/hooks/active_job.rb +15 -14
  20. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  21. data/lib/appsignal/hooks/shoryuken.rb +3 -63
  22. data/lib/appsignal/integrations/action_cable.rb +5 -7
  23. data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
  24. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
  25. data/lib/appsignal/integrations/data_mapper.rb +1 -0
  26. data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
  27. data/lib/appsignal/integrations/dry_monitor.rb +1 -0
  28. data/lib/appsignal/integrations/excon.rb +1 -0
  29. data/lib/appsignal/integrations/http.rb +1 -0
  30. data/lib/appsignal/integrations/net_http.rb +1 -0
  31. data/lib/appsignal/integrations/object.rb +6 -0
  32. data/lib/appsignal/integrations/padrino.rb +21 -25
  33. data/lib/appsignal/integrations/que.rb +13 -20
  34. data/lib/appsignal/integrations/railtie.rb +1 -1
  35. data/lib/appsignal/integrations/rake.rb +45 -15
  36. data/lib/appsignal/integrations/redis.rb +1 -0
  37. data/lib/appsignal/integrations/redis_client.rb +1 -0
  38. data/lib/appsignal/integrations/resque.rb +2 -5
  39. data/lib/appsignal/integrations/shoryuken.rb +75 -0
  40. data/lib/appsignal/integrations/sidekiq.rb +7 -25
  41. data/lib/appsignal/integrations/unicorn.rb +1 -0
  42. data/lib/appsignal/integrations/webmachine.rb +12 -9
  43. data/lib/appsignal/logger.rb +7 -3
  44. data/lib/appsignal/probes/helpers.rb +1 -0
  45. data/lib/appsignal/probes/mri.rb +1 -0
  46. data/lib/appsignal/probes/sidekiq.rb +1 -0
  47. data/lib/appsignal/probes.rb +3 -0
  48. data/lib/appsignal/rack/abstract_middleware.rb +67 -24
  49. data/lib/appsignal/rack/body_wrapper.rb +143 -0
  50. data/lib/appsignal/rack/event_handler.rb +39 -8
  51. data/lib/appsignal/rack/generic_instrumentation.rb +6 -4
  52. data/lib/appsignal/rack/grape_middleware.rb +3 -2
  53. data/lib/appsignal/rack/hanami_middleware.rb +1 -1
  54. data/lib/appsignal/rack/instrumentation_middleware.rb +62 -0
  55. data/lib/appsignal/rack/rails_instrumentation.rb +1 -3
  56. data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -3
  57. data/lib/appsignal/rack/streaming_listener.rb +14 -59
  58. data/lib/appsignal/rack.rb +60 -0
  59. data/lib/appsignal/span.rb +1 -0
  60. data/lib/appsignal/transaction.rb +353 -104
  61. data/lib/appsignal/utils/data.rb +0 -1
  62. data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
  63. data/lib/appsignal/utils/integration_logger.rb +0 -13
  64. data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
  65. data/lib/appsignal/utils/json.rb +0 -1
  66. data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
  67. data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
  68. data/lib/appsignal/utils.rb +6 -0
  69. data/lib/appsignal/version.rb +1 -1
  70. data/lib/appsignal.rb +9 -6
  71. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  72. data/spec/lib/appsignal/config_spec.rb +139 -43
  73. data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
  74. data/spec/lib/appsignal/hooks/activejob_spec.rb +9 -0
  75. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
  76. data/spec/lib/appsignal/hooks/rake_spec.rb +100 -17
  77. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
  78. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
  79. data/spec/lib/appsignal/integrations/padrino_spec.rb +181 -131
  80. data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
  81. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
  82. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -4
  83. data/spec/lib/appsignal/integrations/sinatra_spec.rb +10 -2
  84. data/spec/lib/appsignal/integrations/webmachine_spec.rb +77 -17
  85. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +144 -11
  86. data/spec/lib/appsignal/rack/body_wrapper_spec.rb +263 -0
  87. data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -10
  88. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +70 -17
  89. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +1 -1
  90. data/spec/lib/appsignal/rack/instrumentation_middleware_spec.rb +38 -0
  91. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
  92. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +43 -120
  93. data/spec/lib/appsignal/rack_spec.rb +63 -0
  94. data/spec/lib/appsignal/transaction_spec.rb +1675 -953
  95. data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
  96. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
  97. data/spec/lib/appsignal_spec.rb +517 -13
  98. data/spec/support/helpers/transaction_helpers.rb +44 -20
  99. data/spec/support/matchers/transaction.rb +15 -1
  100. data/spec/support/mocks/dummy_app.rb +1 -1
  101. data/spec/support/testing.rb +1 -1
  102. metadata +12 -4
  103. data/support/check_versions +0 -22
@@ -1,132 +1,211 @@
1
1
  describe Appsignal::Transaction do
2
- before :context do
3
- start_agent
4
- end
5
-
6
- let(:transaction_id) { "1" }
7
- let(:time) { Time.at(fixed_time) }
8
- let(:namespace) { Appsignal::Transaction::HTTP_REQUEST }
9
- let(:env) { {} }
10
- let(:merged_env) { http_request_env_with_data(env) }
11
- let(:options) { {} }
12
- let(:request) { Rack::Request.new(merged_env) }
13
- let(:transaction) { Appsignal::Transaction.new(transaction_id, namespace, request, options) }
14
- let(:log) { StringIO.new }
2
+ let(:time) { Time.at(fixed_time) }
15
3
 
16
4
  before { Timecop.freeze(time) }
17
5
  after { Timecop.return }
18
6
  around do |example|
19
- use_logger_with log do
7
+ start_agent
8
+ keep_transactions do
20
9
  example.run
21
10
  end
22
11
  end
23
12
 
24
- describe "class methods" do
25
- def current_transaction
26
- Appsignal::Transaction.current
27
- end
13
+ describe ".create" do
14
+ context "when no transaction is running" do
15
+ it "returns the created transaction" do
16
+ mock_transaction_id = "mock-uuid"
17
+ allow(SecureRandom).to receive(:uuid).and_return(mock_transaction_id)
18
+
19
+ transaction = create_transaction
20
+ expect(transaction).to be_a Appsignal::Transaction
28
21
 
29
- describe ".create" do
30
- def create_transaction(id = transaction_id)
31
- Appsignal::Transaction.create(id, namespace, request, options)
22
+ expect(transaction).to have_id(mock_transaction_id)
23
+ expect(transaction.transaction_id).to eq(mock_transaction_id)
24
+
25
+ expect(transaction).to have_namespace(default_namespace)
26
+ expect(transaction.namespace).to eq(default_namespace)
32
27
  end
33
28
 
34
- context "when no transaction is running" do
35
- let!(:transaction) { create_transaction }
29
+ it "assigns the transaction to current" do
30
+ transaction = create_transaction
31
+ expect(transaction).to eq current_transaction
32
+ end
36
33
 
34
+ context "with legacy arguments" do
37
35
  it "returns the created transaction" do
38
- expect(transaction).to be_a Appsignal::Transaction
39
- expect(transaction.transaction_id).to eq transaction_id
40
- expect(transaction.namespace).to eq namespace
41
- expect(transaction.request).to eq request
36
+ transaction_id = "mock-id"
37
+ namespace = "my_namespace"
38
+ transaction = legacy_create_transaction(
39
+ :id => transaction_id,
40
+ :namespace => namespace
41
+ )
42
+ expect(transaction).to be_a(Appsignal::Transaction)
42
43
 
43
44
  expect(transaction).to have_id(transaction_id)
45
+ expect(transaction.transaction_id).to eq(transaction_id)
46
+
44
47
  expect(transaction).to have_namespace(namespace)
48
+ expect(transaction.namespace).to eq(namespace)
45
49
  end
46
50
 
47
- it "assigns the transaction to current" do
48
- expect(transaction).to eq current_transaction
51
+ it "logs deprecation warnings" do
52
+ logs =
53
+ capture_logs do
54
+ legacy_create_transaction(
55
+ :id => "mock-id",
56
+ :namespace => "my_namespace",
57
+ :request => Appsignal::Transaction::InternalGenericRequest.new({}),
58
+ :options => { :force => true }
59
+ )
60
+ end
61
+
62
+ expect(logs).to contains_log(
63
+ :warn,
64
+ "Appsignal::Transaction.create: " \
65
+ "A new Transaction is created using the transaction ID argument."
66
+ )
67
+ expect(logs).to contains_log(
68
+ :warn,
69
+ "Appsignal::Transaction.create: " \
70
+ "A Transaction is created using the namespace argument."
71
+ )
72
+ expect(logs).to contains_log(
73
+ :warn,
74
+ "Appsignal::Transaction.create: " \
75
+ "A Transaction is created using the request argument."
76
+ )
77
+ expect(logs).to contains_log(
78
+ :warn,
79
+ "Appsignal::Transaction.create: " \
80
+ "A Transaction is created using the `:force => true` option argument. "
81
+ )
49
82
  end
50
- end
51
83
 
52
- context "when a transaction is already running" do
53
- before { create_transaction }
84
+ it "prints deprecation warnings" do
85
+ err_stream = std_stream
86
+ capture_std_streams(std_stream, err_stream) do
87
+ legacy_create_transaction(
88
+ :id => "mock-id",
89
+ :namespace => "my_namespace",
90
+ :request => Appsignal::Transaction::InternalGenericRequest.new({}),
91
+ :options => { :force => true }
92
+ )
93
+ end
54
94
 
55
- it "does not create a new transaction, but returns the current transaction" do
56
- expect do
57
- new_transaction = create_transaction("2")
58
- expect(new_transaction).to eq(current_transaction)
59
- expect(new_transaction.transaction_id).to eq(transaction_id)
60
- end.to_not(change { current_transaction })
95
+ stderr = err_stream.read
96
+ expect(stderr).to include(
97
+ "appsignal WARNING: Appsignal::Transaction.create: " \
98
+ "A new Transaction is created using the transaction ID argument."
99
+ )
100
+ expect(stderr).to include(
101
+ "appsignal WARNING: Appsignal::Transaction.create: " \
102
+ "A Transaction is created using the namespace argument."
103
+ )
104
+ expect(stderr).to include(
105
+ "appsignal WARNING: Appsignal::Transaction.create: " \
106
+ "A Transaction is created using the request argument."
107
+ )
108
+ expect(stderr).to include(
109
+ "appsignal WARNING: Appsignal::Transaction.create: " \
110
+ "A Transaction is created using the `:force => true` option argument. "
111
+ )
61
112
  end
113
+ end
114
+ end
62
115
 
63
- it "logs a debug message" do
64
- create_transaction("2")
65
- expect(log_contents(log)).to contains_log :warn,
66
- "Trying to start new transaction with id '2', but a " \
67
- "transaction with id '#{transaction_id}' is already " \
68
- "running. Using transaction '#{transaction_id}'."
69
- end
116
+ context "when a transaction is already running" do
117
+ before do
118
+ allow(SecureRandom).to receive(:uuid)
119
+ .and_return(
120
+ "transaction_id_1",
121
+ "transaction_id_2"
122
+ )
123
+ create_transaction
124
+ end
70
125
 
71
- context "with option :force => true" do
72
- it "returns the newly created (and current) transaction" do
73
- original_transaction = current_transaction
74
- expect(original_transaction).to_not be_nil
75
- expect(current_transaction.transaction_id).to eq transaction_id
126
+ it "does not create a new transaction, but returns the current transaction" do
127
+ expect do
128
+ new_transaction = create_transaction
76
129
 
77
- options[:force] = true
78
- expect(create_transaction("2")).to_not eq original_transaction
79
- expect(current_transaction.transaction_id).to eq "2"
80
- end
130
+ expect(new_transaction).to eq(current_transaction)
131
+ end.to_not(change { current_transaction })
132
+ end
133
+
134
+ it "logs a debug message" do
135
+ logs = capture_logs { create_transaction }
136
+
137
+ expect(logs).to contains_log :warn,
138
+ "Trying to start new transaction with id 'transaction_id_2', but a " \
139
+ "transaction with id 'transaction_id_1' is already " \
140
+ "running. Using transaction 'transaction_id_1'."
141
+ end
142
+
143
+ context "with option :force => true" do
144
+ it "returns the newly created (and current) transaction" do
145
+ original_transaction = create_transaction
146
+
147
+ expect(original_transaction).to be_a(Appsignal::Transaction)
148
+ expect(current_transaction).to have_id("transaction_id_1")
149
+
150
+ new_transaction = legacy_create_transaction(
151
+ :id => "transaction_id_2",
152
+ :options => { :force => true }
153
+ )
154
+
155
+ expect(new_transaction).to be_a(Appsignal::Transaction)
156
+ expect(new_transaction).to_not eq(original_transaction)
157
+ expect(new_transaction).to have_id("transaction_id_2")
158
+ expect(current_transaction).to eq(new_transaction)
81
159
  end
82
160
  end
83
161
  end
162
+ end
84
163
 
85
- describe ".current" do
86
- context "when there is a current transaction" do
87
- let!(:transaction) do
88
- Appsignal::Transaction.create(transaction_id, namespace, request, options)
89
- end
164
+ describe ".current" do
165
+ context "when there is a current transaction" do
166
+ let!(:transaction) { create_transaction }
90
167
 
91
- it "reads :appsignal_transaction from the current Thread" do
92
- expect(current_transaction).to eq Thread.current[:appsignal_transaction]
93
- expect(current_transaction).to eq transaction
94
- end
168
+ it "reads :appsignal_transaction from the current Thread" do
169
+ expect(current_transaction).to eq(Thread.current[:appsignal_transaction])
170
+ expect(current_transaction).to eq(transaction)
171
+ end
95
172
 
96
- it "is not a NilTransaction" do
97
- expect(current_transaction.nil_transaction?).to eq false
98
- expect(current_transaction).to be_a Appsignal::Transaction
99
- end
173
+ it "is not a NilTransaction" do
174
+ expect(current_transaction.nil_transaction?).to be(false)
175
+ expect(current_transaction).to be_a(Appsignal::Transaction)
176
+ end
100
177
 
101
- it "returns true for current?" do
102
- expect(Appsignal::Transaction.current?).to be(true)
103
- end
178
+ it "returns true for current?" do
179
+ expect(Appsignal::Transaction.current?).to be(true)
104
180
  end
181
+ end
105
182
 
106
- context "when there is no current transaction" do
107
- it "has no :appsignal_transaction registered on the current Thread" do
108
- expect(Thread.current[:appsignal_transaction]).to be_nil
109
- end
183
+ context "when there is no current transaction" do
184
+ it "has no :appsignal_transaction registered on the current Thread" do
185
+ expect(Thread.current[:appsignal_transaction]).to be_nil
186
+ end
110
187
 
111
- it "returns a NilTransaction stub" do
112
- expect(current_transaction.nil_transaction?).to eq true
113
- expect(current_transaction).to be_a Appsignal::Transaction::NilTransaction
114
- end
188
+ it "returns a NilTransaction stub" do
189
+ expect(current_transaction.nil_transaction?).to be(true)
190
+ expect(current_transaction).to be_a(Appsignal::Transaction::NilTransaction)
191
+ end
115
192
 
116
- it "returns false for current?" do
117
- expect(Appsignal::Transaction.current?).to be(false)
118
- end
193
+ it "returns false for current?" do
194
+ expect(Appsignal::Transaction.current?).to be(false)
119
195
  end
120
196
  end
197
+ end
121
198
 
122
- describe ".complete_current!" do
123
- let!(:transaction) { Appsignal::Transaction.create(transaction_id, namespace, options) }
199
+ describe ".complete_current!" do
200
+ context "with active transaction" do
201
+ let!(:transaction) { create_transaction }
124
202
 
125
203
  it "completes the current transaction" do
126
- expect(transaction).to eq current_transaction
127
- expect(transaction).to receive(:complete).and_call_original
204
+ expect(transaction).to eq(current_transaction)
128
205
 
129
206
  Appsignal::Transaction.complete_current!
207
+
208
+ expect(transaction).to be_completed
130
209
  end
131
210
 
132
211
  it "unsets the current transaction on the current Thread" do
@@ -141,8 +220,11 @@ describe Appsignal::Transaction do
141
220
  end
142
221
 
143
222
  it "logs an error message" do
144
- Appsignal::Transaction.complete_current!
145
- expect(log_contents(log)).to contains_log :error,
223
+ logs =
224
+ capture_logs do
225
+ Appsignal::Transaction.complete_current!
226
+ end
227
+ expect(logs).to contains_log :error,
146
228
  "Failed to complete transaction ##{transaction.transaction_id}. ExampleStandardError"
147
229
  end
148
230
 
@@ -153,9 +235,19 @@ describe Appsignal::Transaction do
153
235
  end
154
236
  end
155
237
  end
238
+
239
+ context "without active transaction" do
240
+ it "does nothing" do
241
+ expect do
242
+ Appsignal::Transaction.complete_current!
243
+ end.to_not(change { Thread.current[:appsignal_transaction] })
244
+ end
245
+ end
156
246
  end
157
247
 
158
248
  describe "#complete" do
249
+ let(:transaction) { create_transaction }
250
+
159
251
  context "when transaction is being sampled" do
160
252
  it "samples data" do
161
253
  transaction.set_tags(:foo => "bar")
@@ -183,11 +275,12 @@ describe Appsignal::Transaction do
183
275
  end
184
276
 
185
277
  it "logs a debug message" do
278
+ allow(SecureRandom).to receive(:uuid).and_return("mock_transaction_id")
186
279
  transaction.discard!
187
- transaction.complete
280
+ logs = capture_logs { transaction.complete }
188
281
 
189
- expect(log_contents(log)).to contains_log :debug,
190
- "Skipping transaction '#{transaction_id}' because it was manually discarded."
282
+ expect(logs).to contains_log :debug,
283
+ "Skipping transaction 'mock_transaction_id' because it was manually discarded."
191
284
  end
192
285
 
193
286
  context "when a discarded transaction is restored" do
@@ -207,6 +300,8 @@ describe Appsignal::Transaction do
207
300
  end
208
301
 
209
302
  context "pausing" do
303
+ let(:transaction) { new_transaction }
304
+
210
305
  describe "#pause!" do
211
306
  it "changes the pause flag to true" do
212
307
  expect do
@@ -242,834 +337,1439 @@ describe Appsignal::Transaction do
242
337
  end
243
338
  end
244
339
 
245
- context "with transaction instance" do
246
- context "initialization" do
247
- it "loads the AppSignal extension" do
248
- expect(transaction.ext).to_not be_nil
249
- end
340
+ context "initialization" do
341
+ let(:transaction) { new_transaction }
250
342
 
251
- context "when extension is not loaded", :extension_installation_failure do
252
- around do |example|
253
- Appsignal::Testing.without_testing { example.run }
254
- end
343
+ it "loads the AppSignal extension" do
344
+ expect(transaction.ext).to_not be_nil
345
+ end
255
346
 
256
- it "does not error on missing extension method calls" do
257
- expect(transaction.ext).to be_kind_of(Appsignal::Extension::MockTransaction)
258
- transaction.start_event
259
- transaction.finish_event(
260
- "name",
261
- "title",
262
- "body",
263
- Appsignal::EventFormatter::DEFAULT
264
- )
265
- end
347
+ context "when extension is not loaded", :extension_installation_failure do
348
+ around do |example|
349
+ Appsignal::Testing.without_testing { example.run }
266
350
  end
267
351
 
268
- it "sets the transaction id" do
269
- expect(transaction.transaction_id).to eq "1"
352
+ it "does not error on missing extension method calls" do
353
+ expect(transaction.ext).to be_kind_of(Appsignal::Extension::MockTransaction)
354
+ transaction.start_event
355
+ transaction.finish_event(
356
+ "name",
357
+ "title",
358
+ "body",
359
+ Appsignal::EventFormatter::DEFAULT
360
+ )
270
361
  end
362
+ end
271
363
 
272
- it "sets the namespace to http_request" do
273
- expect(transaction.namespace).to eq "http_request"
364
+ context "transaction id" do
365
+ before do
366
+ allow(SecureRandom).to receive(:uuid).and_return("mock_transaction_id")
274
367
  end
275
368
 
276
- it "sets the request" do
277
- expect(transaction.request).to_not be_nil
369
+ it "sets the transaction id" do
370
+ expect(transaction).to have_id("mock_transaction_id")
278
371
  end
372
+ end
279
373
 
280
- it "sets the request not to paused" do
281
- expect(transaction.paused).to be_falsy
282
- end
374
+ it "sets the namespace to http_request" do
375
+ expect(transaction.namespace).to eq "http_request"
376
+ end
283
377
 
284
- it "sets no tags by default" do
285
- expect(transaction.tags).to eq({})
286
- end
378
+ it "sets the request" do
379
+ expect(transaction.request).to be_a(Appsignal::Transaction::InternalGenericRequest)
380
+ end
287
381
 
288
- describe "#options" do
289
- subject { transaction.options }
382
+ it "sets the request not to paused" do
383
+ expect(transaction.paused?).to be_falsy
384
+ end
290
385
 
291
- it "sets the default :params_method" do
292
- expect(subject[:params_method]).to eq :params
293
- end
386
+ it "sets no tags by default" do
387
+ expect(transaction.tags).to be_empty
388
+ end
294
389
 
295
- context "with overridden options" do
296
- let(:options) { { :params_method => :filtered_params } }
390
+ describe "#options" do
391
+ let(:options) { transaction.options }
297
392
 
298
- it "sets the overridden :params_method" do
299
- expect(subject[:params_method]).to eq :filtered_params
300
- end
393
+ it "sets the default :params_method" do
394
+ expect(options[:params_method]).to eq :params
395
+ end
396
+
397
+ context "with overridden options" do
398
+ let(:transaction) do
399
+ legacy_new_transaction(:options => { :params_method => :filtered_params })
400
+ end
401
+
402
+ it "sets the overridden :params_method" do
403
+ expect(options[:params_method]).to eq :filtered_params
301
404
  end
302
405
  end
303
406
  end
407
+ end
304
408
 
305
- describe "#store" do
306
- it "should return an empty store when it's not already present" do
307
- expect(transaction.store("test")).to eql({})
308
- end
409
+ describe "#store" do
410
+ let(:transaction) { new_transaction }
411
+
412
+ it "returns an empty store when it's not already present" do
413
+ expect(transaction.store("test")).to eql({})
414
+ end
309
415
 
310
- it "should store changes to the store" do
311
- transaction_store = transaction.store("test")
312
- transaction_store["transaction"] = "value"
416
+ it "stores changes to the store" do
417
+ transaction_store = transaction.store("test")
418
+ transaction_store["transaction"] = "value"
313
419
 
314
- expect(transaction.store("test")).to eql("transaction" => "value")
315
- end
420
+ expect(transaction.store("test")).to eql("transaction" => "value")
316
421
  end
422
+ end
317
423
 
318
- describe "#params" do
319
- subject { transaction.params }
424
+ describe "#params" do
425
+ let(:transaction) { new_transaction }
426
+ let(:params) { transaction.params }
320
427
 
321
- context "with custom params set on transaction" do
322
- before do
323
- transaction.set_params(:foo => "bar")
324
- end
428
+ context "with custom params set on transaction" do
429
+ before { transaction.set_params(:foo => "bar") }
430
+
431
+ it "returns custom parameters" do
432
+ expect(params).to eq(:foo => "bar")
433
+ end
434
+
435
+ context "when params is a callable object" do
436
+ it "calls the params object and sets the return value as parametesr" do
437
+ transaction.set_params { { "param1" => "value1" } }
325
438
 
326
- it "returns custom parameters" do
327
- expect(subject).to eq(:foo => "bar")
439
+ expect(params).to eq("param1" => "value1")
328
440
  end
329
441
  end
442
+ end
443
+
444
+ context "without custom params set on transaction" do
445
+ let(:transaction) do
446
+ legacy_new_transaction(
447
+ :request => legacy_request(
448
+ :params => {
449
+ "action" => "show",
450
+ "controller" => "blog_posts",
451
+ "id" => "1"
330
452
 
331
- context "without custom params set on transaction" do
332
- it "returns parameters from request" do
333
- expect(subject).to eq(
334
- "action" => "show",
335
- "controller" => "blog_posts",
336
- "id" => "1"
453
+ }
337
454
  )
338
- end
455
+ )
456
+ end
457
+
458
+ it "returns parameters from request" do
459
+ expect(params).to eq(
460
+ "action" => "show",
461
+ "controller" => "blog_posts",
462
+ "id" => "1"
463
+ )
339
464
  end
340
465
  end
466
+ end
467
+
468
+ describe "#params=" do
469
+ let(:transaction) { new_transaction }
470
+
471
+ it "sets params on the transaction" do
472
+ params = { "foo" => "bar" }
473
+ silence { transaction.params = params }
474
+
475
+ transaction._sample
476
+ expect(transaction.params).to eq(params)
477
+ expect(transaction).to include_params(params)
478
+ end
479
+
480
+ it "logs a deprecation warning" do
481
+ logs =
482
+ capture_logs do
483
+ transaction.params = { "foo" => "bar" }
484
+ end
485
+
486
+ expect(logs).to contains_log(
487
+ :warn,
488
+ "Transaction#params= is deprecated." \
489
+ "Use Transaction#set_params or #set_params_if_nil instead."
490
+ )
491
+ end
492
+ end
341
493
 
342
- describe "#params=" do
343
- around { |example| keep_transactions { example.run } }
494
+ describe "#set_params" do
495
+ let(:transaction) { new_transaction }
344
496
 
345
- it "sets params on the transaction" do
346
- params = { "foo" => "bar" }
347
- silence { transaction.params = params }
497
+ context "when the params are set" do
498
+ it "updates the params on the transaction" do
499
+ params = { "key" => "value" }
500
+ transaction.set_params(params)
348
501
 
349
502
  transaction._sample
350
503
  expect(transaction.params).to eq(params)
351
504
  expect(transaction).to include_params(params)
352
505
  end
353
506
 
354
- it "logs a deprecation warning" do
355
- transaction.params = { "foo" => "bar" }
507
+ it "updates the params on the transaction with a block" do
508
+ params = { "key" => "value" }
509
+ transaction.set_params { params }
356
510
 
357
- expect(log_contents(log)).to contains_log(
358
- :warn,
359
- "Transaction#params= is deprecated." \
360
- "Use Transaction#set_params or #set_params_if_nil instead."
361
- )
511
+ transaction._sample
512
+ expect(transaction.params).to eq(params)
513
+ expect(transaction).to include_params(params)
362
514
  end
363
- end
364
515
 
365
- describe "#set_params" do
366
- around { |example| keep_transactions { example.run } }
516
+ it "updates with the params argument when both an argument and block are given" do
517
+ arg_params = { "argument" => "value" }
518
+ block_params = { "block" => "value" }
519
+ transaction.set_params(arg_params) { block_params }
367
520
 
368
- context "when the params are set" do
369
- it "updates the params on the transaction" do
370
- params = { "key" => "value" }
371
- transaction.set_params(params)
521
+ transaction._sample
522
+ expect(transaction.params).to eq(arg_params)
523
+ expect(transaction).to include_params(arg_params)
524
+ end
372
525
 
373
- transaction._sample
374
- expect(transaction.params).to eq(params)
375
- expect(transaction).to include_params(params)
376
- end
526
+ it "logs an error if an error occurred storing the params" do
527
+ transaction.set_params { raise "uh oh" }
528
+
529
+ logs = capture_logs { transaction._sample }
530
+ expect(logs).to contains_log(
531
+ :error,
532
+ "Exception while fetching params: RuntimeError: uh oh"
533
+ )
377
534
  end
535
+ end
378
536
 
379
- context "when the given params is nil" do
380
- it "does not update the params on the transaction" do
381
- params = { "key" => "value" }
382
- transaction.set_params(params)
383
- transaction.set_params(nil)
537
+ context "when the given params is nil" do
538
+ it "does not update the params on the transaction" do
539
+ params = { "key" => "value" }
540
+ transaction.set_params(params)
541
+ transaction.set_params(nil)
384
542
 
385
- transaction._sample
386
- expect(transaction.params).to eq(params)
387
- expect(transaction).to include_params(params)
388
- end
543
+ transaction._sample
544
+ expect(transaction.params).to eq(params)
545
+ expect(transaction).to include_params(params)
389
546
  end
390
547
  end
548
+ end
391
549
 
392
- describe "#set_params_if_nil" do
393
- around { |example| keep_transactions { example.run } }
550
+ describe "#set_params_if_nil" do
551
+ let(:transaction) { new_transaction }
394
552
 
395
- context "when the params are not set" do
396
- it "sets the params on the transaction" do
397
- params = { "key" => "value" }
398
- transaction.set_params_if_nil(params)
553
+ context "when the params are not set" do
554
+ it "sets the params on the transaction" do
555
+ params = { "key" => "value" }
556
+ transaction.set_params_if_nil(params)
399
557
 
400
- transaction._sample
401
- expect(transaction.params).to eq(params)
402
- expect(transaction).to include_params(params)
403
- end
558
+ transaction._sample
559
+ expect(transaction.params).to eq(params)
560
+ expect(transaction).to include_params(params)
561
+ end
404
562
 
405
- context "when the given params is nil" do
406
- it "does not update the params on the transaction" do
407
- params = { "key" => "value" }
408
- transaction.set_params(params)
409
- transaction.set_params_if_nil(nil)
563
+ it "updates the params on the transaction with a block" do
564
+ params = { "key" => "value" }
565
+ transaction.set_params_if_nil { params }
410
566
 
411
- transaction._sample
412
- expect(transaction.params).to eq(params)
413
- expect(transaction).to include_params(params)
414
- end
415
- end
567
+ transaction._sample
568
+ expect(transaction.params).to eq(params)
569
+ expect(transaction).to include_params(params)
570
+ end
571
+
572
+ it "updates with the params argument when both an argument and block are given" do
573
+ arg_params = { "argument" => "value" }
574
+ block_params = { "block" => "value" }
575
+ transaction.set_params_if_nil(arg_params) { block_params }
576
+
577
+ transaction._sample
578
+ expect(transaction.params).to eq(arg_params)
579
+ expect(transaction).to include_params(arg_params)
416
580
  end
417
581
 
418
- context "when the params are set" do
582
+ context "when the given params is nil" do
419
583
  it "does not update the params on the transaction" do
420
- preset_params = { "other" => "params" }
421
584
  params = { "key" => "value" }
422
- transaction.set_params(preset_params)
423
- transaction.set_params_if_nil(params)
585
+ transaction.set_params(params)
586
+ transaction.set_params_if_nil(nil)
424
587
 
425
588
  transaction._sample
426
- expect(transaction.params).to eq(preset_params)
427
- expect(transaction).to include_params(preset_params)
589
+ expect(transaction.params).to eq(params)
590
+ expect(transaction).to include_params(params)
428
591
  end
429
592
  end
430
593
  end
431
594
 
432
- describe "#set_tags" do
433
- let(:long_string) { "a" * 10_001 }
434
- before do
435
- transaction.set_tags(
436
- :valid_key => "valid_value",
437
- "valid_string_key" => "valid_value",
438
- :both_symbols => :valid_value,
439
- :integer_value => 1,
440
- :hash_value => { "invalid" => "hash" },
441
- :array_value => %w[invalid array],
442
- :object => Object.new,
443
- :too_long_value => long_string,
444
- long_string => "too_long_key"
445
- )
446
- transaction.sample_data
595
+ context "when the params are set" do
596
+ it "does not update the params on the transaction" do
597
+ preset_params = { "other" => "params" }
598
+ params = { "key" => "value" }
599
+ transaction.set_params(preset_params)
600
+ transaction.set_params_if_nil(params)
601
+
602
+ transaction._sample
603
+ expect(transaction.params).to eq(preset_params)
604
+ expect(transaction).to include_params(preset_params)
447
605
  end
448
606
 
449
- it "stores tags on the transaction" do
450
- expect(transaction).to include_tags(
451
- "valid_key" => "valid_value",
452
- "valid_string_key" => "valid_value",
453
- "both_symbols" => "valid_value",
454
- "integer_value" => 1,
455
- "too_long_value" => "#{"a" * 10_000}...",
456
- long_string => "too_long_key"
457
- )
607
+ it "does not update the params with a block on the transaction" do
608
+ preset_params = { "other" => "params" }
609
+ params = { "key" => "value" }
610
+ transaction.set_params(preset_params)
611
+ transaction.set_params_if_nil { params }
612
+
613
+ transaction._sample
614
+ expect(transaction.params).to eq(preset_params)
615
+ expect(transaction).to include_params(preset_params)
458
616
  end
459
617
  end
618
+ end
460
619
 
461
- describe "#add_breadcrumb" do
462
- context "when over the limit" do
463
- before do
464
- 22.times do |i|
465
- transaction.add_breadcrumb(
466
- "network",
467
- "GET http://localhost",
468
- "User made external network request",
469
- { :code => i + 1 },
470
- Time.parse("10-10-2010 10:00:00 UTC")
471
- )
472
- end
473
- transaction.sample_data
474
- end
620
+ describe "#set_session_data" do
621
+ let(:transaction) { new_transaction }
475
622
 
476
- it "stores last <LIMIT> breadcrumbs on the transaction" do
477
- expect(transaction.to_h["sample_data"]["breadcrumbs"].length).to eql(20)
478
- expect(transaction.to_h["sample_data"]["breadcrumbs"][0]).to eq(
479
- "action" => "GET http://localhost",
480
- "category" => "network",
481
- "message" => "User made external network request",
482
- "metadata" => { "code" => 3 },
483
- "time" => 1286704800 # rubocop:disable Style/NumericLiterals
484
- )
485
- expect(transaction.to_h["sample_data"]["breadcrumbs"][19]).to eq(
486
- "action" => "GET http://localhost",
487
- "category" => "network",
488
- "message" => "User made external network request",
489
- "metadata" => { "code" => 22 },
490
- "time" => 1286704800 # rubocop:disable Style/NumericLiterals
491
- )
492
- end
623
+ context "when the session data is set" do
624
+ it "updates the session data on the transaction" do
625
+ data = { "key" => "value" }
626
+ transaction.set_session_data(data)
627
+
628
+ transaction._sample
629
+ expect(transaction).to include_session_data(data)
493
630
  end
494
631
 
495
- context "with defaults" do
496
- it "stores breadcrumb with defaults on transaction" do
497
- timeframe_start = Time.now.utc.to_i
498
- transaction.add_breadcrumb("user_action", "clicked HOME")
499
- transaction.sample_data
500
- timeframe_end = Time.now.utc.to_i
632
+ it "updates the session data on the transaction with a block" do
633
+ data = { "key" => "value" }
634
+ transaction.set_session_data { data }
501
635
 
502
- expect(transaction).to include_breadcrumb(
503
- "clicked HOME",
504
- "user_action",
505
- "",
506
- {},
507
- be_between(timeframe_start, timeframe_end)
508
- )
509
- end
636
+ transaction._sample
637
+ expect(transaction).to include_session_data(data)
510
638
  end
511
639
 
512
- context "with metadata argument that's not a Hash" do
513
- it "does not add the breadcrumb and logs and error" do
514
- transaction.add_breadcrumb("category", "action", "message", "invalid metadata")
515
- transaction.sample_data
640
+ it "updates with the session data argument when both an argument and block are given" do
641
+ arg_data = { "argument" => "value" }
642
+ block_data = { "block" => "value" }
643
+ transaction.set_session_data(arg_data) { block_data }
516
644
 
517
- expect(transaction).to_not include_breadcrumbs
518
- expect(log_contents(log)).to contains_log(
519
- :error,
520
- "add_breadcrumb: Cannot add breadcrumb. The given metadata argument is not a Hash."
521
- )
522
- end
645
+ transaction._sample
646
+ expect(transaction).to include_session_data(arg_data)
523
647
  end
524
- end
525
648
 
526
- describe "#set_action" do
527
- context "when the action is set" do
528
- it "updates the action name on the transaction" do
529
- action_name = "PagesController#show"
530
- transaction.set_action(action_name)
649
+ it "does not include filtered out session data" do
650
+ Appsignal.config[:filter_session_data] = ["filtered_key"]
651
+ transaction.set_session_data("data" => "value1", "filtered_key" => "filtered_value")
531
652
 
532
- expect(transaction.action).to eq(action_name)
533
- expect(transaction).to have_action(action_name)
534
- end
653
+ transaction._sample
654
+ expect(transaction).to include_session_data("data" => "value1")
535
655
  end
536
656
 
537
- context "when the action is nil" do
538
- it "does not update the action name on the transaction" do
539
- action_name = "PagesController#show"
540
- transaction.set_action(action_name)
541
- transaction.set_action(nil)
657
+ it "logs an error if an error occurred storing the session data" do
658
+ transaction.set_session_data { raise "uh oh" }
542
659
 
543
- expect(transaction.action).to eq(action_name)
544
- expect(transaction).to have_action(action_name)
545
- end
660
+ logs = capture_logs { transaction._sample }
661
+ expect(logs).to contains_log(
662
+ :error,
663
+ "Exception while fetching session data: RuntimeError: uh oh"
664
+ )
546
665
  end
547
666
  end
548
667
 
549
- describe "#set_action_if_nil" do
550
- context "when the action is not set" do
551
- it "updates the action name on the transaction" do
552
- expect(transaction.action).to eq(nil)
553
- expect(transaction).to_not have_action
668
+ context "when the given session data is nil" do
669
+ it "does not update the session data on the transaction" do
670
+ data = { "key" => "value" }
671
+ transaction.set_session_data(data)
672
+ transaction.set_session_data(nil)
554
673
 
555
- action_name = "PagesController#show"
556
- transaction.set_action_if_nil(action_name)
674
+ transaction._sample
675
+ expect(transaction).to include_session_data(data)
676
+ end
677
+ end
678
+ end
557
679
 
558
- expect(transaction.action).to eq(action_name)
559
- expect(transaction).to have_action(action_name)
560
- end
680
+ describe "#set_session_data_if_nil" do
681
+ let(:transaction) { new_transaction }
561
682
 
562
- context "when the given action is nil" do
563
- it "does not update the action name on the transaction" do
564
- action_name = "something"
565
- transaction.set_action("something")
566
- transaction.set_action_if_nil(nil)
683
+ context "when the params are not set" do
684
+ it "sets the params on the transaction" do
685
+ data = { "key" => "value" }
686
+ transaction.set_session_data_if_nil(data)
567
687
 
568
- expect(transaction.action).to eq(action_name)
569
- expect(transaction).to have_action(action_name)
570
- end
571
- end
688
+ transaction._sample
689
+ expect(transaction).to include_session_data(data)
572
690
  end
573
691
 
574
- context "when the action is set" do
575
- it "does not update the action name on the transaction" do
576
- action_name = "something"
577
- transaction.set_action("something")
578
- transaction.set_action_if_nil("something else")
692
+ it "updates the params on the transaction with a block" do
693
+ data = { "key" => "value" }
694
+ transaction.set_session_data_if_nil { data }
579
695
 
580
- expect(transaction.action).to eq(action_name)
581
- expect(transaction).to have_action(action_name)
582
- end
696
+ transaction._sample
697
+ expect(transaction).to include_session_data(data)
583
698
  end
584
- end
585
699
 
586
- describe "#set_namespace" do
587
- context "when the namespace is not nil" do
588
- it "updates the namespace on the transaction" do
589
- namespace = "custom"
590
- transaction.set_namespace(namespace)
700
+ it "updates with the params argument when both an argument and block are given" do
701
+ arg_data = { "argument" => "value" }
702
+ block_data = { "block" => "value" }
703
+ transaction.set_session_data_if_nil(arg_data) { block_data }
591
704
 
592
- expect(transaction.namespace).to eq namespace
593
- expect(transaction).to have_namespace(namespace)
594
- end
705
+ transaction._sample
706
+ expect(transaction).to include_session_data(arg_data)
595
707
  end
596
708
 
597
- context "when the namespace is nil" do
598
- it "does not update the namespace on the transaction" do
599
- namespace = "custom"
600
- transaction.set_namespace(namespace)
601
- transaction.set_namespace(nil)
709
+ context "when the given params is nil" do
710
+ it "does not update the params on the transaction" do
711
+ data = { "key" => "value" }
712
+ transaction.set_session_data(data)
713
+ transaction.set_session_data_if_nil(nil)
602
714
 
603
- expect(transaction.namespace).to eq(namespace)
604
- expect(transaction).to have_namespace(namespace)
715
+ transaction._sample
716
+ expect(transaction).to include_session_data(data)
605
717
  end
606
718
  end
607
719
  end
608
720
 
609
- describe "#set_http_or_background_action" do
610
- context "for a hash with controller and action" do
611
- it "sets the action" do
612
- transaction.set_http_or_background_action(
613
- :controller => "HomeController",
614
- :action => "show"
615
- )
616
- expect(transaction).to have_action("HomeController#show")
617
- end
618
- end
721
+ context "when the params are set" do
722
+ it "does not update the params on the transaction" do
723
+ preset_data = { "other" => "data" }
724
+ data = { "key" => "value" }
725
+ transaction.set_session_data(preset_data)
726
+ transaction.set_session_data_if_nil(data)
619
727
 
620
- context "for a hash with just action" do
621
- it "sets the action" do
622
- transaction.set_http_or_background_action(:action => "show")
623
- expect(transaction).to have_action("show")
624
- end
728
+ transaction._sample
729
+ expect(transaction).to include_session_data(preset_data)
625
730
  end
626
731
 
627
- context "for a hash with class and method" do
628
- it "sets the action" do
629
- transaction.set_http_or_background_action(:class => "Worker", :method => "perform")
630
- expect(transaction).to have_action("Worker#perform")
631
- end
632
- end
732
+ it "does not update the params with a block on the transaction" do
733
+ preset_data = { "other" => "data" }
734
+ data = { "key" => "value" }
735
+ transaction.set_session_data(preset_data)
736
+ transaction.set_session_data_if_nil { data }
633
737
 
634
- context "when action is already set" do
635
- it "does not overwrite the set action" do
636
- transaction.set_action("MyCustomAction#perform")
637
- transaction.set_http_or_background_action(:class => "Worker", :method => "perform")
638
- expect(transaction).to have_action("MyCustomAction#perform")
639
- end
738
+ transaction._sample
739
+ expect(transaction).to include_session_data(preset_data)
640
740
  end
641
741
  end
742
+ end
743
+
744
+ describe "#set_headers" do
745
+ let(:transaction) { new_transaction }
642
746
 
643
- describe "#set_queue_start" do
644
- it "sets the queue start in extension" do
645
- expect(transaction.ext).to receive(:set_queue_start).with(10.0).once
747
+ context "when the headers are set" do
748
+ it "updates the headers on the transaction" do
749
+ headers = { "PATH_INFO" => "value" }
750
+ transaction.set_headers(headers)
646
751
 
647
- transaction.set_queue_start(10.0)
752
+ transaction._sample
753
+ expect(transaction).to include_environment(headers)
648
754
  end
649
755
 
650
- it "does not set the queue start in extension when value is nil" do
651
- expect(transaction.ext).to_not receive(:set_queue_start)
756
+ it "updates the headers on the transaction with a block" do
757
+ headers = { "PATH_INFO" => "value" }
758
+ transaction.set_headers { headers }
652
759
 
653
- transaction.set_queue_start(nil)
760
+ transaction._sample
761
+ expect(transaction).to include_environment(headers)
654
762
  end
655
763
 
656
- it "does not raise an error when the queue start is too big" do
657
- expect(transaction.ext).to receive(:set_queue_start).and_raise(RangeError)
764
+ it "updates with the headers argument when both an argument and block are given" do
765
+ arg_data = { "PATH_INFO" => "/arg-path" }
766
+ block_data = { "PATH_INFO" => "/block-path" }
767
+ transaction.set_headers(arg_data) { block_data }
658
768
 
659
- expect(Appsignal.internal_logger).to receive(:warn).with("Queue start value 10 is too big")
660
-
661
- transaction.set_queue_start(10)
769
+ transaction._sample
770
+ expect(transaction).to include_environment(arg_data)
771
+ end
772
+
773
+ it "does not include filtered out headers" do
774
+ Appsignal.config[:request_headers] = ["MY_HEADER"]
775
+ transaction.set_headers("MY_HEADER" => "value1", "filtered_key" => "filtered_value")
776
+
777
+ transaction._sample
778
+ expect(transaction).to include_environment("MY_HEADER" => "value1")
779
+ end
780
+
781
+ it "logs an error if an error occurred storing the headers" do
782
+ transaction.set_headers { raise "uh oh" }
783
+
784
+ logs = capture_logs { transaction._sample }
785
+ expect(logs).to contains_log(
786
+ :error,
787
+ "Exception while fetching headers: RuntimeError: uh oh"
788
+ )
662
789
  end
663
790
  end
664
791
 
665
- describe "#set_http_or_background_queue_start" do
666
- let(:header_factor) { 1_000 }
667
- let(:env_queue_start) { fixed_time + 20 } # in seconds
792
+ context "when the given headers is nil" do
793
+ it "does not update the headers on the transaction" do
794
+ headers = { "PATH_INFO" => "value" }
795
+ transaction.set_headers(headers)
796
+ transaction.set_headers(nil)
668
797
 
669
- context "when a queue time is found in a request header" do
670
- let(:header_time) { ((fixed_time + 10) * header_factor).to_i } # in milliseconds
671
- let(:env) { { "HTTP_X_REQUEST_START" => "t=#{header_time}" } }
798
+ transaction._sample
799
+ expect(transaction).to include_environment(headers)
800
+ end
801
+ end
802
+ end
672
803
 
673
- it "sets the http header value in milliseconds on the transaction" do
674
- expect(transaction).to receive(:set_queue_start).with(1_389_783_610_000)
804
+ describe "#set_headers_if_nil" do
805
+ let(:transaction) { new_transaction }
675
806
 
676
- transaction.set_http_or_background_queue_start
677
- end
807
+ context "when the params are not set" do
808
+ it "sets the params on the transaction" do
809
+ headers = { "PATH_INFO" => "value" }
810
+ transaction.set_headers_if_nil(headers)
678
811
 
679
- context "when a :queue_start key is found in the transaction environment" do
680
- let(:env) do
681
- {
682
- "HTTP_X_REQUEST_START" => "t=#{header_time}",
683
- :queue_start => env_queue_start
684
- }
685
- end
812
+ transaction._sample
813
+ expect(transaction).to include_environment(headers)
814
+ end
686
815
 
687
- it "sets the http header value in milliseconds on the transaction" do
688
- expect(transaction).to receive(:set_queue_start).with(1_389_783_610_000)
816
+ it "updates the params on the transaction with a block" do
817
+ headers = { "PATH_INFO" => "value" }
818
+ transaction.set_headers_if_nil { headers }
689
819
 
690
- transaction.set_http_or_background_queue_start
691
- end
692
- end
820
+ transaction._sample
821
+ expect(transaction).to include_environment(headers)
693
822
  end
694
823
 
695
- context "when a :queue_start key is found in the transaction environment" do
696
- let(:env) { { :queue_start => env_queue_start } } # in seconds
824
+ it "updates with the params argument when both an argument and block are given" do
825
+ arg_data = { "PATH_INFO" => "/arg-path" }
826
+ block_data = { "PATH_INFO" => "/block-path" }
827
+ transaction.set_headers_if_nil(arg_data) { block_data }
697
828
 
698
- it "sets the :queue_start value in milliseconds on the transaction" do
699
- expect(transaction).to receive(:set_queue_start).with(1_389_783_620_000)
829
+ transaction._sample
830
+ expect(transaction).to include_environment(arg_data)
831
+ end
700
832
 
701
- transaction.set_http_or_background_queue_start
833
+ context "when the given params is nil" do
834
+ it "does not update the params on the transaction" do
835
+ headers = { "PATH_INFO" => "value" }
836
+ transaction.set_headers(headers)
837
+ transaction.set_headers_if_nil(nil)
838
+
839
+ transaction._sample
840
+ expect(transaction).to include_environment(headers)
702
841
  end
703
842
  end
704
843
  end
705
844
 
706
- describe "#set_metadata" do
707
- it "updates the metadata on the transaction" do
708
- transaction.set_metadata("request_method", "GET")
845
+ context "when the params are set" do
846
+ it "does not update the params on the transaction" do
847
+ preset_headers = { "PATH_INFO" => "/first-path" }
848
+ headers = { "PATH_INFO" => "/other-path" }
849
+ transaction.set_headers(preset_headers)
850
+ transaction.set_headers_if_nil(headers)
709
851
 
710
- expect(transaction).to include_metadata("request_method" => "GET")
852
+ transaction._sample
853
+ expect(transaction).to include_environment(preset_headers)
711
854
  end
712
855
 
713
- context "when filter_metadata includes metadata key" do
714
- before { Appsignal.config[:filter_metadata] = ["filter_key"] }
715
- after { Appsignal.config[:filter_metadata] = [] }
716
-
717
- it "does not set the metadata on the transaction" do
718
- transaction.set_metadata(:filter_key, "filtered value")
719
- transaction.set_metadata("filter_key", "filtered value")
856
+ it "does not update the params with a block on the transaction" do
857
+ preset_headers = { "PATH_INFO" => "/first-path" }
858
+ headers = { "PATH_INFO" => "/other-path" }
859
+ transaction.set_headers(preset_headers)
860
+ transaction.set_headers_if_nil { headers }
720
861
 
721
- expect(transaction).to_not include_metadata("filter_key" => anything)
722
- end
862
+ transaction._sample
863
+ expect(transaction).to include_environment(preset_headers)
723
864
  end
865
+ end
866
+ end
724
867
 
725
- context "when the key is nil" do
726
- it "does not update the metadata on the transaction" do
727
- transaction.set_metadata(nil, "GET")
868
+ describe "#set_tags" do
869
+ let(:transaction) { new_transaction }
870
+ let(:long_string) { "a" * 10_001 }
871
+
872
+ it "stores tags on the transaction" do
873
+ transaction.set_tags(
874
+ :valid_key => "valid_value",
875
+ "valid_string_key" => "valid_value",
876
+ :both_symbols => :valid_value,
877
+ :integer_value => 1,
878
+ :hash_value => { "invalid" => "hash" },
879
+ :array_value => %w[invalid array],
880
+ :object => Object.new,
881
+ :too_long_value => long_string,
882
+ long_string => "too_long_key",
883
+ :true_tag => true,
884
+ :false_tag => false
885
+ )
886
+ transaction._sample
887
+
888
+ expect(transaction).to include_tags(
889
+ "valid_key" => "valid_value",
890
+ "valid_string_key" => "valid_value",
891
+ "both_symbols" => "valid_value",
892
+ "integer_value" => 1,
893
+ "too_long_value" => "#{"a" * 10_000}...",
894
+ long_string => "too_long_key",
895
+ "true_tag" => true,
896
+ "false_tag" => false
897
+ )
898
+ end
728
899
 
729
- expect(transaction).to_not include_metadata
730
- end
731
- end
900
+ it "merges the tags when called multiple times" do
901
+ transaction.set_tags(:key1 => "value1")
902
+ transaction.set_tags(:key2 => "value2")
903
+ transaction._sample
904
+
905
+ expect(transaction).to include_tags(
906
+ "key1" => "value1",
907
+ "key2" => "value2"
908
+ )
909
+ end
910
+ end
911
+
912
+ describe "#set_custom_data" do
913
+ let(:transaction) { new_transaction }
914
+
915
+ it "stores custom Hash data on the transaction" do
916
+ transaction.set_custom_data(
917
+ :user => {
918
+ :id => 123,
919
+ :locale => "abc"
920
+ },
921
+ :organization => {
922
+ :slug => "appsignal",
923
+ :plan => "enterprise"
924
+ }
925
+ )
926
+
927
+ transaction._sample
928
+ expect(transaction).to include_custom_data(
929
+ "user" => {
930
+ "id" => 123,
931
+ "locale" => "abc"
932
+ },
933
+ "organization" => {
934
+ "slug" => "appsignal",
935
+ "plan" => "enterprise"
936
+ }
937
+ )
938
+ end
939
+
940
+ it "stores custom Array data on the transaction" do
941
+ transaction.set_custom_data([
942
+ [123, "abc"],
943
+ ["appsignal", "enterprise"]
944
+ ])
945
+
946
+ transaction._sample
947
+ expect(transaction).to include_custom_data([
948
+ [123, "abc"],
949
+ ["appsignal", "enterprise"]
950
+ ])
951
+ end
952
+
953
+ it "does not store non Hash or Array custom data" do
954
+ logs =
955
+ capture_logs do
956
+ transaction.set_custom_data("abc")
957
+ transaction._sample
958
+ expect(transaction).to_not include_custom_data
959
+
960
+ transaction.set_custom_data(123)
961
+ transaction._sample
962
+ expect(transaction).to_not include_custom_data
963
+
964
+ transaction.set_custom_data(Object.new)
965
+ transaction._sample
966
+ expect(transaction).to_not include_custom_data
967
+ end
968
+
969
+ expect(logs).to contains_log(
970
+ :error,
971
+ "set_custom_data: Unsupported data type String received."
972
+ )
973
+ expect(logs).to contains_log(
974
+ :error,
975
+ "set_custom_data: Unsupported data type Integer received."
976
+ )
977
+ expect(logs).to contains_log(
978
+ :error,
979
+ "set_custom_data: Unsupported data type String received."
980
+ )
981
+ end
982
+
983
+ it "overwrites the custom data if called multiple times" do
984
+ transaction.set_custom_data("user" => { "id" => 123 })
985
+ transaction.set_custom_data("user" => { "id" => 456 })
986
+
987
+ transaction._sample
988
+ expect(transaction).to include_custom_data("user" => { "id" => 456 })
989
+ end
990
+ end
732
991
 
733
- context "when the value is nil" do
734
- it "does not update the metadata on the transaction" do
735
- transaction.set_metadata("request_method", nil)
992
+ describe "#add_breadcrumb" do
993
+ let(:transaction) { new_transaction }
736
994
 
737
- expect(transaction).to_not include_metadata
995
+ context "when over the limit" do
996
+ before do
997
+ 22.times do |i|
998
+ transaction.add_breadcrumb(
999
+ "network",
1000
+ "GET http://localhost",
1001
+ "User made external network request",
1002
+ { :code => i + 1 },
1003
+ Time.parse("10-10-2010 10:00:00 UTC")
1004
+ )
738
1005
  end
1006
+ transaction._sample
1007
+ end
1008
+
1009
+ it "stores last <LIMIT> breadcrumbs on the transaction" do
1010
+ expect(transaction.to_h["sample_data"]["breadcrumbs"].length).to eql(20)
1011
+ expect(transaction.to_h["sample_data"]["breadcrumbs"][0]).to eq(
1012
+ "action" => "GET http://localhost",
1013
+ "category" => "network",
1014
+ "message" => "User made external network request",
1015
+ "metadata" => { "code" => 3 },
1016
+ "time" => 1286704800 # rubocop:disable Style/NumericLiterals
1017
+ )
1018
+ expect(transaction.to_h["sample_data"]["breadcrumbs"][19]).to eq(
1019
+ "action" => "GET http://localhost",
1020
+ "category" => "network",
1021
+ "message" => "User made external network request",
1022
+ "metadata" => { "code" => 22 },
1023
+ "time" => 1286704800 # rubocop:disable Style/NumericLiterals
1024
+ )
739
1025
  end
740
1026
  end
741
1027
 
742
- describe "#set_sample_data" do
743
- it "updates the sample data on the transaction" do
744
- transaction.set_sample_data(
745
- "params",
746
- :controller => "blog_posts",
747
- :action => "show",
748
- :id => "1"
1028
+ context "with defaults" do
1029
+ it "stores breadcrumb with defaults on transaction" do
1030
+ timeframe_start = Time.now.utc.to_i
1031
+ transaction.add_breadcrumb("user_action", "clicked HOME")
1032
+ transaction._sample
1033
+ timeframe_end = Time.now.utc.to_i
1034
+
1035
+ expect(transaction).to include_breadcrumb(
1036
+ "clicked HOME",
1037
+ "user_action",
1038
+ "",
1039
+ {},
1040
+ be_between(timeframe_start, timeframe_end)
749
1041
  )
1042
+ end
1043
+ end
750
1044
 
751
- expect(transaction).to include_params(
752
- "action" => "show",
753
- "controller" => "blog_posts",
754
- "id" => "1"
1045
+ context "with metadata argument that's not a Hash" do
1046
+ it "does not add the breadcrumb and logs and error" do
1047
+ logs =
1048
+ capture_logs do
1049
+ transaction.add_breadcrumb("category", "action", "message", "invalid metadata")
1050
+ end
1051
+ transaction._sample
1052
+
1053
+ expect(transaction).to_not include_breadcrumbs
1054
+ expect(logs).to contains_log(
1055
+ :error,
1056
+ "add_breadcrumb: Cannot add breadcrumb. The given metadata argument is not a Hash."
755
1057
  )
756
1058
  end
1059
+ end
1060
+ end
757
1061
 
758
- context "when the data is no Array or Hash" do
759
- it "does not update the sample data on the transaction" do
760
- transaction.set_sample_data("params", "string")
1062
+ describe "#set_action" do
1063
+ let(:transaction) { new_transaction }
761
1064
 
762
- expect(transaction.to_h["sample_data"]).to eq({})
763
- expect(log_contents(log)).to contains_log :error,
764
- %(Invalid sample data for 'params'. Value is not an Array or Hash: '"string"')
765
- end
1065
+ context "when the action is set" do
1066
+ it "updates the action name on the transaction" do
1067
+ action_name = "PagesController#show"
1068
+ transaction.set_action(action_name)
1069
+
1070
+ expect(transaction.action).to eq(action_name)
1071
+ expect(transaction).to have_action(action_name)
1072
+ end
1073
+ end
1074
+
1075
+ context "when the action is nil" do
1076
+ it "does not update the action name on the transaction" do
1077
+ action_name = "PagesController#show"
1078
+ transaction.set_action(action_name)
1079
+ transaction.set_action(nil)
1080
+
1081
+ expect(transaction.action).to eq(action_name)
1082
+ expect(transaction).to have_action(action_name)
766
1083
  end
1084
+ end
1085
+ end
767
1086
 
768
- context "when the data cannot be converted to JSON" do
769
- it "does not update the sample data on the transaction" do
770
- klass = Class.new do
771
- def to_s
772
- raise "foo" # Cause a deliberate error
773
- end
774
- end
775
- transaction.set_sample_data("params", klass.new => 1)
1087
+ describe "#set_action_if_nil" do
1088
+ let(:transaction) { new_transaction }
776
1089
 
777
- expect(transaction).to_not include_params
778
- expect(log_contents(log)).to contains_log :error,
779
- "Error generating data (RuntimeError: foo) for"
1090
+ context "when the action is not set" do
1091
+ it "updates the action name on the transaction" do
1092
+ expect(transaction.action).to eq(nil)
1093
+ expect(transaction).to_not have_action
1094
+
1095
+ action_name = "PagesController#show"
1096
+ transaction.set_action_if_nil(action_name)
1097
+
1098
+ expect(transaction.action).to eq(action_name)
1099
+ expect(transaction).to have_action(action_name)
1100
+ end
1101
+
1102
+ context "when the given action is nil" do
1103
+ it "does not update the action name on the transaction" do
1104
+ action_name = "something"
1105
+ transaction.set_action("something")
1106
+ transaction.set_action_if_nil(nil)
1107
+
1108
+ expect(transaction.action).to eq(action_name)
1109
+ expect(transaction).to have_action(action_name)
780
1110
  end
781
1111
  end
782
1112
  end
783
1113
 
784
- describe "#sample_data" do
785
- let(:env) { { "rack.session" => { "session" => "value" } } }
1114
+ context "when the action is set" do
1115
+ it "does not update the action name on the transaction" do
1116
+ action_name = "something"
1117
+ transaction.set_action("something")
1118
+ transaction.set_action_if_nil("something else")
786
1119
 
787
- it "sets sample data" do
788
- transaction.set_tags "tag" => "value"
789
- transaction.add_breadcrumb "category", "action", "message", "key" => "value"
790
- transaction.sample_data
1120
+ expect(transaction.action).to eq(action_name)
1121
+ expect(transaction).to have_action(action_name)
1122
+ end
1123
+ end
1124
+ end
791
1125
 
792
- expect(transaction).to include_environment(
793
- "REQUEST_METHOD" => "GET",
794
- "SERVER_NAME" => "example.org",
795
- "SERVER_PORT" => "80",
796
- "PATH_INFO" => "/blog"
797
- )
798
- expect(transaction).to include_session_data("session" => "value")
799
- expect(transaction).to include_params(
800
- "controller" => "blog_posts",
801
- "action" => "show",
802
- "id" => "1"
803
- )
804
- expect(transaction).to include_sample_metadata("key" => "value")
805
- expect(transaction).to include_tags("tag" => "value")
806
- expect(transaction).to include_breadcrumb(
807
- "action",
808
- "category",
809
- "message",
810
- { "key" => "value" },
811
- kind_of(Integer)
1126
+ describe "#set_namespace" do
1127
+ let(:transaction) { new_transaction }
1128
+
1129
+ context "when the namespace is not nil" do
1130
+ it "updates the namespace on the transaction" do
1131
+ namespace = "custom"
1132
+ transaction.set_namespace(namespace)
1133
+
1134
+ expect(transaction.namespace).to eq namespace
1135
+ expect(transaction).to have_namespace(namespace)
1136
+ end
1137
+ end
1138
+
1139
+ context "when the namespace is nil" do
1140
+ it "does not update the namespace on the transaction" do
1141
+ namespace = "custom"
1142
+ transaction.set_namespace(namespace)
1143
+ transaction.set_namespace(nil)
1144
+
1145
+ expect(transaction.namespace).to eq(namespace)
1146
+ expect(transaction).to have_namespace(namespace)
1147
+ end
1148
+ end
1149
+ end
1150
+
1151
+ describe "#set_http_or_background_action" do
1152
+ let(:transaction) { new_transaction }
1153
+
1154
+ context "for a hash with controller and action" do
1155
+ it "sets the action" do
1156
+ transaction.set_http_or_background_action(
1157
+ :controller => "HomeController",
1158
+ :action => "show"
812
1159
  )
1160
+ expect(transaction).to have_action("HomeController#show")
813
1161
  end
814
1162
  end
815
1163
 
816
- describe "#set_error" do
817
- let(:env) { http_request_env_with_data }
818
- let(:error) do
819
- e = ExampleStandardError.new("test message")
820
- allow(e).to receive(:backtrace).and_return(["line 1"])
821
- e
1164
+ context "for a hash with just action" do
1165
+ it "sets the action" do
1166
+ transaction.set_http_or_background_action(:action => "show")
1167
+ expect(transaction).to have_action("show")
822
1168
  end
1169
+ end
823
1170
 
824
- it "should also respond to add_exception for backwards compatibility" do
825
- expect(transaction).to respond_to(:add_exception)
1171
+ context "for a hash with class and method" do
1172
+ it "sets the action" do
1173
+ transaction.set_http_or_background_action(:class => "Worker", :method => "perform")
1174
+ expect(transaction).to have_action("Worker#perform")
826
1175
  end
1176
+ end
827
1177
 
828
- it "should not add the error if appsignal is not active" do
829
- allow(Appsignal).to receive(:active?).and_return(false)
830
- expect(transaction.ext).to_not receive(:set_error)
1178
+ context "when action is already set" do
1179
+ it "does not overwrite the set action" do
1180
+ transaction.set_action("MyCustomAction#perform")
1181
+ transaction.set_http_or_background_action(:class => "Worker", :method => "perform")
1182
+ expect(transaction).to have_action("MyCustomAction#perform")
1183
+ end
1184
+ end
1185
+ end
831
1186
 
832
- transaction.set_error(error)
1187
+ describe "#set_queue_start" do
1188
+ let(:transaction) { new_transaction }
1189
+
1190
+ it "sets the queue start in extension" do
1191
+ transaction.set_queue_start(10)
1192
+
1193
+ expect(transaction).to have_queue_start(10)
1194
+ end
1195
+
1196
+ it "does not set the queue start in extension when value is nil" do
1197
+ transaction.set_queue_start(nil)
1198
+
1199
+ expect(transaction).to_not have_queue_start
1200
+ end
1201
+
1202
+ it "does not raise an error when the queue start is too big" do
1203
+ expect(transaction.ext).to receive(:set_queue_start).and_raise(RangeError)
1204
+
1205
+ expect(Appsignal.internal_logger).to receive(:warn).with("Queue start value 10 is too big")
1206
+
1207
+ transaction.set_queue_start(10)
1208
+ end
1209
+ end
1210
+
1211
+ describe "#set_http_or_background_queue_start" do
1212
+ let(:transaction) { legacy_new_transaction(:request => legacy_request(env)) }
1213
+ let(:err_stream) { std_stream }
1214
+ let(:stderr) { err_stream.read }
1215
+ let(:header_factor) { 1_000 }
1216
+ let(:env_queue_start) { fixed_time + 20 } # in seconds
1217
+
1218
+ def set_http_or_background_queue_start
1219
+ capture_std_streams(std_stream, err_stream) do
1220
+ transaction.set_http_or_background_queue_start
833
1221
  end
1222
+ end
834
1223
 
835
- context "when error is not an error" do
836
- let(:error) { Object.new }
1224
+ context "when a queue time is found in a request header" do
1225
+ let(:header_time) { ((fixed_time + 10) * header_factor).to_i } # in milliseconds
1226
+ let(:env) { { "HTTP_X_REQUEST_START" => "t=#{header_time}" } }
837
1227
 
838
- it "does not add the error" do
839
- expect(Appsignal.internal_logger).to receive(:error).with(
840
- "Appsignal::Transaction#set_error: Cannot set error. " \
841
- "The given value is not an exception: #{error.inspect}"
842
- )
843
- expect(transaction.ext).to_not receive(:set_error)
1228
+ it "sets the http header value in milliseconds on the transaction" do
1229
+ set_http_or_background_queue_start
844
1230
 
845
- transaction.set_error(error)
846
- end
1231
+ expect(transaction).to have_queue_start(1_389_783_610_000)
847
1232
  end
848
1233
 
849
- context "for a http request" do
850
- it "should set an error in the extension" do
851
- expect(transaction.ext).to receive(:set_error).with(
852
- "ExampleStandardError",
853
- "test message",
854
- Appsignal::Utils::Data.generate(["line 1"])
855
- )
1234
+ it "logs a deprecation message" do
1235
+ logs = capture_logs { set_http_or_background_queue_start }
856
1236
 
857
- transaction.set_error(error)
858
- end
1237
+ expect(logs).to contains_log(
1238
+ :warn,
1239
+ "The Appsignal::Transaction#set_http_or_background_queue_start " \
1240
+ "method has been deprecated."
1241
+ )
859
1242
  end
860
1243
 
861
- context "when the error has no causes" do
862
- it "should not send the causes information as sample data" do
863
- expect(transaction.ext).to_not receive(:set_sample_data)
1244
+ it "prints a deprecation message" do
1245
+ set_http_or_background_queue_start
864
1246
 
865
- transaction.set_error(error)
1247
+ expect(stderr).to include(
1248
+ "The Appsignal::Transaction#set_http_or_background_queue_start " \
1249
+ "method has been deprecated."
1250
+ )
1251
+ end
1252
+
1253
+ context "when a :queue_start key is found in the transaction environment" do
1254
+ let(:env) do
1255
+ {
1256
+ "HTTP_X_REQUEST_START" => "t=#{header_time}",
1257
+ :queue_start => env_queue_start
1258
+ }
866
1259
  end
1260
+
1261
+ it "sets the http header value in milliseconds on the transaction" do
1262
+ set_http_or_background_queue_start
1263
+
1264
+ expect(transaction).to have_queue_start(1_389_783_610_000)
1265
+ end
1266
+ end
1267
+ end
1268
+
1269
+ context "when a :queue_start key is found in the transaction environment" do
1270
+ let(:env) { { :queue_start => env_queue_start } } # in seconds
1271
+
1272
+ it "sets the :queue_start value in milliseconds on the transaction" do
1273
+ set_http_or_background_queue_start
1274
+
1275
+ expect(transaction).to have_queue_start(1_389_783_620_000)
867
1276
  end
1277
+ end
1278
+ end
1279
+
1280
+ describe "#set_metadata" do
1281
+ let(:transaction) { new_transaction }
1282
+
1283
+ it "updates the metadata on the transaction" do
1284
+ transaction.set_metadata("request_method", "GET")
1285
+
1286
+ expect(transaction).to include_metadata("request_method" => "GET")
1287
+ end
1288
+
1289
+ context "when filter_metadata includes metadata key" do
1290
+ before { Appsignal.config[:filter_metadata] = ["filter_key"] }
1291
+ after { Appsignal.config[:filter_metadata] = [] }
1292
+
1293
+ it "does not set the metadata on the transaction" do
1294
+ transaction.set_metadata(:filter_key, "filtered value")
1295
+ transaction.set_metadata("filter_key", "filtered value")
1296
+
1297
+ expect(transaction).to_not include_metadata("filter_key" => anything)
1298
+ end
1299
+ end
1300
+
1301
+ context "when the key is nil" do
1302
+ it "does not update the metadata on the transaction" do
1303
+ transaction.set_metadata(nil, "GET")
1304
+
1305
+ expect(transaction).to_not include_metadata
1306
+ end
1307
+ end
1308
+
1309
+ context "when the value is nil" do
1310
+ it "does not update the metadata on the transaction" do
1311
+ transaction.set_metadata("request_method", nil)
1312
+
1313
+ expect(transaction).to_not include_metadata
1314
+ end
1315
+ end
1316
+ end
1317
+
1318
+ describe "storing sample data" do
1319
+ let(:transaction) { new_transaction }
1320
+
1321
+ it "stores sample data on the transaction" do
1322
+ transaction.set_params(
1323
+ "string_param" => "string_value",
1324
+ :symbol_param => "symbol_value",
1325
+ "integer" => 123,
1326
+ "float" => 123.45,
1327
+ "array" => ["abc", 456, { "option" => true }],
1328
+ "hash" => { "hash_key" => "hash_value" }
1329
+ )
1330
+
1331
+ transaction._sample
1332
+ expect(transaction).to include_params(
1333
+ "string_param" => "string_value",
1334
+ "symbol_param" => "symbol_value",
1335
+ "integer" => 123,
1336
+ "float" => 123.45,
1337
+ "array" => ["abc", 456, { "option" => true }],
1338
+ "hash" => { "hash_key" => "hash_value" }
1339
+ )
1340
+ end
868
1341
 
869
- context "when the error has multiple causes" do
870
- let(:error) do
871
- e = ExampleStandardError.new("test message")
872
- e2 = RuntimeError.new("cause message")
873
- e3 = StandardError.new("cause message 2")
874
- allow(e).to receive(:backtrace).and_return(["line 1"])
875
- allow(e).to receive(:cause).and_return(e2)
876
- allow(e2).to receive(:cause).and_return(e3)
877
- e
1342
+ it "does not store non-Array and non-Hash data" do
1343
+ logs =
1344
+ capture_logs do
1345
+ transaction.set_params("some string")
1346
+ transaction._sample
1347
+ expect(transaction).to_not include_params
1348
+
1349
+ transaction.set_params(123)
1350
+ transaction._sample
1351
+ expect(transaction).to_not include_params
1352
+
1353
+ transaction.set_params(Class.new)
1354
+ transaction._sample
1355
+ expect(transaction).to_not include_params
1356
+
1357
+ set = Set.new
1358
+ set.add("some value")
1359
+ transaction.set_params(set)
1360
+ transaction._sample
1361
+ expect(transaction).to_not include_params
878
1362
  end
879
1363
 
880
- it "sends the causes information as sample data" do
881
- expect(transaction.ext).to receive(:set_error).with(
882
- "ExampleStandardError",
883
- "test message",
884
- Appsignal::Utils::Data.generate(["line 1"])
885
- )
1364
+ expect(logs).to contains_log :error,
1365
+ %(Invalid sample data for 'params'. Value is not an Array or Hash: '"some string"')
1366
+ expect(logs).to contains_log :error,
1367
+ %(Invalid sample data for 'params'. Value is not an Array or Hash: '123')
1368
+ expect(logs).to contains_log :error,
1369
+ %(Invalid sample data for 'params'. Value is not an Array or Hash: '"#<Class>"')
1370
+ expect(logs).to contains_log :error,
1371
+ %(Invalid sample data for 'params'. Value is not an Array or Hash: '"#<Set>"')
1372
+ end
886
1373
 
887
- expect(transaction.ext).to receive(:set_sample_data).with(
888
- "error_causes",
889
- Appsignal::Utils::Data.generate(
890
- [
891
- {
892
- :name => "RuntimeError",
893
- :message => "cause message"
894
- },
895
- {
896
- :name => "StandardError",
897
- :message => "cause message 2"
898
- }
899
- ]
900
- )
901
- )
1374
+ it "does not store data that can't be converted to JSON" do
1375
+ klass = Class.new do
1376
+ def initialize
1377
+ @calls = 0
1378
+ end
902
1379
 
903
- expect(Appsignal.internal_logger).to_not receive(:debug)
1380
+ def to_s
1381
+ raise "foo" if @calls > 0 # Cause a deliberate error
904
1382
 
905
- transaction.set_error(error)
1383
+ @calls += 1
906
1384
  end
907
1385
  end
908
1386
 
909
- context "when the error has too many causes" do
910
- let(:error) do
911
- e = ExampleStandardError.new("root cause error")
1387
+ transaction.set_params(klass.new => 1)
1388
+ logs = capture_logs { transaction._sample }
1389
+
1390
+ expect(transaction).to_not include_params
1391
+ expect(logs).to contains_log :error,
1392
+ "Error generating data (RuntimeError: foo) for"
1393
+ end
1394
+ end
1395
+
1396
+ describe "#set_sample_data" do
1397
+ let(:transaction) { new_transaction }
1398
+
1399
+ it "updates the sample data on the transaction" do
1400
+ silence do
1401
+ transaction.set_sample_data(
1402
+ "params",
1403
+ :controller => "blog_posts",
1404
+ :action => "show",
1405
+ :id => "1"
1406
+ )
1407
+ end
1408
+
1409
+ expect(transaction).to include_params(
1410
+ "action" => "show",
1411
+ "controller" => "blog_posts",
1412
+ "id" => "1"
1413
+ )
1414
+ end
912
1415
 
913
- 11.times do |i|
914
- next_e = ExampleStandardError.new("wrapper error #{i}")
915
- allow(next_e).to receive(:cause).and_return(e)
916
- e = next_e
1416
+ context "when the data is no Array or Hash" do
1417
+ it "does not update the sample data on the transaction" do
1418
+ logs =
1419
+ capture_logs do
1420
+ silence { transaction.set_sample_data("params", "string") }
917
1421
  end
918
1422
 
919
- allow(e).to receive(:backtrace).and_return(["line 1"])
920
- e
921
- end
1423
+ expect(transaction.to_h["sample_data"]).to eq({})
1424
+ expect(logs).to contains_log :error,
1425
+ %(Invalid sample data for 'params'. Value is not an Array or Hash: '"string"')
1426
+ end
1427
+ end
1428
+
1429
+ context "when the data cannot be converted to JSON" do
1430
+ it "does not update the sample data on the transaction" do
1431
+ klass = Class.new do
1432
+ def to_s
1433
+ raise "foo" # Cause a deliberate error
1434
+ end
1435
+ end
1436
+ logs =
1437
+ capture_logs do
1438
+ silence { transaction.set_sample_data("params", klass.new => 1) }
1439
+ end
1440
+
1441
+ expect(transaction).to_not include_params
1442
+ expect(logs).to contains_log :error,
1443
+ "Error generating data (RuntimeError: foo) for"
1444
+ end
1445
+ end
1446
+ end
1447
+
1448
+ describe "#sample_data" do
1449
+ let(:transaction) { legacy_new_transaction(:request => rack_request(env)) }
1450
+ let(:env) do
1451
+ Rack::MockRequest.env_for(
1452
+ "/blog",
1453
+ "REQUEST_METHOD" => "GET",
1454
+ "SERVER_NAME" => "example.org",
1455
+ "SERVER_PORT" => "80",
1456
+ "PATH_INFO" => "/blog",
1457
+ "rack.session" => { "session" => "value" },
1458
+ :params => {
1459
+ "controller" => "blog_posts",
1460
+ "action" => "show",
1461
+ "id" => "1"
1462
+ }
1463
+ ).merge(
1464
+ :metadata => { "metadata" => "value" }
1465
+ )
1466
+ end
1467
+
1468
+ it "sets sample data from request" do
1469
+ transaction.set_tags "tag" => "value"
1470
+ transaction.add_breadcrumb "category", "action", "message", "key" => "value"
1471
+ silence { transaction.sample_data }
1472
+
1473
+ expect(transaction).to include_environment(
1474
+ "REQUEST_METHOD" => "GET",
1475
+ "SERVER_NAME" => "example.org",
1476
+ "SERVER_PORT" => "80",
1477
+ "PATH_INFO" => "/blog"
1478
+ )
1479
+ expect(transaction).to include_session_data("session" => "value")
1480
+ expect(transaction).to include_params(
1481
+ "controller" => "blog_posts",
1482
+ "action" => "show",
1483
+ "id" => "1"
1484
+ )
1485
+ expect(transaction).to include_sample_metadata("metadata" => "value")
1486
+ expect(transaction).to include_tags("tag" => "value")
1487
+ expect(transaction).to include_breadcrumb(
1488
+ "action",
1489
+ "category",
1490
+ "message",
1491
+ { "key" => "value" },
1492
+ kind_of(Integer)
1493
+ )
1494
+ end
1495
+ end
1496
+
1497
+ describe "#set_error" do
1498
+ let(:transaction) { new_transaction }
1499
+ let(:env) { http_request_env_with_data }
1500
+ let(:error) do
1501
+ e = ExampleStandardError.new("test message")
1502
+ allow(e).to receive(:backtrace).and_return(["line 1"])
1503
+ e
1504
+ end
1505
+
1506
+ it "should also respond to add_exception for backwards compatibility" do
1507
+ expect(transaction).to respond_to(:add_exception)
1508
+ end
1509
+
1510
+ it "should not add the error if appsignal is not active" do
1511
+ allow(Appsignal).to receive(:active?).and_return(false)
1512
+ expect(transaction.ext).to_not receive(:set_error)
1513
+
1514
+ transaction.set_error(error)
1515
+ end
1516
+
1517
+ context "when error is not an error" do
1518
+ let(:error) { Object.new }
1519
+
1520
+ it "does not add the error" do
1521
+ expect(Appsignal.internal_logger).to receive(:error).with(
1522
+ "Appsignal::Transaction#set_error: Cannot set error. " \
1523
+ "The given value is not an exception: #{error.inspect}"
1524
+ )
1525
+ expect(transaction.ext).to_not receive(:set_error)
1526
+
1527
+ transaction.set_error(error)
1528
+ end
1529
+ end
1530
+
1531
+ context "for a http request" do
1532
+ it "should set an error in the extension" do
1533
+ expect(transaction.ext).to receive(:set_error).with(
1534
+ "ExampleStandardError",
1535
+ "test message",
1536
+ Appsignal::Utils::Data.generate(["line 1"])
1537
+ )
1538
+
1539
+ transaction.set_error(error)
1540
+ end
1541
+ end
1542
+
1543
+ context "when the error has no causes" do
1544
+ it "should not send the causes information as sample data" do
1545
+ expect(transaction.ext).to_not receive(:set_sample_data)
1546
+
1547
+ transaction.set_error(error)
1548
+ end
1549
+ end
1550
+
1551
+ context "when the error has multiple causes" do
1552
+ let(:error) do
1553
+ e = ExampleStandardError.new("test message")
1554
+ e2 = RuntimeError.new("cause message")
1555
+ e3 = StandardError.new("cause message 2")
1556
+ allow(e).to receive(:backtrace).and_return(["line 1"])
1557
+ allow(e).to receive(:cause).and_return(e2)
1558
+ allow(e2).to receive(:cause).and_return(e3)
1559
+ e
1560
+ end
1561
+
1562
+ it "sends the causes information as sample data" do
1563
+ expect(transaction.ext).to receive(:set_error).with(
1564
+ "ExampleStandardError",
1565
+ "test message",
1566
+ Appsignal::Utils::Data.generate(["line 1"])
1567
+ )
922
1568
 
923
- it "sends only the first causes as sample data" do
924
- expect(transaction.ext).to receive(:set_error).with(
925
- "ExampleStandardError",
926
- "wrapper error 10",
927
- Appsignal::Utils::Data.generate(["line 1"])
1569
+ expect(transaction.ext).to receive(:set_sample_data).with(
1570
+ "error_causes",
1571
+ Appsignal::Utils::Data.generate(
1572
+ [
1573
+ {
1574
+ :name => "RuntimeError",
1575
+ :message => "cause message"
1576
+ },
1577
+ {
1578
+ :name => "StandardError",
1579
+ :message => "cause message 2"
1580
+ }
1581
+ ]
928
1582
  )
1583
+ )
929
1584
 
930
- expected_error_causes = Array.new(10) do |i|
931
- {
932
- :name => "ExampleStandardError",
933
- :message => "wrapper error #{9 - i}"
934
- }
935
- end
936
-
937
- expected_error_causes.last[:is_root_cause] = false
1585
+ expect(Appsignal.internal_logger).to_not receive(:debug)
938
1586
 
939
- expect(transaction.ext).to receive(:set_sample_data).with(
940
- "error_causes",
941
- Appsignal::Utils::Data.generate(expected_error_causes)
942
- )
1587
+ transaction.set_error(error)
1588
+ end
1589
+ end
943
1590
 
944
- expect(Appsignal.internal_logger).to receive(:debug).with(
945
- "Appsignal::Transaction#set_error: Error has more " \
946
- "than 10 error causes. Only the first 10 " \
947
- "will be reported."
948
- )
1591
+ context "when the error has too many causes" do
1592
+ let(:error) do
1593
+ e = ExampleStandardError.new("root cause error")
949
1594
 
950
- transaction.set_error(error)
1595
+ 11.times do |i|
1596
+ next_e = ExampleStandardError.new("wrapper error #{i}")
1597
+ allow(next_e).to receive(:cause).and_return(e)
1598
+ e = next_e
951
1599
  end
1600
+
1601
+ allow(e).to receive(:backtrace).and_return(["line 1"])
1602
+ e
952
1603
  end
953
1604
 
954
- context "when error message is nil" do
955
- let(:error) do
956
- e = ExampleStandardError.new
957
- allow(e).to receive(:message).and_return(nil)
958
- allow(e).to receive(:backtrace).and_return(["line 1"])
959
- e
960
- end
1605
+ it "sends only the first causes as sample data" do
1606
+ expect(transaction.ext).to receive(:set_error).with(
1607
+ "ExampleStandardError",
1608
+ "wrapper error 10",
1609
+ Appsignal::Utils::Data.generate(["line 1"])
1610
+ )
961
1611
 
962
- it "should not raise an error" do
963
- transaction.set_error(error)
1612
+ expected_error_causes = Array.new(10) do |i|
1613
+ {
1614
+ :name => "ExampleStandardError",
1615
+ :message => "wrapper error #{9 - i}"
1616
+ }
964
1617
  end
965
1618
 
966
- it "should set an error in the extension" do
967
- expect(transaction.ext).to receive(:set_error).with(
968
- "ExampleStandardError",
969
- "",
970
- Appsignal::Utils::Data.generate(["line 1"])
971
- )
1619
+ expected_error_causes.last[:is_root_cause] = false
972
1620
 
973
- transaction.set_error(error)
974
- end
1621
+ expect(transaction.ext).to receive(:set_sample_data).with(
1622
+ "error_causes",
1623
+ Appsignal::Utils::Data.generate(expected_error_causes)
1624
+ )
1625
+
1626
+ expect(Appsignal.internal_logger).to receive(:debug).with(
1627
+ "Appsignal::Transaction#set_error: Error has more " \
1628
+ "than 10 error causes. Only the first 10 " \
1629
+ "will be reported."
1630
+ )
1631
+
1632
+ transaction.set_error(error)
975
1633
  end
976
1634
  end
977
1635
 
978
- describe "#start_event" do
979
- it "starts the event in the extension" do
980
- expect(transaction.ext).to receive(:start_event).with(0).and_call_original
1636
+ context "when error message is nil" do
1637
+ let(:error) do
1638
+ e = ExampleStandardError.new
1639
+ allow(e).to receive(:message).and_return(nil)
1640
+ allow(e).to receive(:backtrace).and_return(["line 1"])
1641
+ e
1642
+ end
981
1643
 
982
- transaction.start_event
1644
+ it "should not raise an error" do
1645
+ transaction.set_error(error)
983
1646
  end
984
1647
 
985
- context "when transaction is paused" do
986
- it "does not start the event" do
987
- transaction.pause!
988
- expect(transaction.ext).to_not receive(:start_event)
1648
+ it "should set an error in the extension" do
1649
+ expect(transaction.ext).to receive(:set_error).with(
1650
+ "ExampleStandardError",
1651
+ "",
1652
+ Appsignal::Utils::Data.generate(["line 1"])
1653
+ )
989
1654
 
990
- transaction.start_event
991
- end
1655
+ transaction.set_error(error)
992
1656
  end
993
1657
  end
1658
+ end
994
1659
 
995
- describe "#finish_event" do
996
- let(:fake_gc_time) { 0 }
1660
+ describe "#start_event" do
1661
+ let(:transaction) { new_transaction }
997
1662
 
998
- it "should finish the event in the extension" do
999
- expect(transaction.ext).to receive(:finish_event).with(
1000
- "name",
1001
- "title",
1002
- "body",
1003
- 1,
1004
- fake_gc_time
1005
- ).and_call_original
1663
+ it "starts the event in the extension" do
1664
+ expect(transaction.ext).to receive(:start_event).with(0).and_call_original
1006
1665
 
1007
- transaction.finish_event(
1008
- "name",
1009
- "title",
1010
- "body",
1011
- 1
1012
- )
1013
- end
1666
+ transaction.start_event
1667
+ end
1014
1668
 
1015
- it "should finish the event in the extension with nil arguments" do
1016
- expect(transaction.ext).to receive(:finish_event).with(
1017
- "name",
1018
- "",
1019
- "",
1020
- 0,
1021
- fake_gc_time
1022
- ).and_call_original
1669
+ context "when transaction is paused" do
1670
+ it "does not start the event" do
1671
+ transaction.pause!
1672
+ expect(transaction.ext).to_not receive(:start_event)
1023
1673
 
1024
- transaction.finish_event(
1025
- "name",
1026
- nil,
1027
- nil,
1028
- nil
1029
- )
1674
+ transaction.start_event
1030
1675
  end
1676
+ end
1677
+ end
1031
1678
 
1032
- context "when transaction is paused" do
1033
- it "does not finish the event" do
1034
- transaction.pause!
1035
- expect(transaction.ext).to_not receive(:finish_event)
1036
-
1037
- transaction.start_event
1038
- end
1039
- end
1679
+ describe "#finish_event" do
1680
+ let(:transaction) { new_transaction }
1681
+ let(:fake_gc_time) { 0 }
1682
+
1683
+ it "should finish the event in the extension" do
1684
+ expect(transaction.ext).to receive(:finish_event).with(
1685
+ "name",
1686
+ "title",
1687
+ "body",
1688
+ 1,
1689
+ fake_gc_time
1690
+ ).and_call_original
1691
+
1692
+ transaction.finish_event(
1693
+ "name",
1694
+ "title",
1695
+ "body",
1696
+ 1
1697
+ )
1040
1698
  end
1041
1699
 
1042
- describe "#record_event" do
1043
- let(:fake_gc_time) { 0 }
1700
+ it "should finish the event in the extension with nil arguments" do
1701
+ expect(transaction.ext).to receive(:finish_event).with(
1702
+ "name",
1703
+ "",
1704
+ "",
1705
+ 0,
1706
+ fake_gc_time
1707
+ ).and_call_original
1708
+
1709
+ transaction.finish_event(
1710
+ "name",
1711
+ nil,
1712
+ nil,
1713
+ nil
1714
+ )
1715
+ end
1044
1716
 
1045
- it "should record the event in the extension" do
1046
- expect(transaction.ext).to receive(:record_event).with(
1047
- "name",
1048
- "title",
1049
- "body",
1050
- 1,
1051
- 1000,
1052
- fake_gc_time
1053
- ).and_call_original
1717
+ context "when transaction is paused" do
1718
+ it "does not finish the event" do
1719
+ transaction.pause!
1720
+ expect(transaction.ext).to_not receive(:finish_event)
1054
1721
 
1055
- transaction.record_event(
1056
- "name",
1057
- "title",
1058
- "body",
1059
- 1000,
1060
- 1
1061
- )
1722
+ transaction.start_event
1062
1723
  end
1724
+ end
1725
+ end
1063
1726
 
1064
- it "should finish the event in the extension with nil arguments" do
1065
- expect(transaction.ext).to receive(:record_event).with(
1066
- "name",
1067
- "",
1068
- "",
1069
- 0,
1070
- 1000,
1071
- fake_gc_time
1072
- ).and_call_original
1727
+ describe "#record_event" do
1728
+ let(:transaction) { new_transaction }
1729
+ let(:fake_gc_time) { 0 }
1730
+
1731
+ it "should record the event in the extension" do
1732
+ expect(transaction.ext).to receive(:record_event).with(
1733
+ "name",
1734
+ "title",
1735
+ "body",
1736
+ 1,
1737
+ 1000,
1738
+ fake_gc_time
1739
+ ).and_call_original
1740
+
1741
+ transaction.record_event(
1742
+ "name",
1743
+ "title",
1744
+ "body",
1745
+ 1000,
1746
+ 1
1747
+ )
1748
+ end
1749
+
1750
+ it "should finish the event in the extension with nil arguments" do
1751
+ expect(transaction.ext).to receive(:record_event).with(
1752
+ "name",
1753
+ "",
1754
+ "",
1755
+ 0,
1756
+ 1000,
1757
+ fake_gc_time
1758
+ ).and_call_original
1759
+
1760
+ transaction.record_event(
1761
+ "name",
1762
+ nil,
1763
+ nil,
1764
+ 1000,
1765
+ nil
1766
+ )
1767
+ end
1768
+
1769
+ context "when transaction is paused" do
1770
+ it "does not record the event" do
1771
+ transaction.pause!
1772
+ expect(transaction.ext).to_not receive(:record_event)
1073
1773
 
1074
1774
  transaction.record_event(
1075
1775
  "name",
@@ -1079,172 +1779,192 @@ describe Appsignal::Transaction do
1079
1779
  nil
1080
1780
  )
1081
1781
  end
1782
+ end
1783
+ end
1082
1784
 
1083
- context "when transaction is paused" do
1084
- it "does not record the event" do
1085
- transaction.pause!
1086
- expect(transaction.ext).to_not receive(:record_event)
1087
-
1088
- transaction.record_event(
1089
- "name",
1090
- nil,
1091
- nil,
1092
- 1000,
1093
- nil
1094
- )
1095
- end
1096
- end
1785
+ describe "#instrument" do
1786
+ it_behaves_like "instrument helper" do
1787
+ let(:transaction) { new_transaction }
1788
+ let(:instrumenter) { transaction }
1097
1789
  end
1790
+ end
1098
1791
 
1099
- describe "#instrument" do
1100
- it_behaves_like "instrument helper" do
1101
- let(:instrumenter) { transaction }
1102
- end
1792
+ context "GenericRequest" do
1793
+ let(:env) { {} }
1794
+ subject { Appsignal::Transaction::GenericRequest.new(env) }
1795
+
1796
+ it "prints a deprecation warning on use" do
1797
+ err_stream = std_stream
1798
+ capture_std_streams(std_stream, err_stream) { subject }
1799
+
1800
+ expect(err_stream.read).to include(
1801
+ "appsignal WARNING: The use of Appsignal::Transaction::GenericRequest is deprecated."
1802
+ )
1103
1803
  end
1104
1804
 
1105
- context "generic request" do
1106
- let(:env) { {} }
1107
- subject { Appsignal::Transaction::GenericRequest.new(env) }
1805
+ it "logs a deprecation warning on use" do
1806
+ logs = capture_logs { silence { subject } }
1108
1807
 
1109
- it "initializes with an empty env" do
1110
- expect(subject.env).to be_empty
1111
- end
1808
+ expect(logs).to contains_log(
1809
+ :warn,
1810
+ "The use of Appsignal::Transaction::GenericRequest is deprecated."
1811
+ )
1812
+ end
1112
1813
 
1113
- context "when given an env" do
1114
- let(:env) do
1115
- {
1116
- :params => { :id => 1 },
1117
- :queue_start => 10
1118
- }
1119
- end
1814
+ it "initializes with an empty env" do
1815
+ expect(subject.env).to be_empty
1816
+ end
1120
1817
 
1121
- it "sets the given env" do
1122
- expect(subject.env).to eq env
1123
- end
1818
+ context "when given an env" do
1819
+ let(:env) do
1820
+ {
1821
+ :params => { :id => 1 },
1822
+ :queue_start => 10
1823
+ }
1824
+ end
1124
1825
 
1125
- it "sets the params present in the env" do
1126
- expect(subject.params).to eq(:id => 1)
1127
- end
1826
+ it "sets the given env" do
1827
+ expect(subject.env).to eq env
1128
1828
  end
1829
+
1830
+ it "sets the params present in the env" do
1831
+ expect(subject.params).to eq(:id => 1)
1832
+ end
1833
+ end
1834
+ end
1835
+
1836
+ # private
1837
+
1838
+ describe "#background_queue_start" do
1839
+ let(:transaction) { legacy_new_transaction(:request => request) }
1840
+ let(:request) { rack_request(env) }
1841
+ let(:env) { {} }
1842
+ subject { transaction.send(:background_queue_start) }
1843
+
1844
+ context "when request is nil" do
1845
+ let(:request) { nil }
1846
+
1847
+ it { is_expected.to eq nil }
1848
+ end
1849
+
1850
+ context "when env is nil" do
1851
+ before { expect(request).to receive(:env).and_return(nil) }
1852
+
1853
+ it { is_expected.to eq nil }
1854
+ end
1855
+
1856
+ context "when queue start is nil" do
1857
+ it { is_expected.to eq nil }
1129
1858
  end
1130
1859
 
1131
- # private
1860
+ context "when queue start is set" do
1861
+ before do
1862
+ env[:queue_start] = fixed_time
1863
+ end
1864
+
1865
+ it { is_expected.to eq 1_389_783_600_000 }
1866
+ end
1867
+ end
1132
1868
 
1133
- describe "#background_queue_start" do
1134
- subject { transaction.send(:background_queue_start) }
1869
+ describe "#http_queue_start" do
1870
+ let(:transaction) { legacy_new_transaction(:request => request) }
1871
+ let(:request) { rack_request(env) }
1872
+ let(:env) { {} }
1873
+ let(:slightly_earlier_time) { fixed_time - 0.4 }
1874
+ let(:slightly_earlier_time_value) { (slightly_earlier_time * factor).to_i }
1875
+ subject { transaction.send(:http_queue_start) }
1135
1876
 
1877
+ shared_examples "http queue start" do
1136
1878
  context "when request is nil" do
1137
1879
  let(:request) { nil }
1138
1880
 
1139
- it { is_expected.to eq nil }
1881
+ it { is_expected.to be_nil }
1140
1882
  end
1141
1883
 
1142
1884
  context "when env is nil" do
1143
- before { expect(transaction.request).to receive(:env).and_return(nil) }
1144
-
1145
- it { is_expected.to eq nil }
1146
- end
1885
+ before { expect(request).to receive(:env).and_return(nil) }
1147
1886
 
1148
- context "when queue start is nil" do
1149
- it { is_expected.to eq nil }
1887
+ it { is_expected.to be_nil }
1150
1888
  end
1151
1889
 
1152
- context "when queue start is set" do
1153
- let(:env) { background_env_with_data }
1890
+ context "with no relevant header set" do
1891
+ let(:env) { {} }
1154
1892
 
1155
- it { is_expected.to eq 1_389_783_600_000 }
1893
+ it { is_expected.to be_nil }
1156
1894
  end
1157
- end
1158
1895
 
1159
- describe "#http_queue_start" do
1160
- let(:slightly_earlier_time) { fixed_time - 0.4 }
1161
- let(:slightly_earlier_time_value) { (slightly_earlier_time * factor).to_i }
1162
- subject { transaction.send(:http_queue_start) }
1896
+ context "with the HTTP_X_REQUEST_START header set" do
1897
+ let(:env) { { "HTTP_X_REQUEST_START" => "t=#{slightly_earlier_time_value}" } }
1163
1898
 
1164
- shared_examples "http queue start" do
1165
- context "when request is nil" do
1166
- let(:request) { nil }
1899
+ it { is_expected.to eq 1_389_783_599_600 }
1900
+
1901
+ context "with unparsable content" do
1902
+ let(:env) { { "HTTP_X_REQUEST_START" => "something" } }
1167
1903
 
1168
1904
  it { is_expected.to be_nil }
1169
1905
  end
1170
1906
 
1171
- context "when env is nil" do
1172
- before { expect(transaction.request).to receive(:env).and_return(nil) }
1907
+ context "with unparsable content at the end" do
1908
+ let(:env) { { "HTTP_X_REQUEST_START" => "t=#{slightly_earlier_time_value}aaaa" } }
1173
1909
 
1174
- it { is_expected.to be_nil }
1910
+ it { is_expected.to eq 1_389_783_599_600 }
1175
1911
  end
1176
1912
 
1177
- context "with no relevant header set" do
1178
- let(:env) { {} }
1913
+ context "with a really low number" do
1914
+ let(:env) { { "HTTP_X_REQUEST_START" => "t=100" } }
1179
1915
 
1180
1916
  it { is_expected.to be_nil }
1181
1917
  end
1182
1918
 
1183
- context "with the HTTP_X_REQUEST_START header set" do
1184
- let(:env) { { "HTTP_X_REQUEST_START" => "t=#{slightly_earlier_time_value}" } }
1919
+ context "with the alternate HTTP_X_QUEUE_START header set" do
1920
+ let(:env) { { "HTTP_X_QUEUE_START" => "t=#{slightly_earlier_time_value}" } }
1185
1921
 
1186
1922
  it { is_expected.to eq 1_389_783_599_600 }
1187
-
1188
- context "with unparsable content" do
1189
- let(:env) { { "HTTP_X_REQUEST_START" => "something" } }
1190
-
1191
- it { is_expected.to be_nil }
1192
- end
1193
-
1194
- context "with unparsable content at the end" do
1195
- let(:env) { { "HTTP_X_REQUEST_START" => "t=#{slightly_earlier_time_value}aaaa" } }
1196
-
1197
- it { is_expected.to eq 1_389_783_599_600 }
1198
- end
1199
-
1200
- context "with a really low number" do
1201
- let(:env) { { "HTTP_X_REQUEST_START" => "t=100" } }
1202
-
1203
- it { is_expected.to be_nil }
1204
- end
1205
-
1206
- context "with the alternate HTTP_X_QUEUE_START header set" do
1207
- let(:env) { { "HTTP_X_QUEUE_START" => "t=#{slightly_earlier_time_value}" } }
1208
-
1209
- it { is_expected.to eq 1_389_783_599_600 }
1210
- end
1211
1923
  end
1212
1924
  end
1925
+ end
1213
1926
 
1214
- context "time in milliseconds" do
1215
- let(:factor) { 1_000 }
1927
+ context "time in milliseconds" do
1928
+ let(:factor) { 1_000 }
1216
1929
 
1217
- it_should_behave_like "http queue start"
1218
- end
1930
+ it_should_behave_like "http queue start"
1931
+ end
1219
1932
 
1220
- context "time in microseconds" do
1221
- let(:factor) { 1_000_000 }
1933
+ context "time in microseconds" do
1934
+ let(:factor) { 1_000_000 }
1222
1935
 
1223
- it_should_behave_like "http queue start"
1224
- end
1936
+ it_should_behave_like "http queue start"
1225
1937
  end
1938
+ end
1226
1939
 
1227
- describe "#sanitized_params" do
1228
- subject { transaction.send(:sanitized_params) }
1940
+ describe "#sanitized_params" do
1941
+ let(:transaction) { new_transaction }
1942
+ subject { transaction.send(:sanitized_params) }
1229
1943
 
1230
- context "with custom params" do
1231
- before do
1232
- transaction.set_params(:foo => "bar", :baz => :bat)
1233
- end
1944
+ context "with params" do
1945
+ before do
1946
+ transaction.set_params(:foo => "bar", :baz => :bat)
1947
+ end
1234
1948
 
1235
- it "returns custom params" do
1236
- is_expected.to eq(:foo => "bar", :baz => :bat)
1237
- end
1949
+ it "returns params" do
1950
+ is_expected.to eq(:foo => "bar", :baz => :bat)
1951
+ end
1238
1952
 
1239
- context "with AppSignal filtering" do
1240
- before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
1241
- after { Appsignal.config.config_hash[:filter_parameters] = [] }
1953
+ context "with AppSignal filtering" do
1954
+ before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
1955
+ after { Appsignal.config.config_hash[:filter_parameters] = [] }
1242
1956
 
1243
- it "returns sanitized custom params" do
1244
- expect(subject).to eq(:foo => "[FILTERED]", :baz => :bat)
1245
- end
1957
+ it "returns sanitized custom params" do
1958
+ expect(subject).to eq(:foo => "[FILTERED]", :baz => :bat)
1246
1959
  end
1247
1960
  end
1961
+ end
1962
+
1963
+ context "params from request" do
1964
+ let(:transaction) { legacy_new_transaction(:request => request, :options => options) }
1965
+ let(:options) { {} }
1966
+ let(:request) { rack_request(env) }
1967
+ let(:env) { {} }
1248
1968
 
1249
1969
  context "without request params" do
1250
1970
  before { allow(transaction.request).to receive(:params).and_return(nil) }
@@ -1253,7 +1973,7 @@ describe Appsignal::Transaction do
1253
1973
  end
1254
1974
 
1255
1975
  context "when request params crashes" do
1256
- before { allow(transaction.request).to receive(:params).and_raise(NoMethodError) }
1976
+ before { expect(request).to receive(:params).and_raise(NoMethodError) }
1257
1977
 
1258
1978
  it { is_expected.to be_nil }
1259
1979
  end
@@ -1272,11 +1992,7 @@ describe Appsignal::Transaction do
1272
1992
  end
1273
1993
 
1274
1994
  context "with an array" do
1275
- let(:request) do
1276
- Appsignal::Transaction::GenericRequest.new(
1277
- background_env_with_data(:params => %w[arg1 arg2])
1278
- )
1279
- end
1995
+ let(:request) { legacy_request(:params => %w[arg1 arg2]) }
1280
1996
 
1281
1997
  it { is_expected.to eq %w[arg1 arg2] }
1282
1998
 
@@ -1290,11 +2006,7 @@ describe Appsignal::Transaction do
1290
2006
 
1291
2007
  context "with env" do
1292
2008
  context "with sanitization" do
1293
- let(:request) do
1294
- Appsignal::Transaction::GenericRequest.new(
1295
- http_request_env_with_data(:params => { :foo => :bar })
1296
- )
1297
- end
2009
+ let(:request) { legacy_request(:params => { :foo => :bar }) }
1298
2010
 
1299
2011
  it "should call the params sanitizer" do
1300
2012
  expect(subject).to eq(:foo => :bar)
@@ -1302,11 +2014,7 @@ describe Appsignal::Transaction do
1302
2014
  end
1303
2015
 
1304
2016
  context "with AppSignal filtering" do
1305
- let(:request) do
1306
- Appsignal::Transaction::GenericRequest.new(
1307
- http_request_env_with_data(:params => { :foo => :bar, :baz => :bat })
1308
- )
1309
- end
2017
+ let(:request) { legacy_request(:params => { :foo => :bar, :baz => :bat }) }
1310
2018
  before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
1311
2019
  after { Appsignal.config.config_hash[:filter_parameters] = [] }
1312
2020
 
@@ -1316,93 +2024,139 @@ describe Appsignal::Transaction do
1316
2024
  end
1317
2025
  end
1318
2026
  end
2027
+ end
1319
2028
 
1320
- describe "#sanitized_environment" do
1321
- let(:allowlisted_keys) { Appsignal.config[:request_headers] }
1322
- subject { transaction.send(:sanitized_environment) }
2029
+ describe "#sanitized_environment" do
2030
+ let(:transaction) { legacy_new_transaction(:request => request) }
2031
+ let(:request) { rack_request(env) }
2032
+ let(:env) { {} }
2033
+ let(:allowlisted_keys) { Appsignal.config[:request_headers] }
2034
+ subject { transaction.send(:sanitized_environment) }
1323
2035
 
1324
- context "when request is nil" do
1325
- let(:request) { nil }
2036
+ context "when request is nil" do
2037
+ let(:request) { nil }
1326
2038
 
1327
- it { is_expected.to be_nil }
1328
- end
2039
+ it { is_expected.to be_nil }
2040
+ end
1329
2041
 
1330
- context "when env is nil" do
1331
- before { expect(transaction.request).to receive(:env).and_return(nil) }
2042
+ context "when env is nil" do
2043
+ before { expect(request).to receive(:env).and_return(nil) }
1332
2044
 
1333
- it { is_expected.to be_nil }
2045
+ it { is_expected.to be_nil }
2046
+ end
2047
+
2048
+ context "when env is present" do
2049
+ let(:env) do
2050
+ {}.tap do |hash|
2051
+ allowlisted_keys.each { |o| hash[o] = 1 } # use all allowlisted keys
2052
+ hash[allowlisted_keys] = nil # don't add if nil
2053
+ hash[:not_allowlisted] = "I will be sanitized"
2054
+ end
1334
2055
  end
1335
2056
 
1336
- context "when env is present" do
1337
- let(:env) do
1338
- {}.tap do |hash|
1339
- allowlisted_keys.each { |o| hash[o] = 1 } # use all allowlisted keys
1340
- hash[allowlisted_keys] = nil # don't add if nil
1341
- hash[:not_allowlisted] = "I will be sanitized"
1342
- end
2057
+ it "only sets allowlisted keys" do
2058
+ expect(subject.keys).to match_array(allowlisted_keys)
2059
+ end
2060
+
2061
+ context "with configured request_headers" do
2062
+ before do
2063
+ Appsignal.config.config_hash[:request_headers] = %w[CONTENT_LENGTH]
1343
2064
  end
1344
2065
 
1345
2066
  it "only sets allowlisted keys" do
1346
- expect(subject.keys).to match_array(allowlisted_keys)
2067
+ expect(subject.keys).to match_array(%w[CONTENT_LENGTH])
1347
2068
  end
2069
+ end
2070
+ end
2071
+ end
1348
2072
 
1349
- context "with configured request_headers" do
1350
- before do
1351
- Appsignal.config.config_hash[:request_headers] = %w[CONTENT_LENGTH]
1352
- end
2073
+ describe "#sanitized_session_data" do
2074
+ let(:transaction) { legacy_new_transaction(:request => request) }
2075
+ let(:request) { rack_request(env) }
2076
+ let(:env) { {} }
2077
+ subject { transaction.send(:sanitized_session_data) }
1353
2078
 
1354
- it "only sets allowlisted keys" do
1355
- expect(subject.keys).to match_array(%w[CONTENT_LENGTH])
1356
- end
1357
- end
1358
- end
2079
+ context "when request is nil" do
2080
+ let(:request) { nil }
2081
+
2082
+ it { is_expected.to be_nil }
1359
2083
  end
1360
2084
 
1361
- describe "#sanitized_session_data" do
1362
- subject { transaction.send(:sanitized_session_data) }
2085
+ context "when session is nil" do
2086
+ before { expect(transaction.request).to receive(:session).and_return(nil) }
1363
2087
 
1364
- context "when request is nil" do
1365
- let(:request) { nil }
2088
+ it { is_expected.to be_nil }
2089
+ end
1366
2090
 
1367
- it { is_expected.to be_nil }
1368
- end
2091
+ context "when session is empty" do
2092
+ before { expect(transaction.request).to receive(:session).and_return({}) }
1369
2093
 
1370
- context "when session is nil" do
1371
- before { expect(transaction.request).to receive(:session).and_return(nil) }
2094
+ it { is_expected.to eq({}) }
2095
+ end
1372
2096
 
1373
- it { is_expected.to be_nil }
1374
- end
2097
+ context "when request class does not have a session method" do
2098
+ let(:request) { Appsignal::Transaction::GenericRequest.new({}) }
2099
+
2100
+ it { is_expected.to be_nil }
2101
+ end
1375
2102
 
1376
- context "when session is empty" do
1377
- before { expect(transaction.request).to receive(:session).and_return({}) }
2103
+ context "with a session" do
2104
+ let(:session_data_filter) { [] }
2105
+ before { Appsignal.config[:filter_session_data] = session_data_filter }
2106
+ after { Appsignal.config[:filter_session_data] = [] }
1378
2107
 
1379
- it { is_expected.to eq({}) }
1380
- end
2108
+ context "with generic session object" do
2109
+ before do
2110
+ expect(transaction).to respond_to(:request)
2111
+ allow(transaction).to receive_message_chain(
2112
+ :request,
2113
+ :session => { :foo => :bar, :abc => :def }
2114
+ )
2115
+ allow(transaction).to receive_message_chain(:request, :fullpath => :bar)
2116
+ end
2117
+
2118
+ context "without session filtering" do
2119
+ it "keeps the session data intact" do
2120
+ expect(subject).to eq(:foo => :bar, :abc => :def)
2121
+ end
2122
+ end
1381
2123
 
1382
- context "when request class does not have a session method" do
1383
- let(:request) { Appsignal::Transaction::GenericRequest.new({}) }
2124
+ context "with session filtering" do
2125
+ let(:session_data_filter) { %w[foo] }
1384
2126
 
1385
- it { is_expected.to be_nil }
2127
+ it "filters the session data" do
2128
+ expect(subject).to eq(:foo => "[FILTERED]", :abc => :def)
2129
+ end
2130
+ end
1386
2131
  end
1387
2132
 
1388
- context "with a session" do
1389
- let(:session_data_filter) { [] }
1390
- before { Appsignal.config[:filter_session_data] = session_data_filter }
1391
- after { Appsignal.config[:filter_session_data] = [] }
2133
+ if defined? ActionDispatch::Request::Session
2134
+ context "with ActionDispatch::Request::Session" do
2135
+ let(:action_dispatch_session) do
2136
+ store = Class.new do
2137
+ def load_session(_env)
2138
+ [1, { :foo => :bar, :abc => :def }]
2139
+ end
1392
2140
 
1393
- context "with generic session object" do
2141
+ def session_exists?(_env)
2142
+ true
2143
+ end
2144
+ end.new
2145
+ ActionDispatch::Request::Session.create(store,
2146
+ ActionDispatch::Request.new("rack.input" => StringIO.new), {})
2147
+ end
1394
2148
  before do
1395
2149
  expect(transaction).to respond_to(:request)
1396
2150
  allow(transaction).to receive_message_chain(
1397
2151
  :request,
1398
- :session => { :foo => :bar, :abc => :def }
2152
+ :session => action_dispatch_session
1399
2153
  )
1400
2154
  allow(transaction).to receive_message_chain(:request, :fullpath => :bar)
1401
2155
  end
1402
2156
 
1403
2157
  context "without session filtering" do
1404
2158
  it "keeps the session data intact" do
1405
- expect(subject).to eq(:foo => :bar, :abc => :def)
2159
+ expect(subject).to eq("foo" => :bar, "abc" => :def)
1406
2160
  end
1407
2161
  end
1408
2162
 
@@ -1410,120 +2164,87 @@ describe Appsignal::Transaction do
1410
2164
  let(:session_data_filter) { %w[foo] }
1411
2165
 
1412
2166
  it "filters the session data" do
1413
- expect(subject).to eq(:foo => "[FILTERED]", :abc => :def)
1414
- end
1415
- end
1416
- end
1417
-
1418
- if defined? ActionDispatch::Request::Session
1419
- context "with ActionDispatch::Request::Session" do
1420
- let(:action_dispatch_session) do
1421
- store = Class.new do
1422
- def load_session(_env)
1423
- [1, { :foo => :bar, :abc => :def }]
1424
- end
1425
-
1426
- def session_exists?(_env)
1427
- true
1428
- end
1429
- end.new
1430
- ActionDispatch::Request::Session.create(store,
1431
- ActionDispatch::Request.new("rack.input" => StringIO.new), {})
1432
- end
1433
- before do
1434
- expect(transaction).to respond_to(:request)
1435
- allow(transaction).to receive_message_chain(
1436
- :request,
1437
- :session => action_dispatch_session
1438
- )
1439
- allow(transaction).to receive_message_chain(:request, :fullpath => :bar)
1440
- end
1441
-
1442
- context "without session filtering" do
1443
- it "keeps the session data intact" do
1444
- expect(subject).to eq("foo" => :bar, "abc" => :def)
1445
- end
1446
- end
1447
-
1448
- context "with session filtering" do
1449
- let(:session_data_filter) { %w[foo] }
1450
-
1451
- it "filters the session data" do
1452
- expect(subject).to eq("foo" => "[FILTERED]", "abc" => :def)
1453
- end
2167
+ expect(subject).to eq("foo" => "[FILTERED]", "abc" => :def)
1454
2168
  end
1455
2169
  end
1456
2170
  end
2171
+ end
1457
2172
 
1458
- context "when not sending session data" do
1459
- before { Appsignal.config[:send_session_data] = false }
2173
+ context "when not sending session data" do
2174
+ before { Appsignal.config[:send_session_data] = false }
1460
2175
 
1461
- it "does not set any session data on the transaction" do
1462
- expect(subject).to be_nil
1463
- end
2176
+ it "does not set any session data on the transaction" do
2177
+ expect(subject).to be_nil
1464
2178
  end
1465
2179
  end
1466
2180
  end
2181
+ end
1467
2182
 
1468
- describe "#sanitized_metadata" do
1469
- subject { transaction.send(:sanitized_metadata) }
2183
+ describe "#sanitized_metadata" do
2184
+ let(:transaction) { legacy_new_transaction(:request => request) }
2185
+ let(:request) { rack_request(env) }
2186
+ let(:env) { {} }
2187
+ subject { transaction.send(:sanitized_metadata) }
1470
2188
 
1471
- context "when request is nil" do
1472
- let(:request) { nil }
2189
+ context "when request is nil" do
2190
+ let(:request) { nil }
1473
2191
 
1474
- it { is_expected.to be_nil }
1475
- end
2192
+ it { is_expected.to be_nil }
2193
+ end
1476
2194
 
1477
- context "when env is nil" do
1478
- before { expect(transaction.request).to receive(:env).and_return(nil) }
2195
+ context "when env is nil" do
2196
+ before { expect(request).to receive(:env).and_return(nil) }
1479
2197
 
1480
- it { is_expected.to be_nil }
1481
- end
2198
+ it { is_expected.to be_nil }
2199
+ end
1482
2200
 
1483
- context "when env is present" do
1484
- let(:env) { { "key" => "value" } }
2201
+ context "when env is present" do
2202
+ let(:env) { { :metadata => { "key" => "value" } } }
1485
2203
 
1486
- it { is_expected.to eq("key" => "value") }
2204
+ it do
2205
+ is_expected.to eq("key" => "value")
2206
+ end
1487
2207
 
1488
- context "with filter_metadata option set" do
1489
- before { Appsignal.config[:filter_metadata] = ["key"] }
1490
- after { Appsignal.config[:filter_metadata] = [] }
2208
+ context "with filter_metadata option set" do
2209
+ before { Appsignal.config[:filter_metadata] = ["key"] }
2210
+ after { Appsignal.config[:filter_metadata] = [] }
1491
2211
 
1492
- it "filters out keys listed in the filter_metadata option" do
1493
- expect(subject.keys).to_not include("key")
1494
- end
2212
+ it "filters out keys listed in the filter_metadata option" do
2213
+ expect(subject.keys).to_not include("key")
1495
2214
  end
1496
2215
  end
1497
2216
  end
2217
+ end
1498
2218
 
1499
- describe "#cleaned_backtrace" do
1500
- subject { transaction.send(:cleaned_backtrace, ["line 1", "line 2"]) }
2219
+ describe "#cleaned_backtrace" do
2220
+ let(:transaction) { new_transaction }
2221
+ subject { transaction.send(:cleaned_backtrace, ["line 1", "line 2"]) }
1501
2222
 
1502
- it "returns the backtrace" do
1503
- expect(subject).to eq ["line 1", "line 2"]
1504
- end
2223
+ it "returns the backtrace" do
2224
+ expect(subject).to eq ["line 1", "line 2"]
2225
+ end
1505
2226
 
1506
- context "with Rails module but without backtrace_cleaner method" do
1507
- it "returns the backtrace uncleaned" do
1508
- stub_const("Rails", Module.new)
1509
- expect(subject).to eq ["line 1", "line 2"]
1510
- end
2227
+ context "with Rails module but without backtrace_cleaner method" do
2228
+ it "returns the backtrace uncleaned" do
2229
+ stub_const("Rails", Module.new)
2230
+ expect(subject).to eq ["line 1", "line 2"]
1511
2231
  end
2232
+ end
1512
2233
 
1513
- if rails_present?
1514
- context "with rails" do
1515
- it "cleans the backtrace with the Rails backtrace cleaner" do
1516
- ::Rails.backtrace_cleaner.add_filter do |line|
1517
- line.tr("2", "?")
1518
- end
1519
- expect(subject).to eq ["line 1", "line ?"]
2234
+ if rails_present?
2235
+ context "with rails" do
2236
+ it "cleans the backtrace with the Rails backtrace cleaner" do
2237
+ ::Rails.backtrace_cleaner.add_filter do |line|
2238
+ line.tr("2", "?")
1520
2239
  end
2240
+ expect(subject).to eq ["line 1", "line ?"]
1521
2241
  end
1522
2242
  end
1523
2243
  end
1524
2244
  end
1525
2245
 
1526
2246
  describe "#cleaned_error_message" do
2247
+ let(:transaction) { new_transaction }
1527
2248
  let(:error) { StandardError.new("Error message") }
1528
2249
  subject { transaction.send(:cleaned_error_message, error) }
1529
2250
 
@@ -1570,6 +2291,7 @@ describe Appsignal::Transaction do
1570
2291
  end
1571
2292
 
1572
2293
  describe ".to_hash / .to_h" do
2294
+ let(:transaction) { new_transaction }
1573
2295
  subject { transaction.to_hash }
1574
2296
 
1575
2297
  context "when extension returns serialized JSON" do
@@ -1578,9 +2300,9 @@ describe Appsignal::Transaction do
1578
2300
  "action" => nil,
1579
2301
  "error" => nil,
1580
2302
  "events" => [],
1581
- "id" => transaction_id,
2303
+ "id" => kind_of(String),
1582
2304
  "metadata" => {},
1583
- "namespace" => namespace,
2305
+ "namespace" => default_namespace,
1584
2306
  "sample_data" => {}
1585
2307
  )
1586
2308
  end
@@ -1613,7 +2335,7 @@ describe Appsignal::Transaction do
1613
2335
  subject.set_http_or_background_queue_start
1614
2336
  subject.set_metadata("key", "value")
1615
2337
  subject.set_sample_data("key", "data")
1616
- subject.sample_data
2338
+ subject._sample
1617
2339
  subject.set_error("a")
1618
2340
  end
1619
2341
  end