spree_api 3.1.14 → 3.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/app/controllers/spree/api/base_controller.rb +2 -2
  4. data/app/controllers/spree/api/v1/addresses_controller.rb +1 -1
  5. data/app/controllers/spree/api/v1/classifications_controller.rb +1 -1
  6. data/app/controllers/spree/api/v1/images_controller.rb +3 -0
  7. data/app/controllers/spree/api/v1/inventory_units_controller.rb +3 -3
  8. data/app/controllers/spree/api/v1/line_items_controller.rb +3 -0
  9. data/app/controllers/spree/api/v1/option_types_controller.rb +16 -7
  10. data/app/controllers/spree/api/v1/option_values_controller.rb +6 -3
  11. data/app/controllers/spree/api/v1/orders_controller.rb +13 -24
  12. data/app/controllers/spree/api/v1/payments_controller.rb +0 -1
  13. data/app/controllers/spree/api/v1/products_controller.rb +8 -5
  14. data/app/controllers/spree/api/v1/stock_items_controller.rb +1 -1
  15. data/app/controllers/spree/api/v1/stock_locations_controller.rb +1 -1
  16. data/app/controllers/spree/api/v1/tags_controller.rb +28 -0
  17. data/app/controllers/spree/api/v1/taxonomies_controller.rb +7 -4
  18. data/app/controllers/spree/api/v1/taxons_controller.rb +4 -1
  19. data/app/controllers/spree/api/v1/users_controller.rb +5 -3
  20. data/app/controllers/spree/api/v1/zones_controller.rb +3 -3
  21. data/app/helpers/spree/api/api_helpers.rb +4 -1
  22. data/app/models/spree/api_configuration.rb +1 -1
  23. data/app/views/spree/api/errors/invalid_api_key.v1.rabl +1 -1
  24. data/app/views/spree/api/errors/invalid_resource.v1.rabl +1 -1
  25. data/app/views/spree/api/errors/must_specify_api_key.v1.rabl +1 -1
  26. data/app/views/spree/api/errors/not_found.v1.rabl +1 -1
  27. data/app/views/spree/api/errors/unauthorized.v1.rabl +1 -1
  28. data/app/views/spree/api/v1/countries/show.v1.rabl +1 -1
  29. data/app/views/spree/api/v1/line_items/show.v1.rabl +2 -2
  30. data/app/views/spree/api/v1/option_types/show.v1.rabl +1 -1
  31. data/app/views/spree/api/v1/orders/invalid_shipping_method.v1.rabl +1 -1
  32. data/app/views/spree/api/v1/orders/payment.v1.rabl +1 -1
  33. data/app/views/spree/api/v1/orders/show.v1.rabl +9 -9
  34. data/app/views/spree/api/v1/payments/credit_over_limit.v1.rabl +1 -1
  35. data/app/views/spree/api/v1/payments/update_forbidden.v1.rabl +1 -1
  36. data/app/views/spree/api/v1/products/show.v1.rabl +5 -5
  37. data/app/views/spree/api/v1/shipments/cannot_ready_shipment.v1.rabl +1 -1
  38. data/app/views/spree/api/v1/shipments/show.v1.rabl +7 -7
  39. data/app/views/spree/api/v1/shipments/small.v1.rabl +8 -8
  40. data/app/views/spree/api/v1/tags/index.v1.rabl +9 -0
  41. data/app/views/spree/api/v1/taxonomies/jstree.rabl +2 -2
  42. data/app/views/spree/api/v1/taxonomies/nested.v1.rabl +2 -2
  43. data/app/views/spree/api/v1/taxons/jstree.rabl +3 -3
  44. data/app/views/spree/api/v1/taxons/show.v1.rabl +1 -1
  45. data/app/views/spree/api/v1/taxons/taxons.v1.rabl +1 -1
  46. data/app/views/spree/api/v1/users/show.v1.rabl +3 -2
  47. data/app/views/spree/api/v1/variants/big.v1.rabl +3 -3
  48. data/app/views/spree/api/v1/variants/small.v1.rabl +3 -2
  49. data/app/views/spree/api/v1/zones/show.v1.rabl +1 -1
  50. data/config/routes.rb +4 -6
  51. data/db/migrate/20100107141738_add_api_key_to_spree_users.rb +2 -2
  52. data/db/migrate/20120411123334_resize_api_key_field.rb +2 -2
  53. data/db/migrate/20120530054546_rename_api_key_to_spree_api_key.rb +1 -1
  54. data/db/migrate/20131017162334_add_index_to_user_spree_api_key.rb +1 -1
  55. data/lib/spree/api/engine.rb +11 -3
  56. data/lib/spree/api/responders/rabl_template.rb +1 -1
  57. data/lib/spree/api/testing_support/caching.rb +2 -2
  58. data/spec/controllers/spree/api/base_controller_spec.rb +96 -0
  59. data/spec/controllers/spree/api/v1/addresses_controller_spec.rb +56 -0
  60. data/spec/controllers/spree/api/v1/checkouts_controller_spec.rb +363 -0
  61. data/spec/controllers/spree/api/v1/classifications_controller_spec.rb +48 -0
  62. data/spec/controllers/spree/api/v1/countries_controller_spec.rb +48 -0
  63. data/spec/controllers/spree/api/v1/credit_cards_controller_spec.rb +80 -0
  64. data/spec/controllers/spree/api/v1/images_controller_spec.rb +114 -0
  65. data/spec/controllers/spree/api/v1/inventory_units_controller_spec.rb +48 -0
  66. data/spec/controllers/spree/api/v1/line_items_controller_spec.rb +203 -0
  67. data/spec/controllers/spree/api/v1/option_types_controller_spec.rb +122 -0
  68. data/spec/controllers/spree/api/v1/option_values_controller_spec.rb +141 -0
  69. data/spec/controllers/spree/api/v1/orders_controller_spec.rb +735 -0
  70. data/spec/controllers/spree/api/v1/payments_controller_spec.rb +234 -0
  71. data/spec/controllers/spree/api/v1/product_properties_controller_spec.rb +147 -0
  72. data/spec/controllers/spree/api/v1/products_controller_spec.rb +409 -0
  73. data/spec/controllers/spree/api/v1/promotion_application_spec.rb +50 -0
  74. data/spec/controllers/spree/api/v1/promotions_controller_spec.rb +64 -0
  75. data/spec/controllers/spree/api/v1/properties_controller_spec.rb +102 -0
  76. data/spec/controllers/spree/api/v1/return_authorizations_controller_spec.rb +161 -0
  77. data/spec/controllers/spree/api/v1/shipments_controller_spec.rb +187 -0
  78. data/spec/controllers/spree/api/v1/states_controller_spec.rb +86 -0
  79. data/spec/controllers/spree/api/v1/stock_items_controller_spec.rb +143 -0
  80. data/spec/controllers/spree/api/v1/stock_locations_controller_spec.rb +113 -0
  81. data/spec/controllers/spree/api/v1/stock_movements_controller_spec.rb +84 -0
  82. data/spec/controllers/spree/api/v1/stores_controller_spec.rb +133 -0
  83. data/spec/controllers/spree/api/v1/tags_controller_spec.rb +102 -0
  84. data/spec/controllers/spree/api/v1/taxonomies_controller_spec.rb +114 -0
  85. data/spec/controllers/spree/api/v1/taxons_controller_spec.rb +177 -0
  86. data/spec/controllers/spree/api/v1/unauthenticated_products_controller_spec.rb +26 -0
  87. data/spec/controllers/spree/api/v1/users_controller_spec.rb +153 -0
  88. data/spec/controllers/spree/api/v1/variants_controller_spec.rb +205 -0
  89. data/spec/controllers/spree/api/v1/zones_controller_spec.rb +91 -0
  90. data/spec/models/spree/legacy_user_spec.rb +19 -0
  91. data/spec/requests/rabl_cache_spec.rb +32 -0
  92. data/spec/requests/ransackable_attributes_spec.rb +79 -0
  93. data/spec/requests/version_spec.rb +19 -0
  94. data/spec/shared_examples/protect_product_actions.rb +17 -0
  95. data/spec/spec_helper.rb +60 -0
  96. data/spec/support/controller_hacks.rb +40 -0
  97. data/spec/support/database_cleaner.rb +14 -0
  98. data/spec/support/have_attributes_matcher.rb +13 -0
  99. data/spree_api.gemspec +7 -4
  100. metadata +99 -14
@@ -0,0 +1,234 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Api::V1::PaymentsController, type: :controller do
5
+ render_views
6
+ let!(:order) { create(:order) }
7
+ let!(:payment) { create(:payment, order: order) }
8
+ let!(:attributes) { [:id, :source_type, :source_id, :amount, :display_amount,
9
+ :payment_method_id, :state, :avs_response,
10
+ :created_at, :updated_at, :number] }
11
+
12
+ let(:resource_scoping) { { order_id: order.to_param } }
13
+
14
+ before do
15
+ stub_authentication!
16
+ end
17
+
18
+ context "as a user" do
19
+ context "when the order belongs to the user" do
20
+ before do
21
+ allow_any_instance_of(Order).to receive_messages user: current_api_user
22
+ end
23
+
24
+ it "can view the payments for their order" do
25
+ api_get :index
26
+ expect(json_response["payments"].first).to have_attributes(attributes)
27
+ end
28
+
29
+ it "can learn how to create a new payment" do
30
+ api_get :new
31
+ expect(json_response["attributes"]).to eq(attributes.map(&:to_s))
32
+ expect(json_response["payment_methods"]).not_to be_empty
33
+ expect(json_response["payment_methods"].first).to have_attributes([:id, :name, :description])
34
+ end
35
+
36
+ it "can create a new payment" do
37
+ api_post :create, payment: { payment_method_id: PaymentMethod.first.id, amount: 50 }
38
+ expect(response.status).to eq(201)
39
+ expect(json_response).to have_attributes(attributes)
40
+ end
41
+
42
+ it "can view a pre-existing payment's details" do
43
+ api_get :show, id: payment.to_param
44
+ expect(json_response).to have_attributes(attributes)
45
+ end
46
+
47
+ it "cannot update a payment" do
48
+ api_put :update, id: payment.to_param, payment: { amount: 2.01 }
49
+ assert_unauthorized!
50
+ end
51
+
52
+ it "cannot authorize a payment" do
53
+ api_put :authorize, id: payment.to_param
54
+ assert_unauthorized!
55
+ end
56
+ end
57
+
58
+ context "when the order does not belong to the user" do
59
+ before do
60
+ allow_any_instance_of(Order).to receive_messages user: stub_model(LegacyUser)
61
+ end
62
+
63
+ it "cannot view payments for somebody else's order" do
64
+ api_get :index, order_id: order.to_param
65
+ assert_unauthorized!
66
+ end
67
+
68
+ it "can view the payments for an order given the order token" do
69
+ api_get :index, order_id: order.to_param, order_token: order.guest_token
70
+ expect(json_response["payments"].first).to have_attributes(attributes)
71
+ end
72
+ end
73
+ end
74
+
75
+ context "as an admin" do
76
+ sign_in_as_admin!
77
+
78
+ it "can view the payments on any order" do
79
+ api_get :index
80
+ expect(response.status).to eq(200)
81
+ expect(json_response["payments"].first).to have_attributes(attributes)
82
+ end
83
+
84
+ context "multiple payments" do
85
+ before { @payment = create(:payment, order: order) }
86
+
87
+ it "can view all payments on an order" do
88
+ api_get :index
89
+ expect(json_response["count"]).to eq(2)
90
+ end
91
+
92
+ it 'can control the page size through a parameter' do
93
+ api_get :index, per_page: 1
94
+ expect(json_response['count']).to eq(1)
95
+ expect(json_response['current_page']).to eq(1)
96
+ expect(json_response['pages']).to eq(2)
97
+ end
98
+ end
99
+
100
+ context "for a given payment" do
101
+ context "updating" do
102
+ context "when the state is checkout" do
103
+ it "can update" do
104
+ payment.update_attributes(state: 'checkout')
105
+ api_put(:update, id: payment.to_param, payment: { amount: 2.01 })
106
+ expect(response.status).to be(200)
107
+ expect(payment.reload.amount).to eq(2.01)
108
+ end
109
+ end
110
+
111
+ context "when the state is pending" do
112
+ it "can update" do
113
+ payment.update_attributes(state: 'pending')
114
+ api_put(:update, id: payment.to_param, payment: { amount: 2.01 })
115
+ expect(response.status).to be(200)
116
+ expect(payment.reload.amount).to eq(2.01)
117
+ end
118
+ end
119
+
120
+ context "update fails" do
121
+ it "returns a 422 status when the amount is invalid" do
122
+ payment.update_attributes(state: 'pending')
123
+ api_put(:update, id: payment.to_param, payment: { amount: 'invalid' })
124
+ expect(response.status).to be(422)
125
+ expect(json_response['error']).to eql('Invalid resource. Please fix errors and try again.')
126
+ end
127
+
128
+ it "returns a 403 status when the payment is not pending" do
129
+ payment.update_attributes(state: 'completed')
130
+ api_put(:update, id: payment.to_param, payment: { amount: 2.01 })
131
+ expect(response.status).to be(403)
132
+ expect(json_response['error']).to eql('This payment cannot be updated because it is completed.')
133
+ end
134
+ end
135
+ end
136
+
137
+ context "authorizing" do
138
+ it "can authorize" do
139
+ api_put :authorize, id: payment.to_param
140
+ expect(response.status).to eq(200)
141
+ expect(payment.reload.state).to eq("pending")
142
+ end
143
+
144
+ context "authorization fails" do
145
+ before do
146
+ fake_response = double(success?: false, to_s: "Could not authorize card")
147
+ expect_any_instance_of(Spree::Gateway::Bogus).to receive(:authorize).and_return(fake_response)
148
+ api_put :authorize, id: payment.to_param
149
+ end
150
+
151
+ it "returns a 422 status" do
152
+ expect(response.status).to eq(422)
153
+ expect(json_response["error"]).to eq "Invalid resource. Please fix errors and try again."
154
+ expect(json_response["errors"]["base"][0]).to eq "Could not authorize card"
155
+ end
156
+
157
+ it "does not raise a stack level error" do
158
+ skip "Investigate why a payment.reload after the request raises 'stack level too deep'"
159
+ expect(payment.reload.state).to eq("failed")
160
+ end
161
+ end
162
+ end
163
+
164
+ context "capturing" do
165
+ it "can capture" do
166
+ api_put :capture, id: payment.to_param
167
+ expect(response.status).to eq(200)
168
+ expect(payment.reload.state).to eq("completed")
169
+ end
170
+
171
+ context "capturing fails" do
172
+ before do
173
+ fake_response = double(success?: false, to_s: "Insufficient funds")
174
+ expect_any_instance_of(Spree::Gateway::Bogus).to receive(:capture).and_return(fake_response)
175
+ end
176
+
177
+ it "returns a 422 status" do
178
+ api_put :capture, id: payment.to_param
179
+ expect(response.status).to eq(422)
180
+ expect(json_response["error"]).to eq "Invalid resource. Please fix errors and try again."
181
+ expect(json_response["errors"]["base"][0]).to eq "Insufficient funds"
182
+ end
183
+ end
184
+ end
185
+
186
+ context "purchasing" do
187
+ it "can purchase" do
188
+ api_put :purchase, id: payment.to_param
189
+ expect(response.status).to eq(200)
190
+ expect(payment.reload.state).to eq("completed")
191
+ end
192
+
193
+ context "purchasing fails" do
194
+ before do
195
+ fake_response = double(success?: false, to_s: "Insufficient funds")
196
+ expect_any_instance_of(Spree::Gateway::Bogus).to receive(:purchase).and_return(fake_response)
197
+ end
198
+
199
+ it "returns a 422 status" do
200
+ api_put :purchase, id: payment.to_param
201
+ expect(response.status).to eq(422)
202
+ expect(json_response["error"]).to eq "Invalid resource. Please fix errors and try again."
203
+ expect(json_response["errors"]["base"][0]).to eq "Insufficient funds"
204
+ end
205
+ end
206
+ end
207
+
208
+ context "voiding" do
209
+ it "can void" do
210
+ api_put :void, id: payment.to_param
211
+ expect(response.status).to eq 200
212
+ expect(payment.reload.state).to eq "void"
213
+ end
214
+
215
+ context "voiding fails" do
216
+ before do
217
+ fake_response = double(success?: false, to_s: "NO REFUNDS")
218
+ expect_any_instance_of(Spree::Gateway::Bogus).to receive(:void).and_return(fake_response)
219
+ end
220
+
221
+ it "returns a 422 status" do
222
+ api_put :void, id: payment.to_param
223
+ expect(response.status).to eq 422
224
+ expect(json_response["error"]).to eq "Invalid resource. Please fix errors and try again."
225
+ expect(json_response["errors"]["base"][0]).to eq "NO REFUNDS"
226
+ expect(payment.reload.state).to eq "checkout"
227
+ end
228
+ end
229
+ end
230
+
231
+ end
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,147 @@
1
+ require 'spec_helper'
2
+ require 'shared_examples/protect_product_actions'
3
+
4
+ module Spree
5
+ describe Api::V1::ProductPropertiesController, type: :controller do
6
+ render_views
7
+
8
+ let!(:product) { create(:product) }
9
+ let!(:property_1) {product.product_properties.create(property_name: "My Property 1", value: "my value 1", position: 0)}
10
+ let!(:property_2) {product.product_properties.create(property_name: "My Property 2", value: "my value 2", position: 1)}
11
+
12
+ let(:attributes) { [:id, :product_id, :property_id, :value, :property_name] }
13
+ let(:resource_scoping) { { product_id: product.to_param } }
14
+
15
+ before do
16
+ stub_authentication!
17
+ end
18
+
19
+ context "if product is deleted" do
20
+ before do
21
+ product.update_column(:deleted_at, 1.day.ago)
22
+ end
23
+
24
+ it "can not see a list of product properties" do
25
+ api_get :index
26
+ expect(response.status).to eq(404)
27
+ end
28
+ end
29
+
30
+ it "can see a list of all product properties" do
31
+ api_get :index
32
+ expect(json_response["product_properties"].count).to eq 2
33
+ expect(json_response["product_properties"].first).to have_attributes(attributes)
34
+ end
35
+
36
+ it "can control the page size through a parameter" do
37
+ api_get :index, per_page: 1
38
+ expect(json_response['product_properties'].count).to eq(1)
39
+ expect(json_response['current_page']).to eq(1)
40
+ expect(json_response['pages']).to eq(2)
41
+ end
42
+
43
+ it 'can query the results through a parameter' do
44
+ Spree::ProductProperty.last.update_attribute(:value, 'loose')
45
+ property = Spree::ProductProperty.last
46
+ api_get :index, q: { value_cont: 'loose' }
47
+ expect(json_response['count']).to eq(1)
48
+ expect(json_response['product_properties'].first['value']).to eq property.value
49
+ end
50
+
51
+ it "can see a single product_property" do
52
+ api_get :show, id: property_1.property_name
53
+ expect(json_response).to have_attributes(attributes)
54
+ end
55
+
56
+ it "can learn how to create a new product property" do
57
+ api_get :new
58
+ expect(json_response["attributes"]).to eq(attributes.map(&:to_s))
59
+ expect(json_response["required_attributes"]).to be_empty
60
+ end
61
+
62
+ it "cannot create a new product property if not an admin" do
63
+ api_post :create, product_property: { property_name: "My Property 3" }
64
+ assert_unauthorized!
65
+ end
66
+
67
+ it "cannot update a product property" do
68
+ api_put :update, id: property_1.property_name, product_property: { value: "my value 456" }
69
+ assert_unauthorized!
70
+ end
71
+
72
+ it "cannot delete a product property" do
73
+ api_delete :destroy, id: property_1.to_param, property_name: property_1.property_name
74
+ assert_unauthorized!
75
+ expect { property_1.reload }.not_to raise_error
76
+ end
77
+
78
+ context "as an admin" do
79
+ sign_in_as_admin!
80
+
81
+ it "can create a new product property" do
82
+ expect do
83
+ api_post :create, product_property: { property_name: "My Property 3", value: "my value 3" }
84
+ end.to change(product.product_properties, :count).by(1)
85
+ expect(json_response).to have_attributes(attributes)
86
+ expect(response.status).to eq(201)
87
+ end
88
+
89
+ context 'when product property does not exist' do
90
+ it 'cannot update product property and responds 404' do
91
+ api_put :update, id: 'does not exist', product_property: { value: 'new value' }
92
+ expect(response.status).to eq(404)
93
+ end
94
+ end
95
+
96
+ context 'when product property exists' do
97
+ context 'when product property is valid' do
98
+ it 'responds 200' do
99
+ api_put :update, id: property_1.property_name, product_property: { value: "my value 456" }
100
+ expect(response.status).to eq(200)
101
+ end
102
+ end
103
+
104
+ context 'when product property is invalid' do
105
+ before(:each) do
106
+ expect_any_instance_of(Spree::ProductProperty).to receive(:update_attributes).and_return false
107
+ end
108
+
109
+ it 'responds 422' do
110
+ api_put :update, id: property_1.property_name, product_property: { value: 'hello' }
111
+ expect(response.status).to eq(422)
112
+ end
113
+ end
114
+ end
115
+
116
+ context 'when product property does not exist' do
117
+ it 'cannot delete product property and responds 404' do
118
+ api_delete :destroy, id: 'does not exist'
119
+ expect(response.status).to eq(404)
120
+ end
121
+ end
122
+
123
+ context 'when product property exists' do
124
+ it "can delete a product property" do
125
+ api_delete :destroy, id: property_1.property_name
126
+ expect(response.status).to eq(204)
127
+ expect { property_1.reload }.to raise_error(ActiveRecord::RecordNotFound)
128
+ end
129
+ end
130
+ end
131
+
132
+ context "with product identified by id" do
133
+ let(:resource_scoping) { { product_id: product.id } }
134
+ it "can see a list of all product properties" do
135
+ api_get :index
136
+ expect(json_response["product_properties"].count).to eq 2
137
+ expect(json_response["product_properties"].first).to have_attributes(attributes)
138
+ end
139
+
140
+ it "can see a single product_property by id" do
141
+ api_get :show, id: property_1.id
142
+ expect(json_response).to have_attributes(attributes)
143
+ end
144
+ end
145
+
146
+ end
147
+ end
@@ -0,0 +1,409 @@
1
+ require 'spec_helper'
2
+ require 'shared_examples/protect_product_actions'
3
+
4
+ module Spree
5
+ describe Api::V1::ProductsController, type: :controller do
6
+ render_views
7
+
8
+ let!(:product) { create(:product) }
9
+ let!(:inactive_product) { create(:product, available_on: Time.current.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 "product has more than one price" do
71
+ before { product.master.prices.create currency: "EUR", amount: 22 }
72
+
73
+ it "returns distinct products only" do
74
+ api_get :index
75
+ expect(assigns(:products).map(&:id).uniq).to eq assigns(:products).map(&:id)
76
+ end
77
+ end
78
+
79
+ it "retrieves a list of products by ids string" do
80
+ second_product = create(:product)
81
+ api_get :index, ids: [product.id, second_product.id].join(",")
82
+ expect(json_response["products"].first).to have_attributes(show_attributes)
83
+ expect(json_response["products"][1]).to have_attributes(show_attributes)
84
+ expect(json_response["total_count"]).to eq(2)
85
+ expect(json_response["current_page"]).to eq(1)
86
+ expect(json_response["pages"]).to eq(1)
87
+ expect(json_response["per_page"]).to eq(Kaminari.config.default_per_page)
88
+ end
89
+
90
+ it "does not return inactive products when queried by ids" do
91
+ api_get :index, ids: [inactive_product.id]
92
+ expect(json_response["count"]).to eq(0)
93
+ end
94
+
95
+ it "does not list unavailable products" do
96
+ api_get :index
97
+ expect(json_response["products"].first["name"]).not_to eq("inactive")
98
+ end
99
+
100
+ context "pagination" do
101
+ it "can select the next page of products" do
102
+ second_product = create(:product)
103
+ api_get :index, page: 2, per_page: 1
104
+ expect(json_response["products"].first).to have_attributes(show_attributes)
105
+ expect(json_response["total_count"]).to eq(2)
106
+ expect(json_response["current_page"]).to eq(2)
107
+ expect(json_response["pages"]).to eq(2)
108
+ end
109
+
110
+ it 'can control the page size through a parameter' do
111
+ create(:product)
112
+ api_get :index, per_page: 1
113
+ expect(json_response['count']).to eq(1)
114
+ expect(json_response['total_count']).to eq(2)
115
+ expect(json_response['current_page']).to eq(1)
116
+ expect(json_response['pages']).to eq(2)
117
+ end
118
+ end
119
+
120
+ it "can search for products" do
121
+ create(:product, name: "The best product in the world")
122
+ api_get :index, q: { name_cont: "best" }
123
+ expect(json_response["products"].first).to have_attributes(show_attributes)
124
+ expect(json_response["count"]).to eq(1)
125
+ end
126
+
127
+ it "gets a single product" do
128
+ product.master.images.create!(attachment: image("thinking-cat.jpg"))
129
+ create(:variant, product: product)
130
+ product.variants.first.images.create!(attachment: image("thinking-cat.jpg"))
131
+ product.set_property("spree", "rocks")
132
+ product.taxons << create(:taxon)
133
+
134
+ api_get :show, id: product.to_param
135
+
136
+ expect(json_response).to have_attributes(show_attributes)
137
+ expect(json_response['variants'].first).to have_attributes([:name,
138
+ :is_master,
139
+ :price,
140
+ :images,
141
+ :in_stock])
142
+
143
+ expect(json_response['variants'].first['images'].first).to have_attributes([:attachment_file_name,
144
+ :attachment_width,
145
+ :attachment_height,
146
+ :attachment_content_type,
147
+ :mini_url,
148
+ :small_url,
149
+ :product_url,
150
+ :large_url])
151
+
152
+ expect(json_response["product_properties"].first).to have_attributes([:value,
153
+ :product_id,
154
+ :property_name])
155
+
156
+ expect(json_response["classifications"].first).to have_attributes([:taxon_id, :position, :taxon])
157
+ expect(json_response["classifications"].first['taxon']).to have_attributes([:id, :name, :pretty_name, :permalink, :taxonomy_id, :parent_id])
158
+ end
159
+
160
+ context "tracking is disabled" do
161
+ before { Config.track_inventory_levels = false }
162
+
163
+ it "still displays valid json with total_on_hand Float::INFINITY" do
164
+ api_get :show, id: product.to_param
165
+ expect(response).to be_ok
166
+ expect(json_response[:total_on_hand]).to eq nil
167
+ end
168
+
169
+ after { Config.track_inventory_levels = true }
170
+ end
171
+
172
+ context "finds a product by slug first then by id" do
173
+ let!(:other_product) { create(:product, slug: "these-are-not-the-droids-you-are-looking-for") }
174
+
175
+ before do
176
+ product.update_column(:slug, "#{other_product.id}-and-1-ways")
177
+ end
178
+
179
+ specify do
180
+ api_get :show, id: product.to_param
181
+ expect(json_response["slug"]).to match(/and-1-ways/)
182
+ product.destroy
183
+
184
+ api_get :show, id: other_product.id
185
+ expect(json_response["slug"]).to match(/droids/)
186
+ end
187
+ end
188
+
189
+ it "cannot see inactive products" do
190
+ api_get :show, id: inactive_product.to_param
191
+ assert_not_found!
192
+ end
193
+
194
+ it "returns a 404 error when it cannot find a product" do
195
+ api_get :show, id: "non-existant"
196
+ assert_not_found!
197
+ end
198
+
199
+ it "can learn how to create a new product" do
200
+ api_get :new
201
+ expect(json_response["attributes"]).to eq(new_attributes.map(&:to_s))
202
+ required_attributes = json_response["required_attributes"]
203
+ expect(required_attributes).to include("name")
204
+ expect(required_attributes).to include("price")
205
+ expect(required_attributes).to include("shipping_category")
206
+ end
207
+
208
+ it_behaves_like "modifying product actions are restricted"
209
+ end
210
+
211
+ context "as an admin" do
212
+ let(:taxon_1) { create(:taxon) }
213
+ let(:taxon_2) { create(:taxon) }
214
+
215
+ sign_in_as_admin!
216
+
217
+ it "can see all products" do
218
+ api_get :index
219
+ expect(json_response["products"].count).to eq(2)
220
+ expect(json_response["count"]).to eq(2)
221
+ expect(json_response["current_page"]).to eq(1)
222
+ expect(json_response["pages"]).to eq(1)
223
+ end
224
+
225
+ # Regression test for #1626
226
+ context "deleted products" do
227
+ before do
228
+ create(:product, deleted_at: 1.day.ago)
229
+ end
230
+
231
+ it "does not include deleted products" do
232
+ api_get :index
233
+ expect(json_response["products"].count).to eq(2)
234
+ end
235
+
236
+ it "can include deleted products" do
237
+ api_get :index, show_deleted: 1
238
+ expect(json_response["products"].count).to eq(3)
239
+ end
240
+ end
241
+
242
+ describe "creating a product" do
243
+ it "can create a new product" do
244
+ api_post :create, product: { name: "The Other Product",
245
+ price: 19.99,
246
+ shipping_category_id: create(:shipping_category).id }
247
+ expect(json_response).to have_attributes(base_attributes)
248
+ expect(response.status).to eq(201)
249
+ end
250
+
251
+ it "creates with embedded variants" do
252
+ product_data.merge!({
253
+ variants: [attributes_for_variant, attributes_for_variant]
254
+ })
255
+
256
+ api_post :create, product: product_data
257
+ expect(response.status).to eq 201
258
+
259
+ variants = json_response['variants']
260
+ expect(variants.count).to eq(2)
261
+ expect(variants.last['option_values'][0]['name']).to eq('small')
262
+ expect(variants.last['option_values'][0]['option_type_name']).to eq('size')
263
+
264
+ expect(json_response['option_types'].count).to eq(2) # size, color
265
+ end
266
+
267
+ it "can create a new product with embedded product_properties" do
268
+ product_data.merge!({
269
+ product_properties_attributes: [{
270
+ property_name: "fabric",
271
+ value: "cotton"
272
+ }]
273
+ })
274
+
275
+ api_post :create, product: product_data
276
+
277
+ expect(json_response['product_properties'][0]['property_name']).to eq('fabric')
278
+ expect(json_response['product_properties'][0]['value']).to eq('cotton')
279
+ end
280
+
281
+ it "can create a new product with option_types" do
282
+ product_data.merge!({
283
+ option_types: ['size', 'color']
284
+ })
285
+
286
+ api_post :create, product: product_data
287
+ expect(json_response['option_types'].count).to eq(2)
288
+ end
289
+
290
+ it "creates product with option_types ids" do
291
+ option_type = create(:option_type)
292
+ product_data.merge!(option_type_ids: [option_type.id])
293
+ api_post :create, product: product_data
294
+ expect(json_response['option_types'].first['id']).to eq option_type.id
295
+ end
296
+
297
+ it "creates with shipping categories" do
298
+ hash = { name: "The Other Product",
299
+ price: 19.99,
300
+ shipping_category: "Free Ships" }
301
+
302
+ api_post :create, product: hash
303
+ expect(response.status).to eq 201
304
+
305
+ shipping_id = ShippingCategory.find_by_name("Free Ships").id
306
+ expect(json_response['shipping_category_id']).to eq shipping_id
307
+ end
308
+
309
+ it "puts the created product in the given taxons" do
310
+ product_data[:taxon_ids] = [taxon_1.id, taxon_2.id]
311
+ api_post :create, product: product_data
312
+ expect(json_response["taxon_ids"]).to eq([taxon_1.id, taxon_2.id])
313
+ end
314
+
315
+ # Regression test for #2140
316
+ context "with authentication_required set to false" do
317
+ before do
318
+ Spree::Api::Config.requires_authentication = false
319
+ end
320
+
321
+ after do
322
+ Spree::Api::Config.requires_authentication = true
323
+ end
324
+
325
+ it "can still create a product" do
326
+ api_post :create, product: product_data, token: "fake"
327
+ expect(json_response).to have_attributes(show_attributes)
328
+ expect(response.status).to eq(201)
329
+ end
330
+ end
331
+
332
+ it "cannot create a new product with invalid attributes" do
333
+ api_post :create, product: { foo: :bar }
334
+ expect(response.status).to eq(422)
335
+ expect(json_response["error"]).to eq("Invalid resource. Please fix errors and try again.")
336
+ errors = json_response["errors"]
337
+ errors.delete("slug") # Don't care about this one.
338
+ expect(errors.keys).to match_array(["name", "price", "shipping_category"])
339
+ end
340
+ end
341
+
342
+ context 'updating a product' do
343
+ it "can update a product" do
344
+ api_put :update, id: product.to_param, product: { name: "New and Improved Product!" }
345
+ expect(response.status).to eq(200)
346
+ end
347
+
348
+ it "can create new option types on a product" do
349
+ api_put :update, id: product.to_param, product: { option_types: ['shape', 'color'] }
350
+ expect(json_response['option_types'].count).to eq(2)
351
+ end
352
+
353
+ it "can create new variants on a product" do
354
+ api_put :update, id: product.to_param, product: { variants: [attributes_for_variant, attributes_for_variant.merge(sku: "ABC-#{Kernel.rand(9999)}")] }
355
+ expect(response.status).to eq 200
356
+ expect(json_response['variants'].count).to eq(2) # 2 variants
357
+
358
+ variants = json_response['variants'].select { |v| !v['is_master'] }
359
+ expect(variants.last['option_values'][0]['name']).to eq('small')
360
+ expect(variants.last['option_values'][0]['option_type_name']).to eq('size')
361
+
362
+ expect(json_response['option_types'].count).to eq(2) # size, color
363
+ end
364
+
365
+ it "can update an existing variant on a product" do
366
+ variant_hash = {
367
+ sku: '123', price: 19.99, options: [{name: "size", value: "small"}]
368
+ }
369
+ variant_id = product.variants.create!({ product: product }.merge(variant_hash)).id
370
+
371
+ api_put :update, id: product.to_param, product: {
372
+ variants: [
373
+ variant_hash.merge(
374
+ id: variant_id.to_s,
375
+ sku: '456',
376
+ options: [{name: "size", value: "large" }]
377
+ )
378
+ ]
379
+ }
380
+
381
+ expect(json_response['variants'].count).to eq(1)
382
+ variants = json_response['variants'].select { |v| !v['is_master'] }
383
+ expect(variants.last['option_values'][0]['name']).to eq('large')
384
+ expect(variants.last['sku']).to eq('456')
385
+ expect(variants.count).to eq(1)
386
+ end
387
+
388
+ it "cannot update a product with an invalid attribute" do
389
+ api_put :update, id: product.to_param, product: { name: "" }
390
+ expect(response.status).to eq(422)
391
+ expect(json_response["error"]).to eq("Invalid resource. Please fix errors and try again.")
392
+ expect(json_response["errors"]["name"]).to eq(["can't be blank"])
393
+ end
394
+
395
+ it "puts the updated product in the given taxons" do
396
+ api_put :update, id: product.to_param, product: { taxon_ids: [taxon_1.id, taxon_2.id] }
397
+ expect(json_response["taxon_ids"].to_set).to eql([taxon_1.id, taxon_2.id].to_set)
398
+ end
399
+ end
400
+
401
+ it "can delete a product" do
402
+ expect(product.deleted_at).to be_nil
403
+ api_delete :destroy, id: product.to_param
404
+ expect(response.status).to eq(204)
405
+ expect(product.reload.deleted_at).not_to be_nil
406
+ end
407
+ end
408
+ end
409
+ end