appsignal 3.10.0-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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +88 -0
  4. data/Gemfile +1 -0
  5. data/benchmark.rake +99 -42
  6. data/lib/appsignal/cli/demo.rb +0 -1
  7. data/lib/appsignal/config.rb +54 -98
  8. data/lib/appsignal/demo.rb +15 -20
  9. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
  10. data/lib/appsignal/event_formatter.rb +3 -2
  11. data/lib/appsignal/helpers/instrumentation.rb +331 -19
  12. data/lib/appsignal/hooks/action_cable.rb +21 -16
  13. data/lib/appsignal/hooks/active_job.rb +14 -8
  14. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  15. data/lib/appsignal/hooks/shoryuken.rb +3 -63
  16. data/lib/appsignal/integrations/action_cable.rb +5 -7
  17. data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
  18. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
  19. data/lib/appsignal/integrations/data_mapper.rb +1 -0
  20. data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
  21. data/lib/appsignal/integrations/dry_monitor.rb +1 -0
  22. data/lib/appsignal/integrations/excon.rb +1 -0
  23. data/lib/appsignal/integrations/http.rb +1 -0
  24. data/lib/appsignal/integrations/net_http.rb +1 -0
  25. data/lib/appsignal/integrations/object.rb +6 -0
  26. data/lib/appsignal/integrations/que.rb +13 -20
  27. data/lib/appsignal/integrations/railtie.rb +1 -1
  28. data/lib/appsignal/integrations/rake.rb +1 -5
  29. data/lib/appsignal/integrations/redis.rb +1 -0
  30. data/lib/appsignal/integrations/redis_client.rb +1 -0
  31. data/lib/appsignal/integrations/resque.rb +2 -5
  32. data/lib/appsignal/integrations/shoryuken.rb +75 -0
  33. data/lib/appsignal/integrations/sidekiq.rb +7 -15
  34. data/lib/appsignal/integrations/unicorn.rb +1 -0
  35. data/lib/appsignal/integrations/webmachine.rb +2 -5
  36. data/lib/appsignal/logger.rb +7 -3
  37. data/lib/appsignal/probes/helpers.rb +1 -0
  38. data/lib/appsignal/probes/mri.rb +1 -0
  39. data/lib/appsignal/probes/sidekiq.rb +1 -0
  40. data/lib/appsignal/probes.rb +3 -0
  41. data/lib/appsignal/rack/abstract_middleware.rb +18 -12
  42. data/lib/appsignal/rack/event_handler.rb +39 -8
  43. data/lib/appsignal/rack/generic_instrumentation.rb +1 -0
  44. data/lib/appsignal/rack/grape_middleware.rb +2 -1
  45. data/lib/appsignal/rack/streaming_listener.rb +1 -0
  46. data/lib/appsignal/rack.rb +29 -0
  47. data/lib/appsignal/span.rb +1 -0
  48. data/lib/appsignal/transaction.rb +308 -101
  49. data/lib/appsignal/utils/data.rb +0 -1
  50. data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
  51. data/lib/appsignal/utils/integration_logger.rb +0 -13
  52. data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
  53. data/lib/appsignal/utils/json.rb +0 -1
  54. data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
  55. data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
  56. data/lib/appsignal/utils.rb +6 -0
  57. data/lib/appsignal/version.rb +1 -1
  58. data/lib/appsignal.rb +6 -5
  59. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  60. data/spec/lib/appsignal/config_spec.rb +138 -43
  61. data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
  62. data/spec/lib/appsignal/hooks/activejob_spec.rb +9 -0
  63. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
  64. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
  65. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
  66. data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
  67. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
  68. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -4
  69. data/spec/lib/appsignal/integrations/webmachine_spec.rb +13 -1
  70. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +48 -3
  71. data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -10
  72. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
  73. data/spec/lib/appsignal/rack_spec.rb +63 -0
  74. data/spec/lib/appsignal/transaction_spec.rb +1634 -1071
  75. data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
  76. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
  77. data/spec/lib/appsignal_spec.rb +323 -10
  78. data/spec/support/helpers/transaction_helpers.rb +44 -20
  79. data/spec/support/matchers/transaction.rb +15 -1
  80. data/spec/support/testing.rb +1 -1
  81. metadata +6 -2
@@ -0,0 +1,459 @@
1
+ describe "Appsignal::Integrations::DelayedJobHook" do
2
+ before(:context) do
3
+ module Delayed
4
+ class Plugin
5
+ def self.callbacks
6
+ end
7
+ end
8
+
9
+ class Worker
10
+ def self.plugins
11
+ @plugins ||= []
12
+ end
13
+ end
14
+ end
15
+ require "appsignal/integrations/delayed_job_plugin"
16
+ end
17
+ after(:context) { Object.send(:remove_const, :Delayed) }
18
+ before { start_agent }
19
+
20
+ # We haven't found a way to test the hooks, we'll have to do that manually
21
+
22
+ describe ".invoke_with_instrumentation" do
23
+ let(:plugin) { Appsignal::Integrations::DelayedJobPlugin }
24
+ let(:time) { Time.parse("01-01-2001 10:01:00UTC") }
25
+ let(:created_at) { time - 3600 }
26
+ let(:run_at) { time - 3600 }
27
+ let(:payload_object) { double(:args => args) }
28
+ let(:job_data) do
29
+ {
30
+ :id => 123,
31
+ :name => "TestClass#perform",
32
+ :priority => 1,
33
+ :attempts => 1,
34
+ :queue => "default",
35
+ :created_at => created_at,
36
+ :run_at => run_at,
37
+ :payload_object => payload_object
38
+ }
39
+ end
40
+ let(:args) { ["argument"] }
41
+ let(:job) { double(job_data) }
42
+ let(:invoked_block) { proc {} }
43
+
44
+ def perform
45
+ Timecop.freeze(time) do
46
+ keep_transactions do
47
+ plugin.invoke_with_instrumentation(job, invoked_block)
48
+ end
49
+ end
50
+ end
51
+
52
+ context "with a normal call" do
53
+ it "wraps it in a transaction" do
54
+ perform
55
+
56
+ transaction = last_transaction
57
+ expect(transaction).to have_namespace("background_job")
58
+ expect(transaction).to have_action("TestClass#perform")
59
+ expect(transaction).to_not have_error
60
+ expect(transaction).to include_event(:name => "perform_job.delayed_job")
61
+ expect(transaction).to include_tags(
62
+ "priority" => 1,
63
+ "attempts" => 1,
64
+ "queue" => "default",
65
+ "id" => "123"
66
+ )
67
+ expect(transaction).to include_params(["argument"])
68
+ end
69
+
70
+ context "with more complex params" do
71
+ let(:args) do
72
+ {
73
+ :foo => "Foo",
74
+ :bar => "Bar"
75
+ }
76
+ end
77
+
78
+ it "adds the more complex arguments" do
79
+ perform
80
+
81
+ expect(last_transaction).to include_params("foo" => "Foo", "bar" => "Bar")
82
+ end
83
+
84
+ context "with parameter filtering" do
85
+ before do
86
+ Appsignal.config = project_fixture_config("production")
87
+ Appsignal.config[:filter_parameters] = ["foo"]
88
+ end
89
+
90
+ it "filters selected arguments" do
91
+ perform
92
+
93
+ expect(last_transaction).to include_params("foo" => "[FILTERED]", "bar" => "Bar")
94
+ end
95
+ end
96
+ end
97
+
98
+ context "with run_at in the future" do
99
+ let(:run_at) { Time.parse("2017-01-01 10:01:00UTC") }
100
+
101
+ it "reports queue_start with run_at time" do
102
+ perform
103
+
104
+ expect(last_transaction).to have_queue_start(run_at.to_i * 1000)
105
+ end
106
+ end
107
+
108
+ context "with class method job" do
109
+ let(:job_data) do
110
+ { :name => "CustomClassMethod.perform", :payload_object => payload_object }
111
+ end
112
+
113
+ it "wraps it in a transaction using the class method job name" do
114
+ perform
115
+ expect(last_transaction).to have_action("CustomClassMethod.perform")
116
+ end
117
+ end
118
+
119
+ context "with custom name call" do
120
+ before { perform }
121
+
122
+ context "with appsignal_name defined" do
123
+ context "with payload_object being an object" do
124
+ context "with value" do
125
+ let(:payload_object) { double(:appsignal_name => "CustomClass#perform") }
126
+
127
+ it "wraps it in a transaction using the custom name" do
128
+ expect(last_transaction).to have_action("CustomClass#perform")
129
+ end
130
+ end
131
+
132
+ context "with non-String value" do
133
+ let(:payload_object) { double(:appsignal_name => Object.new) }
134
+
135
+ it "wraps it in a transaction using the original job name" do
136
+ expect(last_transaction).to have_action("TestClass#perform")
137
+ end
138
+ end
139
+
140
+ context "with class method name as job" do
141
+ let(:payload_object) { double(:appsignal_name => "CustomClassMethod.perform") }
142
+
143
+ it "wraps it in a transaction using the custom name" do
144
+ perform
145
+ expect(last_transaction).to have_action("CustomClassMethod.perform")
146
+ end
147
+ end
148
+ end
149
+
150
+ context "with payload_object being a Hash" do
151
+ context "with value" do
152
+ let(:payload_object) { double(:appsignal_name => "CustomClassHash#perform") }
153
+
154
+ it "wraps it in a transaction using the custom name" do
155
+ expect(last_transaction).to have_action("CustomClassHash#perform")
156
+ end
157
+ end
158
+
159
+ context "with non-String value" do
160
+ let(:payload_object) { double(:appsignal_name => Object.new) }
161
+
162
+ it "wraps it in a transaction using the original job name" do
163
+ expect(last_transaction).to have_action("TestClass#perform")
164
+ end
165
+ end
166
+
167
+ context "with class method name as job" do
168
+ let(:payload_object) { { :appsignal_name => "CustomClassMethod.perform" } }
169
+
170
+ it "wraps it in a transaction using the custom name" do
171
+ perform
172
+ expect(last_transaction).to have_action("CustomClassMethod.perform")
173
+ end
174
+ end
175
+ end
176
+
177
+ context "with payload_object acting like a Hash and returning a non-String value" do
178
+ class ClassActingAsHash
179
+ def self.[](_key)
180
+ Object.new
181
+ end
182
+
183
+ def self.appsignal_name
184
+ "ClassActingAsHash#perform"
185
+ end
186
+ end
187
+ let(:payload_object) { ClassActingAsHash }
188
+
189
+ # We check for hash values before object values
190
+ # this means ClassActingAsHash returns `Object.new` instead
191
+ # of `self.appsignal_name`. Since this isn't a valid `String`
192
+ # we return the default job name as action name.
193
+ it "wraps it in a transaction using the original job name" do
194
+ expect(last_transaction).to have_action("TestClass#perform")
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ context "with only job class name" do
201
+ let(:job_data) do
202
+ { :name => "Banana", :payload_object => payload_object }
203
+ end
204
+
205
+ it "appends #perform to the class name" do
206
+ perform
207
+ expect(last_transaction).to have_action("Banana#perform")
208
+ end
209
+ end
210
+
211
+ if active_job_present?
212
+ require "active_job"
213
+
214
+ context "when wrapped by ActiveJob" do
215
+ let(:payload_object) do
216
+ ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper.new(
217
+ "arguments" => args,
218
+ "job_class" => "TestClass",
219
+ "job_id" => 123,
220
+ "locale" => :en,
221
+ "queue_name" => "default"
222
+ )
223
+ end
224
+ let(:job) do
225
+ double(
226
+ :id => 123,
227
+ :name => "ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper",
228
+ :priority => 1,
229
+ :attempts => 1,
230
+ :queue => "default",
231
+ :created_at => created_at,
232
+ :run_at => run_at,
233
+ :payload_object => payload_object
234
+ )
235
+ end
236
+ let(:args) { ["activejob_argument"] }
237
+
238
+ it "wraps it in a transaction with the correct params" do
239
+ perform
240
+
241
+ transaction = last_transaction
242
+ expect(transaction).to have_namespace("background_job")
243
+ expect(transaction).to have_action("TestClass#perform")
244
+ expect(transaction).to_not have_error
245
+ expect(transaction).to include_event("name" => "perform_job.delayed_job")
246
+ expect(transaction).to include_tags(
247
+ "priority" => 1,
248
+ "attempts" => 1,
249
+ "queue" => "default",
250
+ "id" => "123"
251
+ )
252
+ expect(transaction).to include_params(["activejob_argument"])
253
+ end
254
+
255
+ context "with more complex params" do
256
+ let(:args) do
257
+ {
258
+ :foo => "Foo",
259
+ :bar => "Bar"
260
+ }
261
+ end
262
+
263
+ it "adds the more complex arguments" do
264
+ perform
265
+ transaction = last_transaction
266
+ expect(transaction).to have_action("TestClass#perform")
267
+ expect(transaction).to include_params(
268
+ "foo" => "Foo",
269
+ "bar" => "Bar"
270
+ )
271
+ end
272
+
273
+ context "with parameter filtering" do
274
+ before do
275
+ Appsignal.config = project_fixture_config("production")
276
+ Appsignal.config[:filter_parameters] = ["foo"]
277
+ end
278
+
279
+ it "filters selected arguments" do
280
+ perform
281
+ transaction = last_transaction
282
+ expect(transaction).to have_action("TestClass#perform")
283
+ expect(transaction).to include_params(
284
+ "foo" => "[FILTERED]",
285
+ "bar" => "Bar"
286
+ )
287
+ end
288
+ end
289
+ end
290
+
291
+ context "with run_at in the future" do
292
+ let(:run_at) { Time.parse("2017-01-01 10:01:00UTC") }
293
+
294
+ it "reports queue_start with run_at time" do
295
+ perform
296
+
297
+ expect(last_transaction).to have_queue_start(run_at.to_i * 1000)
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
303
+
304
+ context "with an erroring call" do
305
+ let(:error) { ExampleException.new("uh oh") }
306
+ before do
307
+ expect(invoked_block).to receive(:call).and_raise(error)
308
+ end
309
+
310
+ it "adds the error to the transaction" do
311
+ expect do
312
+ perform
313
+ end.to raise_error(error)
314
+
315
+ transaction = last_transaction
316
+ expect(transaction).to have_namespace("background_job")
317
+ expect(transaction).to have_action("TestClass#perform")
318
+ expect(transaction).to have_error("ExampleException", "uh oh")
319
+ end
320
+ end
321
+ end
322
+
323
+ describe ".extract_value" do
324
+ let(:plugin) { Appsignal::Integrations::DelayedJobPlugin }
325
+
326
+ context "for a hash" do
327
+ let(:hash) { { :key => "value", :bool_false => false } }
328
+
329
+ context "when the key exists" do
330
+ subject { plugin.extract_value(hash, :key) }
331
+
332
+ it { is_expected.to eq "value" }
333
+
334
+ context "when the value is false" do
335
+ subject { plugin.extract_value(hash, :bool_false) }
336
+
337
+ it { is_expected.to be false }
338
+ end
339
+ end
340
+
341
+ context "when the key does not exist" do
342
+ subject { plugin.extract_value(hash, :nonexistent_key) }
343
+
344
+ it { is_expected.to be_nil }
345
+
346
+ context "with a default value" do
347
+ subject { plugin.extract_value(hash, :nonexistent_key, 1) }
348
+
349
+ it { is_expected.to eq 1 }
350
+ end
351
+ end
352
+ end
353
+
354
+ context "for a struct" do
355
+ before :context do
356
+ TestStruct = Struct.new(:key)
357
+ end
358
+ let(:struct) { TestStruct.new("value") }
359
+
360
+ context "when the key exists" do
361
+ subject { plugin.extract_value(struct, :key) }
362
+
363
+ it { is_expected.to eq "value" }
364
+ end
365
+
366
+ context "when the key does not exist" do
367
+ subject { plugin.extract_value(struct, :nonexistent_key) }
368
+
369
+ it { is_expected.to be_nil }
370
+
371
+ context "with a default value" do
372
+ subject { plugin.extract_value(struct, :nonexistent_key, 1) }
373
+
374
+ it { is_expected.to eq 1 }
375
+ end
376
+ end
377
+ end
378
+
379
+ context "for a struct with a method" do
380
+ before :context do
381
+ class TestStructClass < Struct.new(:id) # rubocop:disable Style/StructInheritance
382
+ def appsignal_name
383
+ "TestStruct#perform"
384
+ end
385
+
386
+ def bool_false
387
+ false
388
+ end
389
+ end
390
+ end
391
+ let(:struct) { TestStructClass.new("id") }
392
+
393
+ context "when the Struct responds to a method" do
394
+ subject { plugin.extract_value(struct, :appsignal_name) }
395
+
396
+ it "returns the method value" do
397
+ is_expected.to eq "TestStruct#perform"
398
+ end
399
+
400
+ context "when the value is false" do
401
+ subject { plugin.extract_value(struct, :bool_false) }
402
+
403
+ it "returns the method value" do
404
+ is_expected.to be false
405
+ end
406
+ end
407
+ end
408
+
409
+ context "when the key does not exist" do
410
+ subject { plugin.extract_value(struct, :nonexistent_key) }
411
+
412
+ context "without a method with the same name" do
413
+ it "returns nil" do
414
+ is_expected.to be_nil
415
+ end
416
+ end
417
+
418
+ context "with a default value" do
419
+ let(:default_value) { :my_default_value }
420
+ subject { plugin.extract_value(struct, :nonexistent_key, default_value) }
421
+
422
+ it "returns the default value" do
423
+ is_expected.to eq default_value
424
+ end
425
+ end
426
+ end
427
+ end
428
+
429
+ context "for an object" do
430
+ let(:object) { double(:existing_method => "value") }
431
+
432
+ context "when the method exists" do
433
+ subject { plugin.extract_value(object, :existing_method) }
434
+
435
+ it { is_expected.to eq "value" }
436
+ end
437
+
438
+ context "when the method does not exist" do
439
+ subject { plugin.extract_value(object, :nonexistent_method) }
440
+
441
+ it { is_expected.to be_nil }
442
+
443
+ context "and there is a default value" do
444
+ subject { plugin.extract_value(object, :nonexistent_method, 1) }
445
+
446
+ it { is_expected.to eq 1 }
447
+ end
448
+ end
449
+ end
450
+
451
+ context "when we need to call to_s on the value" do
452
+ let(:object) { double(:existing_method => 1) }
453
+
454
+ subject { plugin.extract_value(object, :existing_method, nil, true) }
455
+
456
+ it { is_expected.to eq "1" }
457
+ end
458
+ end
459
+ end
@@ -39,7 +39,6 @@ if DependencyHelper.que_present?
39
39
  allow(Que).to receive(:execute)
40
40
 
41
41
  start_agent
42
- expect(Appsignal.active?).to be_truthy
43
42
  end
44
43
  around { |example| keep_transactions { example.run } }
45
44
 
@@ -66,7 +65,7 @@ if DependencyHelper.que_present?
66
65
  "title" => ""
67
66
  )
68
67
  expect(transaction).to include_params(%w[1 birds])
69
- expect(transaction).to include_sample_metadata(
68
+ expect(transaction).to include_tags(
70
69
  "attempts" => 0,
71
70
  "id" => 123,
72
71
  "priority" => 100,
@@ -95,7 +94,7 @@ if DependencyHelper.que_present?
95
94
  expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
96
95
  expect(transaction).to have_error(error.class.name, error.message)
97
96
  expect(transaction).to include_params(%w[1 birds])
98
- expect(transaction).to include_sample_metadata(
97
+ expect(transaction).to include_tags(
99
98
  "attempts" => 0,
100
99
  "id" => 123,
101
100
  "priority" => 100,
@@ -120,7 +119,7 @@ if DependencyHelper.que_present?
120
119
  expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
121
120
  expect(transaction).to have_error(error.class.name, error.message)
122
121
  expect(transaction).to include_params(%w[1 birds])
123
- expect(transaction).to include_sample_metadata(
122
+ expect(transaction).to include_tags(
124
123
  "attempts" => 0,
125
124
  "id" => 123,
126
125
  "priority" => 100,
@@ -0,0 +1,167 @@
1
+ require "appsignal/integrations/shoryuken"
2
+
3
+ describe Appsignal::Integrations::ShoryukenMiddleware do
4
+ class DemoShoryukenWorker
5
+ end
6
+
7
+ let(:time) { "2010-01-01 10:01:00UTC" }
8
+ let(:worker_instance) { DemoShoryukenWorker.new }
9
+ let(:queue) { "some-funky-queue-name" }
10
+ let(:sqs_msg) { double(:message_id => "msg1", :attributes => {}) }
11
+ let(:body) { {} }
12
+ before { start_agent }
13
+ around { |example| keep_transactions { example.run } }
14
+
15
+ def perform_shoryuken_job(&block)
16
+ block ||= lambda {}
17
+ Timecop.freeze(Time.parse(time)) do
18
+ described_class.new.call(
19
+ worker_instance,
20
+ queue,
21
+ sqs_msg,
22
+ body,
23
+ &block
24
+ )
25
+ end
26
+ end
27
+
28
+ context "with a performance call" do
29
+ let(:sent_timestamp) { Time.parse("1976-11-18 0:00:00UTC").to_i * 1000 }
30
+ let(:sqs_msg) do
31
+ double(:message_id => "msg1", :attributes => { "SentTimestamp" => sent_timestamp })
32
+ end
33
+
34
+ context "with complex argument" do
35
+ let(:body) { { :foo => "Foo", :bar => "Bar" } }
36
+
37
+ it "wraps the job in a transaction with the correct params" do
38
+ expect { perform_shoryuken_job }.to change { created_transactions.length }.by(1)
39
+
40
+ transaction = last_transaction
41
+ expect(transaction).to have_id
42
+ expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
43
+ expect(transaction).to have_action("DemoShoryukenWorker#perform")
44
+ expect(transaction).to_not have_error
45
+ expect(transaction).to include_event(
46
+ "body" => "",
47
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
48
+ "count" => 1,
49
+ "name" => "perform_job.shoryuken",
50
+ "title" => ""
51
+ )
52
+ expect(transaction).to include_params("foo" => "Foo", "bar" => "Bar")
53
+ expect(transaction).to include_tags(
54
+ "message_id" => "msg1",
55
+ "queue" => queue,
56
+ "SentTimestamp" => sent_timestamp
57
+ )
58
+ expect(transaction).to have_queue_start(sent_timestamp)
59
+ expect(transaction).to be_completed
60
+ end
61
+
62
+ context "with parameter filtering" do
63
+ before do
64
+ Appsignal.config = project_fixture_config("production")
65
+ Appsignal.config[:filter_parameters] = ["foo"]
66
+ end
67
+
68
+ it "filters selected arguments" do
69
+ perform_shoryuken_job
70
+
71
+ expect(last_transaction).to include_params("foo" => "[FILTERED]", "bar" => "Bar")
72
+ end
73
+ end
74
+ end
75
+
76
+ context "with a string as an argument" do
77
+ let(:body) { "foo bar" }
78
+
79
+ it "handles string arguments" do
80
+ perform_shoryuken_job
81
+
82
+ expect(last_transaction).to include_params("params" => body)
83
+ end
84
+ end
85
+
86
+ context "with primitive type as argument" do
87
+ let(:body) { 1 }
88
+
89
+ it "handles primitive types as arguments" do
90
+ perform_shoryuken_job
91
+
92
+ expect(last_transaction).to include_params("params" => body)
93
+ end
94
+ end
95
+ end
96
+
97
+ context "with exception" do
98
+ it "sets the exception on the transaction" do
99
+ expect do
100
+ expect do
101
+ perform_shoryuken_job { raise ExampleException, "error message" }
102
+ end.to raise_error(ExampleException)
103
+ end.to change { created_transactions.length }.by(1)
104
+
105
+ transaction = last_transaction
106
+ expect(transaction).to have_id
107
+ expect(transaction).to have_action("DemoShoryukenWorker#perform")
108
+ expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
109
+ expect(transaction).to have_error("ExampleException", "error message")
110
+ expect(transaction).to be_completed
111
+ end
112
+ end
113
+
114
+ context "with batched jobs" do
115
+ let(:sqs_msg) do
116
+ [
117
+ double(
118
+ :message_id => "msg2",
119
+ :attributes => {
120
+ "SentTimestamp" => (Time.parse("1976-11-18 01:00:00UTC").to_i * 1000).to_s
121
+ }
122
+ ),
123
+ double(
124
+ :message_id => "msg1",
125
+ :attributes => { "SentTimestamp" => sent_timestamp.to_s }
126
+ )
127
+ ]
128
+ end
129
+ let(:body) do
130
+ [
131
+ "foo bar",
132
+ { :id => "123", :foo => "Foo", :bar => "Bar" }
133
+ ]
134
+ end
135
+ let(:sent_timestamp) { Time.parse("1976-11-18 01:00:00UTC").to_i * 1000 }
136
+
137
+ it "creates a transaction for the batch" do
138
+ expect do
139
+ perform_shoryuken_job {} # rubocop:disable Lint/EmptyBlock
140
+ end.to change { created_transactions.length }.by(1)
141
+
142
+ transaction = last_transaction
143
+ expect(transaction).to have_id
144
+ expect(transaction).to have_action("DemoShoryukenWorker#perform")
145
+ expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
146
+ expect(transaction).to_not have_error
147
+ expect(transaction).to include_event(
148
+ "body" => "",
149
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
150
+ "count" => 1,
151
+ "name" => "perform_job.shoryuken",
152
+ "title" => ""
153
+ )
154
+ expect(transaction).to include_params(
155
+ "msg2" => "foo bar",
156
+ "msg1" => { "id" => "123", "foo" => "Foo", "bar" => "Bar" }
157
+ )
158
+ expect(transaction).to include_tags(
159
+ "batch" => true,
160
+ "queue" => "some-funky-queue-name",
161
+ "SentTimestamp" => sent_timestamp.to_s # Earliest/oldest timestamp from messages
162
+ )
163
+ # Queue time based on earliest/oldest timestamp from messages
164
+ expect(transaction).to have_queue_start(sent_timestamp)
165
+ end
166
+ end
167
+ end
@@ -362,7 +362,7 @@ describe Appsignal::Integrations::SidekiqMiddleware, :with_yaml_parse_error => f
362
362
  perform_sidekiq_job { raise error, "uh oh" }
363
363
  end.to raise_error(error)
364
364
 
365
- expect(transaction).to have_id(jid)
365
+ expect(transaction).to have_id
366
366
  expect(transaction).to have_namespace(namespace)
367
367
  expect(transaction).to have_action("TestClass#perform")
368
368
  expect(transaction).to have_error("ExampleException", "uh oh")
@@ -373,7 +373,7 @@ describe Appsignal::Integrations::SidekiqMiddleware, :with_yaml_parse_error => f
373
373
  )
374
374
  expect(transaction).to_not include_environment
375
375
  expect(transaction).to include_params(expected_args)
376
- expect(transaction).to_not include_tags
376
+ expect(transaction).to include_tags("request_id" => jid)
377
377
  expect(transaction).to_not include_breadcrumbs
378
378
  expect_transaction_to_have_sidekiq_event(transaction)
379
379
  end
@@ -418,11 +418,11 @@ describe Appsignal::Integrations::SidekiqMiddleware, :with_yaml_parse_error => f
418
418
  .with("sidekiq_queue_job_count", 1, { :queue => "default", :status => :processed })
419
419
  perform_sidekiq_job
420
420
 
421
- expect(transaction).to have_id(jid)
421
+ expect(transaction).to have_id
422
422
  expect(transaction).to have_namespace(namespace)
423
423
  expect(transaction).to have_action("TestClass#perform")
424
424
  expect(transaction).to_not have_error
425
- expect(transaction).to_not include_tags
425
+ expect(transaction).to include_tags("request_id" => jid)
426
426
  expect(transaction).to_not include_environment
427
427
  expect(transaction).to_not include_breadcrumbs
428
428
  expect(transaction).to_not include_params(expected_args)