appsignal 3.5.6-java → 3.6.0-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.
@@ -0,0 +1,220 @@
1
+ describe Appsignal::Rack::BodyWrapper do
2
+ let(:nil_txn) { Appsignal::Transaction::NilTransaction.new }
3
+
4
+ describe "with a body that supports all possible features" do
5
+ it "reduces the supported methods to just each()" do
6
+ # which is the safest thing to do, since the body is likely broken
7
+ fake_body = double(:each => nil, :call => nil, :to_ary => [], :to_path => "/tmp/foo.bin",
8
+ :close => nil)
9
+ wrapped = described_class.wrap(fake_body, nil_txn)
10
+ expect(wrapped).to respond_to(:each)
11
+ expect(wrapped).not_to respond_to(:to_ary)
12
+ expect(wrapped).not_to respond_to(:call)
13
+ expect(wrapped).to respond_to(:close)
14
+ end
15
+ end
16
+
17
+ describe "with a body only supporting each()" do
18
+ it "wraps with appropriate class" do
19
+ fake_body = double
20
+ allow(fake_body).to receive(:each)
21
+
22
+ wrapped = described_class.wrap(fake_body, nil_txn)
23
+ expect(wrapped).to respond_to(:each)
24
+ expect(wrapped).not_to respond_to(:to_ary)
25
+ expect(wrapped).not_to respond_to(:call)
26
+ expect(wrapped).to respond_to(:close)
27
+ end
28
+
29
+ it "reads out the body in full using each" do
30
+ fake_body = double
31
+ expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
32
+ wrapped = described_class.wrap(fake_body, nil_txn)
33
+ expect { |b| wrapped.each(&b) }.to yield_successive_args("a", "b", "c")
34
+ end
35
+
36
+ it "returns an Enumerator if each() gets called without a block" do
37
+ fake_body = double
38
+ expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
39
+
40
+ wrapped = described_class.wrap(fake_body, nil_txn)
41
+ enum = wrapped.each
42
+ expect(enum).to be_kind_of(Enumerator)
43
+ expect { |b| enum.each(&b) }.to yield_successive_args("a", "b", "c")
44
+ end
45
+
46
+ it "sets the exception raised inside each() into the Appsignal transaction" do
47
+ fake_body = double
48
+ expect(fake_body).to receive(:each).once.and_raise(Exception.new("Oops"))
49
+
50
+ txn = double("Appsignal transaction", "nil_transaction?" => false)
51
+ expect(txn).to receive(:set_error).once.with(instance_of(Exception))
52
+
53
+ wrapped = described_class.wrap(fake_body, txn)
54
+ expect do
55
+ expect { |b| wrapped.each(&b) }.to yield_control
56
+ end.to raise_error(/Oops/)
57
+ end
58
+
59
+ it "closes the body and the transaction when it gets closed" do
60
+ fake_body = double
61
+ expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
62
+
63
+ txn = double("Appsignal transaction", "nil_transaction?" => false)
64
+ expect(Appsignal::Transaction).to receive(:complete_current!).once
65
+
66
+ wrapped = described_class.wrap(fake_body, txn)
67
+ expect { |b| wrapped.each(&b) }.to yield_successive_args("a", "b", "c")
68
+ expect { wrapped.close }.not_to raise_error
69
+ end
70
+ end
71
+
72
+ describe "with a body supporting both each() and call" do
73
+ it "wraps with the wrapper that conceals call() and exposes each" do
74
+ fake_body = double
75
+ allow(fake_body).to receive(:each)
76
+ allow(fake_body).to receive(:call)
77
+
78
+ wrapped = described_class.wrap(fake_body, nil_txn)
79
+ expect(wrapped).to respond_to(:each)
80
+ expect(wrapped).not_to respond_to(:to_ary)
81
+ expect(wrapped).not_to respond_to(:call)
82
+ expect(wrapped).not_to respond_to(:to_path)
83
+ expect(wrapped).to respond_to(:close)
84
+ end
85
+ end
86
+
87
+ describe "with a body supporting both to_ary and each" do
88
+ let(:fake_body) { double(:each => nil, :to_ary => []) }
89
+ it "wraps with appropriate class" do
90
+ wrapped = described_class.wrap(fake_body, nil_txn)
91
+ expect(wrapped).to respond_to(:each)
92
+ expect(wrapped).to respond_to(:to_ary)
93
+ expect(wrapped).not_to respond_to(:call)
94
+ expect(wrapped).not_to respond_to(:to_path)
95
+ expect(wrapped).to respond_to(:close)
96
+ end
97
+
98
+ it "reads out the body in full using each" do
99
+ expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
100
+
101
+ wrapped = described_class.wrap(fake_body, nil_txn)
102
+ expect { |b| wrapped.each(&b) }.to yield_successive_args("a", "b", "c")
103
+ end
104
+
105
+ it "sets the exception raised inside each() into the Appsignal transaction" do
106
+ expect(fake_body).to receive(:each).once.and_raise(Exception.new("Oops"))
107
+
108
+ txn = double("Appsignal transaction", "nil_transaction?" => false)
109
+ expect(txn).to receive(:set_error).once.with(instance_of(Exception))
110
+
111
+ wrapped = described_class.wrap(fake_body, txn)
112
+ expect do
113
+ expect { |b| wrapped.each(&b) }.to yield_control
114
+ end.to raise_error(/Oops/)
115
+ end
116
+
117
+ it "reads out the body in full using to_ary" do
118
+ expect(fake_body).to receive(:to_ary).and_return(["one", "two", "three"])
119
+
120
+ wrapped = described_class.wrap(fake_body, nil_txn)
121
+ expect(wrapped.to_ary).to eq(["one", "two", "three"])
122
+ end
123
+
124
+ it "sends the exception raised inside to_ary() into the Appsignal and closes txn" do
125
+ fake_body = double
126
+ allow(fake_body).to receive(:each)
127
+ expect(fake_body).to receive(:to_ary).once.and_raise(Exception.new("Oops"))
128
+ expect(fake_body).not_to receive(:close) # Per spec we expect the body has closed itself
129
+
130
+ txn = double("Appsignal transaction", "nil_transaction?" => false)
131
+ expect(txn).to receive(:set_error).once.with(instance_of(Exception))
132
+ expect(Appsignal::Transaction).to receive(:complete_current!).once
133
+
134
+ wrapped = described_class.wrap(fake_body, txn)
135
+ expect { wrapped.to_ary }.to raise_error(/Oops/)
136
+ end
137
+ end
138
+
139
+ describe "with a body supporting both to_path and each" do
140
+ let(:fake_body) { double(:each => nil, :to_path => nil) }
141
+
142
+ it "wraps with appropriate class" do
143
+ wrapped = described_class.wrap(fake_body, nil_txn)
144
+ expect(wrapped).to respond_to(:each)
145
+ expect(wrapped).not_to respond_to(:to_ary)
146
+ expect(wrapped).not_to respond_to(:call)
147
+ expect(wrapped).to respond_to(:to_path)
148
+ expect(wrapped).to respond_to(:close)
149
+ end
150
+
151
+ it "reads out the body in full using each()" do
152
+ expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
153
+
154
+ wrapped = described_class.wrap(fake_body, nil_txn)
155
+ expect { |b| wrapped.each(&b) }.to yield_successive_args("a", "b", "c")
156
+ end
157
+
158
+ it "sets the exception raised inside each() into the Appsignal transaction" do
159
+ expect(fake_body).to receive(:each).once.and_raise(Exception.new("Oops"))
160
+
161
+ txn = double("Appsignal transaction", "nil_transaction?" => false)
162
+ expect(txn).to receive(:set_error).once.with(instance_of(Exception))
163
+
164
+ wrapped = described_class.wrap(fake_body, txn)
165
+ expect do
166
+ expect { |b| wrapped.each(&b) }.to yield_control
167
+ end.to raise_error(/Oops/)
168
+ end
169
+
170
+ it "sets the exception raised inside to_path() into the Appsignal transaction" do
171
+ allow(fake_body).to receive(:to_path).once.and_raise(Exception.new("Oops"))
172
+
173
+ txn = double("Appsignal transaction", "nil_transaction?" => false)
174
+ expect(txn).to receive(:set_error).once.with(instance_of(Exception))
175
+ expect(txn).not_to receive(:complete) # gets called by the caller via close()
176
+
177
+ wrapped = described_class.wrap(fake_body, txn)
178
+ expect { wrapped.to_path }.to raise_error(/Oops/)
179
+ end
180
+
181
+ it "exposes to_path to the sender" do
182
+ allow(fake_body).to receive(:to_path).and_return("/tmp/file.bin")
183
+
184
+ wrapped = described_class.wrap(fake_body, nil_txn)
185
+ expect(wrapped.to_path).to eq("/tmp/file.bin")
186
+ end
187
+ end
188
+
189
+ describe "with a body only supporting call()" do
190
+ let(:fake_body) { double(:call => nil) }
191
+ it "wraps with appropriate class" do
192
+ wrapped = described_class.wrap(fake_body, nil_txn)
193
+ expect(wrapped).not_to respond_to(:each)
194
+ expect(wrapped).not_to respond_to(:to_ary)
195
+ expect(wrapped).to respond_to(:call)
196
+ expect(wrapped).not_to respond_to(:to_path)
197
+ expect(wrapped).to respond_to(:close)
198
+ end
199
+
200
+ it "passes the stream into the call() of the body" do
201
+ fake_rack_stream = double("stream")
202
+ expect(fake_body).to receive(:call).with(fake_rack_stream)
203
+
204
+ wrapped = described_class.wrap(fake_body, nil_txn)
205
+ expect { wrapped.call(fake_rack_stream) }.not_to raise_error
206
+ end
207
+
208
+ it "sets the exception raised inside call() into the Appsignal transaction" do
209
+ fake_rack_stream = double
210
+ allow(fake_body).to receive(:call).with(fake_rack_stream).and_raise(Exception.new("Oopsie"))
211
+
212
+ txn = double("Appsignal transaction", "nil_transaction?" => false)
213
+ expect(txn).to receive(:set_error).once.with(instance_of(Exception))
214
+ expect(txn).not_to receive(:complete) # gets called by the caller via close()
215
+ wrapped = described_class.wrap(fake_body, txn)
216
+
217
+ expect { wrapped.call(fake_rack_stream) }.to raise_error(/Oopsie/)
218
+ end
219
+ end
220
+ end
@@ -50,7 +50,7 @@ describe Appsignal::Rack::GenericInstrumentation do
50
50
  expect(app).to receive(:call).with(env)
51
51
  end
52
52
 
53
- context "with an exception", :error => true do
53
+ context "with an exception raised from call()", :error => true do
54
54
  let(:error) { ExampleException }
55
55
  let(:app) do
56
56
  double.tap do |d|
@@ -58,8 +58,9 @@ describe Appsignal::Rack::GenericInstrumentation do
58
58
  end
59
59
  end
60
60
 
61
- it "records the exception" do
61
+ it "records the exception and completes the transaction" do
62
62
  expect_any_instance_of(Appsignal::Transaction).to receive(:set_error).with(error)
63
+ expect(Appsignal::Transaction).to receive(:complete_current!)
63
64
  end
64
65
  end
65
66
 
@@ -65,7 +65,8 @@ if DependencyHelper.rails_present?
65
65
 
66
66
  describe "#call_with_appsignal_monitoring" do
67
67
  def run
68
- middleware.call(env)
68
+ _status, _headers, body = middleware.call(env)
69
+ body.close # Rack will always call close() on the body
69
70
  end
70
71
 
71
72
  it "calls the wrapped app" do
@@ -126,7 +127,8 @@ if DependencyHelper.rails_present?
126
127
  end
127
128
  end
128
129
 
129
- it "records the exception" do
130
+ it "records the exception and completes the transaction" do
131
+ expect(Appsignal::Transaction).to receive(:complete_current!)
130
132
  expect { run }.to raise_error(error)
131
133
 
132
134
  transaction_hash = last_transaction.to_h
@@ -3,7 +3,9 @@ if DependencyHelper.sinatra_present?
3
3
 
4
4
  module SinatraRequestHelpers
5
5
  def make_request(env)
6
- middleware.call(env)
6
+ _status, _headers, body = middleware.call(env)
7
+ # Close the body so that the transaction gets completed
8
+ body&.close
7
9
  end
8
10
 
9
11
  def make_request_with_error(env, error)
@@ -90,10 +90,11 @@ describe Appsignal::Rack::StreamingListener do
90
90
  context "with an exception in the instrumentation call" do
91
91
  let(:error) { ExampleException }
92
92
 
93
- it "should add the exception to the transaction" do
93
+ it "should add the exception to the transaction and complete the transaction" do
94
94
  allow(app).to receive(:call).and_raise(error)
95
95
 
96
96
  expect(transaction).to receive(:set_error).with(error)
97
+ expect(Appsignal::Transaction).to receive(:complete_current!).and_call_original
97
98
 
98
99
  expect do
99
100
  listener.call_with_appsignal_monitoring(env)
@@ -101,64 +102,19 @@ describe Appsignal::Rack::StreamingListener do
101
102
  end
102
103
  end
103
104
 
104
- it "should wrap the body in a wrapper" do
105
- expect(Appsignal::StreamWrapper).to receive(:new)
106
- .with("body", transaction)
107
- .and_return(wrapper)
108
-
105
+ it "should wrap the response body in a wrapper" do
109
106
  body = listener.call_with_appsignal_monitoring(env)[2]
110
107
 
111
- expect(body).to be_a(Appsignal::StreamWrapper)
108
+ expect(body).to be_kind_of(Appsignal::Rack::BodyWrapper)
112
109
  end
113
110
  end
114
111
  end
115
112
 
116
113
  describe Appsignal::StreamWrapper do
117
- let(:stream) { double }
118
- let(:transaction) do
119
- Appsignal::Transaction.create(SecureRandom.uuid, Appsignal::Transaction::HTTP_REQUEST, {})
120
- end
121
- let(:wrapper) { Appsignal::StreamWrapper.new(stream, transaction) }
122
-
123
- describe "#each" do
124
- it "calls the original stream" do
125
- expect(stream).to receive(:each)
126
-
127
- wrapper.each
128
- end
129
-
130
- context "when #each raises an error" do
131
- let(:error) { ExampleException }
132
-
133
- it "records the exception" do
134
- allow(stream).to receive(:each).and_raise(error)
135
-
136
- expect(transaction).to receive(:set_error).with(error)
137
-
138
- expect { wrapper.send(:each) }.to raise_error(error)
139
- end
140
- end
141
- end
142
-
143
- describe "#close" do
144
- it "closes the original stream and completes the transaction" do
145
- expect(stream).to receive(:close)
146
- expect(Appsignal::Transaction).to receive(:complete_current!)
147
-
148
- wrapper.close
149
- end
150
-
151
- context "when #close raises an error" do
152
- let(:error) { ExampleException }
153
-
154
- it "records the exception and completes the transaction" do
155
- allow(stream).to receive(:close).and_raise(error)
156
-
157
- expect(transaction).to receive(:set_error).with(error)
158
- expect(transaction).to receive(:complete)
159
-
160
- expect { wrapper.send(:close) }.to raise_error(error)
161
- end
162
- end
114
+ it ".new returns an EnumerableWrapper" do
115
+ fake_body = double(:each => nil)
116
+ fake_txn = double
117
+ stream_wrapper = Appsignal::StreamWrapper.new(fake_body, fake_txn)
118
+ expect(stream_wrapper).to be_kind_of(Appsignal::Rack::EnumerableBodyWrapper)
163
119
  end
164
120
  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: 3.5.6
4
+ version: 3.6.0
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-02-08 00:00:00.000000000 Z
13
+ date: 2024-02-26 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -282,6 +282,7 @@ files:
282
282
  - lib/appsignal/probes/helpers.rb
283
283
  - lib/appsignal/probes/mri.rb
284
284
  - lib/appsignal/probes/sidekiq.rb
285
+ - lib/appsignal/rack/body_wrapper.rb
285
286
  - lib/appsignal/rack/generic_instrumentation.rb
286
287
  - lib/appsignal/rack/rails_instrumentation.rb
287
288
  - lib/appsignal/rack/sinatra_instrumentation.rb
@@ -380,6 +381,7 @@ files:
380
381
  - spec/lib/appsignal/probes/gvl_spec.rb
381
382
  - spec/lib/appsignal/probes/mri_spec.rb
382
383
  - spec/lib/appsignal/probes/sidekiq_spec.rb
384
+ - spec/lib/appsignal/rack/body_wrapper_spec.rb
383
385
  - spec/lib/appsignal/rack/generic_instrumentation_spec.rb
384
386
  - spec/lib/appsignal/rack/rails_instrumentation_spec.rb
385
387
  - spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb
@@ -465,7 +467,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
465
467
  - !ruby/object:Gem::Version
466
468
  version: '0'
467
469
  requirements: []
468
- rubygems_version: 3.4.11
470
+ rubygems_version: 3.4.15
469
471
  signing_key:
470
472
  specification_version: 4
471
473
  summary: Logs performance and exception data from your app to appsignal.com