paid 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -1
  3. data/.travis.yml +16 -0
  4. data/History.txt +4 -0
  5. data/README.md +58 -0
  6. data/Rakefile +9 -29
  7. data/VERSION +1 -0
  8. data/bin/paid-console +7 -0
  9. data/gemfiles/default-with-activesupport.gemfile +10 -0
  10. data/gemfiles/json.gemfile +12 -0
  11. data/gemfiles/yajl.gemfile +12 -0
  12. data/lib/paid.rb +129 -177
  13. data/lib/paid/account.rb +14 -1
  14. data/lib/paid/api_class.rb +336 -0
  15. data/lib/paid/api_list.rb +47 -0
  16. data/lib/paid/api_resource.rb +8 -25
  17. data/lib/paid/api_singleton.rb +5 -0
  18. data/lib/paid/customer.rb +36 -21
  19. data/lib/paid/errors/api_error.rb +6 -0
  20. data/lib/paid/event.rb +22 -1
  21. data/lib/paid/invoice.rb +16 -21
  22. data/lib/paid/plan.rb +18 -2
  23. data/lib/paid/subscription.rb +17 -11
  24. data/lib/paid/transaction.rb +19 -12
  25. data/lib/paid/util.rb +53 -106
  26. data/lib/paid/version.rb +1 -1
  27. data/paid.gemspec +10 -11
  28. data/tasks/api_test.rb +187 -0
  29. data/test/mock_resource.rb +69 -0
  30. data/test/paid/account_test.rb +41 -4
  31. data/test/paid/api_class_test.rb +412 -0
  32. data/test/paid/api_list_test.rb +17 -0
  33. data/test/paid/api_resource_test.rb +13 -343
  34. data/test/paid/api_singleton_test.rb +12 -0
  35. data/test/paid/authentication_test.rb +50 -0
  36. data/test/paid/customer_test.rb +189 -29
  37. data/test/paid/event_test.rb +74 -0
  38. data/test/paid/invoice_test.rb +101 -20
  39. data/test/paid/plan_test.rb +84 -8
  40. data/test/paid/status_codes_test.rb +63 -0
  41. data/test/paid/subscription_test.rb +100 -20
  42. data/test/paid/transaction_test.rb +110 -37
  43. data/test/paid/util_test.rb +15 -24
  44. data/test/test_data.rb +144 -93
  45. data/test/test_helper.rb +6 -4
  46. metadata +32 -26
  47. data/Gemfile.lock +0 -54
  48. data/README.rdoc +0 -35
  49. data/lib/data/ca-certificates.crt +0 -0
  50. data/lib/paid/alias.rb +0 -16
  51. data/lib/paid/api_operations/create.rb +0 -17
  52. data/lib/paid/api_operations/delete.rb +0 -11
  53. data/lib/paid/api_operations/list.rb +0 -17
  54. data/lib/paid/api_operations/update.rb +0 -57
  55. data/lib/paid/certificate_blacklist.rb +0 -55
  56. data/lib/paid/list_object.rb +0 -37
  57. data/lib/paid/paid_object.rb +0 -187
  58. data/lib/paid/singleton_api_resource.rb +0 -20
  59. data/lib/tasks/paid_tasks.rake +0 -4
  60. data/test/paid/alias_test.rb +0 -22
  61. data/test/paid/certificate_blacklist_test.rb +0 -18
  62. data/test/paid/list_object_test.rb +0 -16
  63. data/test/paid/paid_object_test.rb +0 -27
  64. data/test/paid/properties_test.rb +0 -103
@@ -0,0 +1,17 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ module Paid
4
+ class ApiListTest < Test::Unit::TestCase
5
+
6
+ should 'have an object attribute' do
7
+ assert(Paid::APIList.method_defined?(:object))
8
+ assert(Paid::APIList.method_defined?(:object=))
9
+ end
10
+
11
+ should 'have an data attribute' do
12
+ assert(Paid::APIList.method_defined?(:data))
13
+ assert(Paid::APIList.method_defined?(:data=))
14
+ end
15
+
16
+ end
17
+ end
@@ -1,358 +1,28 @@
1
- # -*- coding: utf-8 -*-
2
1
  require File.expand_path('../../test_helper', __FILE__)
3
2
 
4
3
  module Paid
5
4
  class ApiResourceTest < Test::Unit::TestCase
6
- should 'creating a new APIResource should not fetch over the network' do
7
- @mock.expects(:get).never
8
- Paid::Customer.new('someid')
9
- end
10
-
11
- should 'creating a new APIResource from a hash should not fetch over the network' do
12
- @mock.expects(:get).never
13
- Paid::Customer.construct_from(id: 'somecustomer',
14
- object: 'customer',
15
- email: 'someone@example.com',
16
- name: 'Some Business',
17
- account_id: 'acct_1234')
18
- end
19
-
20
- should 'setting an attribute should not cause a network request' do
21
- @mock.expects(:get).never
22
- @mock.expects(:post).never
23
- c = Paid::Customer.new('test_customer')
24
- c.name = 'Another Name'
25
- end
26
-
27
- should 'accessing id should not issue a fetch' do
28
- @mock.expects(:get).never
29
- c = Paid::Customer.new('test_customer')
30
- c.id
31
- end
32
-
33
- should 'not specifying api credentials should raise an exception' do
34
- Paid.api_key = nil
35
- assert_raises Paid::AuthenticationError do
36
- Paid::Customer.new('test_customer').refresh
37
- end
38
- end
39
-
40
- should 'specifying api credentials containing whitespace should raise an exception' do
41
- Paid.api_key = 'key '
42
- assert_raises Paid::AuthenticationError do
43
- Paid::Customer.new('test_customer').refresh
44
- end
45
- end
46
5
 
47
- should 'specifying invalid api credentials should raise an exception' do
48
- Paid.api_key = 'invalid'
49
- response = test_response(test_invalid_api_key_error, 401)
50
- assert_raises Paid::AuthenticationError do
51
- @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
52
- Paid::Customer.retrieve('failing_customer')
53
- end
6
+ should 'have an id attribute' do
7
+ assert(Paid::APIResource.method_defined?(:id))
8
+ assert(Paid::APIResource.method_defined?(:id=))
54
9
  end
55
10
 
56
- should 'AuthenticationErrors should have an http status, http body, and JSON body' do
57
- Paid.api_key = 'invalid'
58
- response = test_response(test_invalid_api_key_error, 401)
59
- begin
60
- @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
61
- Paid::Customer.retrieve('failing_customer')
62
- rescue Paid::AuthenticationError => e
63
- assert_equal(401, e.http_status)
64
- assert_equal(true, !!e.http_body)
65
- assert_equal(true, !!e.json_body[:error][:message])
66
- assert_equal(test_invalid_api_key_error[:error][:message], e.json_body[:error][:message])
67
- end
11
+ should 'have an object attribute' do
12
+ assert(Paid::APIResource.method_defined?(:object))
13
+ assert(Paid::APIResource.method_defined?(:object=))
68
14
  end
69
15
 
70
- should 'send paid account as header when set' do
71
- paid_account = 'acct_0000'
72
- Paid.expects(:execute_request).with do |opts|
73
- opts[:headers][:paid_account] == paid_account
74
- end.returns(test_response(test_transaction))
75
-
76
- Paid::Transaction.create({ amount: 100 },
77
- paid_account: paid_account, api_key: 'sk_test_local')
16
+ should 'have a default path' do
17
+ mr = MockResource.new('fake_id')
18
+ assert_equal("#{MockResource.path}/fake_id", mr.path)
78
19
  end
79
20
 
80
- should 'not send paid account as header when not set' do
81
- Paid.expects(:execute_request).with do |opts|
82
- opts[:headers][:paid_account].nil?
83
- end.returns(test_response(test_transaction))
84
-
85
- Paid::Transaction.create(
86
- {
87
- amount: 200,
88
- description: 'This is a description.',
89
- customer: 'somecustomer'
90
-
91
- },
92
- 'sk_test_local'
93
- )
94
- end
95
-
96
- context 'when specifying per-object credentials' do
97
- context 'with no global API key set' do
98
- should 'use the per-object credential when creating' do
99
- Paid.expects(:execute_request).with do |opts|
100
- opts[:headers][:authorization] == 'Bearer sk_test_local'
101
- end.returns(test_response(test_transaction))
102
-
103
- Paid::Transaction.create(
104
- {
105
- amount: 200,
106
- description: 'This is a description.',
107
- customer: 'somecustomer'
108
- },
109
- 'sk_test_local'
110
- )
111
- end
112
- end
113
-
114
- context 'with a global API key set' do
115
- setup do
116
- Paid.api_key = 'global'
117
- end
118
-
119
- teardown do
120
- Paid.api_key = nil
121
- end
122
-
123
- should 'use the per-object credential when creating' do
124
- Paid.expects(:execute_request).with do |opts|
125
- opts[:headers][:authorization] == 'Bearer local'
126
- end.returns(test_response(test_transaction))
127
-
128
- Paid::Transaction.create(
129
- {
130
- amount: 200,
131
- description: 'This is a description.',
132
- customer: 'somecustomer'
133
- },
134
- 'local'
135
- )
136
- end
137
-
138
- should 'use the per-object credential when retrieving and making other calls' do
139
- Paid.expects(:execute_request).with do |opts|
140
- opts[:url] == "#{Paid.api_base}/v0/transactions/tr_test_transaction" &&
141
- opts[:headers][:authorization] == 'Bearer local'
142
- end.returns(test_response(test_transaction))
143
-
144
- ch = Paid::Transaction.retrieve('tr_test_transaction', 'local')
145
- end
146
- end
21
+ should 'raise an InvalidRequestError when no ID is present for instance path' do
22
+ @mock.expects(:get).never
23
+ c = MockResource.new
24
+ assert_raises(Paid::InvalidRequestError) { c.refresh }
147
25
  end
148
26
 
149
- context 'with valid credentials' do
150
- should 'send along the idempotency-key header' do
151
- Paid.expects(:execute_request).with do |opts|
152
- opts[:headers][:idempotency_key] == 'bar'
153
- end.returns(test_response(test_transaction))
154
-
155
- Paid::Transaction.create(
156
- {
157
- amount: 200,
158
- description: 'This is a description.',
159
- customer: 'somecustomer'
160
- },
161
- idempotency_key: 'bar',
162
- api_key: 'local'
163
- )
164
- end
165
-
166
- should 'urlencode values in GET params' do
167
- response = test_response(test_transaction_array)
168
- @mock.expects(:get).with("#{Paid.api_base}/v0/transactions?customer=test%20customer", nil, nil).returns(response)
169
- transactions = Paid::Transaction.all(customer: 'test customer').data
170
- assert transactions.is_a? Array
171
- end
172
-
173
- should 'a 400 should give an InvalidRequestError with http status, body, and JSON body' do
174
- response = test_response(test_missing_id_error, 400)
175
- @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
176
- begin
177
- Paid::Customer.retrieve('foo')
178
- rescue Paid::InvalidRequestError => e
179
- assert_equal(400, e.http_status)
180
- assert_equal(true, !!e.http_body)
181
- assert_equal(true, e.json_body.is_a?(Hash))
182
- end
183
- end
184
-
185
- should 'a 401 should give an AuthenticationError with http status, body, and JSON body' do
186
- response = test_response(test_missing_id_error, 401)
187
- @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
188
- begin
189
- Paid::Customer.retrieve('foo')
190
- rescue Paid::AuthenticationError => e
191
- assert_equal(401, e.http_status)
192
- assert_equal(true, !!e.http_body)
193
- assert_equal(true, e.json_body.is_a?(Hash))
194
- end
195
- end
196
-
197
- should 'a 404 should give an InvalidRequestError with http status, body, and JSON body' do
198
- response = test_response(test_missing_id_error, 404)
199
- @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
200
- begin
201
- Paid::Customer.retrieve('foo')
202
- rescue Paid::InvalidRequestError => e
203
- assert_equal(404, e.http_status)
204
- assert_equal(true, !!e.http_body)
205
- assert_equal(true, e.json_body.is_a?(Hash))
206
- end
207
- end
208
-
209
- should 'setting a nil value for a param should exclude that param from the request' do
210
- @mock.expects(:get).with do |url, _api_key, _params|
211
- uri = URI(url)
212
- query = CGI.parse(uri.query)
213
- (url =~ %r{^#{Paid.api_base}/v0/transactions?} &&
214
- query.keys.sort == %w(offset sad))
215
- end.returns(test_response(count: 1, data: [test_transaction]))
216
- Paid::Transaction.all(count: nil, offset: 5, sad: false)
217
-
218
- @mock.expects(:post).with do |url, api_key, params|
219
- url == "#{Paid.api_base}/v0/transactions" &&
220
- api_key.nil? &&
221
- CGI.parse(params) == { 'amount' => ['100'] }
222
- end.returns(test_response(count: 1, data: [test_transaction]))
223
- Paid::Transaction.create(amount: 100)
224
- end
225
-
226
- should 'requesting with a unicode ID should result in a request' do
227
- response = test_response(test_missing_id_error, 404)
228
- @mock.expects(:get).once.with("#{Paid.api_base}/v0/customers/%E2%98%83", nil, nil).raises(RestClient::ExceptionWithResponse.new(response, 404))
229
- c = Paid::Customer.new('☃')
230
- assert_raises(Paid::InvalidRequestError) { c.refresh }
231
- end
232
-
233
- should 'requesting with no ID should result in an InvalidRequestError with no request' do
234
- c = Paid::Customer.new
235
- assert_raises(Paid::InvalidRequestError) { c.refresh }
236
- end
237
-
238
- should 'making a GET request with parameters should have a query string and no body' do
239
- params = { limit: 1 }
240
- @mock.expects(:get).once.with("#{Paid.api_base}/v0/transactions?limit=1", nil, nil).returns(test_response([test_transaction]))
241
- Paid::Transaction.all(params)
242
- end
243
-
244
- should 'making a POST request with parameters should have a body and no query string' do
245
- params = { amount: 100, alias: 'test_alias' }
246
- @mock.expects(:post).once.with do |_url, get, post|
247
- get.nil? && CGI.parse(post) == { 'amount' => ['100'], 'alias' => ['test_alias'] }
248
- end.returns(test_response(test_transaction))
249
- Paid::Transaction.create(params)
250
- end
251
-
252
- should 'loading an object should issue a GET request' do
253
- @mock.expects(:get).once.returns(test_response(test_customer))
254
- c = Paid::Customer.new('test_customer')
255
- c.refresh
256
- end
257
-
258
- should 'using array accessors should be the same as the method interface' do
259
- @mock.expects(:get).once.returns(test_response(test_customer))
260
- c = Paid::Customer.new('test_customer')
261
- c.refresh
262
- assert_equal c.created, c[:created]
263
- assert_equal c.created, c['created']
264
- c['created'] = 12_345
265
- assert_equal c.created, 12_345
266
- end
267
-
268
- should 'accessing a property other than id or parent on an unfetched object should fetch it' do
269
- @mock.expects(:get).once.returns(test_response(test_customer))
270
- c = Paid::Customer.new('test_customer')
271
- c.transactions
272
- end
273
-
274
- should 'updating an object should issue a POST request with only the changed properties' do
275
- @mock.expects(:post).with do |url, api_key, params|
276
- url == "#{Paid.api_base}/v0/customers/cus_test_customer" &&
277
- api_key.nil? &&
278
- CGI.parse(params) == { 'description' => ['another_mn'] }
279
- end.once.returns(test_response(test_customer))
280
- c = Paid::Customer.construct_from(test_customer)
281
- c.description = 'another_mn'
282
- c.save
283
- end
284
-
285
- should 'updating should merge in returned properties' do
286
- @mock.expects(:post).once.returns(test_response(test_customer))
287
- c = Paid::Customer.new('cus_test_customer')
288
- c.description = 'another_mn'
289
- c.save
290
- # assert_equal false, c.livemode
291
- end
292
-
293
- should 'deleting should send no props and result in an object that has no props other deleted' do
294
- @mock.expects(:get).never
295
- @mock.expects(:post).never
296
- @mock.expects(:delete).with("#{Paid.api_base}/v0/customers/cus_test_customer", nil, nil).once.returns(test_response('id' => 'test_customer', 'deleted' => true))
297
-
298
- c = Paid::Customer.construct_from(test_customer)
299
- c.delete
300
- assert_equal true, c.deleted
301
-
302
- assert_raises NoMethodError do
303
- c.livemode
304
- end
305
- end
306
-
307
- should 'loading an object with properties that have specific types should instantiate those classes' do
308
- @mock.expects(:get).once.returns(test_response(test_transaction))
309
- t = Paid::Transaction.retrieve('test_transaction')
310
- assert t.is_a?(Paid::PaidObject) && t.object == 'transaction'
311
- end
312
-
313
- should 'loading all of an APIResource should return an array of recursively instantiated objects' do
314
- @mock.expects(:get).once.returns(test_response(test_transaction_array))
315
- t = Paid::Transaction.all.data
316
- assert t.is_a? Array
317
- assert t[0].is_a? Paid::Transaction
318
- assert t[0].is_a?(Paid::PaidObject) && t[0].object == 'transaction'
319
- end
320
-
321
- context 'error checking' do
322
- should '404s should raise an InvalidRequestError' do
323
- response = test_response(test_missing_id_error, 404)
324
- @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
325
-
326
- rescued = false
327
- begin
328
- Paid::Customer.new('test_customer').refresh
329
- assert false # shouldn't get here either
330
- rescue Paid::InvalidRequestError => e # we don't use assert_raises because we want to examine e
331
- rescued = true
332
- assert e.is_a? Paid::InvalidRequestError
333
- assert_equal 'id', e.param
334
- assert_equal 'Missing id', e.message
335
- end
336
-
337
- assert_equal true, rescued
338
- end
339
-
340
- should '5XXs should raise an APIError' do
341
- response = test_response(test_api_error, 500)
342
- @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 500))
343
-
344
- rescued = false
345
- begin
346
- Paid::Customer.new('test_customer').refresh
347
- assert false # shouldn't get here either
348
- rescue Paid::APIError => e # we don't use assert_raises because we want to examine e
349
- rescued = true
350
- assert e.is_a? Paid::APIError
351
- end
352
-
353
- assert_equal true, rescued
354
- end
355
- end
356
- end
357
27
  end
358
28
  end
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ module Paid
4
+ class ApiSingletonTest < Test::Unit::TestCase
5
+
6
+ should 'have an object attribute' do
7
+ assert(Paid::APISingleton.method_defined?(:object))
8
+ assert(Paid::APISingleton.method_defined?(:object=))
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,50 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path('../../test_helper', __FILE__)
3
+
4
+ module Paid
5
+ class StatusCodesTest < Test::Unit::TestCase
6
+
7
+ context 'AuthenticationError' do
8
+ should 'be raised with no API credentials' do
9
+ Paid.api_key = nil
10
+ assert_raises(Paid::AuthenticationError) do
11
+ MockResource.retrieve('fake_id')
12
+ end
13
+ end
14
+
15
+ should 'be raised with invalid credentials' do
16
+ Paid.api_key = 'invalid api key' # spaces aren't valid
17
+ assert_raises(Paid::AuthenticationError) do
18
+ MockResource.new('fake_id').refresh
19
+ end
20
+ end
21
+
22
+ context 'that has been raised' do
23
+ setup do
24
+ Paid.api_key = 'invalid'
25
+ response = test_response(test_invalid_api_key_error, 401)
26
+ begin
27
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
28
+ MockResource.retrieve('failing')
29
+ rescue Paid::AuthenticationError => e
30
+ @error = e
31
+ end
32
+ end
33
+
34
+ should 'have an http status of 401' do
35
+ assert_equal(401, @error.http_status)
36
+ end
37
+
38
+ should 'have an http body' do
39
+ assert(!!@error.http_body)
40
+ end
41
+
42
+ should 'have a JSON body with an error message' do
43
+ assert(!!@error.json_body[:error][:message])
44
+ assert_equal(test_invalid_api_key_error[:error][:message], @error.json_body[:error][:message])
45
+ end
46
+ end
47
+ end
48
+
49
+ end
50
+ end