solidus_api 1.0.0.pre

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 (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