analytics-ruby 2.2.3.pre → 2.2.4.pre

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,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