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
data/paid.gemspec CHANGED
@@ -1,18 +1,16 @@
1
1
  $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
2
 
3
- # Maintain your gem's version:
4
- require "paid/version"
3
+ require 'paid/version'
5
4
 
6
- # Describe your gem and declare its dependencies:
7
- Gem::Specification.new do |s|
8
- s.name = 'paid'
9
- s.version = Paid::VERSION
10
- s.authors = ['Ryan Jackson']
11
- s.email = ['ryan@paidapi.com']
12
- s.homepage = 'http://docs.paidapi.com'
13
- s.summary = 'Ruby bindings for Paid API'
5
+ spec = Gem::Specification.new do |s|
6
+ s.name = 'paid'
7
+ s.summary = 'Ruby bindings for Paid API'
14
8
  s.description = 'Paid is the programmatic way to manage payments. See https://paidapi.com for details.'
15
- s.license = 'MIT'
9
+ s.homepage = 'http://docs.paidapi.com'
10
+ s.authors = ['Jon Calhoun', 'Ryan Jackson']
11
+ s.email = ['joncalhoun@gmail.com', 'ryan@paidapi.com']
12
+ s.version = Paid::VERSION
13
+ s.license = 'MIT'
16
14
 
17
15
  s.add_dependency('rest-client', '~> 1.4')
18
16
  s.add_dependency('mime-types', '>= 1.25', '< 3.0')
@@ -28,3 +26,4 @@ Gem::Specification.new do |s|
28
26
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
29
27
  s.require_paths = ['lib']
30
28
  end
29
+
data/tasks/api_test.rb ADDED
@@ -0,0 +1,187 @@
1
+ require 'paid'
2
+
3
+ class APITest
4
+ def initialize(api_key)
5
+ Paid.api_key = api_key
6
+ end
7
+
8
+ def run
9
+ account = run_account_test
10
+ customer = run_customer_tests
11
+ plan = run_plan_tests
12
+ subscription = run_subscription_tests(customer, plan)
13
+ transaction = run_transaction_tests(customer)
14
+ invoice = run_invoice_tests(customer)
15
+ event = run_events_tests
16
+ run_advanced_tests(transaction, invoice, subscription)
17
+ end
18
+
19
+ def run_advanced_tests(transaction, invoice, subscription)
20
+ puts "Marking the transaction as paid..."
21
+ transaction.mark_as_paid
22
+ puts "Marked transaction=#{transaction.id} as paid."
23
+
24
+ puts "Issuing the invoice..."
25
+ invoice.issue
26
+ puts "Invoice=#{invoice.id} issued."
27
+
28
+ puts "Marking the invoice as paid..."
29
+ invoice.mark_as_paid(:via => :ach)
30
+ puts "Marked invoice=#{invoice.id} as paid."
31
+
32
+ puts "Cancelling subscription=#{subscription.id}..."
33
+ subscription.cancel
34
+ puts "Subscription cancelled."
35
+ end
36
+
37
+ def run_events_tests
38
+ puts "Looking up all events..."
39
+ events = Paid::Event.all
40
+ puts "Found #{events.length} events."
41
+
42
+ puts "Retrieving the first event..."
43
+ event = Paid::Event.retrieve(events.first.id)
44
+ puts "Retrieved the event with the id=#{event.id}"
45
+
46
+ event
47
+ end
48
+
49
+ def run_invoice_tests(customer)
50
+ puts "Creating an invoice with customer=#{customer.id}"
51
+ invoice = customer.generate_invoice
52
+ puts "Created: #{invoice.inspect}"
53
+
54
+ puts "Looking up all invoices..."
55
+ invoices = Paid::Invoice.all
56
+ puts "Found #{invoices.length} invoices."
57
+
58
+ puts "Retrieving the generated invoice..."
59
+ invoice = Paid::Invoice.retrieve(invoice.id)
60
+ puts "Retrieved the invoice with the id=#{invoice.id}"
61
+
62
+ puts "Retrieving the customer=#{customer.id}'s invoices..."
63
+ invoices = customer.invoices
64
+ puts "Retrieved: #{invoices.inspect}"
65
+
66
+ invoice
67
+ end
68
+
69
+ def run_transaction_tests(customer)
70
+ puts "Creating 2 transactions with customer=#{customer.id}"
71
+ transaction = Paid::Transaction.create({
72
+ :amount => 100,
73
+ :description => 'a description',
74
+ :customer => customer.id,
75
+ :paid => false
76
+ })
77
+ transaction_b = Paid::Transaction.create({
78
+ :amount => 1000,
79
+ :description => 'another description',
80
+ :customer => customer.id,
81
+ :paid => false
82
+ })
83
+ puts "Created: #{transaction.inspect} + 1 other"
84
+
85
+ puts "Looking up all transactions..."
86
+ transactions = Paid::Transaction.all
87
+ puts "Found #{transactions.length} transactions."
88
+
89
+ puts "Retrieving the created transaction..."
90
+ transaction = Paid::Transaction.retrieve(transaction.id)
91
+ puts "Retrieved the transaction with the id=#{transaction.id}"
92
+
93
+ puts "Updating the transaction's name..."
94
+ transaction.description = "an updated description..."
95
+ transaction.save
96
+ puts "Updated the transaction with id=#{transaction.id} with the new description #{transaction.description.inspect}"
97
+
98
+ puts "Looking up customer=#{customer.id}'s transactions..."
99
+ transactions = customer.transactions
100
+ puts "Retrieved: #{transactions.inspect}"
101
+
102
+ transaction
103
+ end
104
+
105
+ def run_subscription_tests(customer, plan)
106
+ puts "Creating a subscription for customer=#{customer.id} and plan=#{plan.id}..."
107
+ subscription = Paid::Subscription.create({
108
+ :starts_on => (Time.now + 1 * 60 * 60 * 24).strftime("%Y-%m-%d"),
109
+ :plan => plan.id,
110
+ :customer => customer.id
111
+ })
112
+ puts "Created: #{subscription.inspect}"
113
+
114
+ puts "Looking up all subscriptions..."
115
+ subscriptions = Paid::Subscription.all
116
+ puts "Found #{subscriptions.length} subscriptions."
117
+
118
+ puts "Retrieving the created subscription..."
119
+ subscription = Paid::Subscription.retrieve(subscription.id)
120
+ puts "Retrieved the subscription with the id=#{subscription.id}"
121
+
122
+ subscription
123
+ end
124
+
125
+ def run_plan_tests
126
+ puts "Creating a plan..."
127
+ plan = Paid::Plan.create({
128
+ :description => "Plan for testing stuff",
129
+ :name => "Test Plan #{Time.now.to_i}-#{rand(2000)}",
130
+ :interval => "month",
131
+ :interval_count => 1,
132
+ :amount => 5000
133
+ })
134
+ puts "Created: #{plan.inspect}"
135
+
136
+ puts "Looking up all plans..."
137
+ plans = Paid::Plan.all
138
+ puts "Found #{plans.length} plans."
139
+
140
+ puts "Retrieving the created plan..."
141
+ plan = Paid::Plan.retrieve(plan.id)
142
+ puts "Retrieved the plan with the id=#{plan.id}"
143
+
144
+ plan
145
+ end
146
+
147
+ def run_customer_tests
148
+ puts "Creating a customer..."
149
+ customer = Paid::Customer.create({
150
+ :name => "Paid",
151
+ :email => "hello@paidapi.com",
152
+ :description => "Obviously this is just a description.",
153
+ :phone => "4155069330",
154
+ :address_line1 => "2261 Market Street",
155
+ :address_line2 => "#567",
156
+ :address_city => "San Francisco",
157
+ :address_state => "CA",
158
+ :address_zip => "94114"
159
+ })
160
+ puts "Created: #{customer.inspect}"
161
+
162
+ puts "Looking up all customers..."
163
+ customers = Paid::Customer.all
164
+ puts "Found #{customers.length} customers"
165
+
166
+ puts "Retrieving the created customer..."
167
+ customer = Paid::Customer.retrieve(customer.id)
168
+ puts "Retrieved the customer with id=#{customer.id}"
169
+
170
+ # TODO(joncalhoun): Add by_external_id lookup test
171
+
172
+ puts "Updating the customer's name..."
173
+ customer.name = "Paid Inc"
174
+ customer.save
175
+ puts "Updated the customer with id=#{customer.id} with the new name #{customer.name.inspect}"
176
+
177
+ customer
178
+ end
179
+
180
+ def run_account_test
181
+ puts "Looking up the account..."
182
+ account = Paid::Account.retrieve
183
+ puts "Retrieved the account with id=#{account.id}"
184
+ account
185
+ end
186
+
187
+ end
@@ -0,0 +1,69 @@
1
+ # Setup a fake resource for testing the APIResource
2
+
3
+ class NestedResource < Paid::APIResource
4
+ attribute :price
5
+
6
+ def self.path
7
+ "/nested_resources"
8
+ end
9
+ end
10
+
11
+ class MockResource < Paid::APIResource
12
+ attribute :name
13
+ attribute :tarray
14
+ attribute :thash
15
+ attribute :nested, NestedResource
16
+
17
+ api_class_method :retrieve, :get, ":path/:id", :arguments => [:id]
18
+ api_class_method :all, :get, :constructor => Paid::APIList.constructor(MockResource)
19
+ api_class_method :create, :post
20
+ api_class_method :many_args_get, :get, ":path/:b/many", :arguments => [:a, :b, :c]
21
+ api_class_method :many_args_post, :post, ":path/:b/many", :arguments => [:a, :b, :c]
22
+ api_class_method :crazy_path, :get, ":crazy"
23
+
24
+ api_class_method :with_con_self, :get, :constructor => :self
25
+ api_class_method :with_con_class, :get, :constructor => MockResource
26
+ api_class_method :with_con_lambda, :get, :constructor => lambda{ |json| "lamdba result" }
27
+ api_class_method :with_con_default, :get
28
+
29
+ def self.default_lambda
30
+ lambda do |this|
31
+ self.default_values
32
+ end
33
+ end
34
+ api_class_method :with_lambda, :post, :default_params => self.default_lambda
35
+ api_class_method :with_symbol, :post, :default_params => :default_values
36
+ api_class_method :with_symbol_and_args, :post, :default_params => :default_values, :arguments => [:name]
37
+
38
+ api_instance_method :refresh, :get, :constructor => :self
39
+ api_instance_method :save, :put, :default_params => changed_lambda, :constructor => :self
40
+ api_instance_method :delete, :delete
41
+ api_instance_method :custom_path, :get, ":path", :arguments => [:path]
42
+ api_instance_method :name_path, :get, ":name"
43
+ api_instance_method :crazy_path, :get, ":crazy"
44
+
45
+ api_instance_method :with_con_self, :get, :constructor => :self
46
+ api_instance_method :with_con_class, :get, :constructor => MockResource
47
+ api_instance_method :with_con_lambda, :get, :constructor => lambda{ |json| "lamdba result" }
48
+ api_instance_method :with_con_default, :get
49
+
50
+ api_instance_method :with_lambda, :put, :default_params => changed_lambda
51
+ api_instance_method :with_symbol, :put, :default_params => :changed_attributes
52
+ api_instance_method :with_symbol_and_args, :put, :default_params => :changed_attributes, :arguments => [:name]
53
+
54
+ def self.path
55
+ "/mocks"
56
+ end
57
+
58
+ def self.crazy
59
+ "/crazy_path"
60
+ end
61
+
62
+ def self.default_values
63
+ {
64
+ :name => "default name",
65
+ :tarray => [1,2,3]
66
+ }
67
+ end
68
+
69
+ end
@@ -2,11 +2,48 @@ require File.expand_path('../../test_helper', __FILE__)
2
2
 
3
3
  module Paid
4
4
  class AccountTest < Test::Unit::TestCase
5
- should "account should be retrievable" do
6
- resp = {:email => "test+bindings@paidapi.com"}
7
- @mock.expects(:get).once.returns(test_response(resp))
5
+ setup do
6
+ @account_url = "#{Paid.api_base}/v0/account"
7
+ end
8
+
9
+ should 'be retrievable' do
10
+ @mock.expects(:get).once.with(@account_url, anything, anything).returns(test_response(test_account))
8
11
  a = Paid::Account.retrieve
9
- assert_equal "test+bindings@paidapi.com", a.email
12
+ assert(a.is_a?(Paid::Account))
13
+ assert_equal(test_account[:id], a.id)
14
+ end
15
+
16
+ context 'Retrieved Paid::Account instance' do
17
+ setup do
18
+ @mock.expects(:get).once.returns(test_response(test_account))
19
+ @account = Paid::Account.retrieve
20
+ end
21
+
22
+ should 'have the id attribute' do
23
+ assert_equal(test_account[:id], @account.id)
24
+ end
25
+
26
+ should 'have the object attribute' do
27
+ assert_equal(test_account[:object], @account.object)
28
+ end
29
+
30
+ should 'have the business_name attribute' do
31
+ assert_equal(test_account[:business_name], @account.business_name)
32
+ end
33
+
34
+ should 'have the business_url attribute' do
35
+ assert_equal(test_account[:business_url], @account.business_url)
36
+ end
37
+
38
+ should 'have the business_logo attribute' do
39
+ assert_equal(test_account[:business_logo], @account.business_logo)
40
+ end
10
41
  end
42
+
43
+ should 'be registered' do
44
+ assert(APIClass.subclasses.include?(Paid::Account))
45
+ assert_equal(Paid::Account, APIClass.subclass_fetch("account"))
46
+ end
47
+
11
48
  end
12
49
  end
@@ -0,0 +1,412 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path('../../test_helper', __FILE__)
3
+
4
+ module Paid
5
+ class ApiClassTest < Test::Unit::TestCase
6
+
7
+ context 'Non-network actions' do
8
+ setup do
9
+ @mock.expects(:get).never
10
+ @mock.expects(:post).never
11
+ @mock.expects(:put).never
12
+ @mock.expects(:delete).never
13
+ end
14
+
15
+ should 'not fetch over the network when creating a new APIClass' do
16
+ MockResource.new('fake_id')
17
+ end
18
+
19
+ should 'not fetch over the network when creating a new APIClass from a hash' do
20
+ MockResource.construct(test_mock_resource)
21
+ end
22
+
23
+ should 'not fetch over the network when setting an attribute' do
24
+ c = MockResource.new('fake_id')
25
+ c.name = 'Another Name'
26
+ end
27
+
28
+ should 'not fetch over the network when accessing an attribute' do
29
+ c = MockResource.new('fake_id')
30
+ c.id
31
+ end
32
+ end
33
+
34
+ # These GET, POST, etc should really be in a dif test, but this is easier.
35
+ context 'Making a GET request' do
36
+ should 'urlencode values in params and have no payload' do
37
+ response = test_response(test_mock_resource_list)
38
+
39
+ # The test is to basically make sure this expectation passes.
40
+ @mock.expects(:get).with do |url, headers, params|
41
+ (url == "#{Paid.api_base}#{MockResource.path}?page=1&filter=test%20filter" ||
42
+ url == "#{Paid.api_base}#{MockResource.path}?filter=test%20filter&page=1") &&
43
+ params == nil
44
+ end.returns(response)
45
+
46
+ list = MockResource.all({
47
+ :page => 1,
48
+ :filter => 'test filter',
49
+ })
50
+ end
51
+ end
52
+
53
+ context 'Making a DELETE request' do
54
+ should 'urlencode values in params and have no payload' do
55
+ @mock.expects(:get).once.returns(test_response(test_mock_resource))
56
+ mock_resource = MockResource.retrieve("fake_id")
57
+
58
+ @mock.expects(:delete).once.with do |url, headers, payload|
59
+ url == "#{Paid.api_base}#{mock_resource.path}?reason=delinquent%20payments" && payload.nil?
60
+ end.returns(test_response({}))
61
+ mock_resource.delete(:reason => "delinquent payments")
62
+ end
63
+ end
64
+
65
+ context 'Making a PUT request' do
66
+ should 'have a payload with no query string' do
67
+ @mock.expects(:get).once.returns(test_response(test_mock_resource))
68
+ mock_resource = MockResource.retrieve("fake_id")
69
+ mock_resource.name = "new name"
70
+
71
+ @mock.expects(:put).once.with do |url, header, payload|
72
+ payload == { :name => "new name" }
73
+ end.returns(test_response(test_mock_resource))
74
+ mock_resource.save
75
+ end
76
+ end
77
+
78
+ context 'Making a POST request' do
79
+ should 'have a payload with no query string' do
80
+ params = { :name => "some name" }
81
+ @mock.expects(:post).once.with("#{Paid.api_base}#{MockResource.path}", anything, params).returns(test_response(test_mock_resource))
82
+
83
+ MockResource.create(params)
84
+ end
85
+ end
86
+
87
+ context 'APIClass :default_params' do
88
+ context 'api_instance_method' do
89
+ setup do
90
+ @response = test_response(test_mock_resource_list)
91
+ @mr = MockResource.new(test_mock_resource)
92
+ end
93
+
94
+ should 'call any provided lambdas and set the result as params' do
95
+ @mock.expects(:put).once.with(anything, anything, { :name => "new name" }).returns(@response)
96
+
97
+ @mr.name = "new name"
98
+ @mr.with_lambda
99
+ end
100
+
101
+ should 'call any method defined by a symbol and set the results as params' do
102
+ @mock.expects(:put).once.with(anything, anything, { :name => "new name" }).returns(@response)
103
+
104
+ @mr.name = "new name"
105
+ @mr.with_symbol
106
+ end
107
+
108
+ context 'order priority' do
109
+ # Intended order of priority:
110
+ # 1. Params. eg in mock_resource.save(params, opts), params should take
111
+ # priority with duplicate keys.
112
+ # 2. Arguments. These should take priority over default values from :params
113
+ # method.
114
+ # 3. Results from method defined by :default_params. This can be a symbol that is
115
+ # the name of a method to call, or a lambda (though a hash is preferred).
116
+ should 'prioritize params argument over args and default_params method' do
117
+ params = {
118
+ :name => "not arg name",
119
+ :tarray => ["not method tarray"],
120
+ :thash => { :val => "custom hash" }
121
+ }
122
+ @mock.expects(:put).once.with(anything, anything, params).returns(@response)
123
+
124
+ @mr.tarray = ["method array"]
125
+ @mr.with_symbol_and_args("arg name", params)
126
+ end
127
+
128
+ should 'prioritize arguments over :default_params' do
129
+ @mock.expects(:put).once.with(anything, anything, { :name => "arg name" }).returns(@response)
130
+
131
+ @mr.name = "changed name"
132
+ @mr.with_symbol_and_args("arg name")
133
+ end
134
+
135
+ should 'use :default_params method as even when args and params are present' do
136
+ @mock.expects(:put).once.with(anything, anything, {
137
+ :thash => { :val => "params hash" },
138
+ :name => "arg name",
139
+ :tarray => ["change array"],
140
+ }).returns(@response)
141
+
142
+ @mr.tarray = ["change array"]
143
+ @mr.with_symbol_and_args("arg name", { :thash => { :val => "params hash" } })
144
+ end
145
+ end
146
+ end
147
+
148
+ context 'api_class_method' do
149
+ setup do
150
+ @response = test_response(test_mock_resource_list)
151
+ end
152
+
153
+ should 'call any provided lambdas and set the result as params' do
154
+ @mock.expects(:post).once.with(anything, anything, MockResource.default_values).returns(@response)
155
+ MockResource.with_lambda
156
+ end
157
+
158
+ should 'call any method defined by a symbol and set the results as params' do
159
+ @mock.expects(:post).once.with(anything, anything, MockResource.default_values).returns(@response)
160
+ MockResource.with_symbol
161
+ end
162
+
163
+ context 'order priority' do
164
+ # Intended order of priority:
165
+ # 1. Params. eg in mock_resource.save(params, opts), params should take
166
+ # priority with duplicate keys.
167
+ # 2. Arguments. These should take priority over default values from :params
168
+ # method.
169
+ # 3. Results from method defined by :default_params. This can be a symbol that is
170
+ # the name of a method to call, or a lambda (though a hash is preferred).
171
+ should 'prioritize params argument over args and default_params method' do
172
+ params = {
173
+ :name => "not arg name",
174
+ :tarray => ["not method tarray"],
175
+ :thash => { :val => "custom hash" }
176
+ }
177
+ @mock.expects(:post).once.with(anything, anything, params).returns(@response)
178
+ MockResource.with_symbol_and_args("name arg", params)
179
+ end
180
+
181
+ should 'prioritize arguments over :default_params' do
182
+ params = Util.sorta_deep_clone(MockResource.default_values)
183
+ params[:name] = "name arg"
184
+ @mock.expects(:post).once.with(anything, anything, params).returns(@response)
185
+ MockResource.with_symbol_and_args("name arg", params)
186
+ end
187
+
188
+ should 'use :default_params method as even when args and params are present' do
189
+ params = {
190
+ :name => "name arg",
191
+ :tarray => MockResource.default_values[:tarray].dup,
192
+ :thash => { :val => "custom hash" }
193
+ }
194
+ @mock.expects(:post).once.with(anything, anything, params).returns(@response)
195
+ MockResource.with_symbol_and_args("name arg", { :thash => { :val => "custom hash" }})
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+
202
+ context 'APIClass#attribute' do
203
+ should 'create a getter method' do
204
+ assert(MockResource.method_defined?(:name))
205
+ end
206
+
207
+ should 'create a setter method' do
208
+ assert(MockResource.method_defined?(:name=))
209
+ end
210
+
211
+ should 'have no changed attributes after init' do
212
+ mr = MockResource.new(test_mock_resource)
213
+ assert(mr.changed_attributes.empty?)
214
+ end
215
+
216
+ should 'keep track of changed attributes' do
217
+ mr = MockResource.new(test_mock_resource)
218
+ assert(mr.changed_attributes.empty?)
219
+ mr.name = "new name"
220
+ assert_equal({:name => "new name"}, mr.changed_attributes)
221
+ end
222
+
223
+ should 'keep track of changed arrays' do
224
+ mr = MockResource.new(test_mock_resource)
225
+ assert(mr.changed_attributes.empty?)
226
+ mr.tarray << "new"
227
+ assert_equal({:tarray => test_mock_resource[:tarray] + ["new"]}, mr.changed_attributes)
228
+ end
229
+
230
+ should 'keep track of changed hashes' do
231
+ mr = MockResource.new(test_mock_resource)
232
+ assert(mr.changed_attributes.empty?)
233
+ mr.thash[:some_key] = "new value"
234
+ assert_equal({:thash => { :some_key => "new value" }}, mr.changed_attributes)
235
+ end
236
+
237
+ context 'constructors' do
238
+ should 'instantiate on #new' do
239
+ mr = MockResource.new(test_mock_resource)
240
+ assert(mr.nested.is_a?(NestedResource))
241
+ end
242
+
243
+ should 'instantiate on #construct' do
244
+ mr = MockResource.construct(test_mock_resource)
245
+ assert(mr.nested.is_a?(NestedResource))
246
+ end
247
+
248
+ should 'instantiate on #refresh_from' do
249
+ mr = MockResource.new('fake_id')
250
+ assert(mr.nested.nil?)
251
+
252
+ mr.refresh_from(test_mock_resource)
253
+ assert(mr.nested.is_a?(NestedResource))
254
+ end
255
+ end
256
+ end
257
+
258
+ context 'APIClass :constructor' do
259
+ context 'for api_class_method' do
260
+ setup do
261
+ @mock.expects(:get).once.returns(test_response(test_mock_resource))
262
+ end
263
+
264
+ should 'create a new MockResource with :constructor => :self' do
265
+ mr = MockResource.with_con_self
266
+ assert(mr.is_a?(MockResource))
267
+ end
268
+
269
+ should 'create a new MockResource with :constructor => MockResource' do
270
+ mr = MockResource.with_con_class
271
+ assert(mr.is_a?(MockResource))
272
+ end
273
+
274
+ should 'use the lambda with :constructor => lambda{...}' do
275
+ mr = MockResource.with_con_lambda
276
+ assert_equal("lamdba result", mr)
277
+ end
278
+
279
+ should 'create a new MockResource with no constructor defined' do
280
+ mr = MockResource.with_con_default
281
+ assert(mr.is_a?(MockResource))
282
+ end
283
+ end
284
+
285
+ context 'for api_instance_method' do
286
+ setup do
287
+ @mock.expects(:get).once.returns(test_response(test_mock_resource))
288
+ @mr = MockResource.new('fake_id')
289
+ end
290
+
291
+ should 'update this instance with :constructor => :self' do
292
+ @mr.with_con_self
293
+ assert(@mr.is_a?(MockResource))
294
+ assert_equal(test_mock_resource[:id], @mr.id)
295
+ end
296
+
297
+ should 'create a new instance with :constructor => MockResource' do
298
+ res = @mr.with_con_class
299
+ assert(res.is_a?(MockResource))
300
+ assert_not_equal(@mr.id, res.id)
301
+ end
302
+
303
+ should 'use the lambda with :constructor => lambda{...}' do
304
+ res = @mr.with_con_lambda
305
+ assert_equal("lamdba result", res)
306
+ end
307
+
308
+ should 'update this instance with no constructor defined' do
309
+ @mr.with_con_default
310
+ assert(@mr.is_a?(MockResource))
311
+ assert_equal(test_mock_resource[:id], @mr.id)
312
+ end
313
+ end
314
+ end
315
+
316
+ context 'APIClass api_*_method arguments' do
317
+ should 'throw an ArgumentError if too few arguments are provided' do
318
+ assert_raises(ArgumentError) { MockResource.retrieve }
319
+ end
320
+
321
+ should 'throw an ArgumentError with the name of missing arguments' do
322
+ begin
323
+ MockResource.retrieve
324
+ assert(false, "ArgumentError was expected.")
325
+ rescue ArgumentError => e
326
+ assert(e.message =~ /id/)
327
+ end
328
+ end
329
+
330
+ should 'throw an ArgumentError if too many arguments are provided' do
331
+ assert_raises(ArgumentError) { MockResource.retrieve(1, 2, {}, {}) }
332
+ end
333
+
334
+ should 'throw an ArgumentError if the param argument is invalid' do
335
+ assert_raises(ArgumentError) { MockResource.retrieve(1, 2) }
336
+ end
337
+
338
+ should 'throw an ArgumentError if the opts argument is invalid' do
339
+ assert_raises(ArgumentError) { MockResource.retrieve(1, {}, "abc") }
340
+ end
341
+
342
+ should 'not throw an ArgumentError if the opts or params are nil' do
343
+ @mock.expects(:get).times(2).returns(test_response(test_mock_resource))
344
+ MockResource.retrieve(1, nil, {})
345
+ MockResource.retrieve(1, {}, nil)
346
+ end
347
+
348
+ should 'urlencode unused arguments via GET' do
349
+ response = test_response(test_mock_resource)
350
+ @mock.expects(:get).with do |url, headers, params|
351
+ (url == "#{Paid.api_base}#{MockResource.path}/bval/many?a=aval&c=cval" ||
352
+ url == "#{Paid.api_base}#{MockResource.path}/bval/many?c=cval&a=aval") &&
353
+ params.nil?
354
+ end.returns(response)
355
+
356
+ MockResource.many_args_get("aval", "bval", "cval")
357
+ end
358
+
359
+ should 'assign unused arguments to the params payload via POST' do
360
+ response = test_response(test_mock_resource)
361
+ @mock.expects(:post).with("#{Paid.api_base}#{MockResource.path}/bval/many", anything, { :a => "aval", :c => "cval" }).returns(response)
362
+
363
+ MockResource.many_args_post("aval", "bval", "cval")
364
+ end
365
+ end
366
+
367
+ context 'APIClass api_instance_method paths' do
368
+ should 'use the provided argument if it is present' do
369
+ response = test_response(test_mock_resource)
370
+ @mock.expects(:get).with("#{Paid.api_base}/custom_path", anything, anything).returns(response)
371
+
372
+ mr = MockResource.new(test_mock_resource)
373
+ mr.custom_path("/custom_path")
374
+ end
375
+
376
+ should 'use the instance method for path values' do
377
+ response = test_response(test_mock_resource)
378
+ name = "/mock_resource_name"
379
+ @mock.expects(:get).with("#{Paid.api_base}#{name}", anything, anything).returns(response)
380
+
381
+ mr = MockResource.new(test_mock_resource)
382
+ mr.name = name
383
+ mr.name_path
384
+ end
385
+
386
+ should 'use the class method for path values' do
387
+ response = test_response(test_mock_resource)
388
+ @mock.expects(:get).with("#{Paid.api_base}#{MockResource.crazy}", anything, anything).returns(response)
389
+
390
+ mr = MockResource.new(test_mock_resource)
391
+ mr.crazy_path
392
+ end
393
+ end
394
+
395
+ context 'APIClass api_class_method paths' do
396
+ should 'use the provided argument if it is present' do
397
+ response = test_response(test_mock_resource)
398
+ @mock.expects(:get).with("#{Paid.api_base}#{MockResource.path}/fake_id", anything, anything).returns(response)
399
+
400
+ mr = MockResource.retrieve('fake_id')
401
+ end
402
+
403
+ should 'use the class method for path values' do
404
+ response = test_response(test_mock_resource)
405
+ @mock.expects(:get).with("#{Paid.api_base}#{MockResource.crazy}", anything, anything).returns(response)
406
+
407
+ MockResource.crazy_path
408
+ end
409
+ end
410
+
411
+ end
412
+ end