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,153 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Api::UsersController, :type => :controller do
5
+ render_views
6
+
7
+ let(:user) { create(:user, spree_api_key: SecureRandom.hex) }
8
+ let(:stranger) { create(:user, :email => 'stranger@example.com') }
9
+ let(:attributes) { [:id, :email, :created_at, :updated_at] }
10
+
11
+ context "as a normal user" do
12
+ it "can get own details" do
13
+ api_get :show, id: user.id, token: user.spree_api_key
14
+
15
+ expect(json_response['email']).to eq user.email
16
+ end
17
+
18
+ it "cannot get other users details" do
19
+ api_get :show, id: stranger.id, token: user.spree_api_key
20
+
21
+ assert_not_found!
22
+ end
23
+
24
+ it "can learn how to create a new user" do
25
+ api_get :new, token: user.spree_api_key
26
+ expect(json_response["attributes"]).to eq(attributes.map(&:to_s))
27
+ end
28
+
29
+ it "can create a new user" do
30
+ user_params = {
31
+ :email => 'new@example.com', :password => 'spree123', :password_confirmation => 'spree123'
32
+ }
33
+
34
+ api_post :create, :user => user_params, token: user.spree_api_key
35
+ expect(json_response['email']).to eq 'new@example.com'
36
+ end
37
+
38
+ # there's no validations on LegacyUser?
39
+ xit "cannot create a new user with invalid attributes" do
40
+ api_post :create, :user => {}, token: user.spree_api_key
41
+ expect(response.status).to eq(422)
42
+ expect(json_response["error"]).to eq("Invalid resource. Please fix errors and try again.")
43
+ errors = json_response["errors"]
44
+ end
45
+
46
+ it "can update own details" do
47
+ country = create(:country)
48
+ api_put :update, id: user.id, token: user.spree_api_key, user: {
49
+ email: "mine@example.com",
50
+ bill_address_attributes: {
51
+ first_name: 'First',
52
+ last_name: 'Last',
53
+ address1: '1 Test Rd',
54
+ city: 'City',
55
+ country_id: country.id,
56
+ state_id: 1,
57
+ zipcode: '55555',
58
+ phone: '5555555555'
59
+ },
60
+ ship_address_attributes: {
61
+ first_name: 'First',
62
+ last_name: 'Last',
63
+ address1: '1 Test Rd',
64
+ city: 'City',
65
+ country_id: country.id,
66
+ state_id: 1,
67
+ zipcode: '55555',
68
+ phone: '5555555555'
69
+ }
70
+ }
71
+ expect(json_response['email']).to eq 'mine@example.com'
72
+ expect(json_response['bill_address']).to_not be_nil
73
+ expect(json_response['ship_address']).to_not be_nil
74
+ end
75
+
76
+ it "cannot update other users details" do
77
+ api_put :update, id: stranger.id, token: user.spree_api_key, user: { :email => "mine@example.com" }
78
+ assert_not_found!
79
+ end
80
+
81
+ it "can delete itself" do
82
+ api_delete :destroy, id: user.id, token: user.spree_api_key
83
+ expect(response.status).to eq(204)
84
+ end
85
+
86
+ it "cannot delete other user" do
87
+ api_delete :destroy, id: stranger.id, token: user.spree_api_key
88
+ assert_not_found!
89
+ end
90
+
91
+ it "should only get own details on index" do
92
+ 2.times { create(:user) }
93
+ api_get :index, token: user.spree_api_key
94
+
95
+ expect(Spree.user_class.count).to eq 3
96
+ expect(json_response['count']).to eq 1
97
+ expect(json_response['users'].size).to eq 1
98
+ end
99
+ end
100
+
101
+ context "as an admin" do
102
+ before { stub_authentication! }
103
+
104
+ sign_in_as_admin!
105
+
106
+ it "gets all users" do
107
+ allow(Spree::LegacyUser).to receive(:find_by).with(hash_including(:spree_api_key)) { current_api_user }
108
+
109
+ 2.times { create(:user) }
110
+
111
+ api_get :index
112
+ expect(Spree.user_class.count).to eq 2
113
+ expect(json_response['count']).to eq 2
114
+ expect(json_response['users'].size).to eq 2
115
+ end
116
+
117
+ it 'can control the page size through a parameter' do
118
+ 2.times { create(:user) }
119
+ api_get :index, :per_page => 1
120
+ expect(json_response['count']).to eq(1)
121
+ expect(json_response['current_page']).to eq(1)
122
+ expect(json_response['pages']).to eq(2)
123
+ end
124
+
125
+ it 'can query the results through a paramter' do
126
+ expected_result = create(:user, :email => 'brian@spreecommerce.com')
127
+ api_get :index, :q => { :email_cont => 'brian' }
128
+ expect(json_response['count']).to eq(1)
129
+ expect(json_response['users'].first['email']).to eq expected_result.email
130
+ end
131
+
132
+ it "can create" do
133
+ api_post :create, :user => { :email => "new@example.com", :password => 'spree123', :password_confirmation => 'spree123' }
134
+ expect(json_response).to have_attributes(attributes)
135
+ expect(response.status).to eq(201)
136
+ end
137
+
138
+ it "can destroy user without orders" do
139
+ user.orders.destroy_all
140
+ api_delete :destroy, :id => user.id
141
+ expect(response.status).to eq(204)
142
+ end
143
+
144
+ it "cannot destroy user with orders" do
145
+ create(:completed_order_with_totals, :user => user)
146
+ api_delete :destroy, :id => user.id
147
+ expect(json_response["exception"]).to eq "Spree::Core::DestroyWithOrdersError"
148
+ expect(response.status).to eq(422)
149
+ end
150
+
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,235 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Api::VariantsController, :type => :controller do
5
+ render_views
6
+
7
+ let!(:product) { create(:product) }
8
+ let!(:variant) do
9
+ variant = product.master
10
+ variant.option_values << create(:option_value)
11
+ variant
12
+ end
13
+
14
+ let!(:base_attributes) { Api::ApiHelpers.variant_attributes }
15
+ let!(:show_attributes) { base_attributes.dup.push(:in_stock, :display_price) }
16
+ let!(:new_attributes) { base_attributes }
17
+
18
+ before do
19
+ stub_authentication!
20
+ end
21
+
22
+ describe "#index" do
23
+
24
+ it "can see a paginated list of variants" do
25
+ api_get :index
26
+ first_variant = json_response["variants"].first
27
+ first_variant.should have_attributes(show_attributes)
28
+ first_variant["stock_items"].should be_present
29
+ json_response["count"].should == 1
30
+ json_response["current_page"].should == 1
31
+ json_response["pages"].should == 1
32
+ end
33
+
34
+ it 'can control the page size through a parameter' do
35
+ create(:variant)
36
+ api_get :index, :per_page => 1
37
+ json_response['count'].should == 1
38
+ json_response['current_page'].should == 1
39
+ json_response['pages'].should == 3
40
+ end
41
+
42
+ it 'can query the results through a paramter' do
43
+ expected_result = create(:variant, :sku => 'FOOBAR')
44
+ api_get :index, :q => { :sku_cont => 'FOO' }
45
+ json_response['count'].should == 1
46
+ json_response['variants'].first['sku'].should eq expected_result.sku
47
+ end
48
+
49
+ it "variants returned contain option values data" do
50
+ api_get :index
51
+ option_values = json_response["variants"].last["option_values"]
52
+ option_values.first.should have_attributes([:name,
53
+ :presentation,
54
+ :option_type_name,
55
+ :option_type_id])
56
+ end
57
+
58
+ it "variants returned contain images data" do
59
+ variant.images.create!(:attachment => image("thinking-cat.jpg"))
60
+
61
+ api_get :index
62
+
63
+ json_response["variants"].last.should have_attributes([:images])
64
+ json_response['variants'].first['images'].first.should have_attributes([:attachment_file_name,
65
+ :attachment_width,
66
+ :attachment_height,
67
+ :attachment_content_type,
68
+ :mini_url,
69
+ :small_url,
70
+ :product_url,
71
+ :large_url])
72
+
73
+ end
74
+
75
+ # Regression test for #2141
76
+ context "a deleted variant" do
77
+ before do
78
+ variant.update_column(:deleted_at, Time.now)
79
+ end
80
+
81
+ it "is not returned in the results" do
82
+ api_get :index
83
+ json_response["variants"].count.should == 0
84
+ end
85
+
86
+ it "is not returned even when show_deleted is passed" do
87
+ api_get :index, :show_deleted => true
88
+ json_response["variants"].count.should == 0
89
+ end
90
+ end
91
+
92
+ context "stock filtering" do
93
+ subject { api_get :index, in_stock_only: true }
94
+
95
+ context "variant is out of stock" do
96
+ before do
97
+ variant.stock_items.update_all(count_on_hand: 0)
98
+ end
99
+
100
+ it "is not returned in the results" do
101
+ subject
102
+ expect(json_response["variants"].count).to eq 0
103
+ end
104
+ end
105
+
106
+ context "variant is in stock" do
107
+ before do
108
+ variant.stock_items.update_all(count_on_hand: 10)
109
+ end
110
+
111
+ it "is returned in the results" do
112
+ subject
113
+ expect(json_response["variants"].count).to eq 1
114
+ end
115
+ end
116
+ end
117
+
118
+ context "pagination" do
119
+ it "can select the next page of variants" do
120
+ second_variant = create(:variant)
121
+ api_get :index, :page => 2, :per_page => 1
122
+ json_response["variants"].first.should have_attributes(show_attributes)
123
+ json_response["total_count"].should == 3
124
+ json_response["current_page"].should == 2
125
+ json_response["pages"].should == 3
126
+ end
127
+ end
128
+
129
+ context "stock item filter" do
130
+ let(:stock_location) { variant.stock_locations.first }
131
+ let!(:inactive_stock_location) { create(:stock_location, propagate_all_variants: true, name: "My special stock location", active: false) }
132
+
133
+ it "only returns stock items for active stock locations" do
134
+ api_get :index
135
+ variant = json_response['variants'].first
136
+ stock_items = variant['stock_items'].map { |si| si['stock_location_name'] }
137
+
138
+ expect(stock_items).to include stock_location.name
139
+ expect(stock_items).not_to include inactive_stock_location.name
140
+ end
141
+ end
142
+ end
143
+
144
+ describe "#show" do
145
+
146
+ it "can see a single variant" do
147
+ api_get :show, :id => variant.to_param
148
+ json_response.should have_attributes(show_attributes)
149
+ json_response["stock_items"].should be_present
150
+ option_values = json_response["option_values"]
151
+ option_values.first.should have_attributes([:name,
152
+ :presentation,
153
+ :option_type_name,
154
+ :option_type_id])
155
+ end
156
+
157
+ it "can see a single variant with images" do
158
+ variant.images.create!(:attachment => image("thinking-cat.jpg"))
159
+
160
+ api_get :show, :id => variant.to_param
161
+
162
+ json_response.should have_attributes(show_attributes + [:images])
163
+ option_values = json_response["option_values"]
164
+ option_values.first.should have_attributes([:name,
165
+ :presentation,
166
+ :option_type_name,
167
+ :option_type_id])
168
+ end
169
+ end
170
+
171
+ it "can learn how to create a new variant" do
172
+ api_get :new
173
+ expect(json_response["attributes"]).to eq(new_attributes.map(&:to_s))
174
+ expect(json_response["required_attributes"]).to be_empty
175
+ end
176
+
177
+ it "cannot create a new variant if not an admin" do
178
+ api_post :create, :variant => { :sku => "12345" }
179
+ assert_unauthorized!
180
+ end
181
+
182
+ it "cannot update a variant" do
183
+ api_put :update, :id => variant.to_param, :variant => { :sku => "12345" }
184
+ assert_not_found!
185
+ end
186
+
187
+ it "cannot delete a variant" do
188
+ api_delete :destroy, :id => variant.to_param
189
+ assert_not_found!
190
+ expect { variant.reload }.not_to raise_error
191
+ end
192
+
193
+ context "as an admin" do
194
+ sign_in_as_admin!
195
+ let(:resource_scoping) { { :product_id => variant.product.to_param } }
196
+
197
+ # Test for #2141
198
+ context "deleted variants" do
199
+ before do
200
+ variant.update_column(:deleted_at, Time.now)
201
+ end
202
+
203
+ it "are visible by admin" do
204
+ api_get :index, :show_deleted => 1
205
+ expect(json_response["variants"].count).to eq(1)
206
+ end
207
+ end
208
+
209
+ it "can create a new variant" do
210
+ api_post :create, :variant => { :sku => "12345" }
211
+ expect(json_response).to have_attributes(new_attributes)
212
+ expect(response.status).to eq(201)
213
+ expect(json_response["sku"]).to eq("12345")
214
+
215
+ expect(variant.product.variants.count).to eq(1)
216
+ end
217
+
218
+ it "can update a variant" do
219
+ api_put :update, :id => variant.to_param, :variant => { :sku => "12345" }
220
+ expect(response.status).to eq(200)
221
+ end
222
+
223
+ it "can delete a variant" do
224
+ api_delete :destroy, :id => variant.to_param
225
+ expect(response.status).to eq(204)
226
+ expect { Spree::Variant.find(variant.id) }.to raise_error(ActiveRecord::RecordNotFound)
227
+ end
228
+
229
+ it 'variants returned contain cost price data' do
230
+ api_get :index
231
+ expect(json_response["variants"].first.has_key?(:cost_price)).to eq true
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Api::ZonesController, :type => :controller do
5
+ render_views
6
+
7
+ let!(:attributes) { [:id, :name, :zone_members] }
8
+
9
+ before do
10
+ stub_authentication!
11
+ @zone = create(:zone, :name => 'Europe')
12
+ end
13
+
14
+ it "gets list of zones" do
15
+ api_get :index
16
+ expect(json_response['zones'].first).to have_attributes(attributes)
17
+ end
18
+
19
+ it 'can control the page size through a parameter' do
20
+ create(:zone)
21
+ api_get :index, :per_page => 1
22
+ expect(json_response['count']).to eq(1)
23
+ expect(json_response['current_page']).to eq(1)
24
+ expect(json_response['pages']).to eq(2)
25
+ end
26
+
27
+ it 'can query the results through a paramter' do
28
+ expected_result = create(:zone, :name => 'South America')
29
+ api_get :index, :q => { :name_cont => 'south' }
30
+ expect(json_response['count']).to eq(1)
31
+ expect(json_response['zones'].first['name']).to eq expected_result.name
32
+ end
33
+
34
+ it "gets a zone" do
35
+ api_get :show, :id => @zone.id
36
+ expect(json_response).to have_attributes(attributes)
37
+ expect(json_response['name']).to eq @zone.name
38
+ expect(json_response['zone_members'].size).to eq @zone.zone_members.count
39
+ end
40
+
41
+ context "specifying a rabl template to use" do
42
+ before do
43
+ Spree::Api::ZonesController.class_eval do
44
+ def custom_show
45
+ respond_with(zone)
46
+ end
47
+ end
48
+ end
49
+
50
+ it "uses the specified template" do
51
+ @routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
52
+ r.draw { get 'custom_show' => 'spree/api/zones#custom_show' }
53
+ end
54
+
55
+ request.headers['X-Spree-Template'] = 'show'
56
+ api_get :custom_show, :id => @zone.id
57
+ expect(response).to render_template('spree/api/zones/show')
58
+ end
59
+
60
+ it "falls back to the default template if the specified template does not exist" do
61
+ request.headers['X-Spree-Template'] = 'invoice'
62
+ api_get :show, :id => @zone.id
63
+ expect(response).to render_template('spree/api/zones/show')
64
+ end
65
+ end
66
+
67
+ context "as an admin" do
68
+ sign_in_as_admin!
69
+
70
+ it "can create a new zone" do
71
+ params = {
72
+ :zone => {
73
+ :name => "North Pole",
74
+ :zone_members => [
75
+ {
76
+ :zoneable_type => "Spree::Country",
77
+ :zoneable_id => 1
78
+ }
79
+ ]
80
+ }
81
+ }
82
+
83
+ api_post :create, params
84
+ expect(response.status).to eq(201)
85
+ expect(json_response).to have_attributes(attributes)
86
+ expect(json_response["zone_members"]).not_to be_empty
87
+ end
88
+
89
+ it "updates a zone" do
90
+ params = { :id => @zone.id,
91
+ :zone => {
92
+ :name => "North Pole",
93
+ :zone_members => [
94
+ {
95
+ :zoneable_type => "Spree::Country",
96
+ :zoneable_id => 1
97
+ }
98
+ ]
99
+ }
100
+ }
101
+
102
+ api_put :update, params
103
+ expect(response.status).to eq(200)
104
+ expect(json_response['name']).to eq 'North Pole'
105
+ expect(json_response['zone_members']).not_to be_blank
106
+ end
107
+
108
+ it "can delete a zone" do
109
+ api_delete :destroy, :id => @zone.id
110
+ expect(response.status).to eq(204)
111
+ expect { @zone.reload }.to raise_error(ActiveRecord::RecordNotFound)
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,187 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe 'Api Feature Specs', type: :request do
5
+ before { Spree::Api::Config[:requires_authentication] = false }
6
+ let!(:promotion) { FactoryGirl.create(:promotion, :with_order_adjustment, code: 'foo', weighted_order_adjustment_amount: 10) }
7
+ let(:promotion_code) { promotion.codes.first }
8
+ let(:bill_address) { FactoryGirl.create(:address) }
9
+ let(:ship_address) { FactoryGirl.create(:address) }
10
+ let(:variant_1) { FactoryGirl.create(:variant, price: 100.00) }
11
+ let(:variant_2) { FactoryGirl.create(:variant, price: 200.00) }
12
+ let(:payment_method) { FactoryGirl.create(:check_payment_method) }
13
+ let!(:shipping_method) do
14
+ FactoryGirl.create(:shipping_method).tap do |shipping_method|
15
+ shipping_method.zones.first.zone_members.create!(zoneable: ship_address.country)
16
+ shipping_method.calculator.set_preference(:amount, 10.0)
17
+ end
18
+ end
19
+
20
+ def parsed
21
+ JSON.parse(response.body)
22
+ end
23
+
24
+ def login
25
+ expect {
26
+ post '/api/users', user: { email: "featurecheckoutuser@example.com", password: "featurecheckoutuser" }
27
+ }.to change { Spree.user_class.count }.by 1
28
+ expect(response).to have_http_status(:created)
29
+ @user = Spree.user_class.find(parsed['id'])
30
+
31
+ # copied from api testing helpers support since we can't really sign in
32
+ allow(Spree::LegacyUser).to receive(:find_by).with(hash_including(:spree_api_key)) { @user }
33
+ end
34
+
35
+ def create_order(order_params: {})
36
+ expect { post '/api/orders', order_params }.to change { Order.count }.by 1
37
+ expect(response).to have_http_status(:created)
38
+ @order = Order.find(parsed['id'])
39
+ expect(@order.email).to eq "featurecheckoutuser@example.com"
40
+ end
41
+
42
+ def update_order(order_params: {})
43
+ put "/api/orders/#{@order.number}", order_params
44
+ expect(response).to have_http_status(:ok)
45
+ end
46
+
47
+ def create_line_item(variant, quantity = 1)
48
+ expect {
49
+ post "/api/orders/#{@order.number}/line_items", line_item: { variant_id: variant.id, quantity: quantity }
50
+ }.to change { @order.line_items.count }.by 1
51
+ expect(response).to have_http_status(:created)
52
+ end
53
+
54
+ def add_promotion(promotion)
55
+ expect {
56
+ put "/api/orders/#{@order.number}/apply_coupon_code", coupon_code: promotion_code.value
57
+ }.to change { @order.promotions.count }.by 1
58
+ expect(response).to have_http_status(:ok)
59
+ end
60
+
61
+ def add_address(address, billing: true)
62
+ address_type = billing ? :bill_address : :ship_address
63
+ # It seems we are missing an order-scoped address api endpoint since we need
64
+ # to use update here.
65
+ expect {
66
+ update_order(order_params: { order: { address_type => address.attributes.except('id') } })
67
+ }.to change { @order.reload.public_send(address_type) }.to address
68
+ end
69
+
70
+ def add_payment
71
+ expect {
72
+ post "/api/orders/#{@order.number}/payments", payment: { payment_method_id: payment_method.id }
73
+ }.to change { @order.reload.payments.count }.by 1
74
+ expect(response).to have_http_status(:created)
75
+ expect(@order.payments.last.payment_method).to eq payment_method
76
+ end
77
+
78
+ def advance
79
+ put "/api/checkouts/#{@order.number}/advance"
80
+ expect(response).to have_http_status(:ok)
81
+ end
82
+
83
+ def complete
84
+ put "/api/checkouts/#{@order.number}/complete"
85
+ expect(response).to have_http_status(:ok)
86
+ end
87
+
88
+ def assert_order_expectations
89
+ @order.reload
90
+ expect(@order.state).to eq 'complete'
91
+ expect(@order.completed_at).to be_a ActiveSupport::TimeWithZone
92
+ expect(@order.item_total).to eq 600.00
93
+ expect(@order.total).to eq 600.00
94
+ expect(@order.adjustment_total).to eq -10.00
95
+ expect(@order.shipment_total).to eq 10.00
96
+ expect(@order.user).to eq @user
97
+ expect(@order.bill_address).to eq bill_address
98
+ expect(@order.ship_address).to eq ship_address
99
+ expect(@order.payments.length).to eq 1
100
+ expect(@order.line_items.any? { |li| li.variant == variant_1 && li.quantity == 2 }).to eq true
101
+ expect(@order.line_items.any? { |li| li.variant == variant_2 && li.quantity == 2 }).to eq true
102
+ expect(@order.promotions).to eq [promotion]
103
+ end
104
+
105
+ it "is able to checkout with individualized requests" do
106
+ login
107
+ create_order
108
+
109
+ create_line_item(variant_1, 2)
110
+ add_promotion(promotion)
111
+ create_line_item(variant_2, 2)
112
+
113
+ add_address(bill_address)
114
+ add_address(ship_address, billing: false)
115
+
116
+ add_payment
117
+
118
+ advance
119
+ complete
120
+
121
+ assert_order_expectations
122
+ end
123
+
124
+ it "is able to checkout with the create request" do
125
+ login
126
+
127
+ create_order(order_params: {
128
+ order: {
129
+ bill_address: bill_address.as_json.except('id'),
130
+ ship_address: ship_address.as_json.except('id'),
131
+ line_items: {
132
+ 0 => { variant_id: variant_1.id, quantity: 2 },
133
+ 1 => { variant_id: variant_2.id, quantity: 2 }
134
+ },
135
+ # Would like to do this, but the save process from the orders controller
136
+ # does not actually call what it needs to to apply this coupon code :(
137
+ # coupon_code: promotion.code,
138
+
139
+ # Would like to do this, but it puts the payment in a complete state,
140
+ # which the order does not like when transitioning from confirm to complete
141
+ # since it looks to process pending payments.
142
+ # payments: [ { payment_method: payment_method.name, state: "pending" } ],
143
+ }
144
+ })
145
+
146
+ add_promotion(promotion)
147
+ add_payment
148
+
149
+ advance
150
+ complete
151
+
152
+ assert_order_expectations
153
+ end
154
+
155
+ it "is able to checkout with the update request" do
156
+ login
157
+
158
+ create_order
159
+ update_order(order_params: {
160
+ order: {
161
+ bill_address: bill_address.as_json.except('id'),
162
+ ship_address: ship_address.as_json.except('id'),
163
+ line_items: {
164
+ 0 => { variant_id: variant_1.id, quantity: 2 },
165
+ 1 => { variant_id: variant_2.id, quantity: 2 }
166
+ },
167
+ # Would like to do this, but the save process from the orders controller
168
+ # does not actually call what it needs to to apply this coupon code :(
169
+ # coupon_code: promotion.code,
170
+
171
+ # Would like to do this, but it puts the payment in a complete state,
172
+ # which the order does not like when transitioning from confirm to complete
173
+ # since it looks to process pending payments.
174
+ # payments: [ { payment_method: payment_method.name, state: "pending" } ],
175
+ }
176
+ })
177
+
178
+ add_promotion(promotion)
179
+ add_payment
180
+
181
+ advance
182
+ complete
183
+
184
+ assert_order_expectations
185
+ end
186
+ end
187
+ end
Binary file