appsignal 4.0.2-java → 4.0.4-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.
@@ -1363,30 +1363,5 @@ describe Appsignal::Config do
1363
1363
 
1364
1364
  expect(dsl.cpu_count).to eq(1.0)
1365
1365
  end
1366
-
1367
- describe "#app_path=" do
1368
- it "prints a deprecation warning" do
1369
- err_stream = std_stream
1370
- capture_std_streams(std_stream, err_stream) do
1371
- dsl.app_path = "foo"
1372
- end
1373
-
1374
- expect(err_stream.read).to include(
1375
- "appsignal WARNING: The `Appsignal.configure`'s `app_path=` writer is deprecated"
1376
- )
1377
- end
1378
-
1379
- it "logs a deprecation warning" do
1380
- logs =
1381
- capture_logs do
1382
- silence { dsl.app_path = "foo" }
1383
- end
1384
-
1385
- expect(logs).to contains_log(
1386
- :warn,
1387
- "The `Appsignal.configure`'s `app_path=` writer is deprecated"
1388
- )
1389
- end
1390
- end
1391
1366
  end
1392
1367
  end
@@ -115,12 +115,33 @@ describe Appsignal::Environment do
115
115
  end
116
116
 
117
117
  describe ".report_supported_gems" do
118
- it "reports about all AppSignal supported gems in the bundle" do
118
+ it "reports about all AppSignal supported gems in the bundle using Bundler all_specs" do
119
119
  logs = capture_logs { described_class.report_supported_gems }
120
120
 
121
121
  expect(logs).to be_empty
122
122
 
123
- bundle_gem_specs = ::Bundler.rubygems.all_specs
123
+ unless Bundler.rubygems.respond_to?(:all_specs)
124
+ skip "Using new Bundler version without `all_specs` method"
125
+ end
126
+ bundle_gem_specs = silence { ::Bundler.rubygems.all_specs }
127
+ rack_spec = bundle_gem_specs.find { |s| s.name == "rack" }
128
+ rake_spec = bundle_gem_specs.find { |s| s.name == "rake" }
129
+ expect_environment_metadata("ruby_rack_version", rack_spec.version.to_s)
130
+ expect_environment_metadata("ruby_rake_version", rake_spec.version.to_s)
131
+ expect(rack_spec.version.to_s).to_not be_empty
132
+ expect(rake_spec.version.to_s).to_not be_empty
133
+ end
134
+
135
+ it "reports about all AppSignal supported gems in the bundle using bundler installed_specs" do
136
+ unless Bundler.rubygems.respond_to?(:installed_specs)
137
+ skip "Using old Bundler version without `installed_specs` method"
138
+ end
139
+
140
+ logs = capture_logs { described_class.report_supported_gems }
141
+
142
+ expect(logs).to be_empty
143
+
144
+ bundle_gem_specs = ::Bundler.rubygems.installed_specs
124
145
  rack_spec = bundle_gem_specs.find { |s| s.name == "rack" }
125
146
  rake_spec = bundle_gem_specs.find { |s| s.name == "rake" }
126
147
  expect_environment_metadata("ruby_rack_version", rack_spec.version.to_s)
@@ -80,4 +80,15 @@ describe Appsignal::Hooks::AtExit::AtExitCallback do
80
80
  end.to_not change { created_transactions.count }.from(1)
81
81
  end
82
82
  end
83
+
84
+ it "doesn't report the error if it is a SignalException exception" do
85
+ with_error(SignalException, "TERM") do |error|
86
+ Appsignal.report_error(error)
87
+ expect(created_transactions.count).to eq(1)
88
+
89
+ expect do
90
+ call_callback
91
+ end.to_not change { created_transactions.count }.from(1)
92
+ end
93
+ end
83
94
  end
@@ -229,15 +229,36 @@ if DependencyHelper.rails_present?
229
229
  expect(last_transaction).to have_error("ExampleStandardError", "error message")
230
230
  end
231
231
 
232
- it "ignores Sidekiq::JobRetry::Skip errors" do
233
- require "sidekiq"
234
- require "sidekiq/job_retry"
232
+ context "Sidekiq internal errors" do
233
+ before do
234
+ require "sidekiq"
235
+ require "sidekiq/job_retry"
236
+ end
235
237
 
236
- with_rails_error_reporter do
237
- Rails.error.handle { raise Sidekiq::JobRetry::Skip, "error message" }
238
+ it "ignores Sidekiq::JobRetry::Handled errors" do
239
+ with_rails_error_reporter do
240
+ Rails.error.handle { raise Sidekiq::JobRetry::Handled, "error message" }
241
+ end
242
+
243
+ expect(last_transaction).to_not have_error
238
244
  end
239
245
 
240
- expect(last_transaction).to_not have_error
246
+ it "ignores Sidekiq::JobRetry::Skip errors" do
247
+ with_rails_error_reporter do
248
+ Rails.error.handle { raise Sidekiq::JobRetry::Skip, "error message" }
249
+ end
250
+
251
+ expect(last_transaction).to_not have_error
252
+ end
253
+
254
+ it "doesn't crash when no Sidekiq error classes are found" do
255
+ hide_const("Sidekiq::JobRetry")
256
+ with_rails_error_reporter do
257
+ Rails.error.handle { raise ExampleStandardError, "error message" }
258
+ end
259
+
260
+ expect(last_transaction).to have_error("ExampleStandardError", "error message")
261
+ end
241
262
  end
242
263
 
243
264
  context "when no transaction is active" do
@@ -5,23 +5,29 @@ describe Appsignal::Rack::BodyWrapper do
5
5
  set_current_transaction(transaction)
6
6
  end
7
7
 
8
- describe "with a body that supports all possible features" do
9
- it "reduces the supported methods to just each()" do
10
- # which is the safest thing to do, since the body is likely broken
11
- fake_body = double(
12
- :each => nil,
13
- :call => nil,
14
- :to_ary => [],
15
- :to_path => "/tmp/foo.bin",
16
- :close => nil
17
- )
8
+ it "forwards method calls to the body if the method doesn't exist" do
9
+ fake_body = double(
10
+ :body => ["some body"],
11
+ :some_method => :some_value
12
+ )
13
+
14
+ wrapped = described_class.wrap(fake_body, transaction)
15
+ expect(wrapped).to respond_to(:body)
16
+ expect(wrapped.body).to eq(["some body"])
17
+
18
+ expect(wrapped).to respond_to(:some_method)
19
+ expect(wrapped.some_method).to eq(:some_value)
20
+ end
18
21
 
19
- wrapped = described_class.wrap(fake_body, transaction)
20
- expect(wrapped).to respond_to(:each)
21
- expect(wrapped).to_not respond_to(:to_ary)
22
- expect(wrapped).to_not respond_to(:call)
23
- expect(wrapped).to respond_to(:close)
24
- end
22
+ it "doesn't respond to methods the Rack::BodyProxy doesn't respond to" do
23
+ body = Rack::BodyProxy.new(["body"])
24
+ wrapped = described_class.wrap(body, transaction)
25
+
26
+ expect(wrapped).to_not respond_to(:to_str)
27
+ expect { wrapped.to_str }.to raise_error(NoMethodError)
28
+
29
+ expect(wrapped).to_not respond_to(:body)
30
+ expect { wrapped.body }.to raise_error(NoMethodError)
25
31
  end
26
32
 
27
33
  describe "with a body only supporting each()" do
@@ -97,15 +103,17 @@ describe Appsignal::Rack::BodyWrapper do
97
103
  end
98
104
 
99
105
  describe "with a body supporting both each() and call" do
100
- it "wraps with the wrapper that conceals call() and exposes each" do
101
- fake_body = double
102
- allow(fake_body).to receive(:each)
103
- allow(fake_body).to receive(:call)
106
+ it "wraps with the wrapper that exposes each" do
107
+ fake_body = double(
108
+ :each => true,
109
+ :call => "original call"
110
+ )
104
111
 
105
112
  wrapped = described_class.wrap(fake_body, transaction)
106
113
  expect(wrapped).to respond_to(:each)
107
114
  expect(wrapped).to_not respond_to(:to_ary)
108
- expect(wrapped).to_not respond_to(:call)
115
+ expect(wrapped).to respond_to(:call)
116
+ expect(wrapped.call).to eq("original call")
109
117
  expect(wrapped).to_not respond_to(:to_path)
110
118
  expect(wrapped).to respond_to(:close)
111
119
  end
@@ -52,13 +52,41 @@ describe Appsignal::Transmitter do
52
52
  }
53
53
  ).to_return(:status => 200)
54
54
  end
55
- let(:response) { instance.transmit(:the => :payload) }
55
+
56
+ let(:response) { instance.transmit({ :the => :payload }) }
56
57
 
57
58
  it "returns Net::HTTP response" do
58
59
  expect(response).to be_kind_of(Net::HTTPResponse)
59
60
  expect(response.code).to eq "200"
60
61
  end
61
62
 
63
+ describe "with :ndjson format" do
64
+ before do
65
+ stub_request(:post, "https://push.appsignal.com/1/action").with(
66
+ :query => {
67
+ :api_key => "abc",
68
+ :environment => "production",
69
+ :gem_version => Appsignal::VERSION,
70
+ :hostname => config[:hostname],
71
+ :name => "TestApp"
72
+ },
73
+ :body => "{\"the\":\"payload\"}\n{\"part\":\"two\"}",
74
+ :headers => {
75
+ "Content-Type" => "application/x-ndjson; charset=UTF-8"
76
+ }
77
+ ).to_return(:status => 200)
78
+ end
79
+
80
+ let(:response) do
81
+ instance.transmit([{ :the => :payload }, { :part => :two }], :format => :ndjson)
82
+ end
83
+
84
+ it "returns Net::HTTP response" do
85
+ expect(response).to be_kind_of(Net::HTTPResponse)
86
+ expect(response.code).to eq "200"
87
+ end
88
+ end
89
+
62
90
  context "with ca_file_path config option set" do
63
91
  context "when file does not exist" do
64
92
  before do
@@ -106,7 +134,7 @@ describe Appsignal::Transmitter do
106
134
  end
107
135
 
108
136
  describe "#http_post" do
109
- subject { instance.send(:http_post, "the" => "payload") }
137
+ subject { instance.send(:http_post, { "the" => "payload" }, :format => :json) }
110
138
 
111
139
  it "sets the path" do
112
140
  expect(subject.path).to eq instance.uri.request_uri
@@ -115,6 +143,24 @@ describe Appsignal::Transmitter do
115
143
  it "sets the correct headers" do
116
144
  expect(subject["Content-Type"]).to eq "application/json; charset=UTF-8"
117
145
  end
146
+
147
+ it "serialises the payload to JSON" do
148
+ expect(subject.body).to eq "{\"the\":\"payload\"}"
149
+ end
150
+
151
+ describe "with :ndjson format" do
152
+ subject do
153
+ instance.send(:http_post, [{ "the" => "payload" }, { "part" => "two" }], :format => :ndjson)
154
+ end
155
+
156
+ it "sets the correct headers" do
157
+ expect(subject["Content-Type"]).to eq "application/x-ndjson; charset=UTF-8"
158
+ end
159
+
160
+ it "serialises the payload to NDJSON" do
161
+ expect(subject.body).to eq "{\"the\":\"payload\"}\n{\"part\":\"two\"}"
162
+ end
163
+ end
118
164
  end
119
165
 
120
166
  describe "#http_client" do
@@ -581,6 +581,11 @@ describe Appsignal do
581
581
  expect(Appsignal.active?).to be_falsy
582
582
  end
583
583
  end
584
+
585
+ it "calls stop on the check-in scheduler" do
586
+ expect(Appsignal::CheckIn.scheduler).to receive(:stop)
587
+ Appsignal.stop
588
+ end
584
589
  end
585
590
 
586
591
  describe ".started?" do
@@ -0,0 +1,21 @@
1
+ module TakeAtMostHelper
2
+ # Assert that it takes at most a certain amount of time to run a block.
3
+ #
4
+ # @example
5
+ # # Assert that it takes at most 1 second to run the block
6
+ # take_at_most(1) { sleep 0.5 }
7
+ #
8
+ # @param time [Integer, Float] The maximum amount of time the block is allowed to
9
+ # run in seconds.
10
+ # @yield Block to run.
11
+ # @raise [StandardError] Raises error if the block takes longer than the
12
+ # specified time to run.
13
+ def take_at_most(time)
14
+ start = Time.now
15
+ yield
16
+ elapsed = Time.now - start
17
+ return if elapsed <= time
18
+
19
+ raise "Expected block to take at most #{time} seconds, but took #{elapsed}"
20
+ end
21
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appsignal
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.2
4
+ version: 4.0.4
5
5
  platform: java
6
6
  authors:
7
7
  - Robert Beekman
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-08-23 00:00:00.000000000 Z
13
+ date: 2024-08-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -202,6 +202,7 @@ files:
202
202
  - lib/appsignal/capistrano.rb
203
203
  - lib/appsignal/check_in.rb
204
204
  - lib/appsignal/check_in/cron.rb
205
+ - lib/appsignal/check_in/scheduler.rb
205
206
  - lib/appsignal/cli.rb
206
207
  - lib/appsignal/cli/demo.rb
207
208
  - lib/appsignal/cli/diagnose.rb
@@ -309,6 +310,7 @@ files:
309
310
  - lib/appsignal/utils/integration_logger.rb
310
311
  - lib/appsignal/utils/integration_memory_logger.rb
311
312
  - lib/appsignal/utils/json.rb
313
+ - lib/appsignal/utils/ndjson.rb
312
314
  - lib/appsignal/utils/query_params_sanitizer.rb
313
315
  - lib/appsignal/utils/rails_helper.rb
314
316
  - lib/appsignal/utils/stdout_and_logger_message.rb
@@ -322,7 +324,8 @@ files:
322
324
  - spec/lib/appsignal/auth_check_spec.rb
323
325
  - spec/lib/appsignal/capistrano2_spec.rb
324
326
  - spec/lib/appsignal/capistrano3_spec.rb
325
- - spec/lib/appsignal/check_in_spec.rb
327
+ - spec/lib/appsignal/check_in/cron_spec.rb
328
+ - spec/lib/appsignal/check_in/scheduler_spec.rb
326
329
  - spec/lib/appsignal/cli/demo_spec.rb
327
330
  - spec/lib/appsignal/cli/diagnose/paths_spec.rb
328
331
  - spec/lib/appsignal/cli/diagnose/utils_spec.rb
@@ -450,6 +453,7 @@ files:
450
453
  - spec/support/helpers/rails_helper.rb
451
454
  - spec/support/helpers/std_streams_helper.rb
452
455
  - spec/support/helpers/system_helpers.rb
456
+ - spec/support/helpers/take_at_most_helper.rb
453
457
  - spec/support/helpers/time_helpers.rb
454
458
  - spec/support/helpers/transaction_helpers.rb
455
459
  - spec/support/helpers/wait_for_helper.rb
@@ -1,136 +0,0 @@
1
- describe Appsignal::CheckIn::Cron do
2
- let(:config) { project_fixture_config }
3
- let(:cron_checkin) { described_class.new(:identifier => "cron-checkin-name") }
4
- let(:transmitter) { Appsignal::Transmitter.new("http://cron_checkins/", config) }
5
-
6
- before(:each) do
7
- allow(Appsignal).to receive(:active?).and_return(true)
8
- config.logger = Logger.new(StringIO.new)
9
- allow(Appsignal::CheckIn::Cron).to receive(:transmitter).and_return(transmitter)
10
- end
11
-
12
- describe "when Appsignal is not active" do
13
- it "should not transmit any events" do
14
- allow(Appsignal).to receive(:active?).and_return(false)
15
- expect(transmitter).not_to receive(:transmit)
16
-
17
- cron_checkin.start
18
- cron_checkin.finish
19
- end
20
- end
21
-
22
- describe "#start" do
23
- it "should send a cron check-in start" do
24
- expect(transmitter).to receive(:transmit).with(hash_including(
25
- :identifier => "cron-checkin-name",
26
- :kind => "start",
27
- :check_in_type => "cron"
28
- )).and_return(Net::HTTPResponse.new(nil, "200", nil))
29
-
30
- expect(Appsignal.internal_logger).to receive(:debug).with(
31
- "Transmitted cron check-in `cron-checkin-name` (#{cron_checkin.digest}) start event"
32
- )
33
- expect(Appsignal.internal_logger).not_to receive(:error)
34
-
35
- cron_checkin.start
36
- end
37
-
38
- it "should log an error if it fails" do
39
- expect(transmitter).to receive(:transmit).with(hash_including(
40
- :identifier => "cron-checkin-name",
41
- :kind => "start",
42
- :check_in_type => "cron"
43
- )).and_return(Net::HTTPResponse.new(nil, "499", nil))
44
-
45
- expect(Appsignal.internal_logger).not_to receive(:debug)
46
- expect(Appsignal.internal_logger).to receive(:error).with(
47
- "Failed to transmit cron check-in start event: status code was 499"
48
- )
49
-
50
- cron_checkin.start
51
- end
52
- end
53
-
54
- describe "#finish" do
55
- it "should send a cron check-in finish" do
56
- expect(transmitter).to receive(:transmit).with(hash_including(
57
- :identifier => "cron-checkin-name",
58
- :kind => "finish",
59
- :check_in_type => "cron"
60
- )).and_return(Net::HTTPResponse.new(nil, "200", nil))
61
-
62
- expect(Appsignal.internal_logger).to receive(:debug).with(
63
- "Transmitted cron check-in `cron-checkin-name` (#{cron_checkin.digest}) finish event"
64
- )
65
- expect(Appsignal.internal_logger).not_to receive(:error)
66
-
67
- cron_checkin.finish
68
- end
69
-
70
- it "should log an error if it fails" do
71
- expect(transmitter).to receive(:transmit).with(hash_including(
72
- :identifier => "cron-checkin-name",
73
- :kind => "finish",
74
- :check_in_type => "cron"
75
- )).and_return(Net::HTTPResponse.new(nil, "499", nil))
76
-
77
- expect(Appsignal.internal_logger).not_to receive(:debug)
78
- expect(Appsignal.internal_logger).to receive(:error).with(
79
- "Failed to transmit cron check-in finish event: status code was 499"
80
- )
81
-
82
- cron_checkin.finish
83
- end
84
- end
85
-
86
- describe ".cron" do
87
- describe "when a block is given" do
88
- it "should send a cron check-in start and finish and return the block output" do
89
- expect(transmitter).to receive(:transmit).with(hash_including(
90
- :kind => "start",
91
- :identifier => "cron-checkin-with-block",
92
- :check_in_type => "cron"
93
- )).and_return(nil)
94
-
95
- expect(transmitter).to receive(:transmit).with(hash_including(
96
- :kind => "finish",
97
- :identifier => "cron-checkin-with-block",
98
- :check_in_type => "cron"
99
- )).and_return(nil)
100
-
101
- output = Appsignal::CheckIn.cron("cron-checkin-with-block") { "output" }
102
- expect(output).to eq("output")
103
- end
104
-
105
- it "should not send a cron check-in finish event when an error is raised" do
106
- expect(transmitter).to receive(:transmit).with(hash_including(
107
- :kind => "start",
108
- :identifier => "cron-checkin-with-block",
109
- :check_in_type => "cron"
110
- )).and_return(nil)
111
-
112
- expect(transmitter).not_to receive(:transmit).with(hash_including(
113
- :kind => "finish",
114
- :identifier => "cron-checkin-with-block",
115
- :check_in_type => "cron"
116
- ))
117
-
118
- expect do
119
- Appsignal::CheckIn.cron("cron-checkin-with-block") { raise "error" }
120
- end.to raise_error(RuntimeError, "error")
121
- end
122
- end
123
-
124
- describe "when no block is given" do
125
- it "should only send a cron check-in finish event" do
126
- expect(transmitter).to receive(:transmit).with(hash_including(
127
- :kind => "finish",
128
- :identifier => "cron-checkin-without-block",
129
- :check_in_type => "cron"
130
- )).and_return(nil)
131
-
132
- Appsignal::CheckIn.cron("cron-checkin-without-block")
133
- end
134
- end
135
- end
136
- end