rdstation-ruby-client 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ module RDStation
2
+ class ErrorHandler
3
+ class ExpiredCodeGrant
4
+ attr_reader :api_response, :response_headers, :error
5
+
6
+ ERROR_CODE = 'EXPIRED_CODE_GRANT'.freeze
7
+ EXCEPTION_CLASS = RDStation::Error::ExpiredCodeGrant
8
+
9
+ def initialize(api_response)
10
+ @api_response = api_response
11
+ @error = JSON.parse(api_response.body)['errors']
12
+ @response_headers = api_response.headers
13
+ end
14
+
15
+ def raise_error
16
+ return unless expired_code?
17
+ raise EXCEPTION_CLASS.new(error['error_message'], api_response)
18
+ end
19
+
20
+ private
21
+
22
+ def expired_code?
23
+ error['error_type'] == ERROR_CODE
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ module RDStation
2
+ class ErrorHandler
3
+ class InvalidCredentials
4
+ attr_reader :api_response, :error
5
+
6
+ ERROR_CODE = 'ACCESS_DENIED'.freeze
7
+ EXCEPTION_CLASS = RDStation::Error::InvalidCredentials
8
+
9
+ def initialize(api_response)
10
+ @api_response = api_response
11
+ @error = JSON.parse(api_response.body)['errors']
12
+ end
13
+
14
+ def raise_error
15
+ return unless credentials_errors?
16
+ raise EXCEPTION_CLASS.new(error['error_message'], api_response)
17
+ end
18
+
19
+ private
20
+
21
+ def credentials_errors?
22
+ error['error_type'] == ERROR_CODE
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module RDStation
2
+ class ErrorHandler
3
+ class ResourceNotFound
4
+ attr_reader :api_response, :error
5
+
6
+ ERROR_CODE = 'RESOURCE_NOT_FOUND'.freeze
7
+ EXCEPTION_CLASS = RDStation::Error::ResourceNotFound
8
+
9
+ def initialize(api_response)
10
+ @api_response = api_response
11
+ @error = JSON.parse(api_response.body)['errors']
12
+ end
13
+
14
+ def raise_error
15
+ return unless resource_not_found?
16
+ raise EXCEPTION_CLASS.new(error['error_message'], api_response)
17
+ end
18
+
19
+ private
20
+
21
+ def resource_not_found?
22
+ error['error_type'] == ERROR_CODE
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module RDStation
2
+ class ErrorHandler
3
+ class Unauthorized
4
+ attr_reader :api_response, :response_body, :error
5
+
6
+ ERROR_CODE = 'UNAUTHORIZED'.freeze
7
+ EXCEPTION_CLASS = RDStation::Error::Unauthorized
8
+
9
+ def initialize(api_response)
10
+ @api_response = api_response
11
+ @error = JSON.parse(api_response.body)['errors']
12
+ end
13
+
14
+ def raise_error
15
+ return unless unauthorized?
16
+ raise EXCEPTION_CLASS.new(error['error_message'], api_response)
17
+ end
18
+
19
+ private
20
+
21
+ def unauthorized?
22
+ error['error_type'] == ERROR_CODE
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ module RDStation
3
+ # More info: https://developers.rdstation.com/pt-BR/reference/contacts
4
+ class Fields
5
+ include HTTParty
6
+
7
+ BASE_URL = 'https://api.rd.services/platform/contacts/fields'.freeze
8
+
9
+ def initialize(auth_token)
10
+ @auth_token = auth_token
11
+ end
12
+
13
+ def all
14
+ response = self.class.get(BASE_URL, headers: required_headers)
15
+ response_body = JSON.parse(response.body)
16
+ return response_body unless response_body['errors']
17
+ RDStation::ErrorHandler.new(response).raise_errors
18
+ end
19
+
20
+ private
21
+
22
+ def required_headers
23
+ { "Authorization" => "Bearer #{@auth_token}", "Content-Type" => "application/json" }
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module RDStation
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '1.0.0'.freeze
3
3
  end
@@ -21,8 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency 'rspec'
24
- spec.add_development_dependency 'vcr'
25
- spec.add_development_dependency 'webmock'
24
+ spec.add_development_dependency 'webmock', '~> 2.1'
26
25
  spec.add_development_dependency 'turn'
27
26
 
28
27
  spec.add_dependency "httparty", "~> 0.12"
@@ -0,0 +1,177 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RDStation::Authentication do
4
+ let(:token_endpoint) { 'https://api.rd.services/auth/token' }
5
+ let(:request_headers) { { 'Content-Type' => 'application/json' } }
6
+
7
+ let(:token_request_with_valid_code) do
8
+ {
9
+ client_id: 'client_id',
10
+ client_secret: 'client_secret',
11
+ code: 'valid_code'
12
+ }
13
+ end
14
+
15
+ let(:token_request_with_invalid_code) do
16
+ {
17
+ client_id: 'client_id',
18
+ client_secret: 'client_secret',
19
+ code: 'invalid_code'
20
+ }
21
+ end
22
+
23
+ let(:token_request_with_expired_code) do
24
+ {
25
+ client_id: 'client_id',
26
+ client_secret: 'client_secret',
27
+ code: 'expired_code'
28
+ }
29
+ end
30
+
31
+ let(:token_request_with_valid_refresh_token) do
32
+ {
33
+ client_id: 'client_id',
34
+ client_secret: 'client_secret',
35
+ refresh_token: 'valid_refresh_token'
36
+ }
37
+ end
38
+
39
+ let(:token_request_with_invalid_refresh_token) do
40
+ {
41
+ client_id: 'client_id',
42
+ client_secret: 'client_secret',
43
+ refresh_token: 'invalid_refresh_token'
44
+ }
45
+ end
46
+
47
+ let(:credentials) do
48
+ {
49
+ 'access_token' => '123456',
50
+ 'expires_in' => 86_400,
51
+ 'refresh_token' => 'refreshtoken'
52
+ }
53
+ end
54
+
55
+ let(:credentials_response) do
56
+ {
57
+ status: 200,
58
+ headers: { 'Content-Type' => 'application/json' },
59
+ body: credentials.to_json
60
+ }
61
+ end
62
+
63
+ let(:invalid_code_response) do
64
+ {
65
+ status: 401,
66
+ headers: { 'Content-Type' => 'application/json' },
67
+ body: {
68
+ errors: {
69
+ error_type: 'ACCESS_DENIED',
70
+ error_message: 'Wrong credentials provided.'
71
+ }
72
+ }.to_json
73
+ }
74
+ end
75
+
76
+ let(:expired_code_response) do
77
+ {
78
+ status: 401,
79
+ headers: { 'Content-Type' => 'application/json' },
80
+ body: {
81
+ errors: {
82
+ error_type: 'EXPIRED_CODE_GRANT',
83
+ error_message: 'The authorization code grant has expired.'
84
+ }
85
+ }.to_json
86
+ }
87
+ end
88
+
89
+ let(:authentication) { described_class.new('client_id', 'client_secret') }
90
+
91
+ describe '#authenticate' do
92
+ context 'when the code is valid' do
93
+ before do
94
+ stub_request(:post, token_endpoint)
95
+ .with(
96
+ headers: request_headers,
97
+ body: token_request_with_valid_code.to_json
98
+ )
99
+ .to_return(credentials_response)
100
+ end
101
+
102
+ it 'returns the credentials' do
103
+ credentials_request = authentication.authenticate('valid_code')
104
+ expect(credentials_request).to eq(credentials)
105
+ end
106
+ end
107
+
108
+ context 'when the code is invalid' do
109
+ before do
110
+ stub_request(:post, token_endpoint)
111
+ .with(
112
+ headers: request_headers,
113
+ body: token_request_with_invalid_code.to_json
114
+ )
115
+ .to_return(invalid_code_response)
116
+ end
117
+
118
+ it 'returns an auth error' do
119
+ expect do
120
+ authentication.authenticate('invalid_code')
121
+ end.to raise_error(RDStation::Error::InvalidCredentials)
122
+ end
123
+ end
124
+
125
+ context 'when the code has expired' do
126
+ before do
127
+ stub_request(:post, token_endpoint)
128
+ .with(
129
+ headers: request_headers,
130
+ body: token_request_with_expired_code.to_json
131
+ )
132
+ .to_return(expired_code_response)
133
+ end
134
+
135
+ it 'returns an expired code error' do
136
+ expect do
137
+ authentication.authenticate('expired_code')
138
+ end.to raise_error(RDStation::Error::ExpiredCodeGrant)
139
+ end
140
+ end
141
+ end
142
+
143
+ describe '#update_access_token' do
144
+ context 'when the refresh token is valid' do
145
+ before do
146
+ stub_request(:post, token_endpoint)
147
+ .with(
148
+ headers: request_headers,
149
+ body: token_request_with_valid_refresh_token.to_json
150
+ )
151
+ .to_return(credentials_response)
152
+ end
153
+
154
+ it 'returns the credentials' do
155
+ credentials_request = authentication.update_access_token('valid_refresh_token')
156
+ expect(credentials_request).to eq(credentials)
157
+ end
158
+ end
159
+
160
+ context 'when the refresh token is invalid' do
161
+ before do
162
+ stub_request(:post, token_endpoint)
163
+ .with(
164
+ headers: request_headers,
165
+ body: token_request_with_invalid_refresh_token.to_json
166
+ )
167
+ .to_return(invalid_code_response)
168
+ end
169
+
170
+ it 'returns an auth error' do
171
+ expect do
172
+ authentication.update_access_token('invalid_refresh_token')
173
+ end.to raise_error(RDStation::Error::InvalidCredentials)
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,434 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RDStation::Contacts do
4
+ let(:valid_uuid) { 'valid_uuid' }
5
+ let(:invalid_uuid) { 'invalid_uuid' }
6
+ let(:valid_email) { 'valid@email.com' }
7
+ let(:invalid_email) { 'invalid@email.com' }
8
+
9
+ let(:endpoint_with_valid_uuid) { "https://api.rd.services/platform/contacts/#{valid_uuid}" }
10
+ let(:endpoint_with_invalid_uuid) { "https://api.rd.services/platform/contacts/#{invalid_uuid}" }
11
+ let(:endpoint_with_valid_email) { "https://api.rd.services/platform/contacts/email:#{valid_email}" }
12
+ let(:endpoint_with_invalid_email) { "https://api.rd.services/platform/contacts/email:#{invalid_email}" }
13
+
14
+ let(:valid_auth_token) { 'valid_auth_token' }
15
+ let(:invalid_auth_token) { 'invalid_auth_token' }
16
+ let(:expired_auth_token) { 'expired_auth_token' }
17
+
18
+ let(:contact_with_valid_token) { described_class.new(valid_auth_token) }
19
+ let(:contact_with_expired_token) { described_class.new(expired_auth_token) }
20
+ let(:contact_with_invalid_token) { described_class.new(invalid_auth_token) }
21
+
22
+ let(:valid_headers) do
23
+ {
24
+ 'Authorization' => "Bearer #{valid_auth_token}",
25
+ 'Content-Type' => 'application/json'
26
+ }
27
+ end
28
+
29
+ let(:invalid_token_headers) do
30
+ {
31
+ 'Authorization' => "Bearer #{invalid_auth_token}",
32
+ 'Content-Type' => 'application/json'
33
+ }
34
+ end
35
+
36
+ let(:expired_token_headers) do
37
+ {
38
+ 'Authorization' => "Bearer #{expired_auth_token}",
39
+ 'Content-Type' => 'application/json'
40
+ }
41
+ end
42
+
43
+ let(:not_found_response) do
44
+ {
45
+ status: 404,
46
+ body: {
47
+ errors: {
48
+ error_type: 'RESOURCE_NOT_FOUND',
49
+ error_message: 'Lead not found.'
50
+ }
51
+ }.to_json
52
+ }
53
+ end
54
+
55
+ let(:invalid_token_response) do
56
+ {
57
+ status: 401,
58
+ body: {
59
+ errors: {
60
+ error_type: 'UNAUTHORIZED',
61
+ error_message: 'Invalid token.'
62
+ }
63
+ }.to_json
64
+ }
65
+ end
66
+
67
+ let(:conflicting_field_response) do
68
+ {
69
+ status: 400,
70
+ body: {
71
+ errors: {
72
+ error_type: 'CONFLICTING_FIELD',
73
+ error_message: 'The payload contains an attribute that was used to identify the resource.'
74
+ }
75
+ }.to_json
76
+ }
77
+ end
78
+
79
+ let(:unrecognized_error) do
80
+ {
81
+ status: 400,
82
+ body: {
83
+ errors: {
84
+ error_type: 'unrecognized error',
85
+ error_message: 'Unexpected error.'
86
+ }
87
+ }.to_json
88
+ }
89
+ end
90
+
91
+ let(:expired_token_response) do
92
+ {
93
+ status: 401,
94
+ headers: { 'WWW-Authenticate' => 'Bearer realm="https://api.rd.services/", error="expired_token", error_description="The access token expired"' },
95
+ body: {
96
+ errors: {
97
+ error_type: 'UNAUTHORIZED',
98
+ error_message: 'Invalid token.'
99
+ }
100
+ }.to_json
101
+ }
102
+ end
103
+
104
+ describe '#by_uuid' do
105
+ context 'with a valid auth token' do
106
+ context 'when the contact exists' do
107
+ let(:contact) do
108
+ { 'name' => 'Lead', 'email' => 'valid@email.com' }
109
+ end
110
+
111
+ before do
112
+ stub_request(:get, endpoint_with_valid_uuid)
113
+ .with(headers: valid_headers)
114
+ .to_return(status: 200, body: contact.to_json)
115
+ end
116
+
117
+ it 'returns the contact' do
118
+ response = contact_with_valid_token.by_uuid('valid_uuid')
119
+ expect(response).to eq(contact)
120
+ end
121
+ end
122
+
123
+ context 'when the contact does not exist' do
124
+ before do
125
+ stub_request(:get, endpoint_with_invalid_uuid)
126
+ .with(headers: valid_headers)
127
+ .to_return(not_found_response)
128
+ end
129
+
130
+ it 'raises a not found error' do
131
+ expect do
132
+ contact_with_valid_token.by_uuid(invalid_uuid)
133
+ end.to raise_error(RDStation::Error::ResourceNotFound)
134
+ end
135
+ end
136
+ end
137
+
138
+ context 'with an invalid auth token' do
139
+ before do
140
+ stub_request(:get, endpoint_with_valid_uuid)
141
+ .with(headers: invalid_token_headers)
142
+ .to_return(invalid_token_response)
143
+ end
144
+
145
+ it 'raises an invalid token error' do
146
+ expect do
147
+ contact_with_invalid_token.by_uuid(valid_uuid)
148
+ end.to raise_error(RDStation::Error::Unauthorized)
149
+ end
150
+ end
151
+
152
+ context 'with an expired auth token' do
153
+ before do
154
+ stub_request(:get, endpoint_with_valid_uuid)
155
+ .with(headers: expired_token_headers)
156
+ .to_return(expired_token_response)
157
+ end
158
+
159
+ it 'raises a expired token error' do
160
+ expect do
161
+ contact_with_expired_token.by_uuid(valid_uuid)
162
+ end.to raise_error(RDStation::Error::ExpiredAccessToken)
163
+ end
164
+ end
165
+ end
166
+
167
+ describe '#by_email' do
168
+ context 'with a valid auth token' do
169
+ context 'when the contact exists' do
170
+ let(:contact) do
171
+ { 'name' => 'Lead', 'email' => 'valid@email.com' }
172
+ end
173
+
174
+ before do
175
+ stub_request(:get, endpoint_with_valid_email)
176
+ .with(headers: valid_headers)
177
+ .to_return(status: 200, body: contact.to_json)
178
+ end
179
+
180
+ it 'returns the contact' do
181
+ response = contact_with_valid_token.by_email(valid_email)
182
+ expect(response).to eq(contact)
183
+ end
184
+ end
185
+
186
+ context 'when the contact does not exist' do
187
+ before do
188
+ stub_request(:get, endpoint_with_invalid_email)
189
+ .with(headers: valid_headers)
190
+ .to_return(not_found_response)
191
+ end
192
+
193
+ it 'raises a not found error' do
194
+ expect do
195
+ contact_with_valid_token.by_email(invalid_email)
196
+ end.to raise_error(RDStation::Error::ResourceNotFound)
197
+ end
198
+ end
199
+ end
200
+
201
+ context 'with an invalid auth token' do
202
+ before do
203
+ stub_request(:get, endpoint_with_valid_email)
204
+ .with(headers: invalid_token_headers)
205
+ .to_return(invalid_token_response)
206
+ end
207
+
208
+ it 'raises an invalid token error' do
209
+ expect do
210
+ contact_with_invalid_token.by_email(valid_email)
211
+ end.to raise_error(RDStation::Error::Unauthorized)
212
+ end
213
+ end
214
+
215
+ context 'with an expired auth token' do
216
+ before do
217
+ stub_request(:get, endpoint_with_valid_email)
218
+ .with(headers: expired_token_headers)
219
+ .to_return(expired_token_response)
220
+ end
221
+
222
+ it 'raises a expired token error' do
223
+ expect do
224
+ contact_with_expired_token.by_email(valid_email)
225
+ end.to raise_error(RDStation::Error::ExpiredAccessToken)
226
+ end
227
+ end
228
+ end
229
+
230
+ describe '#update' do
231
+ context 'with a valid auth_token' do
232
+ let(:valid_auth_token) { 'valid_auth_token' }
233
+ let(:headers) do
234
+ {
235
+ 'Authorization' => "Bearer #{valid_auth_token}",
236
+ 'Content-Type' => 'application/json'
237
+ }
238
+ end
239
+
240
+ context 'with valid params' do
241
+ let(:contact) do
242
+ { 'name' => 'Lead', 'email' => 'valid@email.com' }
243
+ end
244
+
245
+ before do
246
+ stub_request(:patch, endpoint_with_valid_uuid)
247
+ .with(headers: headers)
248
+ .to_return(status: 200, body: contact.to_json)
249
+ end
250
+
251
+ it 'returns the updated contact' do
252
+ updated_contact = contact_with_valid_token.update('valid_uuid', contact)
253
+ expect(updated_contact).to eq(contact)
254
+ end
255
+ end
256
+
257
+ context 'when the contact does not exist' do
258
+ before do
259
+ stub_request(:patch, endpoint_with_invalid_uuid)
260
+ .with(headers: headers)
261
+ .to_return(not_found_response)
262
+ end
263
+
264
+ it 'raises a not found error' do
265
+ expect do
266
+ contact_with_valid_token.update(invalid_uuid, {})
267
+ end.to raise_error(RDStation::Error::ResourceNotFound)
268
+ end
269
+ end
270
+ end
271
+
272
+ context 'with an invalid auth token' do
273
+ let(:invalid_auth_token) { 'invalid_auth_token' }
274
+ let(:headers) do
275
+ {
276
+ 'Authorization' => "Bearer #{invalid_auth_token}",
277
+ 'Content-Type' => 'application/json'
278
+ }
279
+ end
280
+
281
+ before do
282
+ stub_request(:patch, endpoint_with_valid_uuid)
283
+ .with(headers: headers)
284
+ .to_return(invalid_token_response)
285
+ end
286
+
287
+ it 'raises an invalid token error' do
288
+ expect do
289
+ contact_with_invalid_token.update('valid_uuid', {})
290
+ end.to raise_error(RDStation::Error::Unauthorized)
291
+ end
292
+ end
293
+
294
+ context 'with an expired auth token' do
295
+ let(:expired_auth_token) { 'expired_auth_token' }
296
+ let(:headers) do
297
+ {
298
+ 'Authorization' => "Bearer #{expired_auth_token}",
299
+ 'Content-Type' => 'application/json'
300
+ }
301
+ end
302
+
303
+ before do
304
+ stub_request(:patch, endpoint_with_valid_uuid)
305
+ .with(headers: headers)
306
+ .to_return(expired_token_response)
307
+ end
308
+
309
+ it 'raises a expired token error' do
310
+ expect do
311
+ contact_with_expired_token.update('valid_uuid', {})
312
+ end.to raise_error(RDStation::Error::ExpiredAccessToken)
313
+ end
314
+ end
315
+ end
316
+
317
+ describe '#upsert' do
318
+ context 'with a valid auth_token' do
319
+ let(:valid_auth_token) { 'valid_auth_token' }
320
+
321
+ let(:headers) do
322
+ {
323
+ 'Authorization' => "Bearer #{valid_auth_token}",
324
+ 'Content-Type' => 'application/json'
325
+ }
326
+ end
327
+
328
+ context 'with valid params' do
329
+ let(:contact) do
330
+ { 'name' => 'Lead', 'job_title' => 'Developer' }
331
+ end
332
+
333
+ before do
334
+ stub_request(:patch, endpoint_with_valid_email)
335
+ .with(headers: headers)
336
+ .to_return(status: 200, body: contact.to_json)
337
+ end
338
+
339
+ it 'returns the updated contact' do
340
+ updated_contact = contact_with_valid_token.upsert('email', 'valid@email.com', contact)
341
+ expect(updated_contact).to eq(contact)
342
+ end
343
+ end
344
+
345
+ context 'when the contact does not exist' do
346
+ before do
347
+ stub_request(:patch, endpoint_with_invalid_email)
348
+ .with(headers: headers)
349
+ .to_return(not_found_response)
350
+ end
351
+
352
+ it 'raises a not found error' do
353
+ expect do
354
+ contact_with_valid_token.upsert('email', invalid_email, {})
355
+ end.to raise_error(RDStation::Error::ResourceNotFound)
356
+ end
357
+ end
358
+
359
+ context 'when the payload has a conflicting field' do
360
+ let(:conflicting_payload) { { 'email' => valid_email } }
361
+
362
+ before do
363
+ stub_request(:patch, endpoint_with_valid_email)
364
+ .with(headers: headers)
365
+ .to_return(conflicting_field_response)
366
+ end
367
+
368
+ it 'raises a conflicting field error' do
369
+ expect do
370
+ contact_with_valid_token.upsert('email', valid_email, conflicting_payload)
371
+ end.to raise_error(RDStation::Error::ConflictingField)
372
+ end
373
+ end
374
+
375
+ context 'when an unrecognized error occurs' do
376
+ before do
377
+ stub_request(:patch, endpoint_with_valid_email)
378
+ .with(headers: headers)
379
+ .to_return(unrecognized_error)
380
+ end
381
+
382
+ it 'raises an default error' do
383
+ expect do
384
+ contact_with_valid_token.upsert('email', valid_email, {})
385
+ end.to raise_error(RDStation::Error::Default)
386
+ end
387
+ end
388
+ end
389
+
390
+ context 'with an invalid auth token' do
391
+ let(:invalid_auth_token) { 'invalid_auth_token' }
392
+ let(:headers) do
393
+ {
394
+ 'Authorization' => "Bearer #{invalid_auth_token}",
395
+ 'Content-Type' => 'application/json'
396
+ }
397
+ end
398
+
399
+ before do
400
+ stub_request(:patch, endpoint_with_valid_email)
401
+ .with(headers: headers)
402
+ .to_return(invalid_token_response)
403
+ end
404
+
405
+ it 'raises an invalid token error' do
406
+ expect do
407
+ contact_with_invalid_token.upsert('email', valid_email, {})
408
+ end.to raise_error(RDStation::Error::Unauthorized)
409
+ end
410
+ end
411
+
412
+ context 'with an expired auth token' do
413
+ let(:expired_auth_token) { 'expired_auth_token' }
414
+ let(:headers) do
415
+ {
416
+ 'Authorization' => "Bearer #{expired_auth_token}",
417
+ 'Content-Type' => 'application/json'
418
+ }
419
+ end
420
+
421
+ before do
422
+ stub_request(:patch, endpoint_with_valid_email)
423
+ .with(headers: headers)
424
+ .to_return(expired_token_response)
425
+ end
426
+
427
+ it 'raises an expired token error' do
428
+ expect do
429
+ contact_with_expired_token.upsert('email', valid_email, {})
430
+ end.to raise_error(RDStation::Error::ExpiredAccessToken)
431
+ end
432
+ end
433
+ end
434
+ end