appsignal 3.13.0 → 4.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +499 -487
  3. data/CHANGELOG.md +113 -0
  4. data/Rakefile +31 -7
  5. data/benchmark.rake +4 -6
  6. data/build_matrix.yml +45 -39
  7. data/ext/agent.rb +27 -27
  8. data/ext/appsignal_extension.c +25 -0
  9. data/gemfiles/rails-7.2.gemfile +11 -0
  10. data/lib/appsignal/check_in/cron.rb +2 -15
  11. data/lib/appsignal/cli/diagnose.rb +37 -28
  12. data/lib/appsignal/cli/install.rb +5 -1
  13. data/lib/appsignal/config.rb +57 -119
  14. data/lib/appsignal/demo.rb +2 -2
  15. data/lib/appsignal/extension/jruby.rb +14 -0
  16. data/lib/appsignal/helpers/instrumentation.rb +139 -417
  17. data/lib/appsignal/helpers/metrics.rb +0 -16
  18. data/lib/appsignal/hooks/action_cable.rb +8 -8
  19. data/lib/appsignal/hooks/active_job.rb +2 -2
  20. data/lib/appsignal/hooks/at_exit.rb +37 -0
  21. data/lib/appsignal/hooks.rb +1 -16
  22. data/lib/appsignal/integrations/action_cable.rb +2 -2
  23. data/lib/appsignal/integrations/capistrano/appsignal.cap +2 -4
  24. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +1 -4
  25. data/lib/appsignal/integrations/delayed_job_plugin.rb +3 -3
  26. data/lib/appsignal/integrations/que.rb +2 -2
  27. data/lib/appsignal/integrations/railtie.rb +26 -59
  28. data/lib/appsignal/integrations/rake.rb +2 -2
  29. data/lib/appsignal/integrations/resque.rb +2 -2
  30. data/lib/appsignal/integrations/shoryuken.rb +4 -4
  31. data/lib/appsignal/integrations/sidekiq.rb +3 -3
  32. data/lib/appsignal/integrations/webmachine.rb +2 -2
  33. data/lib/appsignal/loaders.rb +1 -1
  34. data/lib/appsignal/probes.rb +0 -9
  35. data/lib/appsignal/rack/abstract_middleware.rb +4 -26
  36. data/lib/appsignal/rack/event_handler.rb +4 -4
  37. data/lib/appsignal/rack/rails_instrumentation.rb +1 -1
  38. data/lib/appsignal/rack.rb +0 -25
  39. data/lib/appsignal/sample_data.rb +95 -0
  40. data/lib/appsignal/transaction.rb +235 -361
  41. data/lib/appsignal/utils/rails_helper.rb +4 -0
  42. data/lib/appsignal/version.rb +1 -1
  43. data/lib/appsignal.rb +19 -71
  44. data/spec/lib/appsignal/auth_check_spec.rb +1 -1
  45. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  46. data/spec/lib/appsignal/capistrano3_spec.rb +53 -13
  47. data/spec/lib/appsignal/check_in_spec.rb +1 -207
  48. data/spec/lib/appsignal/cli/demo_spec.rb +7 -27
  49. data/spec/lib/appsignal/cli/diagnose_spec.rb +145 -110
  50. data/spec/lib/appsignal/config_spec.rb +304 -379
  51. data/spec/lib/appsignal/extension_install_failure_spec.rb +5 -1
  52. data/spec/lib/appsignal/extension_spec.rb +5 -1
  53. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +1 -1
  54. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +1 -2
  55. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +1 -0
  56. data/spec/lib/appsignal/hooks/activejob_spec.rb +7 -12
  57. data/spec/lib/appsignal/hooks/at_exit_spec.rb +72 -0
  58. data/spec/lib/appsignal/hooks/gvl_spec.rb +10 -5
  59. data/spec/lib/appsignal/hooks/http_spec.rb +3 -3
  60. data/spec/lib/appsignal/hooks/net_http_spec.rb +3 -3
  61. data/spec/lib/appsignal/hooks/rake_spec.rb +6 -9
  62. data/spec/lib/appsignal/hooks/redis_client_spec.rb +5 -10
  63. data/spec/lib/appsignal/hooks/redis_spec.rb +4 -7
  64. data/spec/lib/appsignal/hooks/resque_spec.rb +3 -5
  65. data/spec/lib/appsignal/hooks_spec.rb +0 -41
  66. data/spec/lib/appsignal/integrations/data_mapper_spec.rb +29 -20
  67. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +4 -9
  68. data/spec/lib/appsignal/integrations/railtie_spec.rb +179 -157
  69. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +3 -5
  70. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +48 -62
  71. data/spec/lib/appsignal/loaders/hanami_spec.rb +6 -9
  72. data/spec/lib/appsignal/loaders/padrino_spec.rb +6 -10
  73. data/spec/lib/appsignal/loaders/sinatra_spec.rb +6 -9
  74. data/spec/lib/appsignal/loaders_spec.rb +8 -1
  75. data/spec/lib/appsignal/marker_spec.rb +1 -1
  76. data/spec/lib/appsignal/probes_spec.rb +4 -83
  77. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +4 -63
  78. data/spec/lib/appsignal/rack/event_handler_spec.rb +18 -15
  79. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +3 -11
  80. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +4 -5
  81. data/spec/lib/appsignal/sample_data_spec.rb +174 -0
  82. data/spec/lib/appsignal/transaction_spec.rb +791 -1031
  83. data/spec/lib/appsignal/transmitter_spec.rb +6 -8
  84. data/spec/lib/appsignal_spec.rb +294 -643
  85. data/spec/spec_helper.rb +1 -3
  86. data/spec/support/fixtures/projects/valid/config/appsignal.yml +4 -7
  87. data/spec/support/fixtures/projects/valid_with_rails_app/config/application.rb +16 -0
  88. data/spec/support/fixtures/projects/valid_with_rails_app/config/appsignal.yml +56 -0
  89. data/spec/support/fixtures/projects/valid_with_rails_app/config/environment.rb +5 -0
  90. data/spec/support/helpers/api_request_helper.rb +3 -2
  91. data/spec/support/helpers/config_helpers.rb +41 -11
  92. data/spec/support/helpers/dependency_helper.rb +8 -0
  93. data/spec/support/helpers/log_helpers.rb +1 -0
  94. data/spec/support/helpers/rails_helper.rb +6 -6
  95. data/spec/support/helpers/transaction_helpers.rb +2 -24
  96. data/spec/support/matchers/transaction.rb +3 -3
  97. data/spec/support/mocks/appsignal_mock.rb +3 -3
  98. data/spec/support/mocks/mock_probe.rb +2 -0
  99. data/spec/support/testing.rb +2 -2
  100. metadata +12 -22
  101. data/gemfiles/que_beta.gemfile +0 -5
  102. data/lib/appsignal/helpers/heartbeat.rb +0 -20
  103. data/lib/appsignal/integrations/grape.rb +0 -35
  104. data/lib/appsignal/integrations/hanami.rb +0 -13
  105. data/lib/appsignal/integrations/padrino.rb +0 -13
  106. data/lib/appsignal/integrations/sinatra.rb +0 -13
  107. data/lib/appsignal/rack/generic_instrumentation.rb +0 -22
  108. data/lib/appsignal/rack/streaming_listener.rb +0 -28
  109. data/spec/lib/appsignal/integrations/grape_spec.rb +0 -36
  110. data/spec/lib/appsignal/integrations/hanami_spec.rb +0 -17
  111. data/spec/lib/appsignal/integrations/padrino_spec.rb +0 -15
  112. data/spec/lib/appsignal/integrations/sinatra_spec.rb +0 -15
  113. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +0 -81
  114. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +0 -69
  115. data/spec/support/fixtures/projects/valid/config/environments/development.rb +0 -0
  116. data/spec/support/fixtures/projects/valid/config/environments/production.rb +0 -0
  117. data/spec/support/fixtures/projects/valid/config/environments/test.rb +0 -0
  118. data/spec/support/rails/my_app.rb +0 -6
  119. /data/spec/support/fixtures/projects/{valid/config/application.rb → valid_with_rails_app/log/.gitkeep} +0 -0
@@ -1,8 +1,9 @@
1
1
  describe Appsignal::Transaction do
2
+ let(:options) { {} }
2
3
  let(:time) { Time.at(fixed_time) }
3
4
 
4
5
  before do
5
- start_agent
6
+ start_agent(:options => options)
6
7
  Timecop.freeze(time)
7
8
  end
8
9
  after { Timecop.return }
@@ -32,86 +33,13 @@ describe Appsignal::Transaction do
32
33
  transaction = create_transaction
33
34
  expect(transaction).to eq current_transaction
34
35
  end
36
+ end
35
37
 
36
- context "with legacy arguments" do
37
- it "returns the created transaction" do
38
- transaction_id = "mock-id"
39
- namespace = "my_namespace"
40
- transaction = legacy_create_transaction(
41
- :id => transaction_id,
42
- :namespace => namespace
43
- )
44
- expect(transaction).to be_a(Appsignal::Transaction)
45
-
46
- expect(transaction).to have_id(transaction_id)
47
- expect(transaction.transaction_id).to eq(transaction_id)
48
-
49
- expect(transaction).to have_namespace(namespace)
50
- expect(transaction.namespace).to eq(namespace)
51
- end
52
-
53
- it "logs deprecation warnings" do
54
- logs =
55
- capture_logs do
56
- legacy_create_transaction(
57
- :id => "mock-id",
58
- :namespace => "my_namespace",
59
- :request => Appsignal::Transaction::InternalGenericRequest.new({}),
60
- :options => { :force => true }
61
- )
62
- end
63
-
64
- expect(logs).to contains_log(
65
- :warn,
66
- "Appsignal::Transaction.create: " \
67
- "A new Transaction is created using the transaction ID argument."
68
- )
69
- expect(logs).to contains_log(
70
- :warn,
71
- "Appsignal::Transaction.create: " \
72
- "A Transaction is created using the namespace argument."
73
- )
74
- expect(logs).to contains_log(
75
- :warn,
76
- "Appsignal::Transaction.create: " \
77
- "A Transaction is created using the request argument."
78
- )
79
- expect(logs).to contains_log(
80
- :warn,
81
- "Appsignal::Transaction.create: " \
82
- "A Transaction is created using the `:force => true` option argument. "
83
- )
84
- end
85
-
86
- it "prints deprecation warnings" do
87
- err_stream = std_stream
88
- capture_std_streams(std_stream, err_stream) do
89
- legacy_create_transaction(
90
- :id => "mock-id",
91
- :namespace => "my_namespace",
92
- :request => Appsignal::Transaction::InternalGenericRequest.new({}),
93
- :options => { :force => true }
94
- )
95
- end
38
+ context "when an explicit extension transaction is passed in the initialiser" do
39
+ let(:ext) { "some_ext" }
96
40
 
97
- stderr = err_stream.read
98
- expect(stderr).to include(
99
- "appsignal WARNING: Appsignal::Transaction.create: " \
100
- "A new Transaction is created using the transaction ID argument."
101
- )
102
- expect(stderr).to include(
103
- "appsignal WARNING: Appsignal::Transaction.create: " \
104
- "A Transaction is created using the namespace argument."
105
- )
106
- expect(stderr).to include(
107
- "appsignal WARNING: Appsignal::Transaction.create: " \
108
- "A Transaction is created using the request argument."
109
- )
110
- expect(stderr).to include(
111
- "appsignal WARNING: Appsignal::Transaction.create: " \
112
- "A Transaction is created using the `:force => true` option argument. "
113
- )
114
- end
41
+ it "assigns the extension transaction to the transaction" do
42
+ expect(new_transaction(:ext => ext).ext).to be(ext)
115
43
  end
116
44
  end
117
45
 
@@ -137,28 +65,9 @@ describe Appsignal::Transaction do
137
65
  logs = capture_logs { create_transaction }
138
66
 
139
67
  expect(logs).to contains_log :warn,
140
- "Trying to start new transaction with id 'transaction_id_2', but a " \
141
- "transaction with id 'transaction_id_1' is already " \
142
- "running. Using transaction 'transaction_id_1'."
143
- end
144
-
145
- context "with option :force => true" do
146
- it "returns the newly created (and current) transaction" do
147
- original_transaction = create_transaction
148
-
149
- expect(original_transaction).to be_a(Appsignal::Transaction)
150
- expect(current_transaction).to have_id("transaction_id_1")
151
-
152
- new_transaction = legacy_create_transaction(
153
- :id => "transaction_id_2",
154
- :options => { :force => true }
155
- )
156
-
157
- expect(new_transaction).to be_a(Appsignal::Transaction)
158
- expect(new_transaction).to_not eq(original_transaction)
159
- expect(new_transaction).to have_id("transaction_id_2")
160
- expect(current_transaction).to eq(new_transaction)
161
- end
68
+ "Trying to start new transaction, but a transaction with id " \
69
+ "'transaction_id_1' is already running. " \
70
+ "Using transaction 'transaction_id_1'."
162
71
  end
163
72
  end
164
73
  end
@@ -252,7 +161,7 @@ describe Appsignal::Transaction do
252
161
 
253
162
  context "when transaction is being sampled" do
254
163
  it "samples data" do
255
- transaction.set_tags(:foo => "bar")
164
+ transaction.add_tags(:foo => "bar")
256
165
  keep_transactions { transaction.complete }
257
166
  expect(transaction).to include_tags("foo" => "bar")
258
167
  end
@@ -299,6 +208,306 @@ describe Appsignal::Transaction do
299
208
  end
300
209
  end
301
210
  end
211
+
212
+ context "when a transaction has errors" do
213
+ let(:error) do
214
+ e = ExampleStandardError.new("test message")
215
+ allow(e).to receive(:backtrace).and_return(["line 1"])
216
+ e
217
+ end
218
+
219
+ let(:other_error) do
220
+ e = ExampleStandardError.new("other test message")
221
+ allow(e).to receive(:backtrace).and_return(["line 2"])
222
+ e
223
+ end
224
+
225
+ context "when an error is already set on the transaction" do
226
+ it "reports errors as duplicate transactions" do
227
+ transaction.set_error(error)
228
+ transaction.add_error(other_error)
229
+
230
+ expect do
231
+ transaction.complete
232
+ end.to change { created_transactions.count }.from(1).to(2)
233
+
234
+ original_transaction, duplicate_transaction = created_transactions
235
+
236
+ expect(original_transaction).to have_error(
237
+ "ExampleStandardError",
238
+ "test message",
239
+ ["line 1"]
240
+ )
241
+ expect(original_transaction).to be_completed
242
+
243
+ expect(duplicate_transaction).to have_error(
244
+ "ExampleStandardError",
245
+ "other test message",
246
+ ["line 2"]
247
+ )
248
+ expect(duplicate_transaction).to be_completed
249
+ end
250
+ end
251
+
252
+ context "when no error is set on the transaction" do
253
+ it "reports the first error in the original transaction" do
254
+ transaction.add_error(error)
255
+ transaction.add_error(other_error)
256
+
257
+ expect do
258
+ transaction.complete
259
+ end.to change { created_transactions.count }.from(1).to(2)
260
+
261
+ original_transaction, duplicate_transaction = created_transactions
262
+
263
+ expect(original_transaction).to have_error(
264
+ "ExampleStandardError",
265
+ "test message",
266
+ ["line 1"]
267
+ )
268
+ expect(original_transaction).to be_completed
269
+
270
+ expect(duplicate_transaction).to have_error(
271
+ "ExampleStandardError",
272
+ "other test message",
273
+ ["line 2"]
274
+ )
275
+ expect(duplicate_transaction).to be_completed
276
+ end
277
+ end
278
+
279
+ it "stores the last reported errors" do
280
+ transaction.add_error(error)
281
+ transaction.add_error(other_error)
282
+ transaction.complete
283
+
284
+ expect(Appsignal::Transaction.last_errors).to contain_exactly(error, other_error)
285
+ end
286
+
287
+ describe "metadata" do
288
+ let(:tags) { { "tag" => "value" } }
289
+ let(:params) { { "param" => "value" } }
290
+ let(:headers) { { "REQUEST_METHOD" => "value" } }
291
+ let(:session_data) { { "session_data" => "value" } }
292
+ let(:custom_data) { { "custom_data" => "value" } }
293
+ before do
294
+ transaction.set_namespace("My namespace")
295
+ transaction.set_action("My action")
296
+ transaction.set_metadata("path", "/some/path")
297
+ transaction.set_metadata("method", "GET")
298
+ transaction.add_tags(tags)
299
+ transaction.add_params(params)
300
+ transaction.add_headers(headers)
301
+ transaction.add_session_data(session_data)
302
+ transaction.add_custom_data(custom_data)
303
+ transaction.add_breadcrumb("category", "action", "message", { "meta" => "data" })
304
+
305
+ transaction.start_event
306
+ transaction.finish_event("name", "title", "body", 1)
307
+
308
+ transaction.add_error(error)
309
+ transaction.add_error(other_error)
310
+
311
+ transaction.complete
312
+ end
313
+
314
+ it "copies the transaction metadata and sample data on the duplicate transaction" do
315
+ original_transaction, duplicate_transaction = created_transactions
316
+
317
+ duplicate_hash = duplicate_transaction.to_h.tap do |h|
318
+ h.delete("id")
319
+ h.delete("error")
320
+ end
321
+ original_hash = original_transaction.to_h.tap do |h|
322
+ h.delete("id")
323
+ h.delete("error")
324
+ end
325
+ expect(duplicate_hash).to eq(original_hash)
326
+ end
327
+
328
+ it "the duplicate transaction has a different transaction id" do
329
+ original_transaction, duplicate_transaction = created_transactions
330
+
331
+ expect(original_transaction.transaction_id)
332
+ .to_not eq(duplicate_transaction.transaction_id)
333
+ end
334
+
335
+ it "the duplicate transaction has a different extension transaction than the original" do
336
+ original_transaction, duplicate_transaction = created_transactions
337
+
338
+ expect(original_transaction.ext).to_not eq(duplicate_transaction.ext)
339
+ end
340
+
341
+ it "sets is_duplicate set to true on the duplicate transaction" do
342
+ original_transaction, duplicate_transaction = created_transactions
343
+
344
+ expect(original_transaction.is_duplicate).to be(false)
345
+ expect(duplicate_transaction.is_duplicate).to be(true)
346
+ end
347
+ end
348
+
349
+ it "merges sample data from the original transaction in the duplicate transaction" do
350
+ transaction.add_tags("root" => "tag")
351
+ transaction.add_params("root" => "param")
352
+ transaction.add_session_data("root" => "session")
353
+ transaction.add_headers("REQUEST_METHOD" => "root")
354
+ transaction.add_custom_data("root" => "custom")
355
+ transaction.add_breadcrumb("root", "breadcrumb")
356
+ Appsignal.report_error(error) do |t|
357
+ t.add_tags("original" => "tag")
358
+ t.add_params("original" => "param")
359
+ t.add_session_data("original" => "session")
360
+ t.add_headers("REQUEST_PATH" => "/original")
361
+ t.add_custom_data("original" => "custom")
362
+ t.add_breadcrumb("original", "breadcrumb")
363
+ end
364
+ Appsignal.report_error(other_error) do |t|
365
+ t.add_tags("duplicate" => "tag")
366
+ t.add_params("duplicate" => "param")
367
+ t.add_session_data("duplicate" => "session")
368
+ t.add_headers("HTTP_ACCEPT" => "duplicate")
369
+ t.add_custom_data("duplicate" => "custom")
370
+ t.add_breadcrumb("duplicate", "breadcrumb")
371
+ end
372
+ transaction.add_tags("root2" => "tag")
373
+ transaction.add_params("root2" => "param")
374
+ transaction.add_session_data("root2" => "session")
375
+ transaction.add_headers("PATH_INFO" => "/root2")
376
+ transaction.add_custom_data("root2" => "custom")
377
+ transaction.add_breadcrumb("root2", "breadcrumb")
378
+ transaction.complete
379
+
380
+ original_transaction, duplicate_transaction = created_transactions
381
+ # Original
382
+ expect(original_transaction).to include_tags(
383
+ "root" => "tag",
384
+ "original" => "tag",
385
+ "root2" => "tag"
386
+ )
387
+ expect(original_transaction).to_not include_tags("duplicate" => anything)
388
+ expect(original_transaction).to include_params(
389
+ "root" => "param",
390
+ "original" => "param",
391
+ "root2" => "param"
392
+ )
393
+ expect(original_transaction).to_not include_params("duplicate" => anything)
394
+ expect(original_transaction).to include_session_data(
395
+ "root" => "session",
396
+ "original" => "session",
397
+ "root2" => "session"
398
+ )
399
+ expect(original_transaction).to_not include_session_data("duplicate" => anything)
400
+ expect(original_transaction).to include_environment(
401
+ "REQUEST_METHOD" => "root",
402
+ "REQUEST_PATH" => "/original",
403
+ "PATH_INFO" => "/root2"
404
+ )
405
+ expect(original_transaction).to_not include_environment("HTTP_ACCEPT" => anything)
406
+ expect(original_transaction).to include_custom_data(
407
+ "root" => "custom",
408
+ "original" => "custom",
409
+ "root2" => "custom"
410
+ )
411
+ expect(original_transaction).to_not include_custom_data("duplicate" => anything)
412
+ expect(original_transaction).to include_breadcrumb("breadcrumb", "root")
413
+ expect(original_transaction).to include_breadcrumb("breadcrumb", "original")
414
+ expect(original_transaction).to include_breadcrumb("breadcrumb", "root2")
415
+ expect(original_transaction).to_not include_breadcrumb("breadcrumb", "duplicate")
416
+
417
+ # Duplicate
418
+ expect(duplicate_transaction).to include_tags(
419
+ "root" => "tag",
420
+ "duplicate" => "tag",
421
+ "root2" => "tag"
422
+ )
423
+ expect(duplicate_transaction).to_not include_tags("original" => anything)
424
+ expect(duplicate_transaction).to include_params(
425
+ "root" => "param",
426
+ "duplicate" => "param",
427
+ "root2" => "param"
428
+ )
429
+ expect(duplicate_transaction).to_not include_params("original" => anything)
430
+ expect(duplicate_transaction).to include_session_data(
431
+ "root" => "session",
432
+ "duplicate" => "session",
433
+ "root2" => "session"
434
+ )
435
+ expect(duplicate_transaction).to_not include_session_data("original" => anything)
436
+ expect(duplicate_transaction).to include_environment(
437
+ "PATH_INFO" => "/root2",
438
+ "HTTP_ACCEPT" => "duplicate",
439
+ "REQUEST_METHOD" => "root"
440
+ )
441
+ expect(duplicate_transaction).to_not include_environment("REQUEST_PATH" => anything)
442
+ expect(duplicate_transaction).to include_custom_data(
443
+ "root" => "custom",
444
+ "duplicate" => "custom",
445
+ "root2" => "custom"
446
+ )
447
+ expect(duplicate_transaction).to_not include_custom_data("original" => anything)
448
+ expect(duplicate_transaction).to include_breadcrumb("breadcrumb", "root")
449
+ expect(duplicate_transaction).to include_breadcrumb("breadcrumb", "duplicate")
450
+ expect(duplicate_transaction).to include_breadcrumb("breadcrumb", "root2")
451
+ expect(duplicate_transaction).to_not include_breadcrumb("breadcrumb", "original")
452
+ end
453
+
454
+ it "overrides sample data from the original transaction in the duplicate transaction" do
455
+ transaction.add_tags("changeme" => "tag")
456
+ transaction.add_params("changeme" => "param")
457
+ transaction.add_session_data("changeme" => "session")
458
+ transaction.add_headers("REQUEST_METHOD" => "root")
459
+ transaction.add_custom_data("changeme" => "custom")
460
+ Appsignal.report_error(error)
461
+ Appsignal.report_error(other_error) do |t|
462
+ t.add_tags("changeme" => "duplicate_tag")
463
+ t.add_params("changeme" => "duplicate_param")
464
+ t.add_session_data("changeme" => "duplicate_session")
465
+ t.add_headers("REQUEST_METHOD" => "duplicate")
466
+ t.add_custom_data("changeme" => "duplicate_custom")
467
+ end
468
+ transaction.add_tags("changeme" => "changed_tag")
469
+ transaction.add_params("changeme" => "changed_param")
470
+ transaction.add_session_data("changeme" => "changed_session")
471
+ transaction.add_headers("REQUEST_METHOD" => "changed")
472
+ transaction.add_custom_data("changeme" => "changed_custom")
473
+ transaction.complete
474
+
475
+ original_transaction, duplicate_transaction = created_transactions
476
+ # Original
477
+ expect(original_transaction).to include_tags(
478
+ "changeme" => "changed_tag"
479
+ )
480
+ expect(original_transaction).to include_params(
481
+ "changeme" => "changed_param"
482
+ )
483
+ expect(original_transaction).to include_session_data(
484
+ "changeme" => "changed_session"
485
+ )
486
+ expect(original_transaction).to include_environment(
487
+ "REQUEST_METHOD" => "changed"
488
+ )
489
+ expect(original_transaction).to include_custom_data(
490
+ "changeme" => "changed_custom"
491
+ )
492
+
493
+ # Duplicate
494
+ expect(duplicate_transaction).to include_tags(
495
+ "changeme" => "duplicate_tag"
496
+ )
497
+ expect(duplicate_transaction).to include_params(
498
+ "changeme" => "duplicate_param"
499
+ )
500
+ expect(duplicate_transaction).to include_session_data(
501
+ "changeme" => "duplicate_session"
502
+ )
503
+ expect(duplicate_transaction).to include_environment(
504
+ "REQUEST_METHOD" => "duplicate"
505
+ )
506
+ expect(duplicate_transaction).to include_custom_data(
507
+ "changeme" => "duplicate_custom"
508
+ )
509
+ end
510
+ end
302
511
  end
303
512
 
304
513
  context "pausing" do
@@ -363,49 +572,9 @@ describe Appsignal::Transaction do
363
572
  end
364
573
  end
365
574
 
366
- context "transaction id" do
367
- before do
368
- allow(SecureRandom).to receive(:uuid).and_return("mock_transaction_id")
369
- end
370
-
371
- it "sets the transaction id" do
372
- expect(transaction).to have_id("mock_transaction_id")
373
- end
374
- end
375
-
376
575
  it "sets the namespace to http_request" do
377
576
  expect(transaction.namespace).to eq "http_request"
378
577
  end
379
-
380
- it "sets the request" do
381
- expect(transaction.request).to be_a(Appsignal::Transaction::InternalGenericRequest)
382
- end
383
-
384
- it "sets the request not to paused" do
385
- expect(transaction.paused?).to be_falsy
386
- end
387
-
388
- it "sets no tags by default" do
389
- expect(transaction.tags).to be_empty
390
- end
391
-
392
- describe "#options" do
393
- let(:options) { transaction.options }
394
-
395
- it "sets the default :params_method" do
396
- expect(options[:params_method]).to eq :params
397
- end
398
-
399
- context "with overridden options" do
400
- let(:transaction) do
401
- legacy_new_transaction(:options => { :params_method => :filtered_params })
402
- end
403
-
404
- it "sets the overridden :params_method" do
405
- expect(options[:params_method]).to eq :filtered_params
406
- end
407
- end
408
- end
409
578
  end
410
579
 
411
580
  describe "#store" do
@@ -423,174 +592,122 @@ describe Appsignal::Transaction do
423
592
  end
424
593
  end
425
594
 
426
- describe "#params" do
595
+ describe "#add_params" do
427
596
  let(:transaction) { new_transaction }
428
- let(:params) { transaction.params }
429
-
430
- context "with custom params set on transaction" do
431
- before { transaction.set_params(:foo => "bar") }
432
-
433
- it "returns custom parameters" do
434
- expect(params).to eq(:foo => "bar")
435
- end
436
-
437
- context "when params is a callable object" do
438
- it "calls the params object and sets the return value as parametesr" do
439
- transaction.set_params { { "param1" => "value1" } }
440
-
441
- expect(params).to eq("param1" => "value1")
442
- end
443
- end
444
- end
445
-
446
- context "without custom params set on transaction" do
447
- let(:transaction) do
448
- legacy_new_transaction(
449
- :request => legacy_request(
450
- :params => {
451
- "action" => "show",
452
- "controller" => "blog_posts",
453
- "id" => "1"
454
-
455
- }
456
- )
457
- )
458
- end
459
597
 
460
- it "returns parameters from request" do
461
- expect(params).to eq(
462
- "action" => "show",
463
- "controller" => "blog_posts",
464
- "id" => "1"
465
- )
466
- end
598
+ it "has a #set_params alias" do
599
+ expect(transaction.method(:add_params)).to eq(transaction.method(:set_params))
467
600
  end
468
- end
469
601
 
470
- describe "#params=" do
471
- let(:transaction) { new_transaction }
472
-
473
- it "sets params on the transaction" do
474
- params = { "foo" => "bar" }
475
- silence { transaction.params = params }
602
+ it "adds the params to the transaction" do
603
+ params = { "key" => "value" }
604
+ transaction.add_params(params)
476
605
 
477
606
  transaction._sample
478
- expect(transaction.params).to eq(params)
479
607
  expect(transaction).to include_params(params)
480
608
  end
481
609
 
482
- it "logs a deprecation warning" do
483
- logs =
484
- capture_logs do
485
- transaction.params = { "foo" => "bar" }
486
- end
610
+ it "merges the params on the transaction" do
611
+ transaction.add_params("abc" => "value")
612
+ transaction.add_params("def" => "value")
613
+ transaction.add_params { { "xyz" => "value" } }
487
614
 
488
- expect(logs).to contains_log(
489
- :warn,
490
- "Transaction#params= is deprecated." \
491
- "Use Transaction#set_params or #set_params_if_nil instead."
615
+ transaction._sample
616
+ expect(transaction).to include_params(
617
+ "abc" => "value",
618
+ "def" => "value",
619
+ "xyz" => "value"
492
620
  )
493
621
  end
494
- end
495
-
496
- describe "#set_params" do
497
- let(:transaction) { new_transaction }
498
622
 
499
- context "when the params are set" do
500
- it "updates the params on the transaction" do
501
- params = { "key" => "value" }
502
- transaction.set_params(params)
623
+ it "adds the params to the transaction with a block" do
624
+ params = { "key" => "value" }
625
+ transaction.add_params { params }
503
626
 
504
- transaction._sample
505
- expect(transaction.params).to eq(params)
506
- expect(transaction).to include_params(params)
507
- end
627
+ transaction._sample
628
+ expect(transaction).to include_params(params)
629
+ end
508
630
 
509
- it "updates the params on the transaction with a block" do
510
- params = { "key" => "value" }
511
- transaction.set_params { params }
631
+ it "adds the params block value when both an argument and block are given" do
632
+ arg_params = { "argument" => "value" }
633
+ block_params = { "block" => "value" }
634
+ transaction.add_params(arg_params) { block_params }
512
635
 
513
- transaction._sample
514
- expect(transaction.params).to eq(params)
515
- expect(transaction).to include_params(params)
516
- end
636
+ transaction._sample
637
+ expect(transaction).to include_params(block_params)
638
+ end
517
639
 
518
- it "updates with the params argument when both an argument and block are given" do
519
- arg_params = { "argument" => "value" }
520
- block_params = { "block" => "value" }
521
- transaction.set_params(arg_params) { block_params }
640
+ it "logs an error if an error occurred storing the params" do
641
+ transaction.add_params { raise "uh oh" }
522
642
 
523
- transaction._sample
524
- expect(transaction.params).to eq(arg_params)
525
- expect(transaction).to include_params(arg_params)
526
- end
643
+ logs = capture_logs { transaction._sample }
644
+ expect(logs).to contains_log(
645
+ :error,
646
+ "Exception while fetching params: RuntimeError: uh oh"
647
+ )
648
+ end
527
649
 
528
- it "logs an error if an error occurred storing the params" do
529
- transaction.set_params { raise "uh oh" }
650
+ it "does not update the params on the transaction if the given value is nil" do
651
+ params = { "key" => "value" }
652
+ transaction.add_params(params)
653
+ transaction.add_params(nil)
530
654
 
531
- logs = capture_logs { transaction._sample }
532
- expect(logs).to contains_log(
533
- :error,
534
- "Exception while fetching params: RuntimeError: uh oh"
535
- )
536
- end
655
+ transaction._sample
656
+ expect(transaction).to include_params(params)
537
657
  end
538
658
 
539
- context "when the given params is nil" do
540
- it "does not update the params on the transaction" do
541
- params = { "key" => "value" }
542
- transaction.set_params(params)
543
- transaction.set_params(nil)
659
+ context "with AppSignal filtering" do
660
+ let(:options) { { :filter_parameters => %w[foo] } }
661
+
662
+ it "returns sanitized custom params" do
663
+ transaction.add_params("foo" => "value", "baz" => "bat")
544
664
 
545
665
  transaction._sample
546
- expect(transaction.params).to eq(params)
547
- expect(transaction).to include_params(params)
666
+ expect(transaction).to include_params("foo" => "[FILTERED]", "baz" => "bat")
548
667
  end
549
668
  end
550
669
  end
551
670
 
552
- describe "#set_params_if_nil" do
671
+ describe "#add_params_if_nil" do
553
672
  let(:transaction) { new_transaction }
554
673
 
674
+ it "has a #set_params_if_nil alias" do
675
+ expect(transaction.method(:add_params_if_nil)).to eq(transaction.method(:set_params_if_nil))
676
+ end
677
+
555
678
  context "when the params are not set" do
556
- it "sets the params on the transaction" do
679
+ it "adds the params to the transaction" do
557
680
  params = { "key" => "value" }
558
- transaction.set_params_if_nil(params)
681
+ transaction.add_params_if_nil(params)
559
682
 
560
683
  transaction._sample
561
- expect(transaction.params).to eq(params)
562
684
  expect(transaction).to include_params(params)
563
685
  end
564
686
 
565
- it "updates the params on the transaction with a block" do
687
+ it "adds the params to the transaction with a block" do
566
688
  params = { "key" => "value" }
567
- transaction.set_params_if_nil { params }
689
+ transaction.add_params_if_nil { params }
568
690
 
569
691
  transaction._sample
570
- expect(transaction.params).to eq(params)
571
692
  expect(transaction).to include_params(params)
572
693
  end
573
694
 
574
- it "updates with the params argument when both an argument and block are given" do
695
+ it "adds the params block value when both an argument and block are given" do
575
696
  arg_params = { "argument" => "value" }
576
697
  block_params = { "block" => "value" }
577
- transaction.set_params_if_nil(arg_params) { block_params }
698
+ transaction.add_params_if_nil(arg_params) { block_params }
578
699
 
579
700
  transaction._sample
580
- expect(transaction.params).to eq(arg_params)
581
- expect(transaction).to include_params(arg_params)
701
+ expect(transaction).to include_params(block_params)
582
702
  end
583
703
 
584
- context "when the given params is nil" do
585
- it "does not update the params on the transaction" do
586
- params = { "key" => "value" }
587
- transaction.set_params(params)
588
- transaction.set_params_if_nil(nil)
704
+ it "does not update the params on the transaction if the given value is nil" do
705
+ params = { "key" => "value" }
706
+ transaction.add_params(params)
707
+ transaction.add_params_if_nil(nil)
589
708
 
590
- transaction._sample
591
- expect(transaction.params).to eq(params)
592
- expect(transaction).to include_params(params)
593
- end
709
+ transaction._sample
710
+ expect(transaction).to include_params(params)
594
711
  end
595
712
  end
596
713
 
@@ -598,144 +715,156 @@ describe Appsignal::Transaction do
598
715
  it "does not update the params on the transaction" do
599
716
  preset_params = { "other" => "params" }
600
717
  params = { "key" => "value" }
601
- transaction.set_params(preset_params)
602
- transaction.set_params_if_nil(params)
718
+ transaction.add_params(preset_params)
719
+ transaction.add_params_if_nil(params)
603
720
 
604
721
  transaction._sample
605
- expect(transaction.params).to eq(preset_params)
606
722
  expect(transaction).to include_params(preset_params)
607
723
  end
608
724
 
609
725
  it "does not update the params with a block on the transaction" do
610
726
  preset_params = { "other" => "params" }
611
727
  params = { "key" => "value" }
612
- transaction.set_params(preset_params)
613
- transaction.set_params_if_nil { params }
728
+ transaction.add_params(preset_params)
729
+ transaction.add_params_if_nil { params }
614
730
 
615
731
  transaction._sample
616
- expect(transaction.params).to eq(preset_params)
617
732
  expect(transaction).to include_params(preset_params)
618
733
  end
619
734
  end
620
735
  end
621
736
 
622
- describe "#set_session_data" do
737
+ describe "#add_session_data" do
623
738
  let(:transaction) { new_transaction }
624
739
 
625
- context "when the session data is set" do
626
- it "updates the session data on the transaction" do
627
- data = { "key" => "value" }
628
- transaction.set_session_data(data)
740
+ it "has a #set_session_data alias" do
741
+ expect(transaction.method(:add_session_data)).to eq(transaction.method(:set_session_data))
742
+ end
629
743
 
630
- transaction._sample
631
- expect(transaction).to include_session_data(data)
632
- end
744
+ it "adds the session data to the transaction" do
745
+ data = { "key" => "value" }
746
+ transaction.add_session_data(data)
633
747
 
634
- it "updates the session data on the transaction with a block" do
635
- data = { "key" => "value" }
636
- transaction.set_session_data { data }
748
+ transaction._sample
749
+ expect(transaction).to include_session_data(data)
750
+ end
637
751
 
638
- transaction._sample
639
- expect(transaction).to include_session_data(data)
640
- end
752
+ it "merges the session data on the transaction" do
753
+ transaction.add_session_data("abc" => "value")
754
+ transaction.add_session_data("def" => "value")
755
+ transaction.add_session_data { { "xyz" => "value" } }
641
756
 
642
- it "updates with the session data argument when both an argument and block are given" do
643
- arg_data = { "argument" => "value" }
644
- block_data = { "block" => "value" }
645
- transaction.set_session_data(arg_data) { block_data }
757
+ transaction._sample
758
+ expect(transaction).to include_session_data(
759
+ "abc" => "value",
760
+ "def" => "value",
761
+ "xyz" => "value"
762
+ )
763
+ end
646
764
 
647
- transaction._sample
648
- expect(transaction).to include_session_data(arg_data)
649
- end
765
+ it "adds the session data to the transaction with a block" do
766
+ data = { "key" => "value" }
767
+ transaction.add_session_data { data }
650
768
 
651
- it "does not include filtered out session data" do
652
- Appsignal.config[:filter_session_data] = ["filtered_key"]
653
- transaction.set_session_data("data" => "value1", "filtered_key" => "filtered_value")
769
+ transaction._sample
770
+ expect(transaction).to include_session_data(data)
771
+ end
654
772
 
655
- transaction._sample
656
- expect(transaction).to include_session_data("data" => "value1")
657
- end
773
+ it "adds the session data block value when both an argument and block are given" do
774
+ arg_data = { "argument" => "value" }
775
+ block_data = { "block" => "value" }
776
+ transaction.add_session_data(arg_data) { block_data }
658
777
 
659
- it "logs an error if an error occurred storing the session data" do
660
- transaction.set_session_data { raise "uh oh" }
778
+ transaction._sample
779
+ expect(transaction).to include_session_data(block_data)
780
+ end
661
781
 
662
- logs = capture_logs { transaction._sample }
663
- expect(logs).to contains_log(
664
- :error,
665
- "Exception while fetching session data: RuntimeError: uh oh"
666
- )
667
- end
782
+ it "logs an error if an error occurred storing the session data" do
783
+ transaction.add_session_data { raise "uh oh" }
784
+
785
+ logs = capture_logs { transaction._sample }
786
+ expect(logs).to contains_log(
787
+ :error,
788
+ "Exception while fetching session data: RuntimeError: uh oh"
789
+ )
668
790
  end
669
791
 
670
- context "when the given session data is nil" do
671
- it "does not update the session data on the transaction" do
672
- data = { "key" => "value" }
673
- transaction.set_session_data(data)
674
- transaction.set_session_data(nil)
792
+ it "does not update the session data on the transaction if the given value is nil" do
793
+ data = { "key" => "value" }
794
+ transaction.add_session_data(data)
795
+ transaction.add_session_data(nil)
796
+
797
+ transaction._sample
798
+ expect(transaction).to include_session_data(data)
799
+ end
800
+
801
+ context "with filter_session_data" do
802
+ let(:options) { { :filter_session_data => ["filtered_key"] } }
803
+
804
+ it "does not include filtered out session data" do
805
+ transaction.add_session_data("data" => "value1", "filtered_key" => "filtered_value")
675
806
 
676
807
  transaction._sample
677
- expect(transaction).to include_session_data(data)
808
+ expect(transaction).to include_session_data("data" => "value1")
678
809
  end
679
810
  end
680
811
  end
681
812
 
682
- describe "#set_session_data_if_nil" do
813
+ describe "#add_session_data_if_nil" do
683
814
  let(:transaction) { new_transaction }
684
815
 
685
- context "when the params are not set" do
686
- it "sets the params on the transaction" do
816
+ context "when the session data is not set" do
817
+ it "sets the session data on the transaction" do
687
818
  data = { "key" => "value" }
688
- transaction.set_session_data_if_nil(data)
819
+ transaction.add_session_data_if_nil(data)
689
820
 
690
821
  transaction._sample
691
822
  expect(transaction).to include_session_data(data)
692
823
  end
693
824
 
694
- it "updates the params on the transaction with a block" do
825
+ it "updates the session data on the transaction with a block" do
695
826
  data = { "key" => "value" }
696
- transaction.set_session_data_if_nil { data }
827
+ transaction.add_session_data_if_nil { data }
697
828
 
698
829
  transaction._sample
699
830
  expect(transaction).to include_session_data(data)
700
831
  end
701
832
 
702
- it "updates with the params argument when both an argument and block are given" do
833
+ it "updates with the session data block when both an argument and block are given" do
703
834
  arg_data = { "argument" => "value" }
704
835
  block_data = { "block" => "value" }
705
- transaction.set_session_data_if_nil(arg_data) { block_data }
836
+ transaction.add_session_data_if_nil(arg_data) { block_data }
706
837
 
707
838
  transaction._sample
708
- expect(transaction).to include_session_data(arg_data)
839
+ expect(transaction).to include_session_data(block_data)
709
840
  end
710
841
 
711
- context "when the given params is nil" do
712
- it "does not update the params on the transaction" do
713
- data = { "key" => "value" }
714
- transaction.set_session_data(data)
715
- transaction.set_session_data_if_nil(nil)
842
+ it "does not update the session data on the transaction if the given value is nil" do
843
+ data = { "key" => "value" }
844
+ transaction.add_session_data(data)
845
+ transaction.add_session_data_if_nil(nil)
716
846
 
717
- transaction._sample
718
- expect(transaction).to include_session_data(data)
719
- end
847
+ transaction._sample
848
+ expect(transaction).to include_session_data(data)
720
849
  end
721
850
  end
722
851
 
723
- context "when the params are set" do
724
- it "does not update the params on the transaction" do
852
+ context "when the session data are set" do
853
+ it "does not update the session data on the transaction" do
725
854
  preset_data = { "other" => "data" }
726
855
  data = { "key" => "value" }
727
- transaction.set_session_data(preset_data)
728
- transaction.set_session_data_if_nil(data)
856
+ transaction.add_session_data(preset_data)
857
+ transaction.add_session_data_if_nil(data)
729
858
 
730
859
  transaction._sample
731
860
  expect(transaction).to include_session_data(preset_data)
732
861
  end
733
862
 
734
- it "does not update the params with a block on the transaction" do
863
+ it "does not update the session data with a block on the transaction" do
735
864
  preset_data = { "other" => "data" }
736
865
  data = { "key" => "value" }
737
- transaction.set_session_data(preset_data)
738
- transaction.set_session_data_if_nil { data }
866
+ transaction.add_session_data(preset_data)
867
+ transaction.add_session_data_if_nil { data }
739
868
 
740
869
  transaction._sample
741
870
  expect(transaction).to include_session_data(preset_data)
@@ -743,123 +872,141 @@ describe Appsignal::Transaction do
743
872
  end
744
873
  end
745
874
 
746
- describe "#set_headers" do
875
+ describe "#add_headers" do
747
876
  let(:transaction) { new_transaction }
748
877
 
749
- context "when the headers are set" do
750
- it "updates the headers on the transaction" do
751
- headers = { "PATH_INFO" => "value" }
752
- transaction.set_headers(headers)
878
+ it "has a #set_headers alias" do
879
+ expect(transaction.method(:add_headers)).to eq(transaction.method(:set_headers))
880
+ end
753
881
 
754
- transaction._sample
755
- expect(transaction).to include_environment(headers)
756
- end
882
+ it "adds the headers to the transaction" do
883
+ headers = { "PATH_INFO" => "value" }
884
+ transaction.add_headers(headers)
757
885
 
758
- it "updates the headers on the transaction with a block" do
759
- headers = { "PATH_INFO" => "value" }
760
- transaction.set_headers { headers }
886
+ transaction._sample
887
+ expect(transaction).to include_environment(headers)
888
+ end
761
889
 
762
- transaction._sample
763
- expect(transaction).to include_environment(headers)
764
- end
890
+ it "merges the headers on the transaction" do
891
+ transaction.add_headers("PATH_INFO" => "value")
892
+ transaction.add_headers("REQUEST_METHOD" => "value")
893
+ transaction.add_headers { { "HTTP_ACCEPT" => "value" } }
765
894
 
766
- it "updates with the headers argument when both an argument and block are given" do
767
- arg_data = { "PATH_INFO" => "/arg-path" }
768
- block_data = { "PATH_INFO" => "/block-path" }
769
- transaction.set_headers(arg_data) { block_data }
895
+ transaction._sample
896
+ expect(transaction).to include_environment(
897
+ "PATH_INFO" => "value",
898
+ "REQUEST_METHOD" => "value",
899
+ "HTTP_ACCEPT" => "value"
900
+ )
901
+ end
770
902
 
771
- transaction._sample
772
- expect(transaction).to include_environment(arg_data)
773
- end
903
+ it "adds the headers to the transaction with a block" do
904
+ headers = { "PATH_INFO" => "value" }
905
+ transaction.add_headers { headers }
774
906
 
775
- it "does not include filtered out headers" do
776
- Appsignal.config[:request_headers] = ["MY_HEADER"]
777
- transaction.set_headers("MY_HEADER" => "value1", "filtered_key" => "filtered_value")
907
+ transaction._sample
908
+ expect(transaction).to include_environment(headers)
909
+ end
778
910
 
779
- transaction._sample
780
- expect(transaction).to include_environment("MY_HEADER" => "value1")
781
- end
911
+ it "adds the headers block value when both an argument and block are given" do
912
+ arg_data = { "PATH_INFO" => "/arg-path" }
913
+ block_data = { "PATH_INFO" => "/block-path" }
914
+ transaction.add_headers(arg_data) { block_data }
915
+
916
+ transaction._sample
917
+ expect(transaction).to include_environment(block_data)
918
+ end
782
919
 
783
- it "logs an error if an error occurred storing the headers" do
784
- transaction.set_headers { raise "uh oh" }
920
+ it "logs an error if an error occurred storing the headers" do
921
+ transaction.add_headers { raise "uh oh" }
785
922
 
786
- logs = capture_logs { transaction._sample }
787
- expect(logs).to contains_log(
788
- :error,
789
- "Exception while fetching headers: RuntimeError: uh oh"
790
- )
791
- end
923
+ logs = capture_logs { transaction._sample }
924
+ expect(logs).to contains_log(
925
+ :error,
926
+ "Exception while fetching headers: RuntimeError: uh oh"
927
+ )
792
928
  end
793
929
 
794
- context "when the given headers is nil" do
795
- it "does not update the headers on the transaction" do
796
- headers = { "PATH_INFO" => "value" }
797
- transaction.set_headers(headers)
798
- transaction.set_headers(nil)
930
+ it "does not update the headers on the transaction if the given value is nil" do
931
+ headers = { "PATH_INFO" => "value" }
932
+ transaction.add_headers(headers)
933
+ transaction.add_headers(nil)
934
+
935
+ transaction._sample
936
+ expect(transaction).to include_environment(headers)
937
+ end
938
+
939
+ context "with request_headers options" do
940
+ let(:options) { { :request_headers => ["MY_HEADER"] } }
941
+
942
+ it "does not include filtered out headers" do
943
+ transaction.add_headers("MY_HEADER" => "value1", "filtered_key" => "filtered_value")
799
944
 
800
945
  transaction._sample
801
- expect(transaction).to include_environment(headers)
946
+ expect(transaction).to include_environment("MY_HEADER" => "value1")
802
947
  end
803
948
  end
804
949
  end
805
950
 
806
- describe "#set_headers_if_nil" do
951
+ describe "#add_headers_if_nil" do
807
952
  let(:transaction) { new_transaction }
808
953
 
809
- context "when the params are not set" do
810
- it "sets the params on the transaction" do
954
+ it "has a #set_headers_if_nil alias" do
955
+ expect(transaction.method(:add_headers_if_nil)).to eq(transaction.method(:set_headers_if_nil))
956
+ end
957
+
958
+ context "when the headers are not set" do
959
+ it "adds the headers to the transaction" do
811
960
  headers = { "PATH_INFO" => "value" }
812
- transaction.set_headers_if_nil(headers)
961
+ transaction.add_headers_if_nil(headers)
813
962
 
814
963
  transaction._sample
815
964
  expect(transaction).to include_environment(headers)
816
965
  end
817
966
 
818
- it "updates the params on the transaction with a block" do
967
+ it "adds the headers to the transaction with a block" do
819
968
  headers = { "PATH_INFO" => "value" }
820
- transaction.set_headers_if_nil { headers }
969
+ transaction.add_headers_if_nil { headers }
821
970
 
822
971
  transaction._sample
823
972
  expect(transaction).to include_environment(headers)
824
973
  end
825
974
 
826
- it "updates with the params argument when both an argument and block are given" do
975
+ it "adds the headers block value when both an argument and block are given" do
827
976
  arg_data = { "PATH_INFO" => "/arg-path" }
828
977
  block_data = { "PATH_INFO" => "/block-path" }
829
- transaction.set_headers_if_nil(arg_data) { block_data }
978
+ transaction.add_headers_if_nil(arg_data) { block_data }
830
979
 
831
980
  transaction._sample
832
- expect(transaction).to include_environment(arg_data)
981
+ expect(transaction).to include_environment(block_data)
833
982
  end
834
983
 
835
- context "when the given params is nil" do
836
- it "does not update the params on the transaction" do
837
- headers = { "PATH_INFO" => "value" }
838
- transaction.set_headers(headers)
839
- transaction.set_headers_if_nil(nil)
984
+ it "does not update the headers on the transaction if the given value is nil" do
985
+ headers = { "PATH_INFO" => "value" }
986
+ transaction.add_headers(headers)
987
+ transaction.add_headers_if_nil(nil)
840
988
 
841
- transaction._sample
842
- expect(transaction).to include_environment(headers)
843
- end
989
+ transaction._sample
990
+ expect(transaction).to include_environment(headers)
844
991
  end
845
992
  end
846
993
 
847
- context "when the params are set" do
848
- it "does not update the params on the transaction" do
994
+ context "when the headers are set" do
995
+ it "does not update the headers on the transaction" do
849
996
  preset_headers = { "PATH_INFO" => "/first-path" }
850
997
  headers = { "PATH_INFO" => "/other-path" }
851
- transaction.set_headers(preset_headers)
852
- transaction.set_headers_if_nil(headers)
998
+ transaction.add_headers(preset_headers)
999
+ transaction.add_headers_if_nil(headers)
853
1000
 
854
1001
  transaction._sample
855
1002
  expect(transaction).to include_environment(preset_headers)
856
1003
  end
857
1004
 
858
- it "does not update the params with a block on the transaction" do
1005
+ it "does not update the headers with a block on the transaction" do
859
1006
  preset_headers = { "PATH_INFO" => "/first-path" }
860
1007
  headers = { "PATH_INFO" => "/other-path" }
861
- transaction.set_headers(preset_headers)
862
- transaction.set_headers_if_nil { headers }
1008
+ transaction.add_headers(preset_headers)
1009
+ transaction.add_headers_if_nil { headers }
863
1010
 
864
1011
  transaction._sample
865
1012
  expect(transaction).to include_environment(preset_headers)
@@ -867,12 +1014,12 @@ describe Appsignal::Transaction do
867
1014
  end
868
1015
  end
869
1016
 
870
- describe "#set_tags" do
1017
+ describe "#add_tags" do
871
1018
  let(:transaction) { new_transaction }
872
1019
  let(:long_string) { "a" * 10_001 }
873
1020
 
874
1021
  it "stores tags on the transaction" do
875
- transaction.set_tags(
1022
+ transaction.add_tags(
876
1023
  :valid_key => "valid_value",
877
1024
  "valid_string_key" => "valid_value",
878
1025
  :both_symbols => :valid_value,
@@ -900,8 +1047,8 @@ describe Appsignal::Transaction do
900
1047
  end
901
1048
 
902
1049
  it "merges the tags when called multiple times" do
903
- transaction.set_tags(:key1 => "value1")
904
- transaction.set_tags(:key2 => "value2")
1050
+ transaction.add_tags(:key1 => "value1")
1051
+ transaction.add_tags(:key2 => "value2")
905
1052
  transaction._sample
906
1053
 
907
1054
  expect(transaction).to include_tags(
@@ -911,11 +1058,15 @@ describe Appsignal::Transaction do
911
1058
  end
912
1059
  end
913
1060
 
914
- describe "#set_custom_data" do
1061
+ describe "#add_custom_data" do
915
1062
  let(:transaction) { new_transaction }
916
1063
 
917
- it "stores custom Hash data on the transaction" do
918
- transaction.set_custom_data(
1064
+ it "has a #add_custom_data alias" do
1065
+ expect(transaction.method(:add_custom_data)).to eq(transaction.method(:set_custom_data))
1066
+ end
1067
+
1068
+ it "adds a custom Hash data to the transaction" do
1069
+ transaction.add_custom_data(
919
1070
  :user => {
920
1071
  :id => 123,
921
1072
  :locale => "abc"
@@ -939,8 +1090,8 @@ describe Appsignal::Transaction do
939
1090
  )
940
1091
  end
941
1092
 
942
- it "stores custom Array data on the transaction" do
943
- transaction.set_custom_data([
1093
+ it "adds a custom Array data to the transaction" do
1094
+ transaction.add_custom_data([
944
1095
  [123, "abc"],
945
1096
  ["appsignal", "enterprise"]
946
1097
  ])
@@ -955,39 +1106,42 @@ describe Appsignal::Transaction do
955
1106
  it "does not store non Hash or Array custom data" do
956
1107
  logs =
957
1108
  capture_logs do
958
- transaction.set_custom_data("abc")
1109
+ transaction.add_custom_data("abc")
959
1110
  transaction._sample
960
1111
  expect(transaction).to_not include_custom_data
961
1112
 
962
- transaction.set_custom_data(123)
1113
+ transaction.add_custom_data(123)
963
1114
  transaction._sample
964
1115
  expect(transaction).to_not include_custom_data
965
1116
 
966
- transaction.set_custom_data(Object.new)
1117
+ transaction.add_custom_data(Object.new)
967
1118
  transaction._sample
968
1119
  expect(transaction).to_not include_custom_data
969
1120
  end
970
1121
 
971
1122
  expect(logs).to contains_log(
972
1123
  :error,
973
- "set_custom_data: Unsupported data type String received."
1124
+ %(Sample data 'custom_data': Unsupported data type 'String' received: "abc")
974
1125
  )
975
1126
  expect(logs).to contains_log(
976
1127
  :error,
977
- "set_custom_data: Unsupported data type Integer received."
1128
+ %(Sample data 'custom_data': Unsupported data type 'Integer' received: 123)
978
1129
  )
979
1130
  expect(logs).to contains_log(
980
1131
  :error,
981
- "set_custom_data: Unsupported data type String received."
1132
+ %(Sample data 'custom_data': Unsupported data type 'Object' received: #<Object:)
982
1133
  )
983
1134
  end
984
1135
 
985
- it "overwrites the custom data if called multiple times" do
986
- transaction.set_custom_data("user" => { "id" => 123 })
987
- transaction.set_custom_data("user" => { "id" => 456 })
1136
+ it "merges the custom data if called multiple times" do
1137
+ transaction.add_custom_data("abc" => "value")
1138
+ transaction.add_custom_data("def" => "value")
988
1139
 
989
1140
  transaction._sample
990
- expect(transaction).to include_custom_data("user" => { "id" => 456 })
1141
+ expect(transaction).to include_custom_data(
1142
+ "abc" => "value",
1143
+ "def" => "value"
1144
+ )
991
1145
  end
992
1146
  end
993
1147
 
@@ -1150,42 +1304,6 @@ describe Appsignal::Transaction do
1150
1304
  end
1151
1305
  end
1152
1306
 
1153
- describe "#set_http_or_background_action" do
1154
- let(:transaction) { new_transaction }
1155
-
1156
- context "for a hash with controller and action" do
1157
- it "sets the action" do
1158
- transaction.set_http_or_background_action(
1159
- :controller => "HomeController",
1160
- :action => "show"
1161
- )
1162
- expect(transaction).to have_action("HomeController#show")
1163
- end
1164
- end
1165
-
1166
- context "for a hash with just action" do
1167
- it "sets the action" do
1168
- transaction.set_http_or_background_action(:action => "show")
1169
- expect(transaction).to have_action("show")
1170
- end
1171
- end
1172
-
1173
- context "for a hash with class and method" do
1174
- it "sets the action" do
1175
- transaction.set_http_or_background_action(:class => "Worker", :method => "perform")
1176
- expect(transaction).to have_action("Worker#perform")
1177
- end
1178
- end
1179
-
1180
- context "when action is already set" do
1181
- it "does not overwrite the set action" do
1182
- transaction.set_action("MyCustomAction#perform")
1183
- transaction.set_http_or_background_action(:class => "Worker", :method => "perform")
1184
- expect(transaction).to have_action("MyCustomAction#perform")
1185
- end
1186
- end
1187
- end
1188
-
1189
1307
  describe "#set_queue_start" do
1190
1308
  let(:transaction) { new_transaction }
1191
1309
 
@@ -1210,75 +1328,6 @@ describe Appsignal::Transaction do
1210
1328
  end
1211
1329
  end
1212
1330
 
1213
- describe "#set_http_or_background_queue_start" do
1214
- let(:transaction) { legacy_new_transaction(:request => legacy_request(env)) }
1215
- let(:err_stream) { std_stream }
1216
- let(:stderr) { err_stream.read }
1217
- let(:header_factor) { 1_000 }
1218
- let(:env_queue_start) { fixed_time + 20 } # in seconds
1219
-
1220
- def set_http_or_background_queue_start
1221
- capture_std_streams(std_stream, err_stream) do
1222
- transaction.set_http_or_background_queue_start
1223
- end
1224
- end
1225
-
1226
- context "when a queue time is found in a request header" do
1227
- let(:header_time) { ((fixed_time + 10) * header_factor).to_i } # in milliseconds
1228
- let(:env) { { "HTTP_X_REQUEST_START" => "t=#{header_time}" } }
1229
-
1230
- it "sets the http header value in milliseconds on the transaction" do
1231
- set_http_or_background_queue_start
1232
-
1233
- expect(transaction).to have_queue_start(1_389_783_610_000)
1234
- end
1235
-
1236
- it "logs a deprecation message" do
1237
- logs = capture_logs { set_http_or_background_queue_start }
1238
-
1239
- expect(logs).to contains_log(
1240
- :warn,
1241
- "The Appsignal::Transaction#set_http_or_background_queue_start " \
1242
- "method has been deprecated."
1243
- )
1244
- end
1245
-
1246
- it "prints a deprecation message" do
1247
- set_http_or_background_queue_start
1248
-
1249
- expect(stderr).to include(
1250
- "The Appsignal::Transaction#set_http_or_background_queue_start " \
1251
- "method has been deprecated."
1252
- )
1253
- end
1254
-
1255
- context "when a :queue_start key is found in the transaction environment" do
1256
- let(:env) do
1257
- {
1258
- "HTTP_X_REQUEST_START" => "t=#{header_time}",
1259
- :queue_start => env_queue_start
1260
- }
1261
- end
1262
-
1263
- it "sets the http header value in milliseconds on the transaction" do
1264
- set_http_or_background_queue_start
1265
-
1266
- expect(transaction).to have_queue_start(1_389_783_610_000)
1267
- end
1268
- end
1269
- end
1270
-
1271
- context "when a :queue_start key is found in the transaction environment" do
1272
- let(:env) { { :queue_start => env_queue_start } } # in seconds
1273
-
1274
- it "sets the :queue_start value in milliseconds on the transaction" do
1275
- set_http_or_background_queue_start
1276
-
1277
- expect(transaction).to have_queue_start(1_389_783_620_000)
1278
- end
1279
- end
1280
- end
1281
-
1282
1331
  describe "#set_metadata" do
1283
1332
  let(:transaction) { new_transaction }
1284
1333
 
@@ -1289,8 +1338,7 @@ describe Appsignal::Transaction do
1289
1338
  end
1290
1339
 
1291
1340
  context "when filter_metadata includes metadata key" do
1292
- before { Appsignal.config[:filter_metadata] = ["filter_key"] }
1293
- after { Appsignal.config[:filter_metadata] = [] }
1341
+ let(:options) { { :filter_metadata => ["filter_key"] } }
1294
1342
 
1295
1343
  it "does not set the metadata on the transaction" do
1296
1344
  transaction.set_metadata(:filter_key, "filtered value")
@@ -1363,14 +1411,22 @@ describe Appsignal::Transaction do
1363
1411
  expect(transaction).to_not include_params
1364
1412
  end
1365
1413
 
1366
- expect(logs).to contains_log :error,
1367
- %(Invalid sample data for 'params'. Value is not an Array or Hash: '"some string"')
1368
- expect(logs).to contains_log :error,
1369
- %(Invalid sample data for 'params'. Value is not an Array or Hash: '123')
1370
- expect(logs).to contains_log :error,
1371
- %(Invalid sample data for 'params'. Value is not an Array or Hash: '"#<Class>"')
1372
- expect(logs).to contains_log :error,
1373
- %(Invalid sample data for 'params'. Value is not an Array or Hash: '"#<Set>"')
1414
+ expect(logs).to contains_log(
1415
+ :error,
1416
+ %(Sample data 'params': Unsupported data type 'String' received: "some string")
1417
+ )
1418
+ expect(logs).to contains_log(
1419
+ :error,
1420
+ %(Sample data 'params': Unsupported data type 'Integer' received: 123)
1421
+ )
1422
+ expect(logs).to contains_log(
1423
+ :error,
1424
+ %(Sample data 'params': Unsupported data type 'Class' received: #<Class)
1425
+ )
1426
+ expect(logs).to contains_log(
1427
+ :error,
1428
+ %(Sample data 'params': Unsupported data type 'Set' received: #<Set: {"some value"}>)
1429
+ )
1374
1430
  end
1375
1431
 
1376
1432
  it "does not store data that can't be converted to JSON" do
@@ -1400,7 +1456,8 @@ describe Appsignal::Transaction do
1400
1456
 
1401
1457
  it "updates the sample data on the transaction" do
1402
1458
  silence do
1403
- transaction.set_sample_data(
1459
+ transaction.send(
1460
+ :set_sample_data,
1404
1461
  "params",
1405
1462
  :controller => "blog_posts",
1406
1463
  :action => "show",
@@ -1419,7 +1476,7 @@ describe Appsignal::Transaction do
1419
1476
  it "does not update the sample data on the transaction" do
1420
1477
  logs =
1421
1478
  capture_logs do
1422
- silence { transaction.set_sample_data("params", "string") }
1479
+ silence { transaction.send(:set_sample_data, "params", "string") }
1423
1480
  end
1424
1481
 
1425
1482
  expect(transaction.to_h["sample_data"]).to eq({})
@@ -1437,7 +1494,7 @@ describe Appsignal::Transaction do
1437
1494
  end
1438
1495
  logs =
1439
1496
  capture_logs do
1440
- silence { transaction.set_sample_data("params", klass.new => 1) }
1497
+ silence { transaction.send(:set_sample_data, "params", klass.new => 1) }
1441
1498
  end
1442
1499
 
1443
1500
  expect(transaction).to_not include_params
@@ -1447,91 +1504,202 @@ describe Appsignal::Transaction do
1447
1504
  end
1448
1505
  end
1449
1506
 
1450
- describe "#sample_data" do
1451
- let(:transaction) { legacy_new_transaction(:request => rack_request(env)) }
1452
- let(:env) do
1453
- Rack::MockRequest.env_for(
1454
- "/blog",
1455
- "REQUEST_METHOD" => "GET",
1456
- "SERVER_NAME" => "example.org",
1457
- "SERVER_PORT" => "80",
1458
- "PATH_INFO" => "/blog",
1459
- "rack.session" => { "session" => "value" },
1460
- :params => {
1461
- "controller" => "blog_posts",
1462
- "action" => "show",
1463
- "id" => "1"
1464
- }
1465
- ).merge(
1466
- :metadata => { "metadata" => "value" }
1467
- )
1507
+ describe "#add_error" do
1508
+ let(:transaction) { create_transaction }
1509
+
1510
+ let(:error) do
1511
+ e = ExampleStandardError.new("test message")
1512
+ allow(e).to receive(:backtrace).and_return(["line 1"])
1513
+ e
1468
1514
  end
1469
1515
 
1470
- it "sets sample data from request" do
1471
- transaction.set_tags "tag" => "value"
1472
- transaction.add_breadcrumb "category", "action", "message", "key" => "value"
1473
- silence { transaction.sample_data }
1516
+ context "when error argument is not an error" do
1517
+ let(:error) { Object.new }
1518
+
1519
+ it "does not add the error" do
1520
+ logs = capture_logs { transaction.add_error(error) }
1474
1521
 
1475
- expect(transaction).to include_environment(
1476
- "REQUEST_METHOD" => "GET",
1477
- "SERVER_NAME" => "example.org",
1478
- "SERVER_PORT" => "80",
1479
- "PATH_INFO" => "/blog"
1480
- )
1481
- expect(transaction).to include_session_data("session" => "value")
1482
- expect(transaction).to include_params(
1483
- "controller" => "blog_posts",
1484
- "action" => "show",
1485
- "id" => "1"
1486
- )
1487
- expect(transaction).to include_sample_metadata("metadata" => "value")
1488
- expect(transaction).to include_tags("tag" => "value")
1489
- expect(transaction).to include_breadcrumb(
1490
- "action",
1491
- "category",
1492
- "message",
1493
- { "key" => "value" },
1494
- kind_of(Integer)
1495
- )
1522
+ expect(transaction).to_not have_error
1523
+ expect(logs).to contains_log(
1524
+ :error,
1525
+ "Appsignal::Transaction#add_error: Cannot add error. " \
1526
+ "The given value is not an exception: #{error.inspect}"
1527
+ )
1528
+ end
1496
1529
  end
1497
- end
1498
1530
 
1499
- describe "#set_error" do
1500
- let(:transaction) { new_transaction }
1501
- let(:env) { http_request_env_with_data }
1502
- let(:error) { ExampleStandardError.new("test message") }
1531
+ context "when AppSignal is not active" do
1532
+ it "does not add the error" do
1533
+ allow(Appsignal).to receive(:active?).and_return(false)
1503
1534
 
1504
- it "responds to add_exception for backwards compatibility" do
1505
- expect(transaction).to respond_to(:add_exception)
1535
+ transaction.add_error(error)
1536
+
1537
+ expect(transaction).to_not have_error
1538
+ end
1539
+ end
1540
+
1541
+ context "when a block is given" do
1542
+ it "stores the block in the error blocks" do
1543
+ block = proc { "block" }
1544
+
1545
+ transaction.add_error(error, &block)
1546
+
1547
+ expect(transaction.error_blocks).to eq({
1548
+ error => [block]
1549
+ })
1550
+ end
1506
1551
  end
1507
1552
 
1508
- it "does not add the error if not active" do
1509
- allow(Appsignal).to receive(:active?).and_return(false)
1553
+ context "when no error is set in the transaction" do
1554
+ it "sets the error on the transaction" do
1555
+ transaction.add_error(error)
1556
+
1557
+ expect(transaction).to have_error(
1558
+ "ExampleStandardError",
1559
+ "test message",
1560
+ ["line 1"]
1561
+ )
1562
+ end
1510
1563
 
1511
- transaction.set_error(error)
1564
+ it "does store the error in the errors" do
1565
+ transaction.add_error(error)
1512
1566
 
1513
- expect(transaction).to_not have_error
1567
+ expect(transaction.error_blocks).to eq({ error => [] })
1568
+ end
1514
1569
  end
1515
1570
 
1516
- context "when error argument is not an error" do
1517
- let(:error) { Object.new }
1571
+ context "when an error is already set in the transaction" do
1572
+ let(:other_error) do
1573
+ e = ExampleStandardError.new("other test message")
1574
+ allow(e).to receive(:backtrace).and_return(["line 2"])
1575
+ e
1576
+ end
1518
1577
 
1519
- it "does not add the error" do
1520
- logs = capture_logs { transaction.set_error(error) }
1578
+ before { transaction.set_error(other_error) }
1579
+
1580
+ it "stores an error in the errors" do
1581
+ transaction.add_error(error)
1582
+
1583
+ expect(transaction.error_blocks).to eq({
1584
+ other_error => [],
1585
+ error => []
1586
+ })
1587
+ end
1588
+
1589
+ it "does not set the error on the extension" do
1590
+ transaction.add_error(error)
1591
+
1592
+ expect(transaction).to have_error(
1593
+ "ExampleStandardError",
1594
+ "other test message",
1595
+ ["line 2"]
1596
+ )
1597
+ end
1598
+ end
1599
+
1600
+ context "when the error has already been added" do
1601
+ before { transaction.add_error(error) }
1602
+
1603
+ it "does not add the error to the errors" do
1604
+ expect(transaction.error_blocks).to eq({ error => [] })
1605
+
1606
+ transaction.add_error(error)
1607
+
1608
+ expect(transaction.error_blocks).to eq({ error => [] })
1609
+ end
1610
+
1611
+ context "when a block is given" do
1612
+ it "adds the block to the error blocks" do
1613
+ block = proc { "block" }
1614
+
1615
+ transaction.add_error(error, &block)
1616
+
1617
+ expect(transaction.error_blocks).to eq({ error => [block] })
1618
+ end
1619
+ end
1620
+ end
1621
+
1622
+ context "when the errors is at the limit" do
1623
+ let(:seen_error) { ExampleStandardError.new("error 0") }
1624
+
1625
+ before do
1626
+ transaction.add_error(seen_error)
1627
+
1628
+ 9.times do |i|
1629
+ transaction.add_error(ExampleStandardError.new("error #{i}"))
1630
+ end
1631
+ end
1632
+
1633
+ it "does not add a new error to the errors" do
1634
+ expect(transaction).to have_error("ExampleStandardError", "error 0", [])
1635
+ expect(transaction.error_blocks.length).to eq(10)
1636
+ expected_error_blocks = transaction.error_blocks.dup
1637
+
1638
+ transaction.add_error(error)
1639
+
1640
+ expect(transaction).to have_error("ExampleStandardError", "error 0", [])
1641
+ expect(transaction.error_blocks).to eq(expected_error_blocks)
1642
+ end
1643
+
1644
+ it "logs a debug message" do
1645
+ logs = capture_logs { transaction.add_error(error) }
1521
1646
 
1522
- expect(transaction).to_not have_error
1523
1647
  expect(logs).to contains_log(
1524
- :error,
1525
- "Appsignal::Transaction#set_error: Cannot set error. " \
1526
- "The given value is not an exception: #{error.inspect}"
1648
+ :warn,
1649
+ "Appsignal::Transaction#add_error: Transaction has more than 10 distinct errors. " \
1650
+ "Only the first 10 distinct errors will be reported."
1527
1651
  )
1528
1652
  end
1653
+
1654
+ context "when the error has already been added" do
1655
+ it "does not add the error to the errors" do
1656
+ expect(transaction.error_blocks.length).to eq(10)
1657
+
1658
+ transaction.add_error(seen_error)
1659
+
1660
+ expect(transaction.error_blocks.length).to eq(10)
1661
+ end
1662
+
1663
+ it "does add the block to the error blocks" do
1664
+ block = proc { "block" }
1665
+
1666
+ transaction.add_error(seen_error, &block)
1667
+
1668
+ expect(transaction.error_blocks[seen_error]).to eq([block])
1669
+ end
1670
+
1671
+ it "does not log a debug message" do
1672
+ logs = capture_logs { transaction.add_error(seen_error) }
1673
+
1674
+ expect(logs).to_not contains_log(
1675
+ :warn,
1676
+ "Appsignal::Transaction#add_error: Transaction has more than 10 distinct errors. " \
1677
+ "Only the first 10 distinct errors will be reported."
1678
+ )
1679
+ end
1680
+ end
1681
+ end
1682
+ end
1683
+
1684
+ describe "#_set_error" do
1685
+ let(:transaction) { new_transaction }
1686
+ let(:env) { http_request_env_with_data }
1687
+ let(:error) { ExampleStandardError.new("test message") }
1688
+
1689
+ it "responds to add_exception for backwards compatibility" do
1690
+ expect(transaction).to respond_to(:add_exception)
1691
+ end
1692
+
1693
+ it "does not add the error to the errors" do
1694
+ transaction.send(:_set_error, error)
1695
+
1696
+ expect(transaction.error_blocks).to be_empty
1529
1697
  end
1530
1698
 
1531
1699
  context "for a http request" do
1532
1700
  it "sets an error on the transaction" do
1533
1701
  allow(error).to receive(:backtrace).and_return(["line 1"])
1534
- transaction.set_error(error)
1702
+ transaction.send(:_set_error, error)
1535
1703
 
1536
1704
  expect(transaction).to have_error(
1537
1705
  "ExampleStandardError",
@@ -1542,10 +1710,10 @@ describe Appsignal::Transaction do
1542
1710
  end
1543
1711
 
1544
1712
  context "when the error has no causes" do
1545
- it "does not set the causes information as sample data" do
1546
- transaction.set_error(error)
1713
+ it "should set an empty causes array as sample data" do
1714
+ transaction.send(:_set_error, error)
1547
1715
 
1548
- expect(transaction).to_not include_error_causes
1716
+ expect(transaction).to include_error_causes([])
1549
1717
  end
1550
1718
  end
1551
1719
 
@@ -1560,8 +1728,12 @@ describe Appsignal::Transaction do
1560
1728
  e
1561
1729
  end
1562
1730
 
1731
+ let(:error_without_cause) do
1732
+ ExampleStandardError.new("error without cause")
1733
+ end
1734
+
1563
1735
  it "sends the causes information as sample data" do
1564
- transaction.set_error(error)
1736
+ transaction.send(:_set_error, error)
1565
1737
 
1566
1738
  expect(transaction).to have_error(
1567
1739
  "ExampleStandardError",
@@ -1581,6 +1753,19 @@ describe Appsignal::Transaction do
1581
1753
  ]
1582
1754
  )
1583
1755
  end
1756
+
1757
+ it "does not keep error causes from previously set errors" do
1758
+ transaction.send(:_set_error, error)
1759
+ transaction.send(:_set_error, error_without_cause)
1760
+
1761
+ expect(transaction).to have_error(
1762
+ "ExampleStandardError",
1763
+ "error without cause",
1764
+ []
1765
+ )
1766
+
1767
+ expect(transaction).to include_error_causes([])
1768
+ end
1584
1769
  end
1585
1770
 
1586
1771
  context "when the error has too many causes" do
@@ -1607,7 +1792,7 @@ describe Appsignal::Transaction do
1607
1792
  end
1608
1793
  expected_error_causes.last["is_root_cause"] = false
1609
1794
 
1610
- logs = capture_logs { transaction.set_error(error) }
1795
+ logs = capture_logs { transaction.send(:_set_error, error) }
1611
1796
 
1612
1797
  expect(transaction).to have_error(
1613
1798
  "ExampleStandardError",
@@ -1617,7 +1802,7 @@ describe Appsignal::Transaction do
1617
1802
  expect(transaction).to include_error_causes(expected_error_causes)
1618
1803
  expect(logs).to contains_log(
1619
1804
  :debug,
1620
- "Appsignal::Transaction#set_error: Error has more " \
1805
+ "Appsignal::Transaction#add_error: Error has more " \
1621
1806
  "than 10 error causes. Only the first 10 " \
1622
1807
  "will be reported."
1623
1808
  )
@@ -1633,11 +1818,11 @@ describe Appsignal::Transaction do
1633
1818
  end
1634
1819
 
1635
1820
  it "does not raise an error" do
1636
- transaction.set_error(error)
1821
+ transaction.send(:_set_error, error)
1637
1822
  end
1638
1823
 
1639
1824
  it "sets an error on the transaction without an error message" do
1640
- transaction.set_error(error)
1825
+ transaction.send(:_set_error, error)
1641
1826
 
1642
1827
  expect(transaction).to have_error(
1643
1828
  "ExampleStandardError",
@@ -1780,433 +1965,8 @@ describe Appsignal::Transaction do
1780
1965
  end
1781
1966
  end
1782
1967
 
1783
- context "GenericRequest" do
1784
- let(:env) { {} }
1785
- subject { Appsignal::Transaction::GenericRequest.new(env) }
1786
-
1787
- it "prints a deprecation warning on use" do
1788
- err_stream = std_stream
1789
- capture_std_streams(std_stream, err_stream) { subject }
1790
-
1791
- expect(err_stream.read).to include(
1792
- "appsignal WARNING: The use of Appsignal::Transaction::GenericRequest is deprecated."
1793
- )
1794
- end
1795
-
1796
- it "logs a deprecation warning on use" do
1797
- logs = capture_logs { silence { subject } }
1798
-
1799
- expect(logs).to contains_log(
1800
- :warn,
1801
- "The use of Appsignal::Transaction::GenericRequest is deprecated."
1802
- )
1803
- end
1804
-
1805
- it "initializes with an empty env" do
1806
- expect(subject.env).to be_empty
1807
- end
1808
-
1809
- context "when given an env" do
1810
- let(:env) do
1811
- {
1812
- :params => { :id => 1 },
1813
- :queue_start => 10
1814
- }
1815
- end
1816
-
1817
- it "sets the given env" do
1818
- expect(subject.env).to eq env
1819
- end
1820
-
1821
- it "sets the params present in the env" do
1822
- expect(subject.params).to eq(:id => 1)
1823
- end
1824
- end
1825
- end
1826
-
1827
1968
  # private
1828
1969
 
1829
- describe "#background_queue_start" do
1830
- let(:transaction) { legacy_new_transaction(:request => request) }
1831
- let(:request) { rack_request(env) }
1832
- let(:env) { {} }
1833
- subject { transaction.send(:background_queue_start) }
1834
-
1835
- context "when request is nil" do
1836
- let(:request) { nil }
1837
-
1838
- it { is_expected.to eq nil }
1839
- end
1840
-
1841
- context "when env is nil" do
1842
- before { expect(request).to receive(:env).and_return(nil) }
1843
-
1844
- it { is_expected.to eq nil }
1845
- end
1846
-
1847
- context "when queue start is nil" do
1848
- it { is_expected.to eq nil }
1849
- end
1850
-
1851
- context "when queue start is set" do
1852
- before do
1853
- env[:queue_start] = fixed_time
1854
- end
1855
-
1856
- it { is_expected.to eq 1_389_783_600_000 }
1857
- end
1858
- end
1859
-
1860
- describe "#http_queue_start" do
1861
- let(:transaction) { legacy_new_transaction(:request => request) }
1862
- let(:request) { rack_request(env) }
1863
- let(:env) { {} }
1864
- let(:slightly_earlier_time) { fixed_time - 0.4 }
1865
- let(:slightly_earlier_time_value) { (slightly_earlier_time * factor).to_i }
1866
- subject { transaction.send(:http_queue_start) }
1867
-
1868
- shared_examples "http queue start" do
1869
- context "when request is nil" do
1870
- let(:request) { nil }
1871
-
1872
- it { is_expected.to be_nil }
1873
- end
1874
-
1875
- context "when env is nil" do
1876
- before { expect(request).to receive(:env).and_return(nil) }
1877
-
1878
- it { is_expected.to be_nil }
1879
- end
1880
-
1881
- context "with no relevant header set" do
1882
- let(:env) { {} }
1883
-
1884
- it { is_expected.to be_nil }
1885
- end
1886
-
1887
- context "with the HTTP_X_REQUEST_START header set" do
1888
- let(:env) { { "HTTP_X_REQUEST_START" => "t=#{slightly_earlier_time_value}" } }
1889
-
1890
- it { is_expected.to eq 1_389_783_599_600 }
1891
-
1892
- context "with unparsable content" do
1893
- let(:env) { { "HTTP_X_REQUEST_START" => "something" } }
1894
-
1895
- it { is_expected.to be_nil }
1896
- end
1897
-
1898
- context "with unparsable content at the end" do
1899
- let(:env) { { "HTTP_X_REQUEST_START" => "t=#{slightly_earlier_time_value}aaaa" } }
1900
-
1901
- it { is_expected.to eq 1_389_783_599_600 }
1902
- end
1903
-
1904
- context "with a really low number" do
1905
- let(:env) { { "HTTP_X_REQUEST_START" => "t=100" } }
1906
-
1907
- it { is_expected.to be_nil }
1908
- end
1909
-
1910
- context "with the alternate HTTP_X_QUEUE_START header set" do
1911
- let(:env) { { "HTTP_X_QUEUE_START" => "t=#{slightly_earlier_time_value}" } }
1912
-
1913
- it { is_expected.to eq 1_389_783_599_600 }
1914
- end
1915
- end
1916
- end
1917
-
1918
- context "time in milliseconds" do
1919
- let(:factor) { 1_000 }
1920
-
1921
- it_should_behave_like "http queue start"
1922
- end
1923
-
1924
- context "time in microseconds" do
1925
- let(:factor) { 1_000_000 }
1926
-
1927
- it_should_behave_like "http queue start"
1928
- end
1929
- end
1930
-
1931
- describe "#sanitized_params" do
1932
- let(:transaction) { new_transaction }
1933
- subject { transaction.send(:sanitized_params) }
1934
-
1935
- context "with params" do
1936
- before do
1937
- transaction.set_params(:foo => "bar", :baz => :bat)
1938
- end
1939
-
1940
- it "returns params" do
1941
- is_expected.to eq(:foo => "bar", :baz => :bat)
1942
- end
1943
-
1944
- context "with AppSignal filtering" do
1945
- before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
1946
- after { Appsignal.config.config_hash[:filter_parameters] = [] }
1947
-
1948
- it "returns sanitized custom params" do
1949
- expect(subject).to eq(:foo => "[FILTERED]", :baz => :bat)
1950
- end
1951
- end
1952
- end
1953
-
1954
- context "params from request" do
1955
- let(:transaction) { legacy_new_transaction(:request => request, :options => options) }
1956
- let(:options) { {} }
1957
- let(:request) { rack_request(env) }
1958
- let(:env) { {} }
1959
-
1960
- context "without request params" do
1961
- before { allow(transaction.request).to receive(:params).and_return(nil) }
1962
-
1963
- it { is_expected.to be_nil }
1964
- end
1965
-
1966
- context "when request params crashes" do
1967
- before { expect(request).to receive(:params).and_raise(NoMethodError) }
1968
-
1969
- it { is_expected.to be_nil }
1970
- end
1971
-
1972
- context "when request params method does not exist" do
1973
- let(:options) { { :params_method => :nonsense } }
1974
-
1975
- it { is_expected.to be_nil }
1976
- end
1977
-
1978
- context "when not sending params" do
1979
- before { Appsignal.config.config_hash[:send_params] = false }
1980
- after { Appsignal.config.config_hash[:send_params] = true }
1981
-
1982
- it { is_expected.to be_nil }
1983
- end
1984
-
1985
- context "with an array" do
1986
- let(:request) { legacy_request(:params => %w[arg1 arg2]) }
1987
-
1988
- it { is_expected.to eq %w[arg1 arg2] }
1989
-
1990
- context "with AppSignal filtering" do
1991
- before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
1992
- after { Appsignal.config.config_hash[:filter_parameters] = [] }
1993
-
1994
- it { is_expected.to eq %w[arg1 arg2] }
1995
- end
1996
- end
1997
-
1998
- context "with env" do
1999
- context "with sanitization" do
2000
- let(:request) { legacy_request(:params => { :foo => :bar }) }
2001
-
2002
- it "should call the params sanitizer" do
2003
- expect(subject).to eq(:foo => :bar)
2004
- end
2005
- end
2006
-
2007
- context "with AppSignal filtering" do
2008
- let(:request) { legacy_request(:params => { :foo => :bar, :baz => :bat }) }
2009
- before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
2010
- after { Appsignal.config.config_hash[:filter_parameters] = [] }
2011
-
2012
- it "should call the params sanitizer with filtering" do
2013
- expect(subject).to eq(:foo => "[FILTERED]", :baz => :bat)
2014
- end
2015
- end
2016
- end
2017
- end
2018
- end
2019
-
2020
- describe "#sanitized_environment" do
2021
- let(:transaction) { legacy_new_transaction(:request => request) }
2022
- let(:request) { rack_request(env) }
2023
- let(:env) { {} }
2024
- let(:allowlisted_keys) { Appsignal.config[:request_headers] }
2025
- subject { transaction.send(:sanitized_environment) }
2026
-
2027
- context "when request is nil" do
2028
- let(:request) { nil }
2029
-
2030
- it { is_expected.to be_nil }
2031
- end
2032
-
2033
- context "when env is nil" do
2034
- before { expect(request).to receive(:env).and_return(nil) }
2035
-
2036
- it { is_expected.to be_nil }
2037
- end
2038
-
2039
- context "when env is present" do
2040
- let(:env) do
2041
- {}.tap do |hash|
2042
- allowlisted_keys.each { |o| hash[o] = 1 } # use all allowlisted keys
2043
- hash[allowlisted_keys] = nil # don't add if nil
2044
- hash[:not_allowlisted] = "I will be sanitized"
2045
- end
2046
- end
2047
-
2048
- it "only sets allowlisted keys" do
2049
- expect(subject.keys).to match_array(allowlisted_keys)
2050
- end
2051
-
2052
- context "with configured request_headers" do
2053
- before do
2054
- Appsignal.config.config_hash[:request_headers] = %w[CONTENT_LENGTH]
2055
- end
2056
-
2057
- it "only sets allowlisted keys" do
2058
- expect(subject.keys).to match_array(%w[CONTENT_LENGTH])
2059
- end
2060
- end
2061
- end
2062
- end
2063
-
2064
- describe "#sanitized_session_data" do
2065
- let(:transaction) { legacy_new_transaction(:request => request) }
2066
- let(:request) { rack_request(env) }
2067
- let(:env) { {} }
2068
- subject { transaction.send(:sanitized_session_data) }
2069
-
2070
- context "when request is nil" do
2071
- let(:request) { nil }
2072
-
2073
- it { is_expected.to be_nil }
2074
- end
2075
-
2076
- context "when session is nil" do
2077
- before { expect(transaction.request).to receive(:session).and_return(nil) }
2078
-
2079
- it { is_expected.to be_nil }
2080
- end
2081
-
2082
- context "when session is empty" do
2083
- before { expect(transaction.request).to receive(:session).and_return({}) }
2084
-
2085
- it { is_expected.to eq({}) }
2086
- end
2087
-
2088
- context "when request class does not have a session method" do
2089
- let(:request) { Appsignal::Transaction::GenericRequest.new({}) }
2090
-
2091
- it { is_expected.to be_nil }
2092
- end
2093
-
2094
- context "with a session" do
2095
- let(:session_data_filter) { [] }
2096
- before { Appsignal.config[:filter_session_data] = session_data_filter }
2097
- after { Appsignal.config[:filter_session_data] = [] }
2098
-
2099
- context "with generic session object" do
2100
- before do
2101
- expect(transaction).to respond_to(:request)
2102
- allow(transaction).to receive_message_chain(
2103
- :request,
2104
- :session => { :foo => :bar, :abc => :def }
2105
- )
2106
- allow(transaction).to receive_message_chain(:request, :fullpath => :bar)
2107
- end
2108
-
2109
- context "without session filtering" do
2110
- it "keeps the session data intact" do
2111
- expect(subject).to eq(:foo => :bar, :abc => :def)
2112
- end
2113
- end
2114
-
2115
- context "with session filtering" do
2116
- let(:session_data_filter) { %w[foo] }
2117
-
2118
- it "filters the session data" do
2119
- expect(subject).to eq(:foo => "[FILTERED]", :abc => :def)
2120
- end
2121
- end
2122
- end
2123
-
2124
- if defined? ActionDispatch::Request::Session
2125
- context "with ActionDispatch::Request::Session" do
2126
- let(:action_dispatch_session) do
2127
- store = Class.new do
2128
- def load_session(_env)
2129
- [1, { :foo => :bar, :abc => :def }]
2130
- end
2131
-
2132
- def session_exists?(_env)
2133
- true
2134
- end
2135
- end.new
2136
- ActionDispatch::Request::Session.create(store,
2137
- ActionDispatch::Request.new("rack.input" => StringIO.new), {})
2138
- end
2139
- before do
2140
- expect(transaction).to respond_to(:request)
2141
- allow(transaction).to receive_message_chain(
2142
- :request,
2143
- :session => action_dispatch_session
2144
- )
2145
- allow(transaction).to receive_message_chain(:request, :fullpath => :bar)
2146
- end
2147
-
2148
- context "without session filtering" do
2149
- it "keeps the session data intact" do
2150
- expect(subject).to eq("foo" => :bar, "abc" => :def)
2151
- end
2152
- end
2153
-
2154
- context "with session filtering" do
2155
- let(:session_data_filter) { %w[foo] }
2156
-
2157
- it "filters the session data" do
2158
- expect(subject).to eq("foo" => "[FILTERED]", "abc" => :def)
2159
- end
2160
- end
2161
- end
2162
- end
2163
-
2164
- context "when not sending session data" do
2165
- before { Appsignal.config[:send_session_data] = false }
2166
-
2167
- it "does not set any session data on the transaction" do
2168
- expect(subject).to be_nil
2169
- end
2170
- end
2171
- end
2172
- end
2173
-
2174
- describe "#sanitized_metadata" do
2175
- let(:transaction) { legacy_new_transaction(:request => request) }
2176
- let(:request) { rack_request(env) }
2177
- let(:env) { {} }
2178
- subject { transaction.send(:sanitized_metadata) }
2179
-
2180
- context "when request is nil" do
2181
- let(:request) { nil }
2182
-
2183
- it { is_expected.to be_nil }
2184
- end
2185
-
2186
- context "when env is nil" do
2187
- before { expect(request).to receive(:env).and_return(nil) }
2188
-
2189
- it { is_expected.to be_nil }
2190
- end
2191
-
2192
- context "when env is present" do
2193
- let(:env) { { :metadata => { "key" => "value" } } }
2194
-
2195
- it do
2196
- is_expected.to eq("key" => "value")
2197
- end
2198
-
2199
- context "with filter_metadata option set" do
2200
- before { Appsignal.config[:filter_metadata] = ["key"] }
2201
- after { Appsignal.config[:filter_metadata] = [] }
2202
-
2203
- it "filters out keys listed in the filter_metadata option" do
2204
- expect(subject.keys).to_not include("key")
2205
- end
2206
- end
2207
- end
2208
- end
2209
-
2210
1970
  describe "#cleaned_backtrace" do
2211
1971
  let(:transaction) { new_transaction }
2212
1972
  subject { transaction.send(:cleaned_backtrace, ["line 1", "line 2"]) }
@@ -2313,13 +2073,13 @@ describe Appsignal::Transaction do
2313
2073
  describe Appsignal::Transaction::NilTransaction do
2314
2074
  subject { Appsignal::Transaction::NilTransaction.new }
2315
2075
 
2316
- it "should have method stubs" do
2076
+ it "has method stubs" do
2317
2077
  subject.complete
2318
2078
  subject.pause!
2319
2079
  subject.resume!
2320
2080
  subject.paused?
2321
2081
  subject.store(:key)
2322
- subject.set_tags(:tag => 1)
2082
+ subject.add_tags(:tag => 1)
2323
2083
  subject.set_action("action")
2324
2084
  subject.set_http_or_background_action
2325
2085
  subject.set_queue_start(1)