paid 0.1.0 → 1.0.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.
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