analytics-ruby 2.0.13 → 2.1.0

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,191 @@
1
+ require 'spec_helper'
2
+
3
+ module Segment
4
+ class Analytics
5
+ describe Request do
6
+ before do
7
+ # Try and keep debug statements out of tests
8
+ allow(subject.logger).to receive(:error)
9
+ allow(subject.logger).to receive(:debug)
10
+ end
11
+
12
+ describe '#initialize' do
13
+ let!(:net_http) { Net::HTTP.new(anything, anything) }
14
+
15
+ before do
16
+ allow(Net::HTTP).to receive(:new) { net_http }
17
+ end
18
+
19
+ it 'sets an initalized Net::HTTP read_timeout' do
20
+ expect(net_http).to receive(:use_ssl=)
21
+ described_class.new
22
+ end
23
+
24
+ it 'sets an initalized Net::HTTP read_timeout' do
25
+ expect(net_http).to receive(:read_timeout=)
26
+ described_class.new
27
+ end
28
+
29
+ it 'sets an initalized Net::HTTP open_timeout' do
30
+ expect(net_http).to receive(:open_timeout=)
31
+ described_class.new
32
+ end
33
+
34
+ it 'sets the http client' do
35
+ expect(subject.instance_variable_get(:@http)).to_not be_nil
36
+ end
37
+
38
+ context 'no options are set' do
39
+ it 'sets a default path' do
40
+ expect(subject.instance_variable_get(:@path)).to eq(described_class::PATH)
41
+ end
42
+
43
+ it 'sets a default retries' do
44
+ expect(subject.instance_variable_get(:@retries)).to eq(described_class::RETRIES)
45
+ end
46
+
47
+ it 'sets a default backoff' do
48
+ expect(subject.instance_variable_get(:@backoff)).to eq(described_class::BACKOFF)
49
+ end
50
+
51
+ 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)
53
+ described_class.new
54
+ end
55
+ end
56
+
57
+ context 'options are given' do
58
+ let(:path) { 'my/cool/path' }
59
+ let(:retries) { 1234 }
60
+ let(:backoff) { 10 }
61
+ let(:host) { 'http://www.example.com' }
62
+ let(:port) { 8080 }
63
+ let(:options) do
64
+ {
65
+ path: path,
66
+ retries: retries,
67
+ backoff: backoff,
68
+ host: host,
69
+ port: port
70
+ }
71
+ end
72
+
73
+ subject { described_class.new(options) }
74
+
75
+ it 'sets passed in path' do
76
+ expect(subject.instance_variable_get(:@path)).to eq(path)
77
+ end
78
+
79
+ it 'sets passed in retries' do
80
+ expect(subject.instance_variable_get(:@retries)).to eq(retries)
81
+ end
82
+
83
+ it 'sets passed in backoff' do
84
+ expect(subject.instance_variable_get(:@backoff)).to eq(backoff)
85
+ end
86
+
87
+ it 'initializes a new Net::HTTP with passed in host and port' do
88
+ expect(Net::HTTP).to receive(:new).with(host, port)
89
+ described_class.new(options)
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '#post' do
95
+ let(:response) { Net::HTTPResponse.new(http_version, status_code, response_body) }
96
+ let(:http_version) { 1.1 }
97
+ let(:status_code) { 200 }
98
+ let(:response_body) { {}.to_json }
99
+ let(:write_key) { 'abcdefg' }
100
+ let(:batch) { [] }
101
+
102
+ before do
103
+ allow(subject.instance_variable_get(:@http)).to receive(:request) { response }
104
+ allow(response).to receive(:body) { response_body }
105
+ end
106
+
107
+ it 'initalizes a new Net::HTTP::Post with path and default headers' do
108
+ 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
111
+ subject.post(write_key, batch)
112
+ end
113
+
114
+ 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)
116
+ subject.post(write_key, batch)
117
+ end
118
+
119
+ context 'with a stub' do
120
+ before do
121
+ allow(described_class).to receive(:stub) { true }
122
+ end
123
+
124
+ it 'returns a 200 response' do
125
+ expect(subject.post(write_key, batch).status).to eq(200)
126
+ end
127
+
128
+ it 'has a nil error' do
129
+ expect(subject.post(write_key, batch).error).to be_nil
130
+ end
131
+
132
+ it 'logs a debug statement' do
133
+ expect(subject.logger).to receive(:debug).with(/stubbed request to/)
134
+ subject.post(write_key, batch)
135
+ end
136
+ end
137
+
138
+ context 'a real request' do
139
+ context 'request is successful' do
140
+ let(:status_code) { 201 }
141
+ it 'returns a response code' do
142
+ expect(subject.post(write_key, batch).status).to eq(status_code)
143
+ end
144
+
145
+ it 'returns a nil error' do
146
+ expect(subject.post(write_key, batch).error).to be_nil
147
+ end
148
+ end
149
+
150
+ context 'request results in errorful response' do
151
+ let(:error) { 'this is an error' }
152
+ let(:response_body) { { error: error }.to_json }
153
+
154
+ it 'returns the parsed error' do
155
+ expect(subject.post(write_key, batch).error).to eq(error)
156
+ end
157
+ end
158
+
159
+ context 'request or parsing of response results in an exception' do
160
+ let(:response_body) { 'Malformed JSON ---' }
161
+
162
+ let(:backoff) { 0 }
163
+
164
+ subject { described_class.new(retries: retries, backoff: backoff) }
165
+
166
+ context 'remaining retries is > 1' do
167
+ let(:retries) { 2 }
168
+
169
+ it 'sleeps' do
170
+ expect(subject).to receive(:sleep).exactly(retries - 1).times
171
+ subject.post(write_key, batch)
172
+ end
173
+ end
174
+
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
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ module Segment
4
+ class Analytics
5
+ describe Response do
6
+ describe '#status' do
7
+ it { expect(subject).to respond_to(:status) }
8
+ end
9
+
10
+ describe '#error' do
11
+ it { expect(subject).to respond_to(:error) }
12
+ end
13
+
14
+ describe '#initialize' do
15
+ let(:status) { 404 }
16
+ let(:error) { 'Oh No' }
17
+
18
+ subject { described_class.new(status, error) }
19
+
20
+ it 'sets the instance variable status' do
21
+ expect(subject.instance_variable_get(:@status)).to eq(status)
22
+ end
23
+
24
+ it 'sets the instance variable error' do
25
+ expect(subject.instance_variable_get(:@error)).to eq(error)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -7,11 +7,11 @@ module Segment
7
7
  it 'accepts string keys' do
8
8
  queue = Queue.new
9
9
  worker = Segment::Analytics::Worker.new(queue, 'secret', 'batch_size' => 100)
10
- worker.instance_variable_get(:@batch_size).should == 100
10
+ expect(worker.instance_variable_get(:@batch_size)).to eq(100)
11
11
  end
12
12
  end
13
13
 
14
- describe '#flush' do
14
+ describe '#run' do
15
15
  before :all do
16
16
  Segment::Analytics::Defaults::Request::BACKOFF = 0.1
17
17
  end
@@ -20,75 +20,81 @@ module Segment
20
20
  Segment::Analytics::Defaults::Request::BACKOFF = 30.0
21
21
  end
22
22
 
23
- it 'should not error if the endpoint is unreachable' do
24
- Net::HTTP.any_instance.stub(:post).and_raise(Exception)
23
+ it 'does not error if the endpoint is unreachable' do
24
+ expect do
25
+ Net::HTTP.any_instance.stub(:post).and_raise(Exception)
25
26
 
26
- queue = Queue.new
27
- queue << {}
28
- worker = Segment::Analytics::Worker.new(queue, 'secret')
29
- worker.run
27
+ queue = Queue.new
28
+ queue << {}
29
+ worker = Segment::Analytics::Worker.new(queue, 'secret')
30
+ worker.run
30
31
 
31
- queue.should be_empty
32
+ expect(queue).to be_empty
32
33
 
33
- Net::HTTP.any_instance.unstub(:post)
34
+ Net::HTTP.any_instance.unstub(:post)
35
+ end.to_not raise_error
34
36
  end
35
37
 
36
- it 'should execute the error handler if the request is invalid' do
38
+ it 'executes the error handler, before the request phase ends, if the request is invalid' do
37
39
  Segment::Analytics::Request.any_instance.stub(:post).and_return(Segment::Analytics::Response.new(400, "Some error"))
38
40
 
39
- on_error = Proc.new do |status, error|
40
- puts "#{status}, #{error}"
41
+ status = error = nil
42
+ on_error = Proc.new do |yielded_status, yielded_error|
43
+ sleep 0.2 # Make this take longer than thread spin-up (below)
44
+ status, error = yielded_status, yielded_error
41
45
  end
42
46
 
43
- on_error.should_receive(:call).once
44
-
45
47
  queue = Queue.new
46
48
  queue << {}
47
49
  worker = Segment::Analytics::Worker.new queue, 'secret', :on_error => on_error
48
- worker.run
50
+
51
+ # This is to ensure that Client#flush doesn’t finish before calling the error handler.
52
+ Thread.new { worker.run }
53
+ sleep 0.1 # First give thread time to spin-up.
54
+ sleep 0.01 while worker.is_requesting?
49
55
 
50
56
  Segment::Analytics::Request::any_instance.unstub(:post)
51
57
 
52
- queue.should be_empty
58
+ expect(queue).to be_empty
59
+ expect(status).to eq(400)
60
+ expect(error).to eq('Some error')
53
61
  end
54
62
 
55
- it 'should not call on_error if the request is good' do
56
-
63
+ it 'does not call on_error if the request is good' do
57
64
  on_error = Proc.new do |status, error|
58
65
  puts "#{status}, #{error}"
59
66
  end
60
67
 
61
- on_error.should_receive(:call).at_most(0).times
68
+ expect(on_error).to_not receive(:call)
62
69
 
63
70
  queue = Queue.new
64
71
  queue << Requested::TRACK
65
72
  worker = Segment::Analytics::Worker.new queue, 'testsecret', :on_error => on_error
66
73
  worker.run
67
74
 
68
- queue.should be_empty
75
+ expect(queue).to be_empty
69
76
  end
70
77
  end
71
78
 
72
79
  describe '#is_requesting?' do
73
- it 'should not return true if there isn\'t a current batch' do
74
-
80
+ it 'does not return true if there isn\'t a current batch' do
75
81
  queue = Queue.new
76
82
  worker = Segment::Analytics::Worker.new(queue, 'testsecret')
77
83
 
78
- worker.is_requesting?.should == false
84
+ expect(worker.is_requesting?).to eq(false)
79
85
  end
80
86
 
81
- it 'should return true if there is a current batch' do
87
+ it 'returns true if there is a current batch' do
82
88
  queue = Queue.new
83
89
  queue << Requested::TRACK
84
90
  worker = Segment::Analytics::Worker.new(queue, 'testsecret')
85
91
 
86
92
  Thread.new do
87
93
  worker.run
88
- worker.is_requesting?.should == false
94
+ expect(worker.is_requesting?).to eq(false)
89
95
  end
90
96
 
91
- eventually { worker.is_requesting?.should be_true }
97
+ eventually { expect(worker.is_requesting?).to eq(true) }
92
98
  end
93
99
  end
94
100
  end
@@ -6,88 +6,99 @@ module Segment
6
6
  let(:analytics) { Segment::Analytics.new :write_key => WRITE_KEY, :stub => true }
7
7
 
8
8
  describe '#track' do
9
- it 'should error without an event' do
9
+ it 'errors without an event' do
10
10
  expect { analytics.track(:user_id => 'user') }.to raise_error(ArgumentError)
11
11
  end
12
12
 
13
- it 'should error without a user_id' do
13
+ it 'errors without a user_id' do
14
14
  expect { analytics.track(:event => 'Event') }.to raise_error(ArgumentError)
15
15
  end
16
16
 
17
- it 'should not error with the required options' do
18
- analytics.track Queued::TRACK
19
- sleep(1)
17
+ it 'does not error with the required options' do
18
+ expect do
19
+ analytics.track Queued::TRACK
20
+ sleep(1)
21
+ end.to_not raise_error
20
22
  end
21
23
  end
22
24
 
23
-
24
25
  describe '#identify' do
25
- it 'should error without a user_id' do
26
+ it 'errors without a user_id' do
26
27
  expect { analytics.identify :traits => {} }.to raise_error(ArgumentError)
27
28
  end
28
29
 
29
- it 'should not error with the required options' do
30
+ it 'does not error with the required options' do
30
31
  analytics.identify Queued::IDENTIFY
31
32
  sleep(1)
32
33
  end
33
34
  end
34
35
 
35
36
  describe '#alias' do
36
- it 'should error without from' do
37
+ it 'errors without from' do
37
38
  expect { analytics.alias :user_id => 1234 }.to raise_error(ArgumentError)
38
39
  end
39
40
 
40
- it 'should error without to' do
41
+ it 'errors without to' do
41
42
  expect { analytics.alias :previous_id => 1234 }.to raise_error(ArgumentError)
42
43
  end
43
44
 
44
- it 'should not error with the required options' do
45
- analytics.alias ALIAS
46
- sleep(1)
45
+ it 'does not error with the required options' do
46
+ expect do
47
+ analytics.alias ALIAS
48
+ sleep(1)
49
+ end.to_not raise_error
47
50
  end
48
51
  end
49
52
 
50
53
  describe '#group' do
51
- it 'should error without group_id' do
54
+ it 'errors without group_id' do
52
55
  expect { analytics.group :user_id => 'foo' }.to raise_error(ArgumentError)
53
56
  end
54
57
 
55
- it 'should error without user_id or anonymous_id' do
58
+ it 'errors without user_id or anonymous_id' do
56
59
  expect { analytics.group :group_id => 'foo' }.to raise_error(ArgumentError)
57
60
  end
58
61
 
59
- it 'should not error with the required options' do
60
- analytics.group Queued::GROUP
61
- sleep(1)
62
+ it 'does not error with the required options' do
63
+ expect do
64
+ analytics.group Queued::GROUP
65
+ sleep(1)
66
+ end.to_not raise_error
62
67
  end
63
68
  end
64
69
 
65
70
  describe '#page' do
66
- it 'should error without user_id or anonymous_id' do
71
+ it 'errors without user_id or anonymous_id' do
67
72
  expect { analytics.page :name => 'foo' }.to raise_error(ArgumentError)
68
73
  end
69
74
 
70
- it 'should not error with the required options' do
71
- analytics.page Queued::PAGE
72
- sleep(1)
75
+ it 'does not error with the required options' do
76
+ expect do
77
+ analytics.page Queued::PAGE
78
+ sleep(1)
79
+ end.to_not raise_error
73
80
  end
74
81
  end
75
82
 
76
83
  describe '#screen' do
77
- it 'should error without user_id or anonymous_id' do
84
+ it 'errors without user_id or anonymous_id' do
78
85
  expect { analytics.screen :name => 'foo' }.to raise_error(ArgumentError)
79
86
  end
80
87
 
81
- it 'should not error with the required options' do
82
- analytics.screen Queued::SCREEN
83
- sleep(1)
88
+ it 'does not error with the required options' do
89
+ expect do
90
+ analytics.screen Queued::SCREEN
91
+ sleep(1)
92
+ end.to_not raise_error
84
93
  end
85
94
  end
86
95
 
87
96
  describe '#flush' do
88
- it 'should flush without error' do
89
- analytics.identify Queued::IDENTIFY
90
- analytics.flush
97
+ it 'flushes without error' do
98
+ expect do
99
+ analytics.identify Queued::IDENTIFY
100
+ analytics.flush
101
+ end.to_not raise_error
91
102
  end
92
103
  end
93
104
  end