appsignal 3.13.0-java → 4.0.0.beta.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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)