appsignal 3.9.3-java → 3.11.0-java

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