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

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