appsignal 2.11.2-java → 2.11.7-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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "2.11.2".freeze
4
+ VERSION = "2.11.7".freeze
5
5
  end
@@ -2,6 +2,8 @@ describe Appsignal::Hooks::ActionCableHook do
2
2
  if DependencyHelper.action_cable_present?
3
3
  context "with ActionCable" do
4
4
  require "action_cable/engine"
5
+ # Require test helper to test with ConnectionStub
6
+ require "action_cable/channel/test_case" if DependencyHelper.rails6_present?
5
7
 
6
8
  describe ".dependencies_present?" do
7
9
  subject { described_class.new.dependencies_present? }
@@ -262,6 +264,49 @@ describe Appsignal::Hooks::ActionCableHook do
262
264
  )
263
265
  end
264
266
  end
267
+
268
+ if DependencyHelper.rails6_present?
269
+ context "with ConnectionStub" do
270
+ let(:connection) { ActionCable::Channel::ConnectionStub.new }
271
+ let(:transaction_id) { "Stubbed transaction id" }
272
+ before do
273
+ # Stub future (private AppSignal) transaction id generated by the hook.
274
+ expect(SecureRandom).to receive(:uuid).and_return(transaction_id)
275
+ end
276
+
277
+ it "does not fail on missing `#env` method on `ConnectionStub`" do
278
+ instance.subscribe_to_channel
279
+
280
+ expect(subject).to include(
281
+ "action" => "MyChannel#subscribed",
282
+ "error" => nil,
283
+ "id" => transaction_id,
284
+ "namespace" => Appsignal::Transaction::ACTION_CABLE,
285
+ "metadata" => {
286
+ "method" => "websocket",
287
+ "path" => "" # No path as the ConnectionStub doesn't have the real request env
288
+ }
289
+ )
290
+ expect(subject["events"].first).to include(
291
+ "allocation_count" => kind_of(Integer),
292
+ "body" => "",
293
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
294
+ "child_allocation_count" => kind_of(Integer),
295
+ "child_duration" => kind_of(Float),
296
+ "child_gc_duration" => kind_of(Float),
297
+ "count" => 1,
298
+ "gc_duration" => kind_of(Float),
299
+ "start" => kind_of(Float),
300
+ "duration" => kind_of(Float),
301
+ "name" => "subscribed.action_cable",
302
+ "title" => ""
303
+ )
304
+ expect(subject["sample_data"]).to include(
305
+ "params" => { "internal" => "true" }
306
+ )
307
+ end
308
+ end
309
+ end
265
310
  end
266
311
 
267
312
  describe "unsubscribe callback" do
@@ -349,6 +394,49 @@ describe Appsignal::Hooks::ActionCableHook do
349
394
  )
350
395
  end
351
396
  end
397
+
398
+ if DependencyHelper.rails6_present?
399
+ context "with ConnectionStub" do
400
+ let(:connection) { ActionCable::Channel::ConnectionStub.new }
401
+ let(:transaction_id) { "Stubbed transaction id" }
402
+ before do
403
+ # Stub future (private AppSignal) transaction id generated by the hook.
404
+ expect(SecureRandom).to receive(:uuid).and_return(transaction_id)
405
+ end
406
+
407
+ it "does not fail on missing `#env` method on `ConnectionStub`" do
408
+ instance.unsubscribe_from_channel
409
+
410
+ expect(subject).to include(
411
+ "action" => "MyChannel#unsubscribed",
412
+ "error" => nil,
413
+ "id" => transaction_id,
414
+ "namespace" => Appsignal::Transaction::ACTION_CABLE,
415
+ "metadata" => {
416
+ "method" => "websocket",
417
+ "path" => "" # No path as the ConnectionStub doesn't have the real request env
418
+ }
419
+ )
420
+ expect(subject["events"].first).to include(
421
+ "allocation_count" => kind_of(Integer),
422
+ "body" => "",
423
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
424
+ "child_allocation_count" => kind_of(Integer),
425
+ "child_duration" => kind_of(Float),
426
+ "child_gc_duration" => kind_of(Float),
427
+ "count" => 1,
428
+ "gc_duration" => kind_of(Float),
429
+ "start" => kind_of(Float),
430
+ "duration" => kind_of(Float),
431
+ "name" => "unsubscribed.action_cable",
432
+ "title" => ""
433
+ )
434
+ expect(subject["sample_data"]).to include(
435
+ "params" => { "internal" => "true" }
436
+ )
437
+ end
438
+ end
439
+ end
352
440
  end
353
441
  end
354
442
  end
@@ -1,55 +1,73 @@
1
1
  describe Appsignal::Hooks::ShoryukenMiddleware do
2
- let(:current_transaction) { background_job_transaction }
3
-
4
2
  class DemoShoryukenWorker
5
3
  end
6
4
 
5
+ let(:time) { "2010-01-01 10:01:00UTC" }
7
6
  let(:worker_instance) { DemoShoryukenWorker.new }
8
- let(:queue) { double }
9
- let(:sqs_msg) { double(:attributes => {}) }
7
+ let(:queue) { "some-funky-queue-name" }
8
+ let(:sqs_msg) { double(:message_id => "msg1", :attributes => {}) }
10
9
  let(:body) { {} }
11
-
12
- before do
13
- allow(Appsignal::Transaction).to receive(:current).and_return(current_transaction)
14
- start_agent
10
+ before(:context) { start_agent }
11
+ around { |example| keep_transactions { example.run } }
12
+
13
+ def perform_job(&block)
14
+ block ||= lambda {}
15
+ Timecop.freeze(Time.parse(time)) do
16
+ Appsignal::Hooks::ShoryukenMiddleware.new.call(
17
+ worker_instance,
18
+ queue,
19
+ sqs_msg,
20
+ body,
21
+ &block
22
+ )
23
+ end
15
24
  end
16
25
 
17
26
  context "with a performance call" do
18
- let(:queue) { "some-funky-queue-name" }
27
+ let(:sent_timestamp) { Time.parse("1976-11-18 0:00:00UTC").to_i * 1000 }
19
28
  let(:sqs_msg) do
20
- double(:attributes => { "SentTimestamp" => Time.parse("1976-11-18 0:00:00UTC").to_i * 1000 })
29
+ double(:message_id => "msg1", :attributes => { "SentTimestamp" => sent_timestamp })
21
30
  end
22
31
 
23
32
  context "with complex argument" do
24
- let(:body) do
25
- {
26
- :foo => "Foo",
27
- :bar => "Bar"
28
- }
29
- end
30
- after do
31
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
32
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
33
- # nothing
34
- end
35
- end
36
- end
33
+ let(:body) { { :foo => "Foo", :bar => "Bar" } }
37
34
 
38
35
  it "wraps the job in a transaction with the correct params" do
39
- expect(Appsignal).to receive(:monitor_transaction).with(
40
- "perform_job.shoryuken",
41
- :class => "DemoShoryukenWorker",
42
- :method => "perform",
43
- :metadata => {
44
- :queue => "some-funky-queue-name",
45
- "SentTimestamp" => 217_123_200_000
46
- },
47
- :params => {
48
- :foo => "Foo",
49
- :bar => "Bar"
50
- },
51
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
36
+ allow_any_instance_of(Appsignal::Transaction).to receive(:set_queue_start).and_call_original
37
+ expect { perform_job }.to change { created_transactions.length }.by(1)
38
+
39
+ transaction = last_transaction
40
+ expect(transaction).to be_completed
41
+ transaction_hash = transaction.to_h
42
+ expect(transaction_hash).to include(
43
+ "action" => "DemoShoryukenWorker#perform",
44
+ "id" => kind_of(String), # AppSignal generated id
45
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
46
+ "error" => nil
52
47
  )
48
+ expect(transaction_hash["events"].first).to include(
49
+ "allocation_count" => kind_of(Integer),
50
+ "body" => "",
51
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
52
+ "child_allocation_count" => kind_of(Integer),
53
+ "child_duration" => kind_of(Float),
54
+ "child_gc_duration" => kind_of(Float),
55
+ "count" => 1,
56
+ "gc_duration" => kind_of(Float),
57
+ "start" => kind_of(Float),
58
+ "duration" => kind_of(Float),
59
+ "name" => "perform_job.shoryuken",
60
+ "title" => ""
61
+ )
62
+ expect(transaction_hash["sample_data"]).to include(
63
+ "params" => { "foo" => "Foo", "bar" => "Bar" },
64
+ "metadata" => {
65
+ "message_id" => "msg1",
66
+ "queue" => queue,
67
+ "SentTimestamp" => sent_timestamp
68
+ }
69
+ )
70
+ expect(transaction).to have_received(:set_queue_start).with(sent_timestamp)
53
71
  end
54
72
 
55
73
  context "with parameter filtering" do
@@ -57,21 +75,16 @@ describe Appsignal::Hooks::ShoryukenMiddleware do
57
75
  Appsignal.config = project_fixture_config("production")
58
76
  Appsignal.config[:filter_parameters] = ["foo"]
59
77
  end
78
+ after do
79
+ Appsignal.config[:filter_parameters] = []
80
+ end
60
81
 
61
82
  it "filters selected arguments" do
62
- expect(Appsignal).to receive(:monitor_transaction).with(
63
- "perform_job.shoryuken",
64
- :class => "DemoShoryukenWorker",
65
- :method => "perform",
66
- :metadata => {
67
- :queue => "some-funky-queue-name",
68
- "SentTimestamp" => 217_123_200_000
69
- },
70
- :params => {
71
- :foo => "[FILTERED]",
72
- :bar => "Bar"
73
- },
74
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
83
+ perform_job
84
+
85
+ transaction_hash = last_transaction.to_h
86
+ expect(transaction_hash["sample_data"]).to include(
87
+ "params" => { "foo" => "[FILTERED]", "bar" => "Bar" }
75
88
  )
76
89
  end
77
90
  end
@@ -81,23 +94,12 @@ describe Appsignal::Hooks::ShoryukenMiddleware do
81
94
  let(:body) { "foo bar" }
82
95
 
83
96
  it "handles string arguments" do
84
- expect(Appsignal).to receive(:monitor_transaction).with(
85
- "perform_job.shoryuken",
86
- :class => "DemoShoryukenWorker",
87
- :method => "perform",
88
- :metadata => {
89
- :queue => "some-funky-queue-name",
90
- "SentTimestamp" => 217_123_200_000
91
- },
92
- :params => { :params => body },
93
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
94
- )
97
+ perform_job
95
98
 
96
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
97
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
98
- # nothing
99
- end
100
- end
99
+ transaction_hash = last_transaction.to_h
100
+ expect(transaction_hash["sample_data"]).to include(
101
+ "params" => { "params" => body }
102
+ )
101
103
  end
102
104
  end
103
105
 
@@ -105,58 +107,103 @@ describe Appsignal::Hooks::ShoryukenMiddleware do
105
107
  let(:body) { 1 }
106
108
 
107
109
  it "handles primitive types as arguments" do
108
- expect(Appsignal).to receive(:monitor_transaction).with(
109
- "perform_job.shoryuken",
110
- :class => "DemoShoryukenWorker",
111
- :method => "perform",
112
- :metadata => {
113
- :queue => "some-funky-queue-name",
114
- "SentTimestamp" => 217_123_200_000
115
- },
116
- :params => { :params => body },
117
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
118
- )
110
+ perform_job
119
111
 
120
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
121
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
122
- # nothing
123
- end
124
- end
112
+ transaction_hash = last_transaction.to_h
113
+ expect(transaction_hash["sample_data"]).to include(
114
+ "params" => { "params" => body }
115
+ )
125
116
  end
126
117
  end
127
118
  end
128
119
 
129
120
  context "with exception" do
130
- let(:transaction) do
131
- Appsignal::Transaction.new(
132
- SecureRandom.uuid,
133
- Appsignal::Transaction::BACKGROUND_JOB,
134
- Appsignal::Transaction::GenericRequest.new({})
121
+ it "sets the exception on the transaction" do
122
+ expect do
123
+ expect do
124
+ perform_job { raise ExampleException, "error message" }
125
+ end.to raise_error(ExampleException)
126
+ end.to change { created_transactions.length }.by(1)
127
+
128
+ transaction = last_transaction
129
+ expect(transaction).to be_completed
130
+ transaction_hash = transaction.to_h
131
+ expect(transaction_hash).to include(
132
+ "action" => "DemoShoryukenWorker#perform",
133
+ "id" => kind_of(String), # AppSignal generated id
134
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
135
+ "error" => {
136
+ "name" => "ExampleException",
137
+ "message" => "error message",
138
+ "backtrace" => kind_of(String)
139
+ }
135
140
  )
136
141
  end
142
+ end
137
143
 
138
- before do
139
- allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
140
- expect(Appsignal::Transaction).to receive(:create)
141
- .with(
142
- kind_of(String),
143
- Appsignal::Transaction::BACKGROUND_JOB,
144
- kind_of(Appsignal::Transaction::GenericRequest)
145
- ).and_return(transaction)
144
+ context "with batched jobs" do
145
+ let(:sqs_msg) do
146
+ [
147
+ double(
148
+ :message_id => "msg2",
149
+ :attributes => { "SentTimestamp" => (Time.parse("1976-11-18 01:00:00UTC").to_i * 1000).to_s }
150
+ ),
151
+ double(
152
+ :message_id => "msg1",
153
+ :attributes => { "SentTimestamp" => sent_timestamp.to_s }
154
+ )
155
+ ]
146
156
  end
147
-
148
- it "sets the exception on the transaction" do
149
- expect(transaction).to receive(:set_error).with(ExampleException)
157
+ let(:body) do
158
+ [
159
+ "foo bar",
160
+ { :id => "123", :foo => "Foo", :bar => "Bar" }
161
+ ]
150
162
  end
163
+ let(:sent_timestamp) { Time.parse("1976-11-18 01:00:00UTC").to_i * 1000 }
151
164
 
152
- after do
165
+ it "creates a transaction for the batch" do
166
+ allow_any_instance_of(Appsignal::Transaction).to receive(:set_queue_start).and_call_original
153
167
  expect do
154
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
155
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
156
- raise ExampleException
157
- end
158
- end
159
- end.to raise_error(ExampleException)
168
+ perform_job {}
169
+ end.to change { created_transactions.length }.by(1)
170
+
171
+ transaction = last_transaction
172
+ expect(transaction).to be_completed
173
+ transaction_hash = transaction.to_h
174
+ expect(transaction_hash).to include(
175
+ "action" => "DemoShoryukenWorker#perform",
176
+ "id" => kind_of(String), # AppSignal generated id
177
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
178
+ "error" => nil
179
+ )
180
+ expect(transaction_hash["events"].first).to include(
181
+ "allocation_count" => kind_of(Integer),
182
+ "body" => "",
183
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
184
+ "child_allocation_count" => kind_of(Integer),
185
+ "child_duration" => kind_of(Float),
186
+ "child_gc_duration" => kind_of(Float),
187
+ "count" => 1,
188
+ "gc_duration" => kind_of(Float),
189
+ "start" => kind_of(Float),
190
+ "duration" => kind_of(Float),
191
+ "name" => "perform_job.shoryuken",
192
+ "title" => ""
193
+ )
194
+ expect(transaction_hash["sample_data"]).to include(
195
+ "params" => {
196
+ "msg2" => "foo bar",
197
+ "msg1" => { "id" => "123", "foo" => "Foo", "bar" => "Bar" }
198
+ },
199
+ "metadata" => {
200
+ "batch" => true,
201
+ "queue" => "some-funky-queue-name",
202
+ "SentTimestamp" => sent_timestamp.to_s # Earliest/oldest timestamp from messages
203
+ }
204
+ )
205
+ # Queue time based on earliest/oldest timestamp from messages
206
+ expect(transaction).to have_received(:set_queue_start).with(sent_timestamp)
160
207
  end
161
208
  end
162
209
  end
@@ -16,14 +16,31 @@ describe Appsignal::Hooks::SidekiqHook do
16
16
  end
17
17
 
18
18
  describe "#install" do
19
- class SidekiqMiddlewareMock < Set
20
- def exists?(middleware)
21
- include?(middleware)
19
+ class SidekiqMiddlewareMockWithPrepend < Array
20
+ alias add <<
21
+ alias exists? include?
22
+
23
+ unless method_defined? :prepend
24
+ def prepend(middleware) # For Ruby < 2.5
25
+ insert(0, middleware)
26
+ end
22
27
  end
23
28
  end
29
+
30
+ class SidekiqMiddlewareMockWithoutPrepend < Array
31
+ alias add <<
32
+ alias exists? include?
33
+
34
+ undef_method :prepend if method_defined? :prepend # For Ruby >= 2.5
35
+ end
36
+
24
37
  module SidekiqMock
38
+ def self.middleware_mock=(mock)
39
+ @middlewares = mock.new
40
+ end
41
+
25
42
  def self.middlewares
26
- @middlewares ||= SidekiqMiddlewareMock.new
43
+ @middlewares
27
44
  end
28
45
 
29
46
  def self.configure_server
@@ -36,15 +53,52 @@ describe Appsignal::Hooks::SidekiqHook do
36
53
  end
37
54
  end
38
55
 
56
+ def add_middleware(middleware)
57
+ Sidekiq.configure_server do |sidekiq_config|
58
+ sidekiq_config.middlewares.add(middleware)
59
+ end
60
+ end
61
+
39
62
  before do
40
63
  Appsignal.config = project_fixture_config
41
64
  stub_const "Sidekiq", SidekiqMock
42
65
  end
43
66
 
44
- it "adds the AppSignal SidekiqPlugin to the Sidekiq middleware chain" do
45
- described_class.new.install
67
+ context "when Sidekiq middleware responds to prepend method" do # Sidekiq 3.3.0 and newer
68
+ before { Sidekiq.middleware_mock = SidekiqMiddlewareMockWithPrepend }
69
+
70
+ it "adds the AppSignal SidekiqPlugin to the Sidekiq middleware chain in the first position" do
71
+ user_middleware1 = proc {}
72
+ add_middleware(user_middleware1)
73
+ described_class.new.install
74
+ user_middleware2 = proc {}
75
+ add_middleware(user_middleware2)
46
76
 
47
- expect(Sidekiq.server_middleware.exists?(Appsignal::Hooks::SidekiqPlugin)).to be(true)
77
+ expect(Sidekiq.server_middleware).to eql([
78
+ Appsignal::Hooks::SidekiqPlugin, # Prepend makes it the first entry
79
+ user_middleware1,
80
+ user_middleware2
81
+ ])
82
+ end
83
+ end
84
+
85
+ context "when Sidekiq middleware does not respond to prepend method" do
86
+ before { Sidekiq.middleware_mock = SidekiqMiddlewareMockWithoutPrepend }
87
+
88
+ it "adds the AppSignal SidekiqPlugin to the Sidekiq middleware chain" do
89
+ user_middleware1 = proc {}
90
+ add_middleware(user_middleware1)
91
+ described_class.new.install
92
+ user_middleware2 = proc {}
93
+ add_middleware(user_middleware2)
94
+
95
+ # Add middlewares in whatever order they were added
96
+ expect(Sidekiq.server_middleware).to eql([
97
+ user_middleware1,
98
+ Appsignal::Hooks::SidekiqPlugin,
99
+ user_middleware2
100
+ ])
101
+ end
48
102
  end
49
103
  end
50
104
  end