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.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/.semaphore/semaphore.yml +37 -9
- data/CHANGELOG.md +10 -1
- data/build_matrix.yml +5 -1
- data/gemfiles/rails-4.2.gemfile +9 -2
- data/gemfiles/rails-5.0.gemfile +1 -0
- data/gemfiles/rails-5.1.gemfile +1 -0
- data/gemfiles/rails-5.2.gemfile +1 -0
- data/gemfiles/rails-6.0.gemfile +1 -0
- data/gemfiles/resque-1.gemfile +7 -0
- data/gemfiles/{resque.gemfile → resque-2.gemfile} +1 -1
- data/lib/appsignal/hooks.rb +2 -0
- data/lib/appsignal/hooks/active_job.rb +89 -0
- data/lib/appsignal/hooks/resque.rb +60 -0
- data/lib/appsignal/hooks/sidekiq.rb +16 -92
- data/lib/appsignal/integrations/que.rb +1 -1
- data/lib/appsignal/integrations/resque.rb +9 -12
- data/lib/appsignal/integrations/resque_active_job.rb +9 -32
- data/lib/appsignal/transaction.rb +10 -0
- data/lib/appsignal/utils/deprecation_message.rb +5 -1
- data/lib/appsignal/version.rb +1 -1
- data/spec/lib/appsignal/hooks/activejob_spec.rb +458 -0
- data/spec/lib/appsignal/hooks/resque_spec.rb +185 -0
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +215 -263
- data/spec/lib/appsignal/integrations/que_spec.rb +25 -6
- data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +20 -179
- data/spec/lib/appsignal/integrations/resque_spec.rb +20 -85
- data/spec/lib/appsignal/probes/sidekiq_spec.rb +10 -7
- data/spec/lib/appsignal/transaction_spec.rb +5 -7
- data/spec/support/helpers/action_mailer_helpers.rb +25 -0
- data/spec/support/helpers/dependency_helper.rb +9 -2
- data/spec/support/helpers/transaction_helpers.rb +6 -0
- data/spec/support/stubs/sidekiq/api.rb +1 -1
- 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::
|
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
|
-
|
514
|
-
|
515
|
-
|
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
|
-
|
518
|
-
|
519
|
-
after { Object.send(:remove_const, "Sidekiq") }
|
374
|
+
class ActiveJobSidekiqTestJob < ActiveJob::Base
|
375
|
+
self.queue_adapter = :sidekiq
|
520
376
|
|
521
|
-
|
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
|
-
|
525
|
-
|
407
|
+
it "reports the transaction from the ActiveJob integration" do
|
408
|
+
perform_job(ActiveJobSidekiqTestJob, given_args)
|
526
409
|
|
527
|
-
|
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
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
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
|
-
|
540
|
-
|
541
|
-
|
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
|
-
|
544
|
-
|
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
|
-
|
551
|
-
|
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
|
-
|
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
|