appsignal 4.0.3-java → 4.0.5-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -0
  3. data/ext/agent.rb +27 -27
  4. data/lib/appsignal/check_in/cron.rb +2 -34
  5. data/lib/appsignal/check_in/scheduler.rb +192 -0
  6. data/lib/appsignal/check_in.rb +18 -0
  7. data/lib/appsignal/cli/diagnose.rb +1 -1
  8. data/lib/appsignal/config.rb +7 -0
  9. data/lib/appsignal/hooks/at_exit.rb +3 -1
  10. data/lib/appsignal/hooks/puma.rb +5 -1
  11. data/lib/appsignal/integrations/puma.rb +45 -0
  12. data/lib/appsignal/rack/abstract_middleware.rb +3 -47
  13. data/lib/appsignal/rack/body_wrapper.rb +15 -0
  14. data/lib/appsignal/rack/event_handler.rb +2 -0
  15. data/lib/appsignal/rack/hanami_middleware.rb +5 -1
  16. data/lib/appsignal/rack.rb +68 -0
  17. data/lib/appsignal/transmitter.rb +30 -7
  18. data/lib/appsignal/utils/ndjson.rb +15 -0
  19. data/lib/appsignal/utils.rb +1 -0
  20. data/lib/appsignal/version.rb +1 -1
  21. data/lib/appsignal.rb +1 -0
  22. data/spec/lib/appsignal/check_in/cron_spec.rb +202 -0
  23. data/spec/lib/appsignal/check_in/scheduler_spec.rb +443 -0
  24. data/spec/lib/appsignal/config_spec.rb +13 -0
  25. data/spec/lib/appsignal/environment_spec.rb +1 -1
  26. data/spec/lib/appsignal/hooks/at_exit_spec.rb +22 -0
  27. data/spec/lib/appsignal/hooks/puma_spec.rb +31 -23
  28. data/spec/lib/appsignal/integrations/puma_spec.rb +150 -0
  29. data/spec/lib/appsignal/probes_spec.rb +1 -6
  30. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +41 -122
  31. data/spec/lib/appsignal/rack/body_wrapper_spec.rb +29 -21
  32. data/spec/lib/appsignal/rack_spec.rb +180 -0
  33. data/spec/lib/appsignal/transmitter_spec.rb +48 -2
  34. data/spec/lib/appsignal_spec.rb +5 -0
  35. data/spec/spec_helper.rb +0 -7
  36. data/spec/support/helpers/config_helpers.rb +2 -1
  37. data/spec/support/helpers/take_at_most_helper.rb +21 -0
  38. data/spec/support/matchers/contains_log.rb +10 -3
  39. data/spec/support/mocks/hash_like.rb +10 -0
  40. data/spec/support/mocks/puma_mock.rb +43 -0
  41. metadata +11 -3
  42. data/spec/lib/appsignal/check_in_spec.rb +0 -136
@@ -0,0 +1,443 @@
1
+ describe Appsignal::CheckIn::Scheduler do
2
+ include WaitForHelper
3
+ include TakeAtMostHelper
4
+
5
+ let(:log_stream) { std_stream }
6
+ let(:logs) { log_contents(log_stream) }
7
+ let(:appsignal_options) { {} }
8
+ let(:transmitter) { Appsignal::Transmitter.new("http://checkin-endpoint.invalid") }
9
+
10
+ before do
11
+ start_agent(:options => appsignal_options, :internal_logger => test_logger(log_stream))
12
+ allow(transmitter).to receive(:transmit).and_return(Net::HTTPSuccess.new("1.1", 200, "OK"))
13
+ allow(Appsignal::CheckIn).to receive(:transmitter).and_return(transmitter)
14
+ allow(Appsignal::CheckIn).to receive(:scheduler).and_return(subject)
15
+ # Shorten debounce intervals to make the tests run faster.
16
+ stub_const("Appsignal::CheckIn::Scheduler::INITIAL_DEBOUNCE_SECONDS", 0.1)
17
+ stub_const("Appsignal::CheckIn::Scheduler::BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS", 0.1)
18
+ end
19
+
20
+ after do
21
+ subject.stop
22
+ end
23
+
24
+ describe "when no event is sent" do
25
+ it "does not start a thread" do
26
+ expect(subject.thread).to be_nil
27
+ end
28
+
29
+ it "does not schedule a debounce" do
30
+ expect(subject.waker).to be_nil
31
+ end
32
+
33
+ it "can be stopped" do
34
+ # Set all debounce intervals to 10 seconds, to make the assertion
35
+ # fail if it waits for the debounce -- this ensures that what is being
36
+ # tested is that no debounces are awaited when stopping the scheduler.
37
+ stub_const("Appsignal::CheckIn::Scheduler::INITIAL_DEBOUNCE_SECONDS", 10)
38
+ stub_const("Appsignal::CheckIn::Scheduler::BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS", 10)
39
+
40
+ take_at_most(0.1) do
41
+ expect { subject.stop }.not_to raise_error
42
+ end
43
+ end
44
+
45
+ it "can be stopped more than once" do
46
+ # Set all debounce intervals to 10 seconds, to make the assertion
47
+ # fail if it waits for the debounce -- this ensures that what is being
48
+ # tested is that no debounces are awaited when stopping the scheduler.
49
+ stub_const("Appsignal::CheckIn::Scheduler::INITIAL_DEBOUNCE_SECONDS", 10)
50
+ stub_const("Appsignal::CheckIn::Scheduler::BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS", 10)
51
+
52
+ take_at_most(0.1) do
53
+ expect { subject.stop }.not_to raise_error
54
+ expect { subject.stop }.not_to raise_error
55
+ end
56
+ end
57
+
58
+ it "closes the queue when stopped" do
59
+ subject.stop
60
+ expect(subject.queue.closed?).to be(true)
61
+ end
62
+ end
63
+
64
+ describe "when an event is sent" do
65
+ it "starts a thread" do
66
+ Appsignal::CheckIn.cron("test")
67
+ expect(subject.thread).to be_a(Thread)
68
+ end
69
+
70
+ it "schedules a debounce" do
71
+ Appsignal::CheckIn.cron("test")
72
+ expect(subject.waker).to be_a(Thread)
73
+ end
74
+
75
+ it "schedules the event to be transmitted" do
76
+ expect(transmitter).to receive(:transmit).with([hash_including(
77
+ :identifier => "test",
78
+ :check_in_type => "cron",
79
+ :kind => "finish"
80
+ )], :format => :ndjson)
81
+
82
+ expect(subject.events).to be_empty
83
+
84
+ Appsignal::CheckIn.cron("test")
85
+
86
+ expect(subject.events).not_to be_empty
87
+
88
+ wait_for("the event to be transmitted") { subject.transmitted == 1 }
89
+
90
+ expect(subject.events).to be_empty
91
+
92
+ expect(logs).to contains_log(:debug, "Scheduling cron check-in `test` finish event")
93
+ expect(logs).to contains_log(:debug, "Transmitted cron check-in `test` finish event")
94
+ end
95
+
96
+ it "waits for the event to be transmitted when stopped" do
97
+ # Set all debounce intervals to 10 seconds, to make the test
98
+ # fail if it waits for the debounce -- this ensures that what is being
99
+ # tested is that the events are transmitted immediately when the
100
+ # scheduler is stopped, without waiting for any debounce.
101
+ stub_const("Appsignal::CheckIn::Scheduler::INITIAL_DEBOUNCE_SECONDS", 10)
102
+ stub_const("Appsignal::CheckIn::Scheduler::BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS", 10)
103
+
104
+ expect(transmitter).to receive(:transmit).with([hash_including(
105
+ :identifier => "test",
106
+ :check_in_type => "cron",
107
+ :kind => "finish"
108
+ )], :format => :ndjson)
109
+
110
+ Appsignal::CheckIn.cron("test")
111
+
112
+ expect(subject.events).not_to be_empty
113
+
114
+ take_at_most(0.1) do
115
+ expect { subject.stop }.not_to raise_error
116
+ end
117
+
118
+ # Check that the thread wasn't killed before the transmission was
119
+ # completed.
120
+ expect(subject.transmitted).to eq(1)
121
+
122
+ expect(subject.events).to be_empty
123
+
124
+ expect(logs).to contains_log(:debug, "Scheduling cron check-in `test` finish event")
125
+ expect(logs).to contains_log(:debug, "Transmitted cron check-in `test` finish event")
126
+ end
127
+
128
+ it "can be stopped more than once" do
129
+ # Set all debounce intervals to 10 seconds, to make the test
130
+ # fail if it waits for the debounce -- this ensures that what is being
131
+ # tested is that the events are transmitted immediately when the
132
+ # scheduler is stopped, without waiting for the debounce interval.
133
+ stub_const("Appsignal::CheckIn::Scheduler::INITIAL_DEBOUNCE_SECONDS", 10)
134
+ stub_const("Appsignal::CheckIn::Scheduler::BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS", 10)
135
+
136
+ Appsignal::CheckIn.cron("test")
137
+ take_at_most(0.1) do
138
+ expect { subject.stop }.not_to raise_error
139
+ end
140
+
141
+ # Check that the thread wasn't killed before the transmission was
142
+ # completed.
143
+ expect(subject.transmitted).to eq(1)
144
+
145
+ take_at_most(0.1) do
146
+ expect { subject.stop }.not_to raise_error
147
+ end
148
+ end
149
+
150
+ it "closes the queue when stopped" do
151
+ Appsignal::CheckIn.cron("test")
152
+ subject.stop
153
+ expect(subject.queue.closed?).to be(true)
154
+ end
155
+
156
+ it "kills the thread when stopped" do
157
+ Appsignal::CheckIn.cron("test")
158
+ subject.stop
159
+ expect(subject.thread.alive?).to be(false)
160
+ end
161
+
162
+ it "unschedules the debounce when stopped" do
163
+ Appsignal::CheckIn.cron("test")
164
+ waker = subject.waker
165
+ subject.stop
166
+ expect(waker.alive?).to be(false)
167
+ expect(subject.waker).to be_nil
168
+ end
169
+ end
170
+
171
+ describe "when many events are sent" do
172
+ describe "within the short debounce interval" do
173
+ it "transmits all events at once" do
174
+ expect(transmitter).to receive(:transmit).with(
175
+ ["first", "second", "third"].map do |identifier|
176
+ hash_including(
177
+ :identifier => identifier,
178
+ :check_in_type => "cron",
179
+ :kind => "finish"
180
+ )
181
+ end, :format => :ndjson
182
+ )
183
+
184
+ Appsignal::CheckIn.cron("first")
185
+ Appsignal::CheckIn.cron("second")
186
+ Appsignal::CheckIn.cron("third")
187
+
188
+ wait_for("the events to be transmitted") { subject.transmitted == 1 }
189
+ end
190
+
191
+ it "transmits all events at once when stopped" do
192
+ # Set a short debounce interval of 10 seconds, to make the final wait
193
+ # fail if it waits for the debounce -- this ensures that what is being
194
+ # tested is that the events are transmitted when the scheduler is
195
+ # stopped.
196
+ stub_const("Appsignal::CheckIn::Scheduler::INITIAL_DEBOUNCE_SECONDS", 10)
197
+
198
+ expect(transmitter).to receive(:transmit).with(
199
+ ["first", "second", "third"].map do |identifier|
200
+ hash_including(
201
+ :identifier => identifier,
202
+ :check_in_type => "cron",
203
+ :kind => "finish"
204
+ )
205
+ end, :format => :ndjson
206
+ )
207
+
208
+ Appsignal::CheckIn.cron("first")
209
+ Appsignal::CheckIn.cron("second")
210
+ Appsignal::CheckIn.cron("third")
211
+
212
+ subject.stop
213
+
214
+ wait_for("the events to be transmitted") { subject.transmitted == 1 }
215
+ end
216
+ end
217
+
218
+ describe "further apart than the short debounce interval" do
219
+ it "transmits the first event and enqueues future events" do
220
+ expect(transmitter).to receive(:transmit).with([hash_including(
221
+ :identifier => "first",
222
+ :check_in_type => "cron",
223
+ :kind => "finish"
224
+ )], :format => :ndjson)
225
+
226
+ Appsignal::CheckIn.cron("first")
227
+
228
+ wait_for("the first event to be transmitted") { subject.transmitted == 1 }
229
+
230
+ Appsignal::CheckIn.cron("second")
231
+ Appsignal::CheckIn.cron("third")
232
+
233
+ expect(subject.events).to match(["second", "third"].map do |identifier|
234
+ hash_including({
235
+ :identifier => identifier,
236
+ :check_in_type => "cron",
237
+ :kind => "finish"
238
+ })
239
+ end)
240
+ end
241
+
242
+ it "transmits the other events after the debounce interval" do
243
+ expect(transmitter).to receive(:transmit)
244
+
245
+ Appsignal::CheckIn.cron("first")
246
+
247
+ wait_for("the first event to be transmitted") { subject.transmitted == 1 }
248
+
249
+ expect(transmitter).to receive(:transmit).with(
250
+ ["second", "third"].map do |identifier|
251
+ hash_including(
252
+ :identifier => identifier,
253
+ :check_in_type => "cron",
254
+ :kind => "finish"
255
+ )
256
+ end, :format => :ndjson
257
+ )
258
+
259
+ Appsignal::CheckIn.cron("second")
260
+ Appsignal::CheckIn.cron("third")
261
+
262
+ expect(subject.events).to_not be_empty
263
+
264
+ wait_for("the other events to be transmitted") { subject.transmitted == 2 }
265
+
266
+ expect(subject.events).to be_empty
267
+
268
+ expect(logs).to contains_log(:debug, "Scheduling cron check-in `first` finish event")
269
+ expect(logs).to contains_log(:debug, "Transmitted cron check-in `first` finish event")
270
+ expect(logs).to contains_log(:debug, "Scheduling cron check-in `second` finish event")
271
+ expect(logs).to contains_log(:debug, "Scheduling cron check-in `third` finish event")
272
+ expect(logs).to contains_log(:debug, "Transmitted 2 check-in events")
273
+ end
274
+
275
+ it "transmits the other events when stopped" do
276
+ # Restore the original long debounce interval of 10 seconds, to make
277
+ # the final wait fail if it waits for the debounce -- this ensures
278
+ # that what is being tested is that the events are transmitted
279
+ # immediately when the scheduler is stopped.
280
+ stub_const("Appsignal::CheckIn::Scheduler::BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS", 10)
281
+
282
+ expect(transmitter).to receive(:transmit)
283
+
284
+ Appsignal::CheckIn.cron("first")
285
+
286
+ wait_for("the event to be transmitted") { subject.transmitted == 1 }
287
+
288
+ expect(transmitter).to receive(:transmit).with(
289
+ ["second", "third"].map do |identifier|
290
+ hash_including(
291
+ :identifier => identifier,
292
+ :check_in_type => "cron",
293
+ :kind => "finish"
294
+ )
295
+ end, :format => :ndjson
296
+ )
297
+
298
+ Appsignal::CheckIn.cron("second")
299
+ Appsignal::CheckIn.cron("third")
300
+
301
+ expect(subject.events).to_not be_empty
302
+
303
+ subject.stop
304
+
305
+ wait_for("the other events to be transmitted") { subject.transmitted == 2 }
306
+
307
+ expect(subject.events).to be_empty
308
+
309
+ expect(logs).to contains_log(:debug, "Scheduling cron check-in `first` finish event")
310
+ expect(logs).to contains_log(:debug, "Transmitted cron check-in `first` finish event")
311
+ expect(logs).to contains_log(:debug, "Scheduling cron check-in `second` finish event")
312
+ expect(logs).to contains_log(:debug, "Scheduling cron check-in `third` finish event")
313
+ expect(logs).to contains_log(:debug, "Transmitted 2 check-in events")
314
+ end
315
+ end
316
+ end
317
+
318
+ describe "when a similar event is sent more than once" do
319
+ it "only transmits one of the similar events" do
320
+ # We must instantiate `Appsignal::CheckIn::Cron` directly, as the
321
+ # `.cron` helper would use a different digest for each invocation.
322
+ cron = Appsignal::CheckIn::Cron.new(:identifier => "test")
323
+
324
+ expect(transmitter).to receive(:transmit).with([hash_including(
325
+ :identifier => "test",
326
+ :check_in_type => "cron",
327
+ :kind => "start"
328
+ )], :format => :ndjson)
329
+
330
+ cron.start
331
+ cron.start
332
+
333
+ wait_for("the event to be transmitted") { subject.transmitted == 1 }
334
+
335
+ expect(logs).to contains_log(
336
+ :debug,
337
+ "Scheduling cron check-in `test` start event (digest #{cron.digest}) to be transmitted"
338
+ )
339
+ expect(logs).to contains_log(
340
+ :debug,
341
+ "Scheduling cron check-in `test` start event (digest #{cron.digest}) to be transmitted"
342
+ )
343
+ expect(logs).to contains_log(
344
+ :debug,
345
+ "Replacing previously scheduled cron check-in `test` start event (digest #{cron.digest})"
346
+ )
347
+ expect(logs).to contains_log(
348
+ :debug,
349
+ "Transmitted cron check-in `test` start event (digest #{cron.digest})"
350
+ )
351
+ end
352
+ end
353
+
354
+ describe "when the scheduler is stopped" do
355
+ it "does not schedule any events to be transmitted" do
356
+ expect(transmitter).not_to receive(:transmit)
357
+
358
+ subject.stop
359
+
360
+ Appsignal::CheckIn.cron("test")
361
+
362
+ expect(subject.events).to be_empty
363
+
364
+ expect(logs).to contains_log(
365
+ :debug,
366
+ /Cannot transmit cron check-in `test` finish event .+: AppSignal is stopped/
367
+ )
368
+ end
369
+ end
370
+
371
+ describe "when AppSignal is not active" do
372
+ let(:appsignal_options) { { :active => false } }
373
+
374
+ it "does not schedule any events to be transmitted" do
375
+ subject.stop
376
+
377
+ Appsignal::CheckIn.cron("test")
378
+
379
+ expect(subject.events).to be_empty
380
+
381
+ expect(logs).to contains_log(
382
+ :debug,
383
+ /Cannot transmit cron check-in `test` finish event .+: AppSignal is not active/
384
+ )
385
+ end
386
+ end
387
+
388
+ describe "when transmitting returns a non-success response code" do
389
+ it "logs the error and continues" do
390
+ expect(transmitter).to receive(:transmit).and_return(
391
+ Net::HTTPNotFound.new("1.1", 404, "Not Found")
392
+ )
393
+
394
+ Appsignal::CheckIn.cron("first")
395
+
396
+ wait_for("the first event to be transmitted") { subject.transmitted == 1 }
397
+
398
+ expect(transmitter).to receive(:transmit).and_return(
399
+ Net::HTTPSuccess.new("1.1", 200, "OK")
400
+ )
401
+
402
+ Appsignal::CheckIn.cron("second")
403
+
404
+ wait_for("the second event to be transmitted") { subject.transmitted == 2 }
405
+
406
+ expect(logs).to contains_log(
407
+ :error,
408
+ /Failed to transmit cron check-in `first` finish event .+: 404 status code/
409
+ )
410
+ expect(logs).to contains_log(
411
+ :debug,
412
+ "Transmitted cron check-in `second` finish event"
413
+ )
414
+ end
415
+ end
416
+
417
+ describe "when transmitting throws an error" do
418
+ it "logs the error and continues" do
419
+ expect(transmitter).to receive(:transmit).and_raise("Something went wrong")
420
+
421
+ Appsignal::CheckIn.cron("first")
422
+
423
+ wait_for("the first event to be transmitted") { subject.transmitted == 1 }
424
+
425
+ expect(transmitter).to receive(:transmit).and_return(
426
+ Net::HTTPSuccess.new("1.1", 200, "OK")
427
+ )
428
+
429
+ Appsignal::CheckIn.cron("second")
430
+
431
+ wait_for("the second event to be transmitted") { subject.transmitted == 2 }
432
+
433
+ expect(logs).to contains_log(
434
+ :error,
435
+ /Failed to transmit cron check-in `first` finish event .+: Something went wrong/
436
+ )
437
+ expect(logs).to contains_log(
438
+ :debug,
439
+ "Transmitted cron check-in `second` finish event"
440
+ )
441
+ end
442
+ end
443
+ end
@@ -1,5 +1,18 @@
1
1
  describe Appsignal::Config do
2
2
  describe ".add_loader_defaults" do
3
+ context "when the config is initialized" do
4
+ before { Appsignal.configure(:test) }
5
+
6
+ it "logs a warning" do
7
+ logs = capture_logs { described_class.add_loader_defaults(:loader1) }
8
+
9
+ expect(logs).to contains_log(
10
+ :warn,
11
+ "The config defaults from the 'loader1' loader are ignored"
12
+ )
13
+ end
14
+ end
15
+
3
16
  it "adds loader defaults to the list" do
4
17
  described_class.add_loader_defaults(:loader1)
5
18
 
@@ -120,7 +120,7 @@ describe Appsignal::Environment do
120
120
 
121
121
  expect(logs).to be_empty
122
122
 
123
- if Bundler.rubygems.respond_to?(:all_specs)
123
+ unless Bundler.rubygems.respond_to?(:all_specs)
124
124
  skip "Using new Bundler version without `all_specs` method"
125
125
  end
126
126
  bundle_gem_specs = silence { ::Bundler.rubygems.all_specs }
@@ -40,6 +40,17 @@ describe Appsignal::Hooks::AtExit::AtExitCallback do
40
40
  Appsignal::Hooks::AtExit::AtExitCallback.call
41
41
  end
42
42
 
43
+ it "reports no transaction if the process didn't exit with an error" do
44
+ logs =
45
+ capture_logs do
46
+ expect do
47
+ call_callback
48
+ end.to_not(change { created_transactions.count })
49
+ end
50
+
51
+ expect(logs).to_not contains_log(:error, "Appsignal.report_error: Cannot add error.")
52
+ end
53
+
43
54
  it "reports an error if there's an unhandled error" do
44
55
  expect do
45
56
  with_error(ExampleException, "error message") do
@@ -80,4 +91,15 @@ describe Appsignal::Hooks::AtExit::AtExitCallback do
80
91
  end.to_not change { created_transactions.count }.from(1)
81
92
  end
82
93
  end
94
+
95
+ it "doesn't report the error if it is a SignalException exception" do
96
+ with_error(SignalException, "TERM") do |error|
97
+ Appsignal.report_error(error)
98
+ expect(created_transactions.count).to eq(1)
99
+
100
+ expect do
101
+ call_callback
102
+ end.to_not change { created_transactions.count }.from(1)
103
+ end
104
+ end
83
105
  end
@@ -1,34 +1,45 @@
1
1
  describe Appsignal::Hooks::PumaHook do
2
2
  context "with puma" do
3
- before(:context) do
4
- class Puma
5
- def self.stats
6
- end
3
+ let(:puma_version) { "6.0.0" }
4
+ before do
5
+ stub_const("Puma", PumaMock)
6
+ stub_const("Puma::Const::VERSION", puma_version)
7
+ end
8
+
9
+ describe "#dependencies_present?" do
10
+ subject { described_class.new.dependencies_present? }
11
+
12
+ context "when Puma present" do
13
+ context "when Puma is newer than version 3.0.0" do
14
+ let(:puma_version) { "3.0.0" }
7
15
 
8
- def self.cli_config
9
- @cli_config ||= CliConfig.new
16
+ it { is_expected.to be_truthy }
10
17
  end
11
- end
12
18
 
13
- class CliConfig
14
- attr_accessor :options
19
+ context "when Puma is older than version 3.0.0" do
20
+ let(:puma_version) { "2.9.9" }
15
21
 
16
- def initialize
17
- @options = {}
22
+ it { is_expected.to be_falsey }
18
23
  end
19
24
  end
20
- end
21
- after(:context) { Object.send(:remove_const, :Puma) }
22
25
 
23
- describe "#dependencies_present?" do
24
- subject { described_class.new.dependencies_present? }
26
+ context "when Puma is not present" do
27
+ before do
28
+ hide_const("Puma")
29
+ end
25
30
 
26
- it { is_expected.to be_truthy }
31
+ it { is_expected.to be_falsey }
32
+ end
27
33
  end
28
34
 
29
35
  describe "installation" do
30
36
  before { Appsignal::Probes.probes.clear }
31
37
 
38
+ it "adds the Puma::Server patch" do
39
+ Appsignal::Hooks::PumaHook.new.install
40
+ expect(::Puma::Server.included_modules).to include(Appsignal::Integrations::PumaServer)
41
+ end
42
+
32
43
  context "when not clustered mode" do
33
44
  it "does not add AppSignal stop behavior Puma::Cluster" do
34
45
  expect(defined?(::Puma::Cluster)).to be_falsy
@@ -39,15 +50,12 @@ describe Appsignal::Hooks::PumaHook do
39
50
 
40
51
  context "when in clustered mode" do
41
52
  before do
42
- class Puma
43
- class Cluster
44
- def stop_workers
45
- @called = true
46
- end
53
+ stub_const("Puma::Cluster", Class.new do
54
+ def stop_workers
55
+ @called = true
47
56
  end
48
- end
57
+ end)
49
58
  end
50
- after { Puma.send(:remove_const, :Cluster) }
51
59
 
52
60
  it "adds behavior to Puma::Cluster.stop_workers" do
53
61
  Appsignal::Hooks::PumaHook.new.install