solidus_api 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of solidus_api might be problematic. Click here for more details.

Files changed (202) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/CHANGELOG.md +1 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE +27 -0
  6. data/Rakefile +16 -0
  7. data/app/controllers/spree/api/addresses_controller.rb +43 -0
  8. data/app/controllers/spree/api/base_controller.rb +189 -0
  9. data/app/controllers/spree/api/checkouts_controller.rb +133 -0
  10. data/app/controllers/spree/api/classifications_controller.rb +18 -0
  11. data/app/controllers/spree/api/config_controller.rb +6 -0
  12. data/app/controllers/spree/api/countries_controller.rb +23 -0
  13. data/app/controllers/spree/api/credit_cards_controller.rb +25 -0
  14. data/app/controllers/spree/api/images_controller.rb +47 -0
  15. data/app/controllers/spree/api/inventory_units_controller.rb +52 -0
  16. data/app/controllers/spree/api/line_items_controller.rb +74 -0
  17. data/app/controllers/spree/api/option_types_controller.rb +49 -0
  18. data/app/controllers/spree/api/option_values_controller.rb +58 -0
  19. data/app/controllers/spree/api/orders_controller.rb +155 -0
  20. data/app/controllers/spree/api/payments_controller.rb +81 -0
  21. data/app/controllers/spree/api/product_properties_controller.rb +72 -0
  22. data/app/controllers/spree/api/products_controller.rb +129 -0
  23. data/app/controllers/spree/api/promotions_controller.rb +26 -0
  24. data/app/controllers/spree/api/properties_controller.rb +71 -0
  25. data/app/controllers/spree/api/return_authorizations_controller.rb +71 -0
  26. data/app/controllers/spree/api/shipments_controller.rb +172 -0
  27. data/app/controllers/spree/api/states_controller.rb +35 -0
  28. data/app/controllers/spree/api/stock_items_controller.rb +84 -0
  29. data/app/controllers/spree/api/stock_locations_controller.rb +50 -0
  30. data/app/controllers/spree/api/stock_movements_controller.rb +42 -0
  31. data/app/controllers/spree/api/stock_transfers_controller.rb +19 -0
  32. data/app/controllers/spree/api/store_credit_events_controller.rb +9 -0
  33. data/app/controllers/spree/api/stores_controller.rb +55 -0
  34. data/app/controllers/spree/api/taxonomies_controller.rb +64 -0
  35. data/app/controllers/spree/api/taxons_controller.rb +93 -0
  36. data/app/controllers/spree/api/transfer_items_controller.rb +42 -0
  37. data/app/controllers/spree/api/users_controller.rb +56 -0
  38. data/app/controllers/spree/api/variants_controller.rb +75 -0
  39. data/app/controllers/spree/api/zones_controller.rb +50 -0
  40. data/app/helpers/spree/api/api_helpers.rb +190 -0
  41. data/app/models/spree/api_configuration.rb +5 -0
  42. data/app/models/spree/option_value_decorator.rb +9 -0
  43. data/app/views/spree/api/addresses/show.v1.rabl +10 -0
  44. data/app/views/spree/api/adjustments/show.v1.rabl +4 -0
  45. data/app/views/spree/api/config/money.v1.rabl +2 -0
  46. data/app/views/spree/api/config/show.v1.rabl +2 -0
  47. data/app/views/spree/api/countries/index.v1.rabl +7 -0
  48. data/app/views/spree/api/countries/show.v1.rabl +5 -0
  49. data/app/views/spree/api/credit_cards/index.v1.rabl +7 -0
  50. data/app/views/spree/api/credit_cards/show.v1.rabl +3 -0
  51. data/app/views/spree/api/errors/gateway_error.v1.rabl +2 -0
  52. data/app/views/spree/api/errors/invalid_api_key.v1.rabl +2 -0
  53. data/app/views/spree/api/errors/invalid_resource.v1.rabl +3 -0
  54. data/app/views/spree/api/errors/must_specify_api_key.v1.rabl +2 -0
  55. data/app/views/spree/api/errors/not_found.v1.rabl +2 -0
  56. data/app/views/spree/api/errors/unauthorized.v1.rabl +2 -0
  57. data/app/views/spree/api/errors/variant_not_in_stock_transfer.v1.rabl +2 -0
  58. data/app/views/spree/api/images/index.v1.rabl +4 -0
  59. data/app/views/spree/api/images/show.v1.rabl +6 -0
  60. data/app/views/spree/api/inventory_units/show.rabl +2 -0
  61. data/app/views/spree/api/line_items/new.v1.rabl +3 -0
  62. data/app/views/spree/api/line_items/show.v1.rabl +15 -0
  63. data/app/views/spree/api/option_types/index.v1.rabl +3 -0
  64. data/app/views/spree/api/option_types/show.v1.rabl +5 -0
  65. data/app/views/spree/api/option_values/index.v1.rabl +3 -0
  66. data/app/views/spree/api/option_values/show.v1.rabl +2 -0
  67. data/app/views/spree/api/orders/address.v1.rabl +0 -0
  68. data/app/views/spree/api/orders/canceled.v1.rabl +0 -0
  69. data/app/views/spree/api/orders/cart.v1.rabl +0 -0
  70. data/app/views/spree/api/orders/complete.v1.rabl +0 -0
  71. data/app/views/spree/api/orders/could_not_apply_coupon.v1.rabl +2 -0
  72. data/app/views/spree/api/orders/could_not_transition.v1.rabl +3 -0
  73. data/app/views/spree/api/orders/expected_total_mismatch.v1.rabl +2 -0
  74. data/app/views/spree/api/orders/index.v1.rabl +7 -0
  75. data/app/views/spree/api/orders/invalid_shipping_method.v1.rabl +2 -0
  76. data/app/views/spree/api/orders/mine.v1.rabl +9 -0
  77. data/app/views/spree/api/orders/order.v1.rabl +9 -0
  78. data/app/views/spree/api/orders/payment.v1.rabl +3 -0
  79. data/app/views/spree/api/orders/show.v1.rabl +52 -0
  80. data/app/views/spree/api/payments/credit_over_limit.v1.rabl +2 -0
  81. data/app/views/spree/api/payments/index.v1.rabl +7 -0
  82. data/app/views/spree/api/payments/new.v1.rabl +5 -0
  83. data/app/views/spree/api/payments/show.v1.rabl +2 -0
  84. data/app/views/spree/api/payments/update_forbidden.v1.rabl +2 -0
  85. data/app/views/spree/api/product_properties/index.v1.rabl +7 -0
  86. data/app/views/spree/api/product_properties/new.v1.rabl +2 -0
  87. data/app/views/spree/api/product_properties/show.v1.rabl +2 -0
  88. data/app/views/spree/api/products/index.v1.rabl +9 -0
  89. data/app/views/spree/api/products/new.v1.rabl +3 -0
  90. data/app/views/spree/api/products/product.v1.rabl +1 -0
  91. data/app/views/spree/api/products/show.v1.rabl +31 -0
  92. data/app/views/spree/api/promotions/handler.v1.rabl +5 -0
  93. data/app/views/spree/api/promotions/show.v1.rabl +2 -0
  94. data/app/views/spree/api/properties/index.v1.rabl +7 -0
  95. data/app/views/spree/api/properties/new.v1.rabl +2 -0
  96. data/app/views/spree/api/properties/show.v1.rabl +2 -0
  97. data/app/views/spree/api/return_authorizations/index.v1.rabl +7 -0
  98. data/app/views/spree/api/return_authorizations/new.v1.rabl +3 -0
  99. data/app/views/spree/api/return_authorizations/show.v1.rabl +2 -0
  100. data/app/views/spree/api/shared/stock_location_required.v1.rabl +2 -0
  101. data/app/views/spree/api/shipments/big.v1.rabl +48 -0
  102. data/app/views/spree/api/shipments/cannot_ready_shipment.v1.rabl +2 -0
  103. data/app/views/spree/api/shipments/mine.v1.rabl +9 -0
  104. data/app/views/spree/api/shipments/show.v1.rabl +32 -0
  105. data/app/views/spree/api/shipments/small.v1.rabl +37 -0
  106. data/app/views/spree/api/shipping_rates/show.v1.rabl +2 -0
  107. data/app/views/spree/api/states/index.v1.rabl +14 -0
  108. data/app/views/spree/api/states/show.v1.rabl +2 -0
  109. data/app/views/spree/api/stock_items/index.v1.rabl +7 -0
  110. data/app/views/spree/api/stock_items/show.v1.rabl +5 -0
  111. data/app/views/spree/api/stock_locations/index.v1.rabl +7 -0
  112. data/app/views/spree/api/stock_locations/show.v1.rabl +8 -0
  113. data/app/views/spree/api/stock_movements/index.v1.rabl +7 -0
  114. data/app/views/spree/api/stock_movements/show.v1.rabl +5 -0
  115. data/app/views/spree/api/stock_transfers/receive.v1.rabl +5 -0
  116. data/app/views/spree/api/store_credit_events/mine.v1.rabl +10 -0
  117. data/app/views/spree/api/stores/index.v1.rabl +4 -0
  118. data/app/views/spree/api/stores/show.v1.rabl +2 -0
  119. data/app/views/spree/api/taxonomies/index.v1.rabl +7 -0
  120. data/app/views/spree/api/taxonomies/jstree.rabl +8 -0
  121. data/app/views/spree/api/taxonomies/nested.v1.rabl +11 -0
  122. data/app/views/spree/api/taxonomies/new.v1.rabl +3 -0
  123. data/app/views/spree/api/taxonomies/show.v1.rabl +15 -0
  124. data/app/views/spree/api/taxons/index.v1.rabl +12 -0
  125. data/app/views/spree/api/taxons/jstree.rabl +8 -0
  126. data/app/views/spree/api/taxons/new.v1.rabl +3 -0
  127. data/app/views/spree/api/taxons/show.v1.rabl +6 -0
  128. data/app/views/spree/api/taxons/taxons.v1.rabl +5 -0
  129. data/app/views/spree/api/transfer_items/show.v1.rabl +6 -0
  130. data/app/views/spree/api/users/index.v1.rabl +7 -0
  131. data/app/views/spree/api/users/new.v1.rabl +3 -0
  132. data/app/views/spree/api/users/show.v1.rabl +10 -0
  133. data/app/views/spree/api/variants/big.v1.rabl +17 -0
  134. data/app/views/spree/api/variants/index.v1.rabl +9 -0
  135. data/app/views/spree/api/variants/new.v1.rabl +2 -0
  136. data/app/views/spree/api/variants/show.v1.rabl +3 -0
  137. data/app/views/spree/api/variants/small.v1.rabl +17 -0
  138. data/app/views/spree/api/zones/index.v1.rabl +7 -0
  139. data/app/views/spree/api/zones/show.v1.rabl +6 -0
  140. data/config/initializers/metal_load_paths.rb +1 -0
  141. data/config/locales/en.yml +29 -0
  142. data/config/routes.rb +139 -0
  143. data/db/migrate/20100107141738_add_api_key_to_spree_users.rb +7 -0
  144. data/db/migrate/20120411123334_resize_api_key_field.rb +7 -0
  145. data/db/migrate/20120530054546_rename_api_key_to_spree_api_key.rb +7 -0
  146. data/db/migrate/20131017162334_add_index_to_user_spree_api_key.rb +7 -0
  147. data/lib/solidus_api.rb +1 -0
  148. data/lib/spree/api/engine.rb +38 -0
  149. data/lib/spree/api/responders/rabl_template.rb +31 -0
  150. data/lib/spree/api/responders.rb +11 -0
  151. data/lib/spree/api/testing_support/caching.rb +10 -0
  152. data/lib/spree/api/testing_support/helpers.rb +44 -0
  153. data/lib/spree/api/testing_support/setup.rb +16 -0
  154. data/lib/spree/api.rb +10 -0
  155. data/lib/spree_api.rb +3 -0
  156. data/script/rails +9 -0
  157. data/solidus_api.gemspec +21 -0
  158. data/spec/controllers/spree/api/addresses_controller_spec.rb +56 -0
  159. data/spec/controllers/spree/api/base_controller_spec.rb +164 -0
  160. data/spec/controllers/spree/api/checkouts_controller_spec.rb +386 -0
  161. data/spec/controllers/spree/api/classifications_controller_spec.rb +48 -0
  162. data/spec/controllers/spree/api/config_controller_spec.rb +23 -0
  163. data/spec/controllers/spree/api/countries_controller_spec.rb +48 -0
  164. data/spec/controllers/spree/api/credit_cards_controller_spec.rb +80 -0
  165. data/spec/controllers/spree/api/images_controller_spec.rb +93 -0
  166. data/spec/controllers/spree/api/inventory_units_controller_spec.rb +50 -0
  167. data/spec/controllers/spree/api/line_items_controller_spec.rb +186 -0
  168. data/spec/controllers/spree/api/option_types_controller_spec.rb +116 -0
  169. data/spec/controllers/spree/api/option_values_controller_spec.rb +135 -0
  170. data/spec/controllers/spree/api/orders_controller_spec.rb +759 -0
  171. data/spec/controllers/spree/api/payments_controller_spec.rb +254 -0
  172. data/spec/controllers/spree/api/product_properties_controller_spec.rb +116 -0
  173. data/spec/controllers/spree/api/products_controller_spec.rb +454 -0
  174. data/spec/controllers/spree/api/promotion_application_spec.rb +50 -0
  175. data/spec/controllers/spree/api/promotions_controller_spec.rb +64 -0
  176. data/spec/controllers/spree/api/properties_controller_spec.rb +102 -0
  177. data/spec/controllers/spree/api/return_authorizations_controller_spec.rb +173 -0
  178. data/spec/controllers/spree/api/shipments_controller_spec.rb +252 -0
  179. data/spec/controllers/spree/api/states_controller_spec.rb +82 -0
  180. data/spec/controllers/spree/api/stock_items_controller_spec.rb +307 -0
  181. data/spec/controllers/spree/api/stock_locations_controller_spec.rb +172 -0
  182. data/spec/controllers/spree/api/stock_movements_controller_spec.rb +84 -0
  183. data/spec/controllers/spree/api/stock_transfers_controller_spec.rb +83 -0
  184. data/spec/controllers/spree/api/store_credit_events_controller_spec.rb +68 -0
  185. data/spec/controllers/spree/api/stores_controller_spec.rb +133 -0
  186. data/spec/controllers/spree/api/taxonomies_controller_spec.rb +114 -0
  187. data/spec/controllers/spree/api/taxons_controller_spec.rb +177 -0
  188. data/spec/controllers/spree/api/transfer_items_controller_spec.rb +152 -0
  189. data/spec/controllers/spree/api/unauthenticated_products_controller_spec.rb +26 -0
  190. data/spec/controllers/spree/api/users_controller_spec.rb +153 -0
  191. data/spec/controllers/spree/api/variants_controller_spec.rb +235 -0
  192. data/spec/controllers/spree/api/zones_controller_spec.rb +115 -0
  193. data/spec/features/checkout_spec.rb +187 -0
  194. data/spec/fixtures/thinking-cat.jpg +0 -0
  195. data/spec/models/spree/legacy_user_spec.rb +45 -0
  196. data/spec/requests/rabl_cache_spec.rb +32 -0
  197. data/spec/shared_examples/protect_product_actions.rb +17 -0
  198. data/spec/spec_helper.rb +60 -0
  199. data/spec/support/controller_hacks.rb +38 -0
  200. data/spec/support/database_cleaner.rb +14 -0
  201. data/spec/support/have_attributes_matcher.rb +13 -0
  202. metadata +334 -0
@@ -0,0 +1,164 @@
1
+ require 'spec_helper'
2
+
3
+ class FakesController < Spree::Api::BaseController
4
+ end
5
+
6
+ describe Spree::Api::BaseController, :type => :controller do
7
+ render_views
8
+ controller(Spree::Api::BaseController) do
9
+ def index
10
+ render :text => { "products" => [] }.to_json
11
+ end
12
+ end
13
+
14
+ before do
15
+ @routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
16
+ r.draw { get 'index', to: 'spree/api/base#index' }
17
+ end
18
+ end
19
+
20
+ context "when validating based on an order token" do
21
+ let!(:order) { create :order }
22
+
23
+ context "with a correct order token" do
24
+ it "succeeds" do
25
+ api_get :index, order_token: order.guest_token, order_id: order.number
26
+ expect(response.status).to eq(200)
27
+ end
28
+
29
+ it "succeeds with an order_number parameter" do
30
+ api_get :index, order_token: order.guest_token, order_number: order.number
31
+ expect(response.status).to eq(200)
32
+ end
33
+ end
34
+
35
+ context "with an incorrect order token" do
36
+ it "returns unauthorized" do
37
+ api_get :index, order_token: "NOT_A_TOKEN", order_id: order.number
38
+ expect(response.status).to eq(401)
39
+ end
40
+ end
41
+ end
42
+
43
+ context "cannot make a request to the API" do
44
+ it "without an API key" do
45
+ api_get :index
46
+ expect(json_response).to eq({ "error" => "You must specify an API key." })
47
+ expect(response.status).to eq(401)
48
+ end
49
+
50
+ it "with an invalid API key" do
51
+ request.headers["X-Spree-Token"] = "fake_key"
52
+ get :index, {}
53
+ expect(json_response).to eq({ "error" => "Invalid API key (fake_key) specified." })
54
+ expect(response.status).to eq(401)
55
+ end
56
+
57
+ it "using an invalid token param" do
58
+ get :index, :token => "fake_key"
59
+ expect(json_response).to eq({ "error" => "Invalid API key (fake_key) specified." })
60
+ end
61
+ end
62
+
63
+ it 'chatches StandardError' do
64
+ expect(subject).to receive(:authenticate_user).and_return(true)
65
+ expect(subject).to receive(:load_user_roles).and_return(true)
66
+ expect(subject).to receive(:index).and_raise("no joy")
67
+ get :index, :token => "fake_key"
68
+ expect(json_response).to eq({ "exception" => "no joy" })
69
+ end
70
+
71
+ it 'raises Exception' do
72
+ expect(subject).to receive(:authenticate_user).and_return(true)
73
+ expect(subject).to receive(:load_user_roles).and_return(true)
74
+ expect(subject).to receive(:index).and_raise(Exception.new("no joy"))
75
+ expect {
76
+ get :index, :token => "fake_key"
77
+ }.to raise_error(Exception, "no joy")
78
+ end
79
+
80
+ it "maps semantic keys to nested_attributes keys" do
81
+ klass = double(:nested_attributes_options => { :line_items => {},
82
+ :bill_address => {} })
83
+ attributes = { 'line_items' => { :id => 1 },
84
+ 'bill_address' => { :id => 2 },
85
+ 'name' => 'test order' }
86
+
87
+ mapped = subject.map_nested_attributes_keys(klass, attributes)
88
+ expect(mapped.has_key?('line_items_attributes')).to be true
89
+ expect(mapped.has_key?('name')).to be true
90
+ end
91
+
92
+ it "lets a subclass override the product associations that are eager-loaded" do
93
+ expect(controller.respond_to?(:product_includes, true)).to be
94
+ end
95
+
96
+ describe '#error_during_processing' do
97
+ controller(FakesController) do
98
+ # GET /foo
99
+ # Simulates a failed API call.
100
+ def foo
101
+ raise StandardError
102
+ end
103
+ end
104
+
105
+ # What would be placed in config/initializers/spree.rb
106
+ Spree::Api::BaseController.error_notifier = Proc.new do |e, controller|
107
+ MockHoneybadger.notify_or_ignore(e, rack_env: controller.request.env)
108
+ end
109
+
110
+ ##
111
+ # Fake HB alert class
112
+ class MockHoneybadger
113
+ # https://github.com/honeybadger-io/honeybadger-ruby/blob/master/lib/honeybadger.rb#L136
114
+ def self.notify_or_ignore(exception, opts = {})
115
+ end
116
+ end
117
+
118
+ before do
119
+ user = double(email: "spree@example.com")
120
+ allow(user).to receive_message_chain :spree_roles, pluck: []
121
+ allow(Spree.user_class).to receive_messages find_by: user
122
+ @routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
123
+ r.draw { get 'foo' => 'fakes#foo' }
124
+ end
125
+ end
126
+
127
+ it 'should notify notify_error_during_processing' do
128
+ expect(MockHoneybadger).to receive(:notify_or_ignore).once.with(kind_of(Exception), rack_env: kind_of(Hash))
129
+ api_get :foo, token: 123
130
+ expect(response.status).to eq(422)
131
+ end
132
+ end
133
+
134
+ context 'lock_order' do
135
+ let!(:order) { create :order }
136
+
137
+ controller(Spree::Api::BaseController) do
138
+ around_filter :lock_order
139
+
140
+ def index
141
+ render :text => { "products" => [] }.to_json
142
+ end
143
+ end
144
+
145
+ context 'without an existing lock' do
146
+ it 'succeeds' do
147
+ api_get :index, order_token: order.guest_token, order_id: order.number
148
+ response.status.should == 200
149
+ end
150
+ end
151
+
152
+ context 'with an existing lock' do
153
+ around do |example|
154
+ Spree::OrderMutex.with_lock!(order) { example.run }
155
+ end
156
+
157
+ it 'returns a 409 conflict' do
158
+ api_get :index, order_token: order.guest_token, order_id: order.number
159
+ response.status.should == 409
160
+ end
161
+ end
162
+ end
163
+
164
+ end
@@ -0,0 +1,386 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Api::CheckoutsController, type: :controller do
5
+ render_views
6
+
7
+ before(:each) do
8
+ stub_authentication!
9
+ Spree::Config[:track_inventory_levels] = false
10
+ country_zone = create(:zone, name: 'CountryZone')
11
+ @state = create(:state)
12
+ @country = @state.country
13
+ country_zone.members.create(zoneable: @country)
14
+ create(:stock_location)
15
+
16
+ @shipping_method = create(:shipping_method, zones: [country_zone])
17
+ @payment_method = create(:credit_card_payment_method)
18
+ end
19
+
20
+ after do
21
+ Spree::Config[:track_inventory_levels] = true
22
+ end
23
+
24
+ context "PUT 'update'" do
25
+ let(:order) do
26
+ order = create(:order_with_line_items)
27
+ # Order should be in a pristine state
28
+ # Without doing this, the order may transition from 'cart' straight to 'delivery'
29
+ order.shipments.delete_all
30
+ order
31
+ end
32
+
33
+ before(:each) do
34
+ allow_any_instance_of(Order).to receive_messages(confirmation_required?: true)
35
+ allow_any_instance_of(Order).to receive_messages(payment_required?: true)
36
+ end
37
+
38
+ it "should transition a recently created order from cart to address" do
39
+ expect(order.state).to eq "cart"
40
+ expect(order.email).not_to be_nil
41
+ api_put :update, id: order.to_param, order_token: order.guest_token
42
+ expect(order.reload.state).to eq "address"
43
+ end
44
+
45
+ it "should transition a recently created order from cart to address with order token in header" do
46
+ expect(order.state).to eq "cart"
47
+ expect(order.email).not_to be_nil
48
+ request.headers["X-Spree-Order-Token"] = order.guest_token
49
+ api_put :update, :id => order.to_param
50
+ expect(order.reload.state).to eq "address"
51
+ end
52
+
53
+ it "can take line_items_attributes as a parameter" do
54
+ line_item = order.line_items.first
55
+ api_put :update, id: order.to_param, order_token: order.guest_token,
56
+ order: { line_items_attributes: { 0 => { id: line_item.id, quantity: 1 } } }
57
+ expect(response.status).to eq(200)
58
+ expect(order.reload.state).to eq "address"
59
+ end
60
+
61
+ it "can take line_items as a parameter" do
62
+ line_item = order.line_items.first
63
+ api_put :update, id: order.to_param, order_token: order.guest_token,
64
+ order: { line_items: { 0 => { id: line_item.id, quantity: 1 } } }
65
+ expect(response.status).to eq(200)
66
+ expect(order.reload.state).to eq "address"
67
+ end
68
+
69
+ it "will return an error if the order cannot transition" do
70
+ skip "not sure if this test is valid"
71
+ order.bill_address = nil
72
+ order.save
73
+ order.update_column(:state, "address")
74
+ api_put :update, id: order.to_param, order_token: order.guest_token
75
+ # Order has not transitioned
76
+ expect(response.status).to eq(422)
77
+ end
78
+
79
+ context "transitioning to delivery" do
80
+ before do
81
+ order.update_column(:state, "address")
82
+ end
83
+
84
+ let(:address) do
85
+ {
86
+ firstname: 'John',
87
+ lastname: 'Doe',
88
+ address1: '7735 Old Georgetown Road',
89
+ city: 'Bethesda',
90
+ phone: '3014445002',
91
+ zipcode: '20814',
92
+ state_id: @state.id,
93
+ country_id: @country.id
94
+ }
95
+ end
96
+
97
+ it "can update addresses and transition from address to delivery" do
98
+ api_put :update,
99
+ id: order.to_param, order_token: order.guest_token,
100
+ order: {
101
+ bill_address_attributes: address,
102
+ ship_address_attributes: address
103
+ }
104
+ expect(json_response['state']).to eq('delivery')
105
+ expect(json_response['bill_address']['firstname']).to eq('John')
106
+ expect(json_response['ship_address']['firstname']).to eq('John')
107
+ expect(response.status).to eq(200)
108
+ end
109
+
110
+ # Regression Spec for #5389 & #5880
111
+ it "can update addresses but not transition to delivery w/o shipping setup" do
112
+ Spree::ShippingMethod.destroy_all
113
+ api_put :update,
114
+ id: order.to_param, order_token: order.guest_token,
115
+ order: {
116
+ bill_address_attributes: address,
117
+ ship_address_attributes: address
118
+ }
119
+ expect(json_response['error']).to eq(I18n.t(:could_not_transition, scope: "spree.api.order"))
120
+ expect(response.status).to eq(422)
121
+ end
122
+
123
+ # Regression test for #4498
124
+ it "does not contain duplicate variant data in delivery return" do
125
+ api_put :update,
126
+ id: order.to_param, order_token: order.guest_token,
127
+ order: {
128
+ bill_address_attributes: address,
129
+ ship_address_attributes: address
130
+ }
131
+ # Shipments manifests should not return the ENTIRE variant
132
+ # This information is already present within the order's line items
133
+ expect(json_response['shipments'].first['manifest'].first['variant']).to be_nil
134
+ expect(json_response['shipments'].first['manifest'].first['variant_id']).to_not be_nil
135
+ end
136
+ end
137
+
138
+ it "can update shipping method and transition from delivery to payment" do
139
+ order.update_column(:state, "delivery")
140
+ shipment = create(:shipment, order: order)
141
+ shipment.refresh_rates
142
+ shipping_rate = shipment.shipping_rates.where(selected: false).first
143
+ api_put :update, id: order.to_param, order_token: order.guest_token,
144
+ order: { shipments_attributes: { "0" => { selected_shipping_rate_id: shipping_rate.id, id: shipment.id } } }
145
+ expect(response.status).to eq(200)
146
+ # Find the correct shipment...
147
+ json_shipment = json_response['shipments'].detect { |s| s["id"] == shipment.id }
148
+ # Find the correct shipping rate for that shipment...
149
+ json_shipping_rate = json_shipment['shipping_rates'].detect { |sr| sr["id"] == shipping_rate.id }
150
+ # ... And finally ensure that it's selected
151
+ expect(json_shipping_rate['selected']).to be true
152
+ # Order should automatically transfer to payment because all criteria are met
153
+ expect(json_response['state']).to eq('payment')
154
+ end
155
+
156
+ it "can update payment method and transition from payment to confirm" do
157
+ order.update_column(:state, "payment")
158
+ Spree::Gateway::Bogus.any_instance.stub(:source_required?).and_return(false)
159
+ api_put :update, id: order.to_param, order_token: order.guest_token,
160
+ order: { payments_attributes: [{ payment_method_id: @payment_method.id }] }
161
+ expect(json_response['state']).to eq('confirm')
162
+ expect(json_response['payments'][0]['payment_method']['name']).to eq(@payment_method.name)
163
+ expect(json_response['payments'][0]['amount']).to eq(order.total.to_s)
164
+ expect(response.status).to eq(200)
165
+ end
166
+
167
+ it "returns errors when source is required and missing" do
168
+ order.update_column(:state, "payment")
169
+ api_put :update, :id => order.to_param, :order_token => order.guest_token,
170
+ :order => { :payments_attributes => [{ :payment_method_id => @payment_method.id }] }
171
+ response.status.should == 422
172
+ source_errors = json_response['errors']['payments.source']
173
+ source_errors.should include("can't be blank")
174
+ end
175
+
176
+ it "can update payment method with source and transition from payment to confirm" do
177
+ order.update_column(:state, "payment")
178
+ source_attributes = {
179
+ number: "4111111111111111",
180
+ month: 1.month.from_now.month,
181
+ year: 1.month.from_now.year,
182
+ verification_value: "123",
183
+ name: "Spree Commerce"
184
+ }
185
+
186
+ api_put :update, id: order.to_param, order_token: order.guest_token,
187
+ order: { payments_attributes: [{ payment_method_id: @payment_method.id.to_s }]},
188
+ payment_source: { @payment_method.id.to_s => source_attributes }
189
+ expect(json_response['payments'][0]['payment_method']['name']).to eq(@payment_method.name)
190
+ expect(json_response['payments'][0]['amount']).to eq(order.total.to_s)
191
+ expect(response.status).to eq(200)
192
+ end
193
+
194
+ it "returns errors when source is missing attributes" do
195
+ order.update_column(:state, "payment")
196
+ api_put :update, id: order.to_param, order_token: order.guest_token,
197
+ order: {
198
+ payments_attributes: [{ payment_method_id: @payment_method.id }]
199
+ },
200
+ payment_source: {
201
+ @payment_method.id.to_s => { name: "Spree" }
202
+ }
203
+
204
+ expect(response.status).to eq(422)
205
+ cc_errors = json_response['errors']['payments.Credit Card']
206
+ expect(cc_errors).to include("Number can't be blank")
207
+ expect(cc_errors).to include("Month is not a number")
208
+ expect(cc_errors).to include("Year is not a number")
209
+ expect(cc_errors).to include("Verification Value can't be blank")
210
+ end
211
+
212
+ it "allow users to reuse a credit card" do
213
+ order.update_column(:state, "payment")
214
+ credit_card = create(:credit_card, user_id: order.user_id, payment_method_id: @payment_method.id)
215
+
216
+ api_put :update, id: order.to_param, order_token: order.guest_token,
217
+ order: { existing_card: credit_card.id }
218
+
219
+ expect(response.status).to eq 200
220
+ expect(order.credit_cards).to match_array [credit_card]
221
+ end
222
+
223
+ it "can transition from confirm to complete" do
224
+ order.update_columns(completed_at: Time.now, state: 'complete')
225
+ allow_any_instance_of(Spree::Order).to receive_messages(payment_required?: false)
226
+ api_put :update, id: order.to_param, order_token: order.guest_token
227
+ expect(json_response['state']).to eq('complete')
228
+ expect(response.status).to eq(200)
229
+ end
230
+
231
+ it "returns the order if the order is already complete" do
232
+ order.update_columns(completed_at: Time.now, state: 'complete')
233
+ api_put :update, id: order.to_param, order_token: order.guest_token
234
+ expect(json_response['number']).to eq(order.number)
235
+ expect(response.status).to eq(200)
236
+ end
237
+
238
+ # Regression test for #3784
239
+ it "can update the special instructions for an order" do
240
+ instructions = "Don't drop it. (Please)"
241
+ api_put :update, id: order.to_param, order_token: order.guest_token,
242
+ order: { special_instructions: instructions }
243
+ expect(json_response['special_instructions']).to eql(instructions)
244
+ end
245
+
246
+ context "as an admin" do
247
+ sign_in_as_admin!
248
+ it "can assign a user to the order" do
249
+ user = create(:user)
250
+ # Need to pass email as well so that validations succeed
251
+ api_put :update, id: order.to_param, order_token: order.guest_token,
252
+ order: { user_id: user.id, email: "guest@spreecommerce.com" }
253
+ expect(response.status).to eq(200)
254
+ expect(json_response['user_id']).to eq(user.id)
255
+ end
256
+ end
257
+
258
+ it "can assign an email to the order" do
259
+ api_put :update, id: order.to_param, order_token: order.guest_token,
260
+ order: { email: "guest@spreecommerce.com" }
261
+ expect(json_response['email']).to eq("guest@spreecommerce.com")
262
+ expect(response.status).to eq(200)
263
+ end
264
+
265
+ it "can apply a coupon code to an order" do
266
+ skip "ensure that the order totals are properly updated, see frontend orders_controller or checkout_controller as example"
267
+
268
+ order.update_column(:state, "payment")
269
+ expect(PromotionHandler::Coupon).to receive(:new).with(order).and_call_original
270
+ expect_any_instance_of(PromotionHandler::Coupon).to receive(:apply).and_return({ coupon_applied?: true })
271
+ api_put :update, :id => order.to_param, order_token: order.guest_token, order: { coupon_code: "foobar" }
272
+ end
273
+ end
274
+
275
+ context "PUT 'next'" do
276
+ let!(:order) { create(:order_with_line_items) }
277
+ it "cannot transition to address without a line item" do
278
+ order.line_items.delete_all
279
+ order.update_column(:email, "spree@example.com")
280
+ api_put :next, id: order.to_param, order_token: order.guest_token
281
+ expect(response.status).to eq(422)
282
+ expect(json_response["errors"]["base"]).to include(Spree.t(:there_are_no_items_for_this_order))
283
+ end
284
+
285
+ it "can transition an order to the next state" do
286
+ order.update_column(:email, "spree@example.com")
287
+
288
+ api_put :next, id: order.to_param, order_token: order.guest_token
289
+ expect(response.status).to eq(200)
290
+ expect(json_response['state']).to eq('address')
291
+ end
292
+
293
+ it "cannot transition if order email is blank" do
294
+ order.update_columns(
295
+ state: 'address',
296
+ email: nil
297
+ )
298
+
299
+ api_put :next, :id => order.to_param, :order_token => order.guest_token
300
+ expect(response.status).to eq(422)
301
+ expect(json_response['error']).to match(/could not be transitioned/)
302
+ end
303
+ end
304
+
305
+ # NOTE: Temporarily making "next" behave just like "complete" when order is in confirm state
306
+ # Using "next" this way is deprecated.
307
+ [:next, :complete].each do |action|
308
+ context "#{action}" do
309
+ context "with order in confirm state" do
310
+ subject do
311
+ api_put action, params
312
+ end
313
+
314
+ let(:params) { {id: order.to_param, order_token: order.guest_token} }
315
+ let(:order) { create(:order_with_line_items) }
316
+
317
+ before do
318
+ order.update_column(:state, "confirm")
319
+
320
+ if action == :next
321
+ #ActiveSupport::Deprecation.should_receive(:warn).once
322
+ end
323
+ end
324
+
325
+ it "can transition from confirm to complete" do
326
+ Spree::Order.any_instance.stub(:payment_required? => false)
327
+ subject
328
+ json_response['state'].should == 'complete'
329
+ response.status.should == 200
330
+ end
331
+
332
+ it "returns a sensible error when no payment method is specified" do
333
+ # api_put :complete, :id => order.to_param, :order_token => order.token, :order => {}
334
+ subject
335
+ json_response["errors"]["base"].should include(Spree.t(:no_payment_found))
336
+ end
337
+
338
+ context "with mismatched expected_total" do
339
+ let(:params) { super().merge(expected_total: order.total + 1) }
340
+
341
+ it "returns an error if expected_total is present and does not match actual total" do
342
+ # api_put :complete, :id => order.to_param, :order_token => order.token, :expected_total => order.total + 1
343
+ subject
344
+ response.status.should == 400
345
+ json_response['errors']['expected_total'].should include(Spree.t(:expected_total_mismatch, :scope => 'api.order'))
346
+ end
347
+ end
348
+ end
349
+ end
350
+
351
+ context 'insufficient stock' do
352
+ let(:order) { create(:order_with_line_items) }
353
+ before do
354
+ expect_any_instance_of(Spree::Order).to receive(:next!).and_raise(Spree::Order::InsufficientStock)
355
+ end
356
+
357
+ subject { api_put :next, :id => order.to_param, :order_token => order.guest_token }
358
+
359
+ it "should return a 422" do
360
+ expect(subject.status).to eq(422)
361
+ end
362
+
363
+ it "returns an error message" do
364
+ subject
365
+ expect(JSON.parse(response.body)).to eq(
366
+ {"errors" => ["Quantity is not available for items in your order"], "type" => "insufficient_stock"}
367
+ )
368
+ end
369
+ end
370
+ end
371
+
372
+ context "PUT 'advance'" do
373
+ let!(:order) { create(:order_with_line_items) }
374
+
375
+ it 'continues to advance advances an order while it can move forward' do
376
+ expect_any_instance_of(Spree::Order).to receive(:next).exactly(3).times.and_return(true, true, false)
377
+ api_put :advance, id: order.to_param, order_token: order.guest_token
378
+ end
379
+
380
+ it 'returns the order' do
381
+ api_put :advance, id: order.to_param, order_token: order.guest_token
382
+ expect(json_response['id']).to eq(order.id)
383
+ end
384
+ end
385
+ end
386
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Api::ClassificationsController, type: :controller do
5
+ let(:taxon) do
6
+ taxon = create(:taxon)
7
+
8
+ 3.times do
9
+ product = create(:product)
10
+ product.taxons << taxon
11
+ end
12
+ taxon
13
+ end
14
+
15
+ before do
16
+ stub_authentication!
17
+ end
18
+
19
+ context "as a user" do
20
+ it "cannot change the order of a product" do
21
+ api_put :update, taxon_id: taxon, product_id: taxon.products.first, position: 1
22
+ expect(response.status).to eq(401)
23
+ end
24
+ end
25
+
26
+ context "as an admin" do
27
+ sign_in_as_admin!
28
+
29
+ let(:last_product) { taxon.products.last }
30
+
31
+ it "can change the order a product" do
32
+ classification = taxon.classifications.find_by(product_id: last_product.id)
33
+ expect(classification.position).to eq(3)
34
+ api_put :update, taxon_id: taxon, product_id: last_product, position: 0
35
+ expect(response.status).to eq(200)
36
+ expect(classification.reload.position).to eq(1)
37
+ end
38
+
39
+ it "should touch the taxon" do
40
+ taxon.update_attributes(updated_at: Time.now - 10.seconds)
41
+ taxon_last_updated_at = taxon.updated_at
42
+ api_put :update, taxon_id: taxon, product_id: last_product, position: 0
43
+ taxon.reload
44
+ expect(taxon_last_updated_at.to_i).to_not eq(taxon.updated_at.to_i)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Api::ConfigController, :type => :controller do
5
+ render_views
6
+
7
+ before do
8
+ stub_authentication!
9
+ end
10
+
11
+ it "returns Spree::Money settings" do
12
+ api_get :money
13
+ expect(response).to be_success
14
+ expect(json_response["symbol"]).to eq("$")
15
+ end
16
+
17
+ it "returns some configuration settings" do
18
+ api_get :show
19
+ expect(response).to be_success
20
+ expect(json_response["default_country_id"]).to eq(Spree::Config[:default_country_id])
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Api::CountriesController, :type => :controller do
5
+ render_views
6
+
7
+ before do
8
+ stub_authentication!
9
+ @state = create(:state)
10
+ @country = @state.country
11
+ end
12
+
13
+ it "gets all countries" do
14
+ api_get :index
15
+ expect(json_response['countries'].first['iso3']).to eq @country.iso3
16
+ end
17
+
18
+ context "with two countries" do
19
+ before { @zambia = create(:country, :name => "Zambia") }
20
+
21
+ it "can view all countries" do
22
+ api_get :index
23
+ expect(json_response['count']).to eq(2)
24
+ expect(json_response['current_page']).to eq(1)
25
+ expect(json_response['pages']).to eq(1)
26
+ end
27
+
28
+ it 'can query the results through a paramter' do
29
+ api_get :index, :q => { :name_cont => 'zam' }
30
+ expect(json_response['count']).to eq(1)
31
+ expect(json_response['countries'].first['name']).to eq @zambia.name
32
+ end
33
+
34
+ it 'can control the page size through a parameter' do
35
+ api_get :index, :per_page => 1
36
+ expect(json_response['count']).to eq(1)
37
+ expect(json_response['current_page']).to eq(1)
38
+ expect(json_response['pages']).to eq(2)
39
+ end
40
+ end
41
+
42
+ it "includes states" do
43
+ api_get :show, :id => @country.id
44
+ states = json_response['states']
45
+ expect(states.first['name']).to eq @state.name
46
+ end
47
+ end
48
+ end