clever_tap_dubit 0.3.2

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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +48 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +16 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +164 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +8 -0
  11. data/bin/setup +8 -0
  12. data/clever_tap.gemspec +33 -0
  13. data/lib/clever_tap/client.rb +113 -0
  14. data/lib/clever_tap/config.rb +25 -0
  15. data/lib/clever_tap/entity.rb +87 -0
  16. data/lib/clever_tap/event.rb +30 -0
  17. data/lib/clever_tap/failed_response.rb +28 -0
  18. data/lib/clever_tap/profile.rb +7 -0
  19. data/lib/clever_tap/response.rb +34 -0
  20. data/lib/clever_tap/successful_response.rb +30 -0
  21. data/lib/clever_tap/uploader.rb +72 -0
  22. data/lib/clever_tap/version.rb +3 -0
  23. data/lib/clever_tap.rb +79 -0
  24. data/lib/clevertap-ruby.rb +1 -0
  25. data/spec/factories/profile.rb +36 -0
  26. data/spec/integrations/clever_tap_spec.rb +81 -0
  27. data/spec/rubocop_spec.rb +12 -0
  28. data/spec/shared/clever_tap_client.rb +13 -0
  29. data/spec/shared/entity.rb +105 -0
  30. data/spec/spec_helper.rb +18 -0
  31. data/spec/units/clever_tap_client_spec.rb +277 -0
  32. data/spec/units/clever_tap_spec.rb +88 -0
  33. data/spec/units/event_spec.rb +43 -0
  34. data/spec/units/failed_response_spec.rb +31 -0
  35. data/spec/units/profile_spec.rb +29 -0
  36. data/spec/units/response_spec.rb +63 -0
  37. data/spec/units/successful_response_spec.rb +112 -0
  38. data/spec/units/uploader_spec.rb +129 -0
  39. data/spec/vcr_cassettes/CleverTap/uploading_a_many_profiles/when_only_some_are_valid/partially_succeds.yml +42 -0
  40. data/spec/vcr_cassettes/CleverTap/uploading_a_profile/when_is_invalid/fails.yml +41 -0
  41. data/spec/vcr_cassettes/CleverTap/uploading_a_profile/when_is_valid/succeed.yml +35 -0
  42. data/spec/vcr_cassettes/CleverTap/uploading_an_event/when_is_valid/succeed.yml +34 -0
  43. data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_invalid_records/calls_on_failed_upload_once.yml +38 -0
  44. data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_invalid_records/returns_an_array_with_one_failed_Response_object.yml +38 -0
  45. data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_valid_records/and_objects_do_not_fit_upload_limit_/calls_on_successful_upload_proc_twice.yml +67 -0
  46. data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_valid_records/and_objects_do_not_fit_upload_limit_/returns_an_array_with_two_successful_Response_objects.yml +67 -0
  47. data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_valid_records/and_objects_fit_upload_limit_/calls_on_successful_upload_proc_once.yml +36 -0
  48. data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_valid_records/and_objects_fit_upload_limit_/returns_an_array_with_one_successful_Response_object.yml +36 -0
  49. data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_age_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
  50. data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_education_status_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +49 -0
  51. data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_email_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
  52. data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_employment_status_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
  53. data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_marital_status_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
  54. data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_phone_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
  55. data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_the_creation_date_field_is_missing/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
  56. data/spec/vcr_cassettes/CleverTap_Uploader/_call/with_invalid_credentials/failed_to_upload_the_profiles.yml +36 -0
  57. data/spec/vcr_cassettes/CleverTap_Uploader/_call/with_valid_data/makes_successful_upload.yml +36 -0
  58. data/spec/vcr_config.rb +13 -0
  59. metadata +192 -0
@@ -0,0 +1,277 @@
1
+ require 'spec_helper'
2
+ require 'shared/clever_tap_client'
3
+
4
+ describe CleverTap::Client, vcr: true do
5
+ subject { described_class.new('123456', 'passcode') }
6
+
7
+ describe 'authentication' do
8
+ it 'send right `account_id`' do
9
+ expect(subject.connection.headers['X-CleverTap-Account-Id']).to eq('123456')
10
+ end
11
+
12
+ it 'send right `passcode`' do
13
+ expect(subject.connection.headers['X-CleverTap-Passcode']).to eq('passcode')
14
+ end
15
+ end
16
+
17
+ describe 'customisations' do
18
+ context 'with different adapter' do
19
+ subject do
20
+ described_class.new('123456', 'passcode') { |config| config.adapter(:test) }
21
+ end
22
+
23
+ it 'override the default adapter' do
24
+ adapter = subject.connection.builder.adapter
25
+
26
+ expect(adapter).to eq(Faraday::Adapter::Test)
27
+ end
28
+ end
29
+
30
+ context 'without an adapter' do
31
+ subject { described_class.new('123456', 'passcode') }
32
+
33
+ it 'use Net::HTTP adapter' do
34
+ adapter = subject.connection.builder.adapter
35
+
36
+ expect(adapter).to eq(Faraday::Adapter::NetHttp)
37
+ end
38
+ end
39
+ end
40
+
41
+ describe '#new' do
42
+ let(:identity_field) { 'ID' }
43
+ let(:account_id) { 'ABC1234' }
44
+ let(:account_passcode) { 'AcCPasScoDe123' }
45
+
46
+ context 'when credentials set in `CleverTap.setup`' do
47
+ subject { CleverTap::Client.new }
48
+
49
+ before do
50
+ CleverTap.setup do |config|
51
+ config.identity_field = identity_field
52
+ config.account_id = account_id
53
+ config.account_passcode = account_passcode
54
+ end
55
+ end
56
+
57
+ it_behaves_like 'configured `Client`'
58
+ end
59
+
60
+ context 'when credentials set in `CleverTap::Client.new`' do
61
+ subject { CleverTap::Client.new(account_id, account_passcode) }
62
+ it_behaves_like 'configured `Client`'
63
+ end
64
+ end
65
+
66
+ def event_factory(id, name)
67
+ CleverTap::Event.new(
68
+ data: { 'FBID' => id.to_s, 'Name' => name.to_s },
69
+ name: 'Web Event',
70
+ identity: 'FBID'
71
+ )
72
+ end
73
+
74
+ def profile_factory
75
+ CleverTap::Profile.new(
76
+ data: { 'ID' => '1414', 'Name' => 'John', 'Phone' => '+44+441234' }
77
+ )
78
+ end
79
+
80
+ describe '#upload' do
81
+ let(:success_proc) { proc { 'sample proc' } }
82
+ let(:response) { subject.upload([event1, event2]) }
83
+
84
+ subject do
85
+ client = described_class.new
86
+ client.on_successful_upload(&success_proc)
87
+ client
88
+ end
89
+
90
+ before do
91
+ CleverTap.setup do |c|
92
+ c.identity_field = 'ID'
93
+ c.account_id = AUTH_ACCOUNT_ID
94
+ c.account_passcode = AUTH_PASSCODE
95
+ end
96
+ end
97
+
98
+ context 'when upload records are homogenous' do
99
+ context 'and valid records' do
100
+ let(:event1) { event_factory('1414', 'John') }
101
+ let(:event2) { event_factory('1515', 'Jill') }
102
+
103
+ context 'and objects fit `upload_limit`' do
104
+ VCR.use_cassette('upload within Event limit') do
105
+ it 'returns an array with one successful `Response` object' do
106
+ expect(response.count).to eq 1
107
+ expect(response.first.success).to be true
108
+ expect(response.first).to be_a(CleverTap::Response)
109
+ end
110
+
111
+ it 'calls `on_successful_upload` proc once' do
112
+ expect(success_proc).to receive(:call).once
113
+ subject.upload([event1, event2])
114
+ end
115
+ end
116
+ end
117
+
118
+ context 'and objects do not fit `upload_limit`' do
119
+ before do
120
+ allow(CleverTap::Event)
121
+ .to receive(:upload_limit).and_return(1)
122
+ end
123
+
124
+ VCR.use_cassette('upload out of Event limit') do
125
+ it 'returns an array with two successful `Response` objects' do
126
+ expect(response.count).to eq 2
127
+ expect(response.all?(&:success)).to be true
128
+ expect(
129
+ response.all? { |r| r.class == CleverTap::Response }
130
+ ).to be true
131
+ end
132
+
133
+ it 'calls `on_successful_upload` proc twice' do
134
+ expect(success_proc).to receive(:call).twice
135
+ subject.upload([event1, event2])
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ context 'and invalid records' do
142
+ let(:failure_proc) { proc { 'sample proc' } }
143
+ let(:response) { subject.upload([profile]) }
144
+
145
+ subject do
146
+ client = described_class.new
147
+ client.on_failed_upload(&failure_proc)
148
+ client
149
+ end
150
+
151
+ let(:profile) { profile_factory }
152
+
153
+ it 'returns an array with one failed `Response` object' do
154
+ expect(response.count).to eq 1
155
+ expect(response.first.class).to eq CleverTap::Response
156
+ expect(response.first.success).to be false
157
+ end
158
+
159
+ it 'calls `on_failed_upload` once' do
160
+ expect(failure_proc).to receive(:call).once
161
+ subject.upload([profile])
162
+ end
163
+ end
164
+ end
165
+
166
+ context 'when objects are not homogenous' do
167
+ let(:event1) { event_factory('1414', 'John') }
168
+ let(:event2) { {} }
169
+
170
+ it 'raises `NotConsistentArrayError`' do
171
+ expect { subject.upload([event1, event2]) }.to raise_error CleverTap::NotConsistentArrayError
172
+ end
173
+ end
174
+ end
175
+
176
+ describe '#request_body' do
177
+ subject { described_class.new('123456', 'passcode') }
178
+ let(:records) do
179
+ [
180
+ { 'ID' => '123', 'Name' => 'John' },
181
+ { 'ID' => '456', 'Name' => 'Jill' }
182
+ ]
183
+ end
184
+
185
+ it 'converts records hash to json' do
186
+ expect(subject.send(:request_body, records))
187
+ .to eq({ 'd' => records }.to_json)
188
+ end
189
+ end
190
+
191
+ describe '#determine_type' do
192
+ subject { described_class.new('123456', 'passcode') }
193
+
194
+ context 'when records are of the same type' do
195
+ let(:records) { [{}, {}] }
196
+
197
+ it 'returns the class of the elements' do
198
+ expect(subject.send(:determine_type, records)).to eq Hash
199
+ end
200
+ end
201
+
202
+ context 'when records are of different type' do
203
+ let(:records) { [{}, []] }
204
+
205
+ it 'raises `NotConsistentArrayError`' do
206
+ expect { subject.send(:determine_type, records) }.to raise_error CleverTap::NotConsistentArrayError
207
+ end
208
+ end
209
+ end
210
+
211
+ describe '#ensure_array' do
212
+ subject { described_class.new('123456', 'passcode') }
213
+ let(:records) { %w[sample sample2] }
214
+ let(:record) { 'sample' }
215
+
216
+ it 'returns an array when an array passed' do
217
+ expect(subject.send(:ensure_array, records)).to eq records
218
+ end
219
+
220
+ it 'returns an array when a single element passed' do
221
+ expect(subject.send(:ensure_array, record)).to eq [record]
222
+ end
223
+ end
224
+
225
+ describe 'setting `account_id` and `passcode`' do
226
+ subject { described_class.new(client_id, client_passcode) }
227
+ let(:client_id) { '123456' }
228
+ let(:client_passcode) { 'passcode' }
229
+ let(:config_id) { 'config_account_id' }
230
+ let(:config_passcode) { 'config_passcode' }
231
+
232
+ def config_client(account, pass)
233
+ CleverTap.setup do |c|
234
+ c.account_id = account
235
+ c.account_passcode = pass
236
+ end
237
+ end
238
+
239
+ context 'when credentials provided in configuration' do
240
+ before { config_client(config_id, config_passcode) }
241
+
242
+ context 'and in initialization as well' do
243
+ it 'has initialization values' do
244
+ expect(subject.send(:assign_account_id, client_id)).to eq client_id
245
+ expect(subject.send(:assign_passcode, client_passcode)).to eq client_passcode
246
+ end
247
+ end
248
+
249
+ context 'and not in initialization' do
250
+ it 'has initialization values' do
251
+ expect(subject.send(:assign_account_id, nil)).to eq config_id
252
+ expect(subject.send(:assign_passcode, nil)).to eq config_passcode
253
+ end
254
+ end
255
+ end
256
+
257
+ context 'when credentials not provided in configuration' do
258
+ before { config_client(nil, nil) }
259
+
260
+ context 'but provided in initialization' do
261
+ it 'has initialization values' do
262
+ expect(subject.send(:assign_account_id, client_id)).to eq client_id
263
+ expect(subject.send(:assign_passcode, client_passcode)).to eq client_passcode
264
+ end
265
+ end
266
+
267
+ context 'and not provided in initialization as well' do
268
+ before { config_client(nil, nil) }
269
+ subject { described_class.new }
270
+
271
+ it 'raises a `RuntimeError` error' do
272
+ expect { subject }.to raise_error RuntimeError
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CleverTap do
4
+ describe '#new' do
5
+ context 'with valid arguments' do
6
+ context 'with keyword arguments' do
7
+ subject(:clever_tap) do
8
+ CleverTap.new(account_id: 'foo',
9
+ passcode: 'passcode',
10
+ identity_field: 'ID',
11
+ configure_faraday: configure_faraday)
12
+ end
13
+
14
+ let(:configure_faraday) { proc {} }
15
+
16
+ it('persist the account_id') { expect(clever_tap.config.account_id).to eq('foo') }
17
+ it('persist the passcode') { expect(clever_tap.config.passcode).to eq('passcode') }
18
+ it('persist the identity field') { expect(clever_tap.config.identity_field).to eq('ID') }
19
+ it('persist the faraday config') { expect(clever_tap.config.configure_faraday).to eq(configure_faraday) }
20
+ end
21
+
22
+ context 'with block' do
23
+ subject(:clever_tap) do
24
+ CleverTap.new do |config|
25
+ config.account_id = 'foo'
26
+ config.passcode = 'passcode'
27
+ config.identity_field = 'ID'
28
+ config.configure_faraday(&configure_faraday)
29
+ end
30
+ end
31
+
32
+ let(:configure_faraday) { proc {} }
33
+
34
+ it('persist the account_id') { expect(clever_tap.config.account_id).to eq('foo') }
35
+ it('persist the passcode') { expect(clever_tap.config.passcode).to eq('passcode') }
36
+ it('persist the identity field') { expect(clever_tap.config.identity_field).to eq('ID') }
37
+ it('persist the faraday config') { expect(clever_tap.config.configure_faraday).to eq(configure_faraday) }
38
+ end
39
+ end
40
+
41
+ context 'with invalid arguments' do
42
+ it 'require `account_id`' do
43
+ expect { CleverTap.new(passcode: 'foo') }
44
+ .to raise_error(RuntimeError, /account_id/)
45
+ end
46
+
47
+ it 'require `passcode`' do
48
+ expect { CleverTap.new(account_id: 'bar') }
49
+ .to raise_error(RuntimeError, /passcode/)
50
+ end
51
+ end
52
+ end
53
+
54
+ describe '#client' do
55
+ subject(:clever_tap) { CleverTap.new(account_id: 'foo', passcode: 'passcode', &configure_faraday) }
56
+ let(:configure_faraday) { proc { |_faraday| } }
57
+
58
+ it 'initialize client with a right auth data' do
59
+ expect(CleverTap::Client).to receive(:new).with('foo', 'passcode', &configure_faraday)
60
+
61
+ clever_tap.client
62
+ end
63
+
64
+ it 'cache the client between calls' do
65
+ id = clever_tap.client.object_id
66
+ expect(clever_tap.client.object_id).to eq(id)
67
+ end
68
+ end
69
+
70
+ describe '.config' do
71
+ let(:identity_field) { 'ID' }
72
+ let(:account_id) { 'ABC1234' }
73
+ let(:account_passcode) { 'AcCPasScoDe123' }
74
+ let(:remove_identity) { true }
75
+
76
+ it 'sets config variables' do
77
+ described_class.setup do |config|
78
+ config.identity_field = identity_field
79
+ config.account_id = account_id
80
+ config.account_passcode = account_passcode
81
+ end
82
+
83
+ expect(described_class.identity_field).to eq identity_field
84
+ expect(described_class.account_id).to eq account_id
85
+ expect(described_class.account_passcode).to eq account_passcode
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'shared/entity'
3
+
4
+ RSpec.describe CleverTap::Event do
5
+ describe '.upload_limit' do
6
+ subject { described_class.upload_limit }
7
+ it { is_expected.to eq 1000 }
8
+ end
9
+
10
+ describe '#to_h' do
11
+ subject { described_class.new(**params).to_h }
12
+
13
+ describe 'choosing `identity`' do
14
+ it_behaves_like 'choosing identity for', 'event'
15
+ end
16
+
17
+ describe 'choosing timestamp' do
18
+ it_behaves_like 'choosing timestamp'
19
+ end
20
+
21
+ describe 'event name' do
22
+ let(:data) { { 'FBID' => '1414', 'Name' => 'John' } }
23
+ let(:params) { { data: data, identity: 'FBID' } }
24
+
25
+ context 'when `name` is not provided' do
26
+ it { expect { subject }.to raise_error(CleverTap::MissingEventNameError) }
27
+ end
28
+
29
+ context 'when `name` is provided' do
30
+ let!(:params_ext) { params.merge!(name: 'Web Event') }
31
+ it { is_expected.to include 'evtName' => 'Web Event' }
32
+ end
33
+ end
34
+
35
+ describe 'type' do
36
+ it_behaves_like 'proper type'
37
+ end
38
+
39
+ describe 'data' do
40
+ it_behaves_like 'constructing data for', 'event'
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe CleverTap::FailedResponse do
4
+ let(:records) { [{ 'id' => 1 }, { 'id' => 2 }] }
5
+ let(:message) { 'LOL an error' }
6
+ let(:code) { 401 }
7
+
8
+ subject { described_class.new(records: records, message: message, code: code) }
9
+
10
+ describe '#status' do
11
+ it { expect(subject.status).to eq 'fail' }
12
+ end
13
+
14
+ describe '#success' do
15
+ it { expect(subject.success).to be false }
16
+ end
17
+
18
+ describe '#errors' do
19
+ it do
20
+ error = { 'status' => 'fail', 'code' => code, 'error' => message }
21
+
22
+ expect(subject.errors).to match_array(
23
+ records.map { |r| error.merge('record' => r) }
24
+ )
25
+ end
26
+ end
27
+
28
+ describe '#message' do
29
+ it { expect(subject.message).to eq message }
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'shared/entity'
3
+
4
+ RSpec.describe CleverTap::Profile do
5
+ describe '.upload_limit' do
6
+ subject { described_class.upload_limit }
7
+ it { is_expected.to eq 100 }
8
+ end
9
+
10
+ describe '#to_h' do
11
+ subject { described_class.new(**params).to_h }
12
+
13
+ describe 'choosing `identity`' do
14
+ it_behaves_like 'choosing identity for', 'profile'
15
+ end
16
+
17
+ describe 'choosing timestamp' do
18
+ it_behaves_like 'choosing timestamp'
19
+ end
20
+
21
+ describe 'type' do
22
+ it_behaves_like 'proper type'
23
+ end
24
+
25
+ describe 'data' do
26
+ it_behaves_like 'constructing data for', 'profile'
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe CleverTap::Response do
4
+ let(:success) do
5
+ { 'status' => 'success', 'processed' => 1, 'unprocessed' => [] }
6
+ end
7
+
8
+ let(:partial) do
9
+ {
10
+ 'status' => 'success',
11
+ 'processed' => 1,
12
+ 'unprocessed' => [{ '{ "ID" => "5", "Name": "John"}' => 'Some error' }]
13
+ }
14
+ end
15
+
16
+ let(:failure) do
17
+ {
18
+ 'status' => 'fail',
19
+ 'error' => 'Account Id not valid',
20
+ 'code' => 401
21
+ }
22
+ end
23
+
24
+ let(:non_json) do
25
+ '<head><title>502 Bad Gateway</title></head>'\
26
+ '<body>' \
27
+ '<center><h1>502 Bad Gateway</h1></center>'\
28
+ '</body>'\
29
+ '</html>'
30
+ end
31
+
32
+ describe '#new' do
33
+ subject { described_class.new(response) }
34
+
35
+ context 'when successful request' do
36
+ let(:response) { OpenStruct.new(body: success.to_json, success?: true) }
37
+
38
+ it { expect(subject.success).to be true }
39
+ it { expect(subject.failures).to eq [] }
40
+ end
41
+
42
+ context 'when partially successful request' do
43
+ let(:response) { OpenStruct.new(body: partial.to_json, success?: true) }
44
+
45
+ it { expect(subject.success).to be false }
46
+ it { expect(subject.failures).to eq partial['unprocessed'] }
47
+ end
48
+
49
+ context 'when failed request' do
50
+ let(:response) { OpenStruct.new(body: failure.to_json, success?: false) }
51
+
52
+ it { expect(subject.success).to be false }
53
+ it { expect(subject.failures).to eq [failure] }
54
+ end
55
+
56
+ context 'when non json response' do
57
+ let(:response) { OpenStruct.new(body: non_json, success?: false) }
58
+
59
+ it { expect(subject.success).to be false }
60
+ it { expect(subject.failures).to eq [{ resp_string: non_json.to_s }.to_json] }
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ describe CleverTap::SuccessfulResponse do
4
+ shared_context 'successful state' do
5
+ let(:raw_response) do
6
+ { 'status' => 'success', 'processed' => 2, 'unprocessed' => [] }
7
+ end
8
+ end
9
+
10
+ shared_context 'partial state' do
11
+ let(:records) { [{ 'id' => 1 }] }
12
+ let(:raw_response) do
13
+ {
14
+ 'status' => 'fail',
15
+ 'processed' => 1,
16
+ 'unprocessed' => records.map { |r| { 'record' => r.to_json } }
17
+ }
18
+ end
19
+ end
20
+
21
+ shared_context 'fail state' do
22
+ let(:records) { [{ 'id' => 1 }, { 'id' => 2 }] }
23
+ let(:raw_response) do
24
+ {
25
+ 'status' => 'fail',
26
+ 'processed' => 0,
27
+ 'unprocessed' => records.map { |r| { 'record' => r.to_json } }
28
+ }
29
+ end
30
+ end
31
+
32
+ subject { described_class.new(raw_response) }
33
+
34
+ describe '#status' do
35
+ context 'with successful status' do
36
+ include_context 'successful state'
37
+
38
+ it { expect(subject.status).to eq 'success' }
39
+ end
40
+
41
+ context 'with partial status' do
42
+ include_context 'partial state'
43
+
44
+ it { expect(subject.status).to eq 'partial' }
45
+ end
46
+
47
+ context 'with fail status' do
48
+ include_context 'fail state'
49
+
50
+ it { expect(subject.status).to eq 'fail' }
51
+ end
52
+ end
53
+
54
+ describe '#success' do
55
+ context 'with successful status' do
56
+ include_context 'successful state'
57
+
58
+ it { expect(subject.success).to be true }
59
+ end
60
+
61
+ context 'with partial status' do
62
+ include_context 'partial state'
63
+
64
+ it { expect(subject.success).to be false }
65
+ end
66
+
67
+ context 'with fail status' do
68
+ include_context 'fail state'
69
+
70
+ it { expect(subject.success).to be false }
71
+ end
72
+ end
73
+
74
+ describe '#errors' do
75
+ context 'with successful status' do
76
+ include_context 'successful state'
77
+
78
+ it { expect(subject.errors).to be_empty }
79
+ end
80
+
81
+ context 'with partial status' do
82
+ include_context 'partial state'
83
+ it { expect(subject.errors).to all(include('record')) }
84
+ end
85
+
86
+ context 'with fail status' do
87
+ include_context 'fail state'
88
+
89
+ it { expect(subject.errors).to all(include('record')) }
90
+ end
91
+ end
92
+
93
+ describe '#message' do
94
+ context 'with successful status' do
95
+ include_context 'successful state'
96
+
97
+ it { expect(subject.message).to eq '' }
98
+ end
99
+
100
+ context 'with partial status' do
101
+ include_context 'partial state'
102
+
103
+ it { expect(subject.message).to eq '' }
104
+ end
105
+
106
+ context 'with fail status' do
107
+ include_context 'fail state'
108
+
109
+ it { expect(subject.message).to eq '' }
110
+ end
111
+ end
112
+ end