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,454 @@
1
+ require 'spec_helper'
2
+ require 'shared_examples/protect_product_actions'
3
+
4
+ module Spree
5
+ describe Spree::Api::ProductsController, :type => :controller do
6
+ render_views
7
+
8
+ let!(:product) { create(:product) }
9
+ let!(:inactive_product) { create(:product, available_on: Time.now.tomorrow, name: "inactive") }
10
+ let(:base_attributes) { Api::ApiHelpers.product_attributes }
11
+ let(:show_attributes) { base_attributes.dup.push(:has_variants) }
12
+ let(:new_attributes) { base_attributes }
13
+
14
+ let(:product_data) do
15
+ { name: "The Other Product",
16
+ price: 19.99,
17
+ shipping_category_id: create(:shipping_category).id }
18
+ end
19
+ let(:attributes_for_variant) do
20
+ h = attributes_for(:variant).except(:option_values, :product)
21
+ h.merge({
22
+ options: [
23
+ { name: "size", value: "small" },
24
+ { name: "color", value: "black" }
25
+ ]
26
+ })
27
+ end
28
+
29
+ before do
30
+ stub_authentication!
31
+ end
32
+
33
+ context "as a normal user" do
34
+ context "with caching enabled" do
35
+ let!(:product_2) { create(:product) }
36
+
37
+ before do
38
+ ActionController::Base.perform_caching = true
39
+ end
40
+
41
+ it "returns unique products" do
42
+ api_get :index
43
+ product_ids = json_response["products"].map { |p| p["id"] }
44
+ expect(product_ids.uniq.count).to eq(product_ids.count)
45
+ end
46
+
47
+ after do
48
+ ActionController::Base.perform_caching = false
49
+ end
50
+ end
51
+
52
+ it "retrieves a list of products" do
53
+ api_get :index
54
+ expect(json_response["products"].first).to have_attributes(show_attributes)
55
+ expect(json_response["total_count"]).to eq(1)
56
+ expect(json_response["current_page"]).to eq(1)
57
+ expect(json_response["pages"]).to eq(1)
58
+ expect(json_response["per_page"]).to eq(Kaminari.config.default_per_page)
59
+ end
60
+
61
+ it "retrieves a list of products by id" do
62
+ api_get :index, :ids => [product.id]
63
+ expect(json_response["products"].first).to have_attributes(show_attributes)
64
+ expect(json_response["total_count"]).to eq(1)
65
+ expect(json_response["current_page"]).to eq(1)
66
+ expect(json_response["pages"]).to eq(1)
67
+ expect(json_response["per_page"]).to eq(Kaminari.config.default_per_page)
68
+ end
69
+
70
+ context "specifying a rabl template for a custom action" do
71
+ before do
72
+ Spree::Api::ProductsController.class_eval do
73
+ def custom_show
74
+ @product = find_product(params[:id])
75
+ respond_with(@product)
76
+ end
77
+ end
78
+ end
79
+
80
+ def set_custom_route
81
+ @routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
82
+ r.draw { get 'custom_show' => 'spree/api/products#custom_show' }
83
+ end
84
+ end
85
+
86
+ it "uses the specified custom template through the request header" do
87
+ set_custom_route
88
+
89
+ request.headers['X-Spree-Template'] = 'show'
90
+ api_get :custom_show, :id => product.id
91
+ expect(response).to render_template('spree/api/products/show')
92
+ end
93
+
94
+ it "uses the specified custom template through the template URL parameter" do
95
+ set_custom_route
96
+
97
+ api_get :custom_show, :id => product.id, :template => 'show'
98
+ expect(response).to render_template('spree/api/products/show')
99
+ end
100
+
101
+ it "falls back to the default template if the specified template does not exist" do
102
+ request.headers['X-Spree-Template'] = 'invoice'
103
+ api_get :show, :id => product.id
104
+ expect(response).to render_template('spree/api/products/show')
105
+ end
106
+ end
107
+
108
+ context "product has more than one price" do
109
+ before { product.master.prices.create currency: "EUR", amount: 22 }
110
+
111
+ it "returns distinct products only" do
112
+ api_get :index
113
+ expect(assigns(:products).map(&:id).uniq).to eq assigns(:products).map(&:id)
114
+ end
115
+ end
116
+
117
+ it "retrieves a list of products by ids string" do
118
+ second_product = create(:product)
119
+ api_get :index, :ids => [product.id, second_product.id].join(",")
120
+ expect(json_response["products"].first).to have_attributes(show_attributes)
121
+ expect(json_response["products"][1]).to have_attributes(show_attributes)
122
+ expect(json_response["total_count"]).to eq(2)
123
+ expect(json_response["current_page"]).to eq(1)
124
+ expect(json_response["pages"]).to eq(1)
125
+ expect(json_response["per_page"]).to eq(Kaminari.config.default_per_page)
126
+ end
127
+
128
+ it "does not return inactive products when queried by ids" do
129
+ api_get :index, :ids => [inactive_product.id]
130
+ expect(json_response["count"]).to eq(0)
131
+ end
132
+
133
+ it "does not list unavailable products" do
134
+ api_get :index
135
+ expect(json_response["products"].first["name"]).not_to eq("inactive")
136
+ end
137
+
138
+ context "pagination" do
139
+ it "can select the next page of products" do
140
+ second_product = create(:product)
141
+ api_get :index, :page => 2, :per_page => 1
142
+ expect(json_response["products"].first).to have_attributes(show_attributes)
143
+ expect(json_response["total_count"]).to eq(2)
144
+ expect(json_response["current_page"]).to eq(2)
145
+ expect(json_response["pages"]).to eq(2)
146
+ end
147
+
148
+ it 'can control the page size through a parameter' do
149
+ create(:product)
150
+ api_get :index, :per_page => 1
151
+ expect(json_response['count']).to eq(1)
152
+ expect(json_response['total_count']).to eq(2)
153
+ expect(json_response['current_page']).to eq(1)
154
+ expect(json_response['pages']).to eq(2)
155
+ end
156
+ end
157
+
158
+ it "can search for products" do
159
+ create(:product, :name => "The best product in the world")
160
+ api_get :index, :q => { :name_cont => "best" }
161
+ expect(json_response["products"].first).to have_attributes(show_attributes)
162
+ expect(json_response["count"]).to eq(1)
163
+ end
164
+
165
+ it "gets a single product" do
166
+ product.master.images.create!(:attachment => image("thinking-cat.jpg"))
167
+ product.variants.create!
168
+ product.variants.first.images.create!(:attachment => image("thinking-cat.jpg"))
169
+ product.set_property("spree", "rocks")
170
+ product.taxons << create(:taxon)
171
+
172
+ api_get :show, :id => product.to_param
173
+
174
+ expect(json_response).to have_attributes(show_attributes)
175
+ expect(json_response['variants'].first).to have_attributes([:name,
176
+ :is_master,
177
+ :price,
178
+ :images,
179
+ :in_stock])
180
+
181
+ expect(json_response['variants'].first['images'].first).to have_attributes([:attachment_file_name,
182
+ :attachment_width,
183
+ :attachment_height,
184
+ :attachment_content_type,
185
+ :mini_url,
186
+ :small_url,
187
+ :product_url,
188
+ :large_url])
189
+
190
+ expect(json_response["product_properties"].first).to have_attributes([:value,
191
+ :product_id,
192
+ :property_name])
193
+
194
+ expect(json_response["classifications"].first).to have_attributes([:taxon_id, :position, :taxon])
195
+ expect(json_response["classifications"].first['taxon']).to have_attributes([:id, :name, :pretty_name, :permalink, :taxonomy_id, :parent_id])
196
+ end
197
+
198
+ context "tracking is disabled" do
199
+ before { Config.track_inventory_levels = false }
200
+
201
+ it "still displays valid json with total_on_hand Float::INFINITY" do
202
+ api_get :show, :id => product.to_param
203
+ expect(response).to be_ok
204
+ expect(json_response[:total_on_hand]).to eq nil
205
+ end
206
+
207
+ after { Config.track_inventory_levels = true }
208
+ end
209
+
210
+ context "finds a product by slug first then by id" do
211
+ let!(:other_product) { create(:product, :slug => "these-are-not-the-droids-you-are-looking-for") }
212
+
213
+ before do
214
+ product.update_column(:slug, "#{other_product.id}-and-1-ways")
215
+ end
216
+
217
+ specify do
218
+ api_get :show, :id => product.to_param
219
+ expect(json_response["slug"]).to match(/and-1-ways/)
220
+ product.destroy
221
+
222
+ api_get :show, :id => other_product.id
223
+ expect(json_response["slug"]).to match(/droids/)
224
+ end
225
+ end
226
+
227
+ it "cannot see inactive products" do
228
+ api_get :show, :id => inactive_product.to_param
229
+ assert_not_found!
230
+ end
231
+
232
+ it "returns a 404 error when it cannot find a product" do
233
+ api_get :show, :id => "non-existant"
234
+ assert_not_found!
235
+ end
236
+
237
+ it "can learn how to create a new product" do
238
+ api_get :new
239
+ expect(json_response["attributes"]).to eq(new_attributes.map(&:to_s))
240
+ required_attributes = json_response["required_attributes"]
241
+ expect(required_attributes).to include("name")
242
+ expect(required_attributes).to include("price")
243
+ expect(required_attributes).to include("shipping_category_id")
244
+ end
245
+
246
+ it_behaves_like "modifying product actions are restricted"
247
+ end
248
+
249
+ context "as an admin" do
250
+ let(:taxon_1) { create(:taxon) }
251
+ let(:taxon_2) { create(:taxon) }
252
+
253
+ sign_in_as_admin!
254
+
255
+ it "can see all products" do
256
+ api_get :index
257
+ expect(json_response["products"].count).to eq(2)
258
+ expect(json_response["count"]).to eq(2)
259
+ expect(json_response["current_page"]).to eq(1)
260
+ expect(json_response["pages"]).to eq(1)
261
+ end
262
+
263
+ # Regression test for #1626
264
+ context "deleted products" do
265
+ before do
266
+ create(:product, :deleted_at => 1.day.ago)
267
+ end
268
+
269
+ it "does not include deleted products" do
270
+ api_get :index
271
+ expect(json_response["products"].count).to eq(2)
272
+ end
273
+
274
+ it "can include deleted products" do
275
+ api_get :index, :show_deleted => 1
276
+ expect(json_response["products"].count).to eq(3)
277
+ end
278
+ end
279
+
280
+ describe "creating a product" do
281
+ it "can create a new product" do
282
+ api_post :create, :product => { :name => "The Other Product",
283
+ :price => 19.99,
284
+ :shipping_category_id => create(:shipping_category).id }
285
+ expect(json_response).to have_attributes(base_attributes)
286
+ expect(response.status).to eq(201)
287
+ end
288
+
289
+ it "creates with embedded variants" do
290
+ product_data.merge!({
291
+ variants: [attributes_for_variant, attributes_for_variant]
292
+ })
293
+
294
+ api_post :create, :product => product_data
295
+ expect(response.status).to eq 201
296
+
297
+ variants = json_response['variants']
298
+ expect(variants.count).to eq(2)
299
+ expect(variants.last['option_values'][0]['name']).to eq('small')
300
+ expect(variants.last['option_values'][0]['option_type_name']).to eq('size')
301
+
302
+ expect(json_response['option_types'].count).to eq(2) # size, color
303
+ end
304
+
305
+ it "can create a new product with embedded product_properties" do
306
+ product_data.merge!({
307
+ product_properties_attributes: [{
308
+ property_name: "fabric",
309
+ value: "cotton"
310
+ }]
311
+ })
312
+
313
+ api_post :create, :product => product_data
314
+
315
+ expect(json_response['product_properties'][0]['property_name']).to eq('fabric')
316
+ expect(json_response['product_properties'][0]['value']).to eq('cotton')
317
+ end
318
+
319
+ it "can create a new product with option_types" do
320
+ product_data.merge!({
321
+ option_types: ['size', 'color']
322
+ })
323
+
324
+ api_post :create, :product => product_data
325
+ expect(json_response['option_types'].count).to eq(2)
326
+ end
327
+
328
+ it "creates with shipping categories" do
329
+ hash = { :name => "The Other Product",
330
+ :price => 19.99,
331
+ :shipping_category => "Free Ships" }
332
+
333
+ api_post :create, :product => hash
334
+ expect(response.status).to eq 201
335
+
336
+ shipping_id = ShippingCategory.find_by_name("Free Ships").id
337
+ expect(json_response['shipping_category_id']).to eq shipping_id
338
+ end
339
+
340
+ it "puts the created product in the given taxon" do
341
+ product_data[:taxon_ids] = taxon_1.id.to_s
342
+ api_post :create, :product => product_data
343
+ expect(json_response["taxon_ids"]).to eq([taxon_1.id,])
344
+ end
345
+
346
+ # Regression test for #4123
347
+ it "puts the created product in the given taxons" do
348
+ product_data[:taxon_ids] = [taxon_1.id, taxon_2.id].join(',')
349
+ api_post :create, :product => product_data
350
+ expect(json_response["taxon_ids"]).to eq([taxon_1.id, taxon_2.id])
351
+ end
352
+
353
+ # Regression test for #2140
354
+ context "with authentication_required set to false" do
355
+ before do
356
+ Spree::Api::Config.requires_authentication = false
357
+ end
358
+
359
+ after do
360
+ Spree::Api::Config.requires_authentication = true
361
+ end
362
+
363
+ it "can still create a product" do
364
+ api_post :create, :product => product_data, :token => "fake"
365
+ expect(json_response).to have_attributes(show_attributes)
366
+ expect(response.status).to eq(201)
367
+ end
368
+ end
369
+
370
+ it "cannot create a new product with invalid attributes" do
371
+ api_post :create, product: {}
372
+ expect(response.status).to eq(422)
373
+ expect(json_response["error"]).to eq("Invalid resource. Please fix errors and try again.")
374
+ errors = json_response["errors"]
375
+ errors.delete("slug") # Don't care about this one.
376
+ expect(errors.keys).to match_array(["name", "price", "shipping_category_id"])
377
+ end
378
+ end
379
+
380
+ context 'updating a product' do
381
+ it "can update a product" do
382
+ api_put :update, :id => product.to_param, :product => { :name => "New and Improved Product!" }
383
+ expect(response.status).to eq(200)
384
+ end
385
+
386
+ it "can create new option types on a product" do
387
+ api_put :update, :id => product.to_param, :product => { :option_types => ['shape', 'color'] }
388
+ expect(json_response['option_types'].count).to eq(2)
389
+ end
390
+
391
+ it "can create new variants on a product" do
392
+ api_put :update, :id => product.to_param, :product => { :variants => [attributes_for_variant, attributes_for_variant.merge(sku: "ABC-#{Kernel.rand(9999)}")] }
393
+ expect(response.status).to eq 200
394
+ expect(json_response['variants'].count).to eq(2) # 2 variants
395
+
396
+ variants = json_response['variants'].select { |v| !v['is_master'] }
397
+ expect(variants.last['option_values'][0]['name']).to eq('small')
398
+ expect(variants.last['option_values'][0]['option_type_name']).to eq('size')
399
+
400
+ expect(json_response['option_types'].count).to eq(2) # size, color
401
+ end
402
+
403
+ it "can update an existing variant on a product" do
404
+ variant_hash = {
405
+ :sku => '123', :price => 19.99, :options => [{:name => "size", :value => "small"}]
406
+ }
407
+ variant_id = product.variants.create!({ product: product }.merge(variant_hash)).id
408
+
409
+ api_put :update, :id => product.to_param, :product => {
410
+ :variants => [
411
+ variant_hash.merge(
412
+ :id => variant_id.to_s,
413
+ :sku => '456',
414
+ :options => [{:name => "size", :value => "large" }]
415
+ )
416
+ ]
417
+ }
418
+
419
+ expect(json_response['variants'].count).to eq(1)
420
+ variants = json_response['variants'].select { |v| !v['is_master'] }
421
+ expect(variants.last['option_values'][0]['name']).to eq('large')
422
+ expect(variants.last['sku']).to eq('456')
423
+ expect(variants.count).to eq(1)
424
+ end
425
+
426
+ it "cannot update a product with an invalid attribute" do
427
+ api_put :update, :id => product.to_param, :product => { :name => "" }
428
+ expect(response.status).to eq(422)
429
+ expect(json_response["error"]).to eq("Invalid resource. Please fix errors and try again.")
430
+ expect(json_response["errors"]["name"]).to eq(["can't be blank"])
431
+ end
432
+
433
+ # Regression test for #4123
434
+ it "puts the created product in the given taxon" do
435
+ api_put :update, :id => product.to_param, :product => {:taxon_ids => taxon_1.id.to_s}
436
+ expect(json_response["taxon_ids"]).to eq([taxon_1.id,])
437
+ end
438
+
439
+ # Regression test for #4123
440
+ it "puts the created product in the given taxons" do
441
+ api_put :update, :id => product.to_param, :product => {:taxon_ids => [taxon_1.id, taxon_2.id].join(',')}
442
+ expect(json_response["taxon_ids"]).to eq([taxon_1.id, taxon_2.id])
443
+ end
444
+ end
445
+
446
+ it "can delete a product" do
447
+ expect(product.deleted_at).to be_nil
448
+ api_delete :destroy, :id => product.to_param
449
+ expect(response.status).to eq(204)
450
+ expect(product.reload.deleted_at).not_to be_nil
451
+ end
452
+ end
453
+ end
454
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree::Api
4
+ describe OrdersController, :type => :controller do
5
+ render_views
6
+
7
+ before do
8
+ stub_authentication!
9
+ end
10
+
11
+ context "with an available promotion" do
12
+ let!(:order) { create(:order_with_line_items, :line_items_count => 1) }
13
+ let!(:promotion) do
14
+ promotion = create(:promotion, name: "10% off", code: "10off")
15
+ calculator = Spree::Calculator::FlatPercentItemTotal.create(preferred_flat_percent: "10")
16
+ action = Spree::Promotion::Actions::CreateItemAdjustments.create(calculator: calculator)
17
+ promotion.actions << action
18
+ promotion
19
+ end
20
+
21
+ it "can apply a coupon code to the order" do
22
+ expect(order.total).to eq(110.00)
23
+ api_put :apply_coupon_code, :id => order.to_param, :coupon_code => "10off", :order_token => order.guest_token
24
+ expect(response.status).to eq(200)
25
+ expect(order.reload.total).to eq(109.00)
26
+ expect(json_response["success"]).to eq("The coupon code was successfully applied to your order.")
27
+ expect(json_response["error"]).to be_blank
28
+ expect(json_response["successful"]).to be true
29
+ expect(json_response["status_code"]).to eq("coupon_code_applied")
30
+ end
31
+
32
+ context "with an expired promotion" do
33
+ before do
34
+ promotion.starts_at = 2.weeks.ago
35
+ promotion.expires_at = 1.week.ago
36
+ promotion.save
37
+ end
38
+
39
+ it "fails to apply" do
40
+ api_put :apply_coupon_code, :id => order.to_param, :coupon_code => "10off", :order_token => order.guest_token
41
+ expect(response.status).to eq(422)
42
+ expect(json_response["success"]).to be_blank
43
+ expect(json_response["error"]).to eq("The coupon code is expired")
44
+ expect(json_response["successful"]).to be false
45
+ expect(json_response["status_code"]).to eq("coupon_code_expired")
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Spree::Api::PromotionsController, :type => :controller do
5
+ render_views
6
+
7
+ shared_examples "a JSON response" do
8
+ it 'should be ok' do
9
+ expect(subject).to be_ok
10
+ end
11
+
12
+ it 'should return JSON' do
13
+ payload = HashWithIndifferentAccess.new(JSON.parse(subject.body))
14
+ expect(payload).to_not be_nil
15
+ Spree::Api::ApiHelpers.promotion_attributes.each do |attribute|
16
+ expect(payload).to be_has_key(attribute)
17
+ end
18
+ end
19
+ end
20
+
21
+ before do
22
+ stub_authentication!
23
+ end
24
+
25
+ let(:promotion) { create :promotion, code: '10off' }
26
+
27
+ describe 'GET #show' do
28
+ subject { api_get :show, id: id }
29
+
30
+ context 'when admin' do
31
+ sign_in_as_admin!
32
+
33
+ context 'when finding by id' do
34
+ let(:id) { promotion.id }
35
+
36
+ it_behaves_like "a JSON response"
37
+ end
38
+
39
+ context 'when finding by code' do
40
+ let(:id) { promotion.codes.first }
41
+
42
+ it_behaves_like "a JSON response"
43
+ end
44
+
45
+ context 'when id does not exist' do
46
+ let(:id) { 'argh' }
47
+
48
+ it 'should be 404' do
49
+ expect(subject.status).to eq(404)
50
+ end
51
+ end
52
+ end
53
+
54
+ context 'when non admin' do
55
+ let(:id) { promotion.id }
56
+
57
+ it 'should be unauthorized' do
58
+ subject
59
+ assert_unauthorized!
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+ module Spree
3
+ describe Spree::Api::PropertiesController, :type => :controller do
4
+ render_views
5
+
6
+ let!(:property_1) { Property.create!(:name => "foo", :presentation => "Foo") }
7
+ let!(:property_2) { Property.create!(:name => "bar", :presentation => "Bar") }
8
+
9
+ let(:attributes) { [:id, :name, :presentation] }
10
+
11
+ before do
12
+ stub_authentication!
13
+ end
14
+
15
+ it "can see a list of all properties" do
16
+ api_get :index
17
+ expect(json_response["properties"].count).to eq(2)
18
+ expect(json_response["properties"].first).to have_attributes(attributes)
19
+ end
20
+
21
+ it "can control the page size through a parameter" do
22
+ api_get :index, :per_page => 1
23
+ expect(json_response['properties'].count).to eq(1)
24
+ expect(json_response['current_page']).to eq(1)
25
+ expect(json_response['pages']).to eq(2)
26
+ end
27
+
28
+ it 'can query the results through a parameter' do
29
+ api_get :index, :q => { :name_cont => 'ba' }
30
+ expect(json_response['count']).to eq(1)
31
+ expect(json_response['properties'].first['presentation']).to eq property_2.presentation
32
+ end
33
+
34
+ it "retrieves a list of properties by id" do
35
+ api_get :index, :ids => [property_1.id]
36
+ expect(json_response["properties"].first).to have_attributes(attributes)
37
+ expect(json_response["count"]).to eq(1)
38
+ end
39
+
40
+ it "retrieves a list of properties by ids string" do
41
+ api_get :index, :ids => [property_1.id, property_2.id].join(",")
42
+ expect(json_response["properties"].first).to have_attributes(attributes)
43
+ expect(json_response["properties"][1]).to have_attributes(attributes)
44
+ expect(json_response["count"]).to eq(2)
45
+ end
46
+
47
+ it "can see a single property" do
48
+ api_get :show, :id => property_1.id
49
+ expect(json_response).to have_attributes(attributes)
50
+ end
51
+
52
+ it "can see a property by name" do
53
+ api_get :show, :id => property_1.name
54
+ expect(json_response).to have_attributes(attributes)
55
+ end
56
+
57
+ it "can learn how to create a new property" do
58
+ api_get :new
59
+ expect(json_response["attributes"]).to eq(attributes.map(&:to_s))
60
+ expect(json_response["required_attributes"]).to be_empty
61
+ end
62
+
63
+ it "cannot create a new property if not an admin" do
64
+ api_post :create, :property => { :name => "My Property 3" }
65
+ assert_unauthorized!
66
+ end
67
+
68
+ it "cannot update a property" do
69
+ api_put :update, :id => property_1.name, :property => { :presentation => "my value 456" }
70
+ assert_unauthorized!
71
+ end
72
+
73
+ it "cannot delete a property" do
74
+ api_delete :destroy, :id => property_1.id
75
+ assert_unauthorized!
76
+ expect { property_1.reload }.not_to raise_error
77
+ end
78
+
79
+ context "as an admin" do
80
+ sign_in_as_admin!
81
+
82
+ it "can create a new property" do
83
+ expect(Spree::Property.count).to eq(2)
84
+ api_post :create, :property => { :name => "My Property 3", :presentation => "my value 3" }
85
+ expect(json_response).to have_attributes(attributes)
86
+ expect(response.status).to eq(201)
87
+ expect(Spree::Property.count).to eq(3)
88
+ end
89
+
90
+ it "can update a property" do
91
+ api_put :update, :id => property_1.name, :property => { :presentation => "my value 456" }
92
+ expect(response.status).to eq(200)
93
+ end
94
+
95
+ it "can delete a property" do
96
+ api_delete :destroy, :id => property_1.name
97
+ expect(response.status).to eq(204)
98
+ expect { property_1.reload }.to raise_error(ActiveRecord::RecordNotFound)
99
+ end
100
+ end
101
+ end
102
+ end