analytics-ruby 2.2.3.pre → 2.2.4.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ module Segment
4
+ class Analytics
5
+ describe BackoffPolicy do
6
+ describe '#initialize' do
7
+ context 'no options are given' do
8
+ it 'sets default min_timeout_ms' do
9
+ actual = subject.instance_variable_get(:@min_timeout_ms)
10
+ expect(actual).to eq(described_class::MIN_TIMEOUT_MS)
11
+ end
12
+
13
+ it 'sets default max_timeout_ms' do
14
+ actual = subject.instance_variable_get(:@max_timeout_ms)
15
+ expect(actual).to eq(described_class::MAX_TIMEOUT_MS)
16
+ end
17
+
18
+ it 'sets default multiplier' do
19
+ actual = subject.instance_variable_get(:@multiplier)
20
+ expect(actual).to eq(described_class::MULTIPLIER)
21
+ end
22
+
23
+ it 'sets default randomization factor' do
24
+ actual = subject.instance_variable_get(:@randomization_factor)
25
+ expect(actual).to eq(described_class::RANDOMIZATION_FACTOR)
26
+ end
27
+ end
28
+
29
+ context 'options are given' do
30
+ let(:min_timeout_ms) { 1234 }
31
+ let(:max_timeout_ms) { 5678 }
32
+ let(:multiplier) { 24 }
33
+ let(:randomization_factor) { 0.4 }
34
+
35
+ let(:options) do
36
+ {
37
+ min_timeout_ms: min_timeout_ms,
38
+ max_timeout_ms: max_timeout_ms,
39
+ multiplier: multiplier,
40
+ randomization_factor: randomization_factor
41
+ }
42
+ end
43
+
44
+ subject { described_class.new(options) }
45
+
46
+ it 'sets passed in min_timeout_ms' do
47
+ actual = subject.instance_variable_get(:@min_timeout_ms)
48
+ expect(actual).to eq(min_timeout_ms)
49
+ end
50
+
51
+ it 'sets passed in max_timeout_ms' do
52
+ actual = subject.instance_variable_get(:@max_timeout_ms)
53
+ expect(actual).to eq(max_timeout_ms)
54
+ end
55
+
56
+ it 'sets passed in multiplier' do
57
+ actual = subject.instance_variable_get(:@multiplier)
58
+ expect(actual).to eq(multiplier)
59
+ end
60
+
61
+ it 'sets passed in randomization_factor' do
62
+ actual = subject.instance_variable_get(:@randomization_factor)
63
+ expect(actual).to eq(randomization_factor)
64
+ end
65
+ end
66
+ end
67
+
68
+ describe '#next_interval' do
69
+ subject {
70
+ described_class.new(
71
+ min_timeout_ms: 1000,
72
+ max_timeout_ms: 10000,
73
+ multiplier: 2,
74
+ randomization_factor: 0.5
75
+ )
76
+ }
77
+
78
+ it 'returns exponentially increasing durations' do
79
+ expect(subject.next_interval).to be_within(500).of(1000)
80
+ expect(subject.next_interval).to be_within(1000).of(2000)
81
+ expect(subject.next_interval).to be_within(2000).of(4000)
82
+ expect(subject.next_interval).to be_within(4000).of(8000)
83
+ end
84
+
85
+ it 'caps maximum duration at max_timeout_secs' do
86
+ 10.times { subject.next_interval }
87
+ expect(subject.next_interval).to eq(10000)
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -3,7 +3,12 @@ require 'spec_helper'
3
3
  module Segment
4
4
  class Analytics
5
5
  describe Client do
6
- let(:client) { Client.new :write_key => WRITE_KEY }
6
+ let(:client) do
7
+ Client.new(:write_key => WRITE_KEY).tap { |client|
8
+ # Ensure that worker doesn't consume items from the queue
9
+ client.instance_variable_set(:@worker, NoopWorker.new)
10
+ }
11
+ end
7
12
  let(:queue) { client.instance_variable_get :@queue }
8
13
 
9
14
  describe '#initialize' do
@@ -38,13 +43,13 @@ module Segment
38
43
  client.track({
39
44
  :user_id => 'user',
40
45
  :event => 'Event',
41
- :properties => [1,2,3]
46
+ :properties => [1, 2, 3]
42
47
  })
43
48
  }.to raise_error(ArgumentError)
44
49
  end
45
50
 
46
51
  it 'uses the timestamp given' do
47
- time = Time.parse("1990-07-16 13:30:00.123 UTC")
52
+ time = Time.parse('1990-07-16 13:30:00.123 UTC')
48
53
 
49
54
  client.track({
50
55
  :event => 'testing the timestamp',
@@ -78,22 +83,22 @@ module Segment
78
83
  :properties => {
79
84
  :time => Time.utc(2013),
80
85
  :time_with_zone => Time.zone.parse('2013-01-01'),
81
- :date_time => DateTime.new(2013,1,1),
82
- :date => Date.new(2013,1,1),
86
+ :date_time => DateTime.new(2013, 1, 1),
87
+ :date => Date.new(2013, 1, 1),
83
88
  :nottime => 'x'
84
89
  }
85
90
  })
86
91
  message = queue.pop
87
92
 
88
- expect(message[:properties][:time]).to eq('2013-01-01T00:00:00.000Z')
89
- expect(message[:properties][:time_with_zone]).to eq('2013-01-01T00:00:00.000Z')
90
- expect(message[:properties][:date_time]).to eq('2013-01-01T00:00:00.000Z')
91
- expect(message[:properties][:date]).to eq('2013-01-01')
92
- expect(message[:properties][:nottime]).to eq('x')
93
+ properties = message[:properties]
94
+ expect(properties[:time]).to eq('2013-01-01T00:00:00.000Z')
95
+ expect(properties[:time_with_zone]).to eq('2013-01-01T00:00:00.000Z')
96
+ expect(properties[:date_time]).to eq('2013-01-01T00:00:00.000+00:00')
97
+ expect(properties[:date]).to eq('2013-01-01')
98
+ expect(properties[:nottime]).to eq('x')
93
99
  end
94
100
  end
95
101
 
96
-
97
102
  describe '#identify' do
98
103
  it 'errors without any user id' do
99
104
  expect { client.identify({}) }.to raise_error(ArgumentError)
@@ -119,19 +124,20 @@ module Segment
119
124
  :traits => {
120
125
  :time => Time.utc(2013),
121
126
  :time_with_zone => Time.zone.parse('2013-01-01'),
122
- :date_time => DateTime.new(2013,1,1),
123
- :date => Date.new(2013,1,1),
127
+ :date_time => DateTime.new(2013, 1, 1),
128
+ :date => Date.new(2013, 1, 1),
124
129
  :nottime => 'x'
125
130
  }
126
131
  })
127
132
 
128
133
  message = queue.pop
129
134
 
130
- expect(message[:traits][:time]).to eq('2013-01-01T00:00:00.000Z')
131
- expect(message[:traits][:time_with_zone]).to eq('2013-01-01T00:00:00.000Z')
132
- expect(message[:traits][:date_time]).to eq('2013-01-01T00:00:00.000Z')
133
- expect(message[:traits][:date]).to eq('2013-01-01')
134
- expect(message[:traits][:nottime]).to eq('x')
135
+ traits = message[:traits]
136
+ expect(traits[:time]).to eq('2013-01-01T00:00:00.000Z')
137
+ expect(traits[:time_with_zone]).to eq('2013-01-01T00:00:00.000Z')
138
+ expect(traits[:date_time]).to eq('2013-01-01T00:00:00.000+00:00')
139
+ expect(traits[:date]).to eq('2013-01-01')
140
+ expect(traits[:nottime]).to eq('x')
135
141
  end
136
142
  end
137
143
 
@@ -156,10 +162,6 @@ module Segment
156
162
  end
157
163
 
158
164
  describe '#group' do
159
- after do
160
- client.flush
161
- end
162
-
163
165
  it 'errors without group_id' do
164
166
  expect { client.group :user_id => 'foo' }.to raise_error(ArgumentError)
165
167
  end
@@ -183,19 +185,20 @@ module Segment
183
185
  :traits => {
184
186
  :time => Time.utc(2013),
185
187
  :time_with_zone => Time.zone.parse('2013-01-01'),
186
- :date_time => DateTime.new(2013,1,1),
187
- :date => Date.new(2013,1,1),
188
+ :date_time => DateTime.new(2013, 1, 1),
189
+ :date => Date.new(2013, 1, 1),
188
190
  :nottime => 'x'
189
191
  }
190
192
  })
191
193
 
192
194
  message = queue.pop
193
195
 
194
- expect(message[:traits][:time]).to eq('2013-01-01T00:00:00.000Z')
195
- expect(message[:traits][:time_with_zone]).to eq('2013-01-01T00:00:00.000Z')
196
- expect(message[:traits][:date_time]).to eq('2013-01-01T00:00:00.000Z')
197
- expect(message[:traits][:date]).to eq('2013-01-01')
198
- expect(message[:traits][:nottime]).to eq('x')
196
+ traits = message[:traits]
197
+ expect(traits[:time]).to eq('2013-01-01T00:00:00.000Z')
198
+ expect(traits[:time_with_zone]).to eq('2013-01-01T00:00:00.000Z')
199
+ expect(traits[:date_time]).to eq('2013-01-01T00:00:00.000+00:00')
200
+ expect(traits[:date]).to eq('2013-01-01')
201
+ expect(traits[:nottime]).to eq('x')
199
202
  end
200
203
  end
201
204
 
@@ -232,31 +235,35 @@ module Segment
232
235
  end
233
236
 
234
237
  describe '#flush' do
238
+ let(:client_with_worker) { Client.new(:write_key => WRITE_KEY) }
239
+
235
240
  it 'waits for the queue to finish on a flush' do
236
- client.identify Queued::IDENTIFY
237
- client.track Queued::TRACK
238
- client.flush
241
+ client_with_worker.identify Queued::IDENTIFY
242
+ client_with_worker.track Queued::TRACK
243
+ client_with_worker.flush
239
244
 
240
- expect(client.queued_messages).to eq(0)
245
+ expect(client_with_worker.queued_messages).to eq(0)
241
246
  end
242
247
 
243
- it 'completes when the process forks' do
244
- client.identify Queued::IDENTIFY
248
+ unless defined? JRUBY_VERSION
249
+ it 'completes when the process forks' do
250
+ client_with_worker.identify Queued::IDENTIFY
245
251
 
246
- Process.fork do
247
- client.track Queued::TRACK
248
- client.flush
249
- expect(client.queued_messages).to eq(0)
250
- end
252
+ Process.fork do
253
+ client_with_worker.track Queued::TRACK
254
+ client_with_worker.flush
255
+ expect(client_with_worker.queued_messages).to eq(0)
256
+ end
251
257
 
252
- Process.wait
253
- end unless defined? JRUBY_VERSION
258
+ Process.wait
259
+ end
260
+ end
254
261
  end
255
262
 
256
263
  context 'common' do
257
264
  check_property = proc { |msg, k, v| msg[k] && msg[k] == v }
258
265
 
259
- let(:data) { { :user_id => 1, :group_id => 2, :previous_id => 3, :anonymous_id => 4, :message_id => 5, :event => "coco barked", :name => "coco" } }
266
+ let(:data) { { :user_id => 1, :group_id => 2, :previous_id => 3, :anonymous_id => 4, :message_id => 5, :event => 'coco barked', :name => 'coco' } }
260
267
 
261
268
  it 'does not convert ids given as fixnums to strings' do
262
269
  [:track, :screen, :page, :identify].each do |s|
@@ -268,6 +275,16 @@ module Segment
268
275
  end
269
276
  end
270
277
 
278
+ it 'returns false if queue is full' do
279
+ client.instance_variable_set(:@max_queue_size, 1)
280
+
281
+ [:track, :screen, :page, :group, :identify, :alias].each do |s|
282
+ expect(client.send(s, data)).to eq(true)
283
+ expect(client.send(s, data)).to eq(false) # Queue is full
284
+ queue.pop(true)
285
+ end
286
+ end
287
+
271
288
  it 'converts message id to string' do
272
289
  [:track, :screen, :page, :group, :identify, :alias].each do |s|
273
290
  client.send(s, data)
@@ -299,7 +316,7 @@ module Segment
299
316
 
300
317
  it 'sends integrations' do
301
318
  [:track, :screen, :page, :group, :identify, :alias].each do |s|
302
- client.send s, :integrations => { :All => true, :Salesforce => false }, :user_id => 1, :group_id => 2, :previous_id => 3, :anonymous_id => 4, :event => "coco barked", :name => "coco"
319
+ client.send s, :integrations => { :All => true, :Salesforce => false }, :user_id => 1, :group_id => 2, :previous_id => 3, :anonymous_id => 4, :event => 'coco barked', :name => 'coco'
303
320
  message = queue.pop(true)
304
321
  expect(message[:integrations][:All]).to eq(true)
305
322
  expect(message[:integrations][:Salesforce]).to eq(false)
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ module Segment
4
+ # End-to-end tests that send events to a segment source and verifies that a
5
+ # webhook connected to the source (configured manually via the app) is able
6
+ # to receive the data sent by this library.
7
+ describe 'End-to-end tests', e2e: true do
8
+ # Segment write key for
9
+ # https://app.segment.com/segment-libraries/sources/analytics_ruby_e2e_test/overview.
10
+ #
11
+ # This source is configured to send events to the Runscope bucket used by
12
+ # this test.
13
+ WRITE_KEY = 'qhdMksLsQTi9MES3CHyzsWRRt4ub5VM6'
14
+
15
+ # Runscope bucket key for https://www.runscope.com/stream/umkvkgv7ndby
16
+ RUNSCOPE_BUCKET_KEY = 'umkvkgv7ndby'
17
+
18
+ let(:client) { Segment::Analytics.new(write_key: WRITE_KEY) }
19
+ let(:runscope_client) { RunscopeClient.new(ENV.fetch('RUNSCOPE_TOKEN')) }
20
+
21
+ it 'tracks events' do
22
+ id = SecureRandom.uuid
23
+ client.track(
24
+ user_id: 'dummy_user_id',
25
+ event: 'E2E Test',
26
+ properties: { id: id }
27
+ )
28
+ client.flush
29
+
30
+ # Allow events to propagate to runscope
31
+ eventually(timeout: 30) {
32
+ expect(has_matching_request?(id)).to eq(true)
33
+ }
34
+ end
35
+
36
+ def has_matching_request?(id)
37
+ captured_requests = runscope_client.requests(RUNSCOPE_BUCKET_KEY)
38
+ captured_requests.any? do |request|
39
+ begin
40
+ body = JSON.parse(request['body'])
41
+ body['properties'] && body['properties']['id'] == id
42
+ rescue JSON::ParserError
43
+ false
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ module Segment
4
+ class Analytics
5
+ describe MessageBatch do
6
+ subject { described_class.new(100) }
7
+
8
+ describe '#<<' do
9
+ it 'appends messages' do
10
+ subject << Message.new('a' => 'b')
11
+ expect(subject.length).to eq(1)
12
+ end
13
+
14
+ it 'rejects messages that exceed the maximum allowed size' do
15
+ max_bytes = Defaults::Message::MAX_BYTES
16
+ hash = { 'a' => 'b' * max_bytes }
17
+ message = Message.new(hash)
18
+
19
+ subject << message
20
+ expect(subject.length).to eq(0)
21
+ end
22
+ end
23
+
24
+ describe '#full?' do
25
+ it 'returns true once item count is exceeded' do
26
+ 99.times { subject << Message.new(a: 'b') }
27
+ expect(subject.full?).to be(false)
28
+
29
+ subject << Message.new(a: 'b')
30
+ expect(subject.full?).to be(true)
31
+ end
32
+
33
+ it 'returns true once max size is almost exceeded' do
34
+ message = Message.new(a: 'b' * (Defaults::Message::MAX_BYTES - 10))
35
+
36
+ # Each message is under the individual limit
37
+ expect(message.json_size).to be < Defaults::Message::MAX_BYTES
38
+
39
+ # Size of the batch is over the limit
40
+ expect(50 * message.json_size).to be > Defaults::MessageBatch::MAX_BYTES
41
+
42
+ expect(subject.full?).to be(false)
43
+ 50.times { subject << message }
44
+ expect(subject.full?).to be(true)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ module Segment
4
+ class Analytics
5
+ describe Message do
6
+ describe '#to_json' do
7
+ it 'caches JSON conversions' do
8
+ # Keeps track of the number of times to_json was called
9
+ nested_obj = Class.new do
10
+ attr_reader :to_json_call_count
11
+
12
+ def initialize
13
+ @to_json_call_count = 0
14
+ end
15
+
16
+ def to_json(*_)
17
+ @to_json_call_count += 1
18
+ '{}'
19
+ end
20
+ end.new
21
+
22
+ message = Message.new('some_key' => nested_obj)
23
+ expect(nested_obj.to_json_call_count).to eq(0)
24
+
25
+ message.to_json
26
+ expect(nested_obj.to_json_call_count).to eq(1)
27
+
28
+ # When called a second time, the call count shouldn't increase
29
+ message.to_json
30
+ expect(nested_obj.to_json_call_count).to eq(1)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -37,19 +37,25 @@ module Segment
37
37
 
38
38
  context 'no options are set' do
39
39
  it 'sets a default path' do
40
- expect(subject.instance_variable_get(:@path)).to eq(described_class::PATH)
40
+ path = subject.instance_variable_get(:@path)
41
+ expect(path).to eq(described_class::PATH)
41
42
  end
42
43
 
43
44
  it 'sets a default retries' do
44
- expect(subject.instance_variable_get(:@retries)).to eq(described_class::RETRIES)
45
+ retries = subject.instance_variable_get(:@retries)
46
+ expect(retries).to eq(described_class::RETRIES)
45
47
  end
46
48
 
47
- it 'sets a default backoff' do
48
- expect(subject.instance_variable_get(:@backoff)).to eq(described_class::BACKOFF)
49
+ it 'sets a default backoff policy' do
50
+ backoff_policy = subject.instance_variable_get(:@backoff_policy)
51
+ expect(backoff_policy).to be_a(Segment::Analytics::BackoffPolicy)
49
52
  end
50
53
 
51
54
  it 'initializes a new Net::HTTP with default host and port' do
52
- expect(Net::HTTP).to receive(:new).with(described_class::HOST, described_class::PORT)
55
+ expect(Net::HTTP).to receive(:new).with(
56
+ described_class::HOST,
57
+ described_class::PORT
58
+ )
53
59
  described_class.new
54
60
  end
55
61
  end
@@ -57,14 +63,14 @@ module Segment
57
63
  context 'options are given' do
58
64
  let(:path) { 'my/cool/path' }
59
65
  let(:retries) { 1234 }
60
- let(:backoff) { 10 }
66
+ let(:backoff_policy) { FakeBackoffPolicy.new([1, 2, 3]) }
61
67
  let(:host) { 'http://www.example.com' }
62
68
  let(:port) { 8080 }
63
69
  let(:options) do
64
70
  {
65
71
  path: path,
66
72
  retries: retries,
67
- backoff: backoff,
73
+ backoff_policy: backoff_policy,
68
74
  host: host,
69
75
  port: port
70
76
  }
@@ -80,8 +86,9 @@ module Segment
80
86
  expect(subject.instance_variable_get(:@retries)).to eq(retries)
81
87
  end
82
88
 
83
- it 'sets passed in backoff' do
84
- expect(subject.instance_variable_get(:@backoff)).to eq(backoff)
89
+ it 'sets passed in backoff backoff policy' do
90
+ expect(subject.instance_variable_get(:@backoff_policy))
91
+ .to eq(backoff_policy)
85
92
  end
86
93
 
87
94
  it 'initializes a new Net::HTTP with passed in host and port' do
@@ -92,7 +99,9 @@ module Segment
92
99
  end
93
100
 
94
101
  describe '#post' do
95
- let(:response) { Net::HTTPResponse.new(http_version, status_code, response_body) }
102
+ let(:response) {
103
+ Net::HTTPResponse.new(http_version, status_code, response_body)
104
+ }
96
105
  let(:http_version) { 1.1 }
97
106
  let(:status_code) { 200 }
98
107
  let(:response_body) { {}.to_json }
@@ -100,19 +109,29 @@ module Segment
100
109
  let(:batch) { [] }
101
110
 
102
111
  before do
103
- allow(subject.instance_variable_get(:@http)).to receive(:request) { response }
112
+ http = subject.instance_variable_get(:@http)
113
+ allow(http).to receive(:request) { response }
104
114
  allow(response).to receive(:body) { response_body }
105
115
  end
106
116
 
107
117
  it 'initalizes a new Net::HTTP::Post with path and default headers' do
108
118
  path = subject.instance_variable_get(:@path)
109
- default_headers = { 'Content-Type' => 'application/json', 'accept' => 'application/json' }
110
- expect(Net::HTTP::Post).to receive(:new).with(path, default_headers).and_call_original
119
+ default_headers = {
120
+ 'Content-Type' => 'application/json',
121
+ 'Accept' => 'application/json',
122
+ 'User-Agent' => "analytics-ruby/#{Analytics::VERSION}"
123
+ }
124
+ expect(Net::HTTP::Post).to receive(:new).with(
125
+ path, default_headers
126
+ ).and_call_original
127
+
111
128
  subject.post(write_key, batch)
112
129
  end
113
130
 
114
131
  it 'adds basic auth to the Net::HTTP::Post' do
115
- expect_any_instance_of(Net::HTTP::Post).to receive(:basic_auth).with(write_key, nil)
132
+ expect_any_instance_of(Net::HTTP::Post).to receive(:basic_auth)
133
+ .with(write_key, nil)
134
+
116
135
  subject.post(write_key, batch)
117
136
  end
118
137
 
@@ -136,6 +155,41 @@ module Segment
136
155
  end
137
156
 
138
157
  context 'a real request' do
158
+ RSpec.shared_examples('retried request') do |status_code, body|
159
+ let(:status_code) { status_code }
160
+ let(:body) { body }
161
+ let(:retries) { 4 }
162
+ let(:backoff_policy) { FakeBackoffPolicy.new([1000, 1000, 1000]) }
163
+ subject {
164
+ described_class.new(retries: retries,
165
+ backoff_policy: backoff_policy)
166
+ }
167
+
168
+ it 'retries the request' do
169
+ expect(subject)
170
+ .to receive(:sleep)
171
+ .exactly(retries - 1).times
172
+ .with(1)
173
+ .and_return(nil)
174
+ subject.post(write_key, batch)
175
+ end
176
+ end
177
+
178
+ RSpec.shared_examples('non-retried request') do |status_code, body|
179
+ let(:status_code) { status_code }
180
+ let(:body) { body }
181
+ let(:retries) { 4 }
182
+ let(:backoff) { 1 }
183
+ subject { described_class.new(retries: retries, backoff: backoff) }
184
+
185
+ it 'does not retry the request' do
186
+ expect(subject)
187
+ .to receive(:sleep)
188
+ .never
189
+ subject.post(write_key, batch)
190
+ end
191
+ end
192
+
139
193
  context 'request is successful' do
140
194
  let(:status_code) { 201 }
141
195
  it 'returns a response code' do
@@ -156,33 +210,32 @@ module Segment
156
210
  end
157
211
  end
158
212
 
159
- context 'request or parsing of response results in an exception' do
160
- let(:response_body) { 'Malformed JSON ---' }
213
+ context 'a request returns a failure status code' do
214
+ # Server errors must be retried
215
+ it_behaves_like('retried request', 500, '{}')
216
+ it_behaves_like('retried request', 503, '{}')
161
217
 
162
- let(:backoff) { 0 }
218
+ # All 4xx errors other than 429 (rate limited) must be retried
219
+ it_behaves_like('retried request', 429, '{}')
220
+ it_behaves_like('non-retried request', 404, '{}')
221
+ it_behaves_like('non-retried request', 400, '{}')
222
+ end
163
223
 
164
- subject { described_class.new(retries: retries, backoff: backoff) }
224
+ context 'request or parsing of response results in an exception' do
225
+ let(:response_body) { 'Malformed JSON ---' }
165
226
 
166
- context 'remaining retries is > 1' do
167
- let(:retries) { 2 }
227
+ subject { described_class.new(retries: 0) }
168
228
 
169
- it 'sleeps' do
170
- expect(subject).to receive(:sleep).exactly(retries - 1).times
171
- subject.post(write_key, batch)
172
- end
229
+ it 'returns a -1 for status' do
230
+ expect(subject.post(write_key, batch).status).to eq(-1)
173
231
  end
174
232
 
175
- context 'remaining retries is 1' do
176
- let(:retries) { 1 }
177
-
178
- it 'returns a -1 for status' do
179
- expect(subject.post(write_key, batch).status).to eq(-1)
180
- end
181
-
182
- it 'has a connection error' do
183
- expect(subject.post(write_key, batch).error).to match(/Connection error/)
184
- end
233
+ it 'has a connection error' do
234
+ error = subject.post(write_key, batch).error
235
+ expect(error).to match(/Connection error/)
185
236
  end
237
+
238
+ it_behaves_like('retried request', 200, 'Malformed JSON ---')
186
239
  end
187
240
  end
188
241
  end