appsignal 4.0.3-java → 4.0.4-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -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