appsignal 4.0.3-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.
@@ -0,0 +1,484 @@
1
+ describe Appsignal::CheckIn::Scheduler do
2
+ include WaitForHelper
3
+ include TakeAtMostHelper
4
+
5
+ let(:transmitter) { Appsignal::Transmitter.new("http://checkin-endpoint.invalid") }
6
+
7
+ before do
8
+ allow(Appsignal).to receive(:active?).and_return(true)
9
+ allow(transmitter).to receive(:transmit).and_return(Net::HTTPSuccess.new("1.1", 200, "OK"))
10
+ allow(Appsignal::CheckIn).to receive(:transmitter).and_return(transmitter)
11
+ allow(Appsignal::CheckIn).to receive(:scheduler).and_return(subject)
12
+ # Shorten debounce intervals to make the tests run faster.
13
+ stub_const("Appsignal::CheckIn::Scheduler::INITIAL_DEBOUNCE_SECONDS", 0.1)
14
+ stub_const("Appsignal::CheckIn::Scheduler::BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS", 0.1)
15
+ end
16
+
17
+ after do
18
+ subject.stop
19
+ end
20
+
21
+ describe "when no event is sent" do
22
+ it "does not start a thread" do
23
+ expect(subject.thread).to be_nil
24
+ end
25
+
26
+ it "does not schedule a debounce" do
27
+ expect(subject.waker).to be_nil
28
+ end
29
+
30
+ it "can be stopped" do
31
+ # Set all debounce intervals to 10 seconds, to make the assertion
32
+ # fail if it waits for the debounce -- this ensures that what is being
33
+ # tested is that no debounces are awaited when stopping the scheduler.
34
+ stub_const("Appsignal::CheckIn::Scheduler::INITIAL_DEBOUNCE_SECONDS", 10)
35
+ stub_const("Appsignal::CheckIn::Scheduler::BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS", 10)
36
+
37
+ take_at_most(0.1) do
38
+ expect { subject.stop }.not_to raise_error
39
+ end
40
+ end
41
+
42
+ it "can be stopped more than once" do
43
+ # Set all debounce intervals to 10 seconds, to make the assertion
44
+ # fail if it waits for the debounce -- this ensures that what is being
45
+ # tested is that no debounces are awaited when stopping the scheduler.
46
+ stub_const("Appsignal::CheckIn::Scheduler::INITIAL_DEBOUNCE_SECONDS", 10)
47
+ stub_const("Appsignal::CheckIn::Scheduler::BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS", 10)
48
+
49
+ take_at_most(0.1) do
50
+ expect { subject.stop }.not_to raise_error
51
+ expect { subject.stop }.not_to raise_error
52
+ end
53
+ end
54
+
55
+ it "closes the queue when stopped" do
56
+ subject.stop
57
+ expect(subject.queue.closed?).to be(true)
58
+ end
59
+ end
60
+
61
+ describe "when an event is sent" do
62
+ it "starts a thread" do
63
+ Appsignal::CheckIn.cron("test")
64
+ expect(subject.thread).to be_a(Thread)
65
+ end
66
+
67
+ it "schedules a debounce" do
68
+ Appsignal::CheckIn.cron("test")
69
+ expect(subject.waker).to be_a(Thread)
70
+ end
71
+
72
+ it "schedules the event to be transmitted" do
73
+ expect(transmitter).to receive(:transmit).with([hash_including(
74
+ :identifier => "test",
75
+ :check_in_type => "cron",
76
+ :kind => "finish"
77
+ )], :format => :ndjson)
78
+
79
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
80
+ message.include?("Scheduling cron check-in `test` finish event")
81
+ end)
82
+
83
+ expect(subject.events).to be_empty
84
+
85
+ Appsignal::CheckIn.cron("test")
86
+
87
+ expect(subject.events).not_to be_empty
88
+
89
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
90
+ message.include?("Transmitted cron check-in `test` finish event")
91
+ end)
92
+
93
+ wait_for("the event to be transmitted") { subject.transmitted == 1 }
94
+
95
+ expect(subject.events).to be_empty
96
+ end
97
+
98
+ it "waits for the event to be transmitted when stopped" do
99
+ # Set all debounce intervals to 10 seconds, to make the test
100
+ # fail if it waits for the debounce -- this ensures that what is being
101
+ # tested is that the events are transmitted immediately when the
102
+ # scheduler is stopped, without waiting for any debounce.
103
+ stub_const("Appsignal::CheckIn::Scheduler::INITIAL_DEBOUNCE_SECONDS", 10)
104
+ stub_const("Appsignal::CheckIn::Scheduler::BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS", 10)
105
+
106
+ expect(transmitter).to receive(:transmit).with([hash_including(
107
+ :identifier => "test",
108
+ :check_in_type => "cron",
109
+ :kind => "finish"
110
+ )], :format => :ndjson)
111
+
112
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
113
+ message.include?("Scheduling cron check-in `test` finish event")
114
+ end)
115
+
116
+ Appsignal::CheckIn.cron("test")
117
+
118
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
119
+ message.include?("Transmitted cron check-in `test` finish event")
120
+ end)
121
+
122
+ expect(subject.events).not_to be_empty
123
+
124
+ take_at_most(0.1) do
125
+ expect { subject.stop }.not_to raise_error
126
+ end
127
+
128
+ # Check that the thread wasn't killed before the transmission was
129
+ # completed.
130
+ expect(subject.transmitted).to eq(1)
131
+
132
+ expect(subject.events).to be_empty
133
+ end
134
+
135
+ it "can be stopped more than once" do
136
+ # Set all debounce intervals to 10 seconds, to make the test
137
+ # fail if it waits for the debounce -- this ensures that what is being
138
+ # tested is that the events are transmitted immediately when the
139
+ # scheduler is stopped, without waiting for the debounce interval.
140
+ stub_const("Appsignal::CheckIn::Scheduler::INITIAL_DEBOUNCE_SECONDS", 10)
141
+ stub_const("Appsignal::CheckIn::Scheduler::BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS", 10)
142
+
143
+ Appsignal::CheckIn.cron("test")
144
+ take_at_most(0.1) do
145
+ expect { subject.stop }.not_to raise_error
146
+ end
147
+
148
+ # Check that the thread wasn't killed before the transmission was
149
+ # completed.
150
+ expect(subject.transmitted).to eq(1)
151
+
152
+ take_at_most(0.1) do
153
+ expect { subject.stop }.not_to raise_error
154
+ end
155
+ end
156
+
157
+ it "closes the queue when stopped" do
158
+ Appsignal::CheckIn.cron("test")
159
+ subject.stop
160
+ expect(subject.queue.closed?).to be(true)
161
+ end
162
+
163
+ it "kills the thread when stopped" do
164
+ Appsignal::CheckIn.cron("test")
165
+ subject.stop
166
+ expect(subject.thread.alive?).to be(false)
167
+ end
168
+
169
+ it "unschedules the debounce when stopped" do
170
+ Appsignal::CheckIn.cron("test")
171
+ waker = subject.waker
172
+ subject.stop
173
+ expect(waker.alive?).to be(false)
174
+ expect(subject.waker).to be_nil
175
+ end
176
+ end
177
+
178
+ describe "when many events are sent" do
179
+ describe "within the short debounce interval" do
180
+ it "transmits all events at once" do
181
+ expect(transmitter).to receive(:transmit).with(
182
+ ["first", "second", "third"].map do |identifier|
183
+ hash_including(
184
+ :identifier => identifier,
185
+ :check_in_type => "cron",
186
+ :kind => "finish"
187
+ )
188
+ end, :format => :ndjson
189
+ )
190
+
191
+ Appsignal::CheckIn.cron("first")
192
+ Appsignal::CheckIn.cron("second")
193
+ Appsignal::CheckIn.cron("third")
194
+
195
+ wait_for("the events to be transmitted") { subject.transmitted == 1 }
196
+ end
197
+
198
+ it "transmits all events at once when stopped" do
199
+ # Set a short debounce interval of 10 seconds, to make the final wait
200
+ # fail if it waits for the debounce -- this ensures that what is being
201
+ # tested is that the events are transmitted when the scheduler is
202
+ # stopped.
203
+ stub_const("Appsignal::CheckIn::Scheduler::INITIAL_DEBOUNCE_SECONDS", 10)
204
+
205
+ expect(transmitter).to receive(:transmit).with(
206
+ ["first", "second", "third"].map do |identifier|
207
+ hash_including(
208
+ :identifier => identifier,
209
+ :check_in_type => "cron",
210
+ :kind => "finish"
211
+ )
212
+ end, :format => :ndjson
213
+ )
214
+
215
+ Appsignal::CheckIn.cron("first")
216
+ Appsignal::CheckIn.cron("second")
217
+ Appsignal::CheckIn.cron("third")
218
+
219
+ subject.stop
220
+
221
+ wait_for("the events to be transmitted") { subject.transmitted == 1 }
222
+ end
223
+ end
224
+
225
+ describe "further apart than the short debounce interval" do
226
+ it "transmits the first event and enqueues future events" do
227
+ expect(transmitter).to receive(:transmit).with([hash_including(
228
+ :identifier => "first",
229
+ :check_in_type => "cron",
230
+ :kind => "finish"
231
+ )], :format => :ndjson)
232
+
233
+ Appsignal::CheckIn.cron("first")
234
+
235
+ wait_for("the first event to be transmitted") { subject.transmitted == 1 }
236
+
237
+ Appsignal::CheckIn.cron("second")
238
+ Appsignal::CheckIn.cron("third")
239
+
240
+ expect(subject.events).to match(["second", "third"].map do |identifier|
241
+ hash_including({
242
+ :identifier => identifier,
243
+ :check_in_type => "cron",
244
+ :kind => "finish"
245
+ })
246
+ end)
247
+ end
248
+
249
+ it "transmits the other events after the debounce interval" do
250
+ expect(transmitter).to receive(:transmit)
251
+
252
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
253
+ message.include?("Scheduling cron check-in `first` finish event")
254
+ end)
255
+
256
+ Appsignal::CheckIn.cron("first")
257
+
258
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
259
+ message.include?("Transmitted cron check-in `first` finish event")
260
+ end)
261
+
262
+ wait_for("the first event to be transmitted") { subject.transmitted == 1 }
263
+
264
+ expect(transmitter).to receive(:transmit).with(
265
+ ["second", "third"].map do |identifier|
266
+ hash_including(
267
+ :identifier => identifier,
268
+ :check_in_type => "cron",
269
+ :kind => "finish"
270
+ )
271
+ end, :format => :ndjson
272
+ )
273
+
274
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
275
+ message.include?("Scheduling cron check-in `second` finish event")
276
+ end)
277
+
278
+ Appsignal::CheckIn.cron("second")
279
+
280
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
281
+ message.include?("Scheduling cron check-in `third` finish event")
282
+ end)
283
+
284
+ Appsignal::CheckIn.cron("third")
285
+
286
+ expect(subject.events).to_not be_empty
287
+
288
+ expect(Appsignal.internal_logger).to receive(:debug).with(
289
+ "Transmitted 2 check-in events"
290
+ )
291
+
292
+ wait_for("the other events to be transmitted") { subject.transmitted == 2 }
293
+
294
+ expect(subject.events).to be_empty
295
+ end
296
+
297
+ it "transmits the other events when stopped" do
298
+ # Restore the original long debounce interval of 10 seconds, to make
299
+ # the final wait fail if it waits for the debounce -- this ensures
300
+ # that what is being tested is that the events are transmitted
301
+ # immediately when the scheduler is stopped.
302
+ stub_const("Appsignal::CheckIn::Scheduler::BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS", 10)
303
+
304
+ expect(transmitter).to receive(:transmit)
305
+
306
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
307
+ message.include?("Scheduling cron check-in `first` finish event")
308
+ end)
309
+
310
+ Appsignal::CheckIn.cron("first")
311
+
312
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
313
+ message.include?("Transmitted cron check-in `first` finish event")
314
+ end)
315
+
316
+ wait_for("the event to be transmitted") { subject.transmitted == 1 }
317
+
318
+ expect(transmitter).to receive(:transmit).with(
319
+ ["second", "third"].map do |identifier|
320
+ hash_including(
321
+ :identifier => identifier,
322
+ :check_in_type => "cron",
323
+ :kind => "finish"
324
+ )
325
+ end, :format => :ndjson
326
+ )
327
+
328
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
329
+ message.include?("Scheduling cron check-in `second` finish event")
330
+ end)
331
+
332
+ Appsignal::CheckIn.cron("second")
333
+
334
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
335
+ message.include?("Scheduling cron check-in `third` finish event")
336
+ end)
337
+
338
+ Appsignal::CheckIn.cron("third")
339
+
340
+ expect(subject.events).to_not be_empty
341
+
342
+ expect(Appsignal.internal_logger).to receive(:debug).with(
343
+ "Transmitted 2 check-in events"
344
+ )
345
+
346
+ subject.stop
347
+
348
+ wait_for("the other events to be transmitted") { subject.transmitted == 2 }
349
+
350
+ expect(subject.events).to be_empty
351
+ end
352
+ end
353
+ end
354
+
355
+ describe "when a similar event is sent more than once" do
356
+ it "only transmits one of the similar events" do
357
+ # We must instantiate `Appsignal::CheckIn::Cron` directly, as the
358
+ # `.cron` helper would use a different digest for each invocation.
359
+ cron = Appsignal::CheckIn::Cron.new(:identifier => "test")
360
+
361
+ expect(transmitter).to receive(:transmit).with([hash_including(
362
+ :identifier => "test",
363
+ :check_in_type => "cron",
364
+ :kind => "start"
365
+ )], :format => :ndjson)
366
+
367
+ expect(Appsignal.internal_logger).to receive(:debug).with(
368
+ "Scheduling cron check-in `test` start event (digest #{cron.digest}) to be transmitted"
369
+ )
370
+
371
+ cron.start
372
+
373
+ expect(Appsignal.internal_logger).to receive(:debug).with(
374
+ "Scheduling cron check-in `test` start event (digest #{cron.digest}) to be transmitted"
375
+ )
376
+
377
+ expect(Appsignal.internal_logger).to receive(:debug).with(
378
+ "Replacing previously scheduled cron check-in `test` start event (digest #{cron.digest})"
379
+ )
380
+
381
+ cron.start
382
+
383
+ expect(Appsignal.internal_logger).to receive(:debug).with(
384
+ "Transmitted cron check-in `test` start event (digest #{cron.digest})"
385
+ )
386
+
387
+ wait_for("the event to be transmitted") { subject.transmitted == 1 }
388
+ end
389
+ end
390
+
391
+ describe "when the scheduler is stopped" do
392
+ it "does not schedule any events to be transmitted" do
393
+ expect(transmitter).not_to receive(:transmit)
394
+
395
+ subject.stop
396
+
397
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
398
+ message.include?("Cannot transmit cron check-in `test` finish event") &&
399
+ message.include?("AppSignal is stopped")
400
+ end)
401
+
402
+ Appsignal::CheckIn.cron("test")
403
+
404
+ expect(subject.events).to be_empty
405
+ end
406
+ end
407
+
408
+ describe "when AppSignal is not active" do
409
+ it "does not schedule any events to be transmitted" do
410
+ allow(Appsignal).to receive(:active?).and_return(false)
411
+
412
+ subject.stop
413
+
414
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
415
+ message.include?("Cannot transmit cron check-in `test` finish event") &&
416
+ message.include?("AppSignal is not active")
417
+ end)
418
+
419
+ Appsignal::CheckIn.cron("test")
420
+
421
+ expect(subject.events).to be_empty
422
+ end
423
+ end
424
+
425
+ describe "when transmitting returns a non-success response code" do
426
+ it "logs the error and continues" do
427
+ expect(transmitter).to receive(:transmit).and_return(
428
+ Net::HTTPNotFound.new("1.1", 404, "Not Found")
429
+ )
430
+
431
+ Appsignal::CheckIn.cron("first")
432
+
433
+ expect(Appsignal.internal_logger).to receive(:error).with(satisfy do |message|
434
+ message.include?("Failed to transmit cron check-in `first` finish event") &&
435
+ message.include?("404 status code")
436
+ end)
437
+
438
+ wait_for("the first event to be transmitted") { subject.transmitted == 1 }
439
+
440
+ expect(transmitter).to receive(:transmit).and_return(
441
+ Net::HTTPSuccess.new("1.1", 200, "OK")
442
+ )
443
+
444
+ Appsignal::CheckIn.cron("second")
445
+
446
+ expect(Appsignal.internal_logger).not_to receive(:error)
447
+
448
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
449
+ message.include?("Transmitted cron check-in `second` finish event")
450
+ end)
451
+
452
+ wait_for("the second event to be transmitted") { subject.transmitted == 2 }
453
+ end
454
+ end
455
+
456
+ describe "when transmitting throws an error" do
457
+ it "logs the error and continues" do
458
+ expect(transmitter).to receive(:transmit).and_raise("Something went wrong")
459
+
460
+ Appsignal::CheckIn.cron("first")
461
+
462
+ expect(Appsignal.internal_logger).to receive(:error).with(satisfy do |message|
463
+ message.include?("Failed to transmit cron check-in `first` finish event") &&
464
+ message.include?("Something went wrong")
465
+ end)
466
+
467
+ wait_for("the first event to be transmitted") { subject.transmitted == 1 }
468
+
469
+ expect(transmitter).to receive(:transmit).and_return(
470
+ Net::HTTPSuccess.new("1.1", 200, "OK")
471
+ )
472
+
473
+ Appsignal::CheckIn.cron("second")
474
+
475
+ expect(Appsignal.internal_logger).not_to receive(:error)
476
+
477
+ expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
478
+ message.include?("Transmitted cron check-in `second` finish event")
479
+ end)
480
+
481
+ wait_for("the second event to be transmitted") { subject.transmitted == 2 }
482
+ end
483
+ end
484
+ end
@@ -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 }
@@ -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
@@ -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