appsignal 2.11.0.alpha.2-java → 2.11.0.beta.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/.semaphore/semaphore.yml +37 -9
  4. data/CHANGELOG.md +10 -1
  5. data/build_matrix.yml +5 -1
  6. data/gemfiles/rails-4.2.gemfile +9 -2
  7. data/gemfiles/rails-5.0.gemfile +1 -0
  8. data/gemfiles/rails-5.1.gemfile +1 -0
  9. data/gemfiles/rails-5.2.gemfile +1 -0
  10. data/gemfiles/rails-6.0.gemfile +1 -0
  11. data/gemfiles/resque-1.gemfile +7 -0
  12. data/gemfiles/{resque.gemfile → resque-2.gemfile} +1 -1
  13. data/lib/appsignal/hooks.rb +2 -0
  14. data/lib/appsignal/hooks/active_job.rb +89 -0
  15. data/lib/appsignal/hooks/resque.rb +60 -0
  16. data/lib/appsignal/hooks/sidekiq.rb +16 -92
  17. data/lib/appsignal/integrations/que.rb +1 -1
  18. data/lib/appsignal/integrations/resque.rb +9 -12
  19. data/lib/appsignal/integrations/resque_active_job.rb +9 -32
  20. data/lib/appsignal/transaction.rb +10 -0
  21. data/lib/appsignal/utils/deprecation_message.rb +5 -1
  22. data/lib/appsignal/version.rb +1 -1
  23. data/spec/lib/appsignal/hooks/activejob_spec.rb +458 -0
  24. data/spec/lib/appsignal/hooks/resque_spec.rb +185 -0
  25. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +215 -263
  26. data/spec/lib/appsignal/integrations/que_spec.rb +25 -6
  27. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +20 -179
  28. data/spec/lib/appsignal/integrations/resque_spec.rb +20 -85
  29. data/spec/lib/appsignal/probes/sidekiq_spec.rb +10 -7
  30. data/spec/lib/appsignal/transaction_spec.rb +5 -7
  31. data/spec/support/helpers/action_mailer_helpers.rb +25 -0
  32. data/spec/support/helpers/dependency_helper.rb +9 -2
  33. data/spec/support/helpers/transaction_helpers.rb +6 -0
  34. data/spec/support/stubs/sidekiq/api.rb +1 -1
  35. metadata +12 -3
@@ -0,0 +1,185 @@
1
+ describe Appsignal::Hooks::ResqueHook do
2
+ describe "#dependency_present?" do
3
+ subject { described_class.new.dependencies_present? }
4
+
5
+ context "when Resque is loaded" do
6
+ before { stub_const "Resque", 1 }
7
+
8
+ it { is_expected.to be_truthy }
9
+ end
10
+
11
+ context "when Resque is not loaded" do
12
+ before { hide_const "Resque" }
13
+
14
+ it { is_expected.to be_falsy }
15
+ end
16
+ end
17
+
18
+ if DependencyHelper.resque_present?
19
+ describe "#install" do
20
+ def perform_job(klass, options = {})
21
+ payload = { "class" => klass.to_s }.merge(options)
22
+ job = ::Resque::Job.new(queue, payload)
23
+ keep_transactions { job.perform }
24
+ end
25
+
26
+ let(:queue) { "default" }
27
+ let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
28
+ before do
29
+ start_agent
30
+
31
+ class ResqueTestJob
32
+ def self.perform(*_args)
33
+ end
34
+ end
35
+
36
+ class ResqueErrorTestJob
37
+ def self.perform
38
+ raise "resque job error"
39
+ end
40
+ end
41
+
42
+ expect(Appsignal).to receive(:stop) # Resque calls stop after every job
43
+ end
44
+ around do |example|
45
+ keep_transactions { example.run }
46
+ end
47
+ after do
48
+ Object.send(:remove_const, :ResqueTestJob)
49
+ Object.send(:remove_const, :ResqueErrorTestJob)
50
+ end
51
+
52
+ it "tracks a transaction on perform" do
53
+ perform_job(ResqueTestJob)
54
+
55
+ transaction = last_transaction
56
+ transaction_hash = transaction.to_h
57
+ expect(transaction_hash).to include(
58
+ "id" => kind_of(String),
59
+ "action" => "ResqueTestJob#perform",
60
+ "error" => nil,
61
+ "namespace" => namespace,
62
+ "metadata" => {},
63
+ "sample_data" => { "tags" => { "queue" => queue } }
64
+ )
65
+ expect(transaction_hash["events"].map { |e| e["name"] })
66
+ .to eql(["perform.resque"])
67
+ end
68
+
69
+ context "with error" do
70
+ it "tracks the error on the transaction" do
71
+ expect do
72
+ perform_job(ResqueErrorTestJob)
73
+ end.to raise_error(RuntimeError, "resque job error")
74
+
75
+ transaction = last_transaction
76
+ transaction_hash = transaction.to_h
77
+ expect(transaction_hash).to include(
78
+ "id" => kind_of(String),
79
+ "action" => "ResqueErrorTestJob#perform",
80
+ "error" => {
81
+ "name" => "RuntimeError",
82
+ "message" => "resque job error",
83
+ "backtrace" => kind_of(String)
84
+ },
85
+ "namespace" => namespace,
86
+ "metadata" => {},
87
+ "sample_data" => { "tags" => { "queue" => queue } }
88
+ )
89
+ end
90
+ end
91
+
92
+ context "with arguments" do
93
+ before do
94
+ Appsignal.config = project_fixture_config("production")
95
+ Appsignal.config[:filter_parameters] = ["foo"]
96
+ end
97
+
98
+ it "filters out configured arguments" do
99
+ perform_job(
100
+ ResqueTestJob,
101
+ "args" => [
102
+ "foo",
103
+ {
104
+ "foo" => "secret",
105
+ "bar" => "Bar",
106
+ "baz" => { "1" => "foo" }
107
+ }
108
+ ]
109
+ )
110
+
111
+ transaction = last_transaction
112
+ transaction_hash = transaction.to_h
113
+ expect(transaction_hash).to include(
114
+ "id" => kind_of(String),
115
+ "action" => "ResqueTestJob#perform",
116
+ "error" => nil,
117
+ "namespace" => namespace,
118
+ "metadata" => {},
119
+ "sample_data" => {
120
+ "tags" => { "queue" => queue },
121
+ "params" => [
122
+ "foo",
123
+ {
124
+ "foo" => "[FILTERED]",
125
+ "bar" => "Bar",
126
+ "baz" => { "1" => "foo" }
127
+ }
128
+ ]
129
+ }
130
+ )
131
+ end
132
+ end
133
+
134
+ context "with active job" do
135
+ before do
136
+ module ActiveJobMock
137
+ module QueueAdapters
138
+ module ResqueAdapter
139
+ module JobWrapper
140
+ class << self
141
+ def perform(job_data)
142
+ # Basic ActiveJob stub for this test.
143
+ # I haven't found a way to run Resque in a testing mode.
144
+ Appsignal.set_action(job_data["job_class"])
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ stub_const "ActiveJob", ActiveJobMock
153
+ end
154
+ after { Object.send(:remove_const, :ActiveJobMock) }
155
+
156
+ it "does not set arguments but lets the ActiveJob intergration handle it" do
157
+ perform_job(
158
+ ResqueTestJob,
159
+ "class" => "ActiveJob::QueueAdapters::ResqueAdapter::JobWrapper",
160
+ "args" => [
161
+ {
162
+ "job_class" => "ResqueTestJobByActiveJob#perform",
163
+ "arguments" => ["an argument", "second argument"]
164
+ }
165
+ ]
166
+ )
167
+
168
+ transaction = last_transaction
169
+ transaction_hash = transaction.to_h
170
+ expect(transaction_hash).to include(
171
+ "id" => kind_of(String),
172
+ "action" => "ResqueTestJobByActiveJob#perform",
173
+ "error" => nil,
174
+ "namespace" => namespace,
175
+ "metadata" => {},
176
+ "sample_data" => {
177
+ "tags" => { "queue" => queue }
178
+ # Params will be set by the ActiveJob integration
179
+ }
180
+ )
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -1,3 +1,54 @@
1
+ describe Appsignal::Hooks::SidekiqHook do
2
+ describe "#dependencies_present?" do
3
+ subject { described_class.new.dependencies_present? }
4
+
5
+ context "when Sidekiq constant is found" do
6
+ before { stub_const "Sidekiq", Class.new }
7
+
8
+ it { is_expected.to be_truthy }
9
+ end
10
+
11
+ context "when Sidekiq constant is not found" do
12
+ before { hide_const "Sidekiq" }
13
+
14
+ it { is_expected.to be_falsy }
15
+ end
16
+ end
17
+
18
+ describe "#install" do
19
+ class SidekiqMiddlewareMock < Set
20
+ def exists?(middleware)
21
+ include?(middleware)
22
+ end
23
+ end
24
+ module SidekiqMock
25
+ def self.middlewares
26
+ @middlewares ||= SidekiqMiddlewareMock.new
27
+ end
28
+
29
+ def self.configure_server
30
+ yield self
31
+ end
32
+
33
+ def self.server_middleware
34
+ yield middlewares if block_given?
35
+ middlewares
36
+ end
37
+ end
38
+
39
+ before do
40
+ Appsignal.config = project_fixture_config
41
+ stub_const "Sidekiq", SidekiqMock
42
+ end
43
+
44
+ it "adds the AppSignal SidekiqPlugin to the Sidekiq middleware chain" do
45
+ described_class.new.install
46
+
47
+ expect(Sidekiq.server_middleware.exists?(Appsignal::Hooks::SidekiqPlugin)).to be(true)
48
+ end
49
+ end
50
+ end
51
+
1
52
  describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
2
53
  class DelayedTestClass; end
3
54
 
@@ -48,37 +99,13 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
48
99
  expect(log_contents(log)).to_not contains_log(:warn, "Unable to load YAML")
49
100
  end
50
101
 
51
- shared_examples "unknown job action name" do
52
- it "sets the action name to unknown" do
53
- transaction_hash = transaction.to_h
54
- expect(transaction_hash).to include("action" => "unknown")
55
- end
56
-
57
- it "stores no sample data" do
58
- transaction_hash = transaction.to_h
59
- expect(transaction_hash).to include(
60
- "sample_data" => {
61
- "environment" => {},
62
- "params" => [],
63
- "tags" => {}
64
- }
65
- )
66
- end
67
-
68
- it "logs a debug message" do
69
- expect(log_contents(log)).to contains_log(
70
- :debug, "Unable to determine an action name from Sidekiq payload: #{item}"
71
- )
72
- end
73
- end
74
-
75
102
  describe "internal Sidekiq job values" do
76
103
  it "does not save internal Sidekiq values as metadata on transaction" do
77
104
  perform_job
78
105
 
79
106
  transaction_hash = transaction.to_h
80
107
  expect(transaction_hash["metadata"].keys)
81
- .to_not include(*Appsignal::Hooks::SidekiqPlugin::JOB_KEYS)
108
+ .to_not include(*Appsignal::Hooks::SidekiqPlugin::EXCLUDED_JOB_KEYS)
82
109
  end
83
110
  end
84
111
 
@@ -201,217 +228,6 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
201
228
  end
202
229
  end
203
230
 
204
- context "when using ActiveJob" do
205
- let(:item) do
206
- {
207
- "class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
208
- "wrapped" => "ActiveJobTestClass",
209
- "queue" => "default",
210
- "args" => [{
211
- "job_class" => "ActiveJobTestJob",
212
- "job_id" => "23e79d48-6966-40d0-b2d4-f7938463a263",
213
- "queue_name" => "default",
214
- "arguments" => [
215
- "foo", { "foo" => "Foo", "bar" => "Bar", "baz" => { 1 => :bar } }
216
- ]
217
- }],
218
- "retry" => true,
219
- "jid" => "efb140489485999d32b5504c",
220
- "created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
221
- "enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f
222
- }
223
- end
224
-
225
- it "creates a transaction with events" do
226
- perform_job
227
-
228
- transaction_hash = transaction.to_h
229
- expect(transaction_hash).to include(
230
- "id" => kind_of(String),
231
- "action" => "ActiveJobTestClass#perform",
232
- "error" => nil,
233
- "namespace" => namespace,
234
- "metadata" => {
235
- "queue" => "default"
236
- },
237
- "sample_data" => {
238
- "environment" => {},
239
- "params" => [
240
- "foo",
241
- {
242
- "foo" => "Foo",
243
- "bar" => "Bar",
244
- "baz" => { "1" => "bar" }
245
- }
246
- ],
247
- "tags" => {}
248
- }
249
- )
250
- # TODO: Not available in transaction.to_h yet.
251
- # https://github.com/appsignal/appsignal-agent/issues/293
252
- expect(transaction.request.env).to eq(
253
- :queue_start => Time.parse("2001-01-01 10:00:00UTC").to_f
254
- )
255
- expect_transaction_to_have_sidekiq_event(transaction_hash)
256
- end
257
-
258
- context "with ActionMailer job" do
259
- let(:item) do
260
- {
261
- "class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
262
- "wrapped" => "ActionMailer::DeliveryJob",
263
- "queue" => "default",
264
- "args" => [{
265
- "job_class" => "ActiveMailerTestJob",
266
- "job_id" => "23e79d48-6966-40d0-b2d4-f7938463a263",
267
- "queue_name" => "default",
268
- "arguments" => [
269
- "MailerClass", "mailer_method", "deliver_now",
270
- "foo", { "foo" => "Foo", "bar" => "Bar", "baz" => { 1 => :bar } }
271
- ]
272
- }],
273
- "retry" => true,
274
- "jid" => "efb140489485999d32b5504c",
275
- "created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
276
- "enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f
277
- }
278
- end
279
-
280
- it "creates a transaction for the ActionMailer class" do
281
- perform_job
282
-
283
- transaction_hash = transaction.to_h
284
- expect(transaction_hash).to include(
285
- "id" => kind_of(String),
286
- "action" => "MailerClass#mailer_method",
287
- "error" => nil,
288
- "namespace" => namespace,
289
- "metadata" => {
290
- "queue" => "default"
291
- },
292
- "sample_data" => {
293
- "environment" => {},
294
- "params" => [
295
- "foo",
296
- {
297
- "foo" => "Foo",
298
- "bar" => "Bar",
299
- "baz" => { "1" => "bar" }
300
- }
301
- ],
302
- "tags" => {}
303
- }
304
- )
305
- end
306
- end
307
-
308
- context "with parameter filtering" do
309
- before do
310
- Appsignal.config = project_fixture_config("production")
311
- Appsignal.config[:filter_parameters] = ["foo"]
312
- end
313
-
314
- it "filters selected arguments" do
315
- perform_job
316
-
317
- transaction_hash = transaction.to_h
318
- expect(transaction_hash["sample_data"]).to include(
319
- "params" => [
320
- "foo",
321
- {
322
- "foo" => "[FILTERED]",
323
- "bar" => "Bar",
324
- "baz" => { "1" => "bar" }
325
- }
326
- ]
327
- )
328
- end
329
- end
330
-
331
- context "when Sidekiq job payload is missing the 'wrapped' value" do
332
- let(:item) do
333
- {
334
- "class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
335
- "queue" => "default",
336
- "args" => [first_argument],
337
- "retry" => true,
338
- "jid" => "efb140489485999d32b5504c",
339
- "created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
340
- "enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f
341
- }
342
- end
343
- before { perform_job }
344
-
345
- context "when the first argument is not a Hash object" do
346
- let(:first_argument) { "foo" }
347
-
348
- include_examples "unknown job action name"
349
- end
350
-
351
- context "when the first argument is a Hash object not containing a job payload" do
352
- let(:first_argument) { { "foo" => "bar" } }
353
-
354
- include_examples "unknown job action name"
355
-
356
- context "when the argument contains an invalid job_class value" do
357
- let(:first_argument) { { "job_class" => :foo } }
358
-
359
- include_examples "unknown job action name"
360
- end
361
- end
362
-
363
- context "when the first argument is a Hash object containing a job payload" do
364
- let(:first_argument) do
365
- {
366
- "job_class" => "ActiveMailerTestJob",
367
- "job_id" => "23e79d48-6966-40d0-b2d4-f7938463a263",
368
- "queue_name" => "default",
369
- "arguments" => [
370
- "foo", { "foo" => "Foo", "bar" => "Bar", "baz" => { 1 => :bar } }
371
- ]
372
- }
373
- end
374
-
375
- it "sets the action name to the job class in the first argument" do
376
- transaction_hash = transaction.to_h
377
- expect(transaction_hash).to include(
378
- "action" => "ActiveMailerTestJob#perform"
379
- )
380
- end
381
-
382
- it "stores the job metadata on the transaction" do
383
- transaction_hash = transaction.to_h
384
- expect(transaction_hash).to include(
385
- "id" => kind_of(String),
386
- "error" => nil,
387
- "namespace" => namespace,
388
- "metadata" => {
389
- "queue" => "default"
390
- },
391
- "sample_data" => {
392
- "environment" => {},
393
- "params" => [
394
- "foo",
395
- {
396
- "foo" => "Foo",
397
- "bar" => "Bar",
398
- "baz" => { "1" => "bar" }
399
- }
400
- ],
401
- "tags" => {}
402
- }
403
- )
404
- end
405
-
406
- it "does not log a debug message" do
407
- expect(log_contents(log)).to_not contains_log(
408
- :debug, "Unable to determine an action name from Sidekiq payload"
409
- )
410
- end
411
- end
412
- end
413
- end
414
-
415
231
  context "with an error" do
416
232
  let(:error) { ExampleException }
417
233
 
@@ -510,47 +326,183 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
510
326
  end
511
327
  end
512
328
 
513
- describe Appsignal::Hooks::SidekiqHook do
514
- describe "#dependencies_present?" do
515
- subject { described_class.new.dependencies_present? }
329
+ if DependencyHelper.active_job_present?
330
+ require "active_job"
331
+ require "action_mailer"
332
+ require "sidekiq/testing"
333
+
334
+ describe "Sidekiq ActiveJob integration" do
335
+ let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
336
+ let(:time) { Time.parse("2001-01-01 10:00:00UTC") }
337
+ let(:log) { StringIO.new }
338
+ let(:given_args) do
339
+ [
340
+ "foo",
341
+ {
342
+ :foo => "Foo",
343
+ "bar" => "Bar",
344
+ "baz" => { "1" => "foo" }
345
+ }
346
+ ]
347
+ end
348
+ let(:expected_args) do
349
+ [
350
+ "foo",
351
+ {
352
+ "_aj_symbol_keys" => ["foo"],
353
+ "foo" => "Foo",
354
+ "bar" => "Bar",
355
+ "baz" => {
356
+ "_aj_symbol_keys" => [],
357
+ "1" => "foo"
358
+ }
359
+ }
360
+ ]
361
+ end
362
+ let(:expected_tags) do
363
+ {}.tap do |hash|
364
+ if DependencyHelper.rails_version >= Gem::Version.new("5.0.0")
365
+ hash["provider_job_id"] = kind_of(String)
366
+ end
367
+ end
368
+ end
369
+ before do
370
+ start_agent
371
+ Appsignal.logger = test_logger(log)
372
+ ActiveJob::Base.queue_adapter = :sidekiq
516
373
 
517
- context "when Sidekiq constant is found" do
518
- before { Object.const_set("Sidekiq", 1) }
519
- after { Object.send(:remove_const, "Sidekiq") }
374
+ class ActiveJobSidekiqTestJob < ActiveJob::Base
375
+ self.queue_adapter = :sidekiq
520
376
 
521
- it { is_expected.to be_truthy }
377
+ def perform(*_args)
378
+ end
379
+ end
380
+
381
+ class ActiveJobSidekiqErrorTestJob < ActiveJob::Base
382
+ self.queue_adapter = :sidekiq
383
+
384
+ def perform(*_args)
385
+ raise "uh oh"
386
+ end
387
+ end
388
+ # Manually add the AppSignal middleware for the Testing environment.
389
+ # It doesn't use configured middlewares by default looks like.
390
+ # We test somewhere else if the middleware is installed properly.
391
+ Sidekiq::Testing.server_middleware do |chain|
392
+ chain.add Appsignal::Hooks::SidekiqPlugin
393
+ end
394
+ end
395
+ around do |example|
396
+ keep_transactions do
397
+ Sidekiq::Testing.fake! do
398
+ example.run
399
+ end
400
+ end
401
+ end
402
+ after do
403
+ Object.send(:remove_const, :ActiveJobSidekiqTestJob)
404
+ Object.send(:remove_const, :ActiveJobSidekiqErrorTestJob)
522
405
  end
523
406
 
524
- context "when Sidekiq constant is not found" do
525
- before { Object.send(:remove_const, "Sidekiq") if defined?(Sidekiq) }
407
+ it "reports the transaction from the ActiveJob integration" do
408
+ perform_job(ActiveJobSidekiqTestJob, given_args)
526
409
 
527
- it { is_expected.to be_falsy }
410
+ transaction = last_transaction
411
+ transaction_hash = transaction.to_h
412
+ expect(transaction_hash).to include(
413
+ "action" => "ActiveJobSidekiqTestJob#perform",
414
+ "error" => nil,
415
+ "namespace" => namespace,
416
+ "metadata" => hash_including(
417
+ "queue" => "default"
418
+ ),
419
+ "sample_data" => hash_including(
420
+ "environment" => {},
421
+ "params" => [expected_args],
422
+ "tags" => expected_tags.merge("queue" => "default")
423
+ )
424
+ )
425
+ expect(transaction.request.env).to eq(:queue_start => time.to_f)
426
+ events = transaction_hash["events"]
427
+ .sort_by { |e| e["start"] }
428
+ .map { |event| event["name"] }
429
+ expect(events)
430
+ .to eq(["perform_job.sidekiq", "perform_start.active_job", "perform.active_job"])
528
431
  end
529
- end
530
432
 
531
- describe "#install" do
532
- before do
533
- Appsignal.config = project_fixture_config
534
- module Sidekiq
535
- def self.middlewares
536
- @middlewares ||= Set.new
537
- end
433
+ context "with error" do
434
+ it "reports the error on the transaction from the ActiveRecord integration" do
435
+ expect do
436
+ perform_job(ActiveJobSidekiqErrorTestJob, given_args)
437
+ end.to raise_error(RuntimeError, "uh oh")
538
438
 
539
- def self.configure_server
540
- yield self
541
- end
439
+ transaction = last_transaction
440
+ transaction_hash = transaction.to_h
441
+ expect(transaction_hash).to include(
442
+ "action" => "ActiveJobSidekiqErrorTestJob#perform",
443
+ "error" => {
444
+ "name" => "RuntimeError",
445
+ "message" => "uh oh",
446
+ "backtrace" => kind_of(String)
447
+ },
448
+ "namespace" => namespace,
449
+ "metadata" => hash_including(
450
+ "queue" => "default"
451
+ ),
452
+ "sample_data" => hash_including(
453
+ "environment" => {},
454
+ "params" => [expected_args],
455
+ "tags" => expected_tags.merge("queue" => "default")
456
+ )
457
+ )
458
+ expect(transaction.request.env).to eq(:queue_start => time.to_f)
459
+ events = transaction_hash["events"]
460
+ .sort_by { |e| e["start"] }
461
+ .map { |event| event["name"] }
462
+ expect(events)
463
+ .to eq(["perform_job.sidekiq", "perform_start.active_job", "perform.active_job"])
464
+ end
465
+ end
466
+
467
+ context "with ActionMailer" do
468
+ include ActionMailerHelpers
542
469
 
543
- def self.server_middleware
544
- yield middlewares
470
+ before do
471
+ class ActionMailerSidekiqTestJob < ActionMailer::Base
472
+ def welcome(*args)
473
+ end
545
474
  end
546
475
  end
476
+
477
+ it "reports ActionMailer data on the transaction" do
478
+ perform_mailer(ActionMailerSidekiqTestJob, :welcome, given_args)
479
+
480
+ transaction = last_transaction
481
+ transaction_hash = transaction.to_h
482
+ expect(transaction_hash).to include(
483
+ "action" => "ActionMailerSidekiqTestJob#welcome",
484
+ "sample_data" => hash_including(
485
+ "params" => ["ActionMailerSidekiqTestJob", "welcome", "deliver_now"] + expected_args
486
+ )
487
+ )
488
+ end
547
489
  end
548
- after { Object.send(:remove_const, "Sidekiq") }
549
490
 
550
- it "adds the AppSignal SidekiqPlugin to the Sidekiq middleware chain" do
551
- described_class.new.install
491
+ def perform_sidekiq
492
+ Timecop.freeze(time) do
493
+ yield
494
+ # Combined with Sidekiq::Testing.fake! and drain_all we get a
495
+ # enqueue_at in the job data.
496
+ Sidekiq::Worker.drain_all
497
+ end
498
+ end
499
+
500
+ def perform_job(job_class, args)
501
+ perform_sidekiq { job_class.perform_later(args) }
502
+ end
552
503
 
553
- expect(Sidekiq.middlewares).to include(Appsignal::Hooks::SidekiqPlugin)
504
+ def perform_mailer(mailer, method, args = nil)
505
+ perform_sidekiq { perform_action_mailer(mailer, method, args) }
554
506
  end
555
507
  end
556
508
  end