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
@@ -1,7 +1,7 @@
1
- class AddApiKeyToSpreeUsers < ActiveRecord::Migration
1
+ class AddApiKeyToSpreeUsers < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  unless defined?(User)
4
- add_column :spree_users, :api_key, :string, :limit => 40
4
+ add_column :spree_users, :api_key, :string, limit: 40
5
5
  end
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
- class ResizeApiKeyField < ActiveRecord::Migration
1
+ class ResizeApiKeyField < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  unless defined?(User)
4
- change_column :spree_users, :api_key, :string, :limit => 48
4
+ change_column :spree_users, :api_key, :string, limit: 48
5
5
  end
6
6
  end
7
7
  end
@@ -1,4 +1,4 @@
1
- class RenameApiKeyToSpreeApiKey < ActiveRecord::Migration
1
+ class RenameApiKeyToSpreeApiKey < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  unless defined?(User)
4
4
  rename_column :spree_users, :api_key, :spree_api_key
@@ -1,4 +1,4 @@
1
- class AddIndexToUserSpreeApiKey < ActiveRecord::Migration
1
+ class AddIndexToUserSpreeApiKey < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  unless defined?(User)
4
4
  add_index :spree_users, :spree_api_key
@@ -17,10 +17,18 @@ module Spree
17
17
  config.json_engine = ActiveSupport::JSON
18
18
  end
19
19
 
20
- config.versioncake.supported_version_numbers = [1]
21
- config.versioncake.extraction_strategy = :http_header
20
+ initializer "spree.api.versioncake" do |_app|
21
+ VersionCake.setup do |config|
22
+ config.resources do |r|
23
+ r.resource %r{.*}, [], [], [1]
24
+ end
22
25
 
23
- initializer "spree.api.environment", :before => :load_config_initializers do |app|
26
+ config.missing_version = 1
27
+ config.extraction_strategy = :http_header
28
+ end
29
+ end
30
+
31
+ initializer "spree.api.environment", before: :load_config_initializers do |app|
24
32
  Spree::Api::Config = Spree::ApiConfiguration.new
25
33
  end
26
34
 
@@ -4,7 +4,7 @@ module Spree
4
4
  module RablTemplate
5
5
  def to_format
6
6
  if template
7
- render template, :status => options[:status] || 200
7
+ render template, status: options[:status] || 200
8
8
  else
9
9
  super
10
10
  end
@@ -1,9 +1,9 @@
1
1
  RSpec.configure do |config|
2
- config.before(:each, :caching => true) do
2
+ config.before(:each, caching: true) do
3
3
  ActionController::Base.perform_caching = true
4
4
  end
5
5
 
6
- config.after(:each, :caching => true) do
6
+ config.after(:each, caching: true) do
7
7
  ActionController::Base.perform_caching = false
8
8
  Rails.cache.clear
9
9
  end
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ class FakesController < Spree::Api::BaseController
4
+ end
5
+
6
+ describe Spree::Api::BaseController, type: :controller do
7
+ render_views
8
+ controller(Spree::Api::BaseController) do
9
+ def index
10
+ render plain: { "products" => [] }.to_json
11
+ end
12
+ end
13
+
14
+ before do
15
+ @routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
16
+ r.draw { get 'index', to: 'spree/api/base#index' }
17
+ end
18
+ end
19
+
20
+ context "when validating based on an order token" do
21
+ let!(:order) { create :order }
22
+
23
+ context "with a correct order token" do
24
+ it "succeeds" do
25
+ api_get :index, order_token: order.guest_token, order_id: order.number
26
+ expect(response.status).to eq(200)
27
+ end
28
+
29
+ it "succeeds with an order_number parameter" do
30
+ api_get :index, order_token: order.guest_token, order_number: order.number
31
+ expect(response.status).to eq(200)
32
+ end
33
+ end
34
+
35
+ context "with an incorrect order token" do
36
+ it "returns unauthorized" do
37
+ api_get :index, order_token: "NOT_A_TOKEN", order_id: order.number
38
+ expect(response.status).to eq(401)
39
+ end
40
+ end
41
+ end
42
+
43
+ context "cannot make a request to the API" do
44
+ it "without an API key" do
45
+ api_get :index
46
+ expect(json_response).to eq({ "error" => "You must specify an API key." })
47
+ expect(response.status).to eq(401)
48
+ end
49
+
50
+ it "with an invalid API key" do
51
+ request.headers["X-Spree-Token"] = "fake_key"
52
+ get :index
53
+ expect(json_response).to eq({ "error" => "Invalid API key (fake_key) specified." })
54
+ expect(response.status).to eq(401)
55
+ end
56
+
57
+ it "using an invalid token param" do
58
+ get :index, params: { token: "fake_key" }
59
+ expect(json_response).to eq({ "error" => "Invalid API key (fake_key) specified." })
60
+ end
61
+ end
62
+
63
+ it 'handles parameter missing exceptions' do
64
+ expect(subject).to receive(:authenticate_user).and_return(true)
65
+ expect(subject).to receive(:load_user_roles).and_return(true)
66
+ expect(subject).to receive(:index).and_raise(ActionController::ParameterMissing.new('foo'))
67
+ get :index, params: { token: 'exception-message' }
68
+ expect(json_response).to eql('exception' => 'param is missing or the value is empty: foo')
69
+ end
70
+
71
+ it 'handles record invalid exceptions' do
72
+ expect(subject).to receive(:authenticate_user).and_return(true)
73
+ expect(subject).to receive(:load_user_roles).and_return(true)
74
+ resource = Spree::Product.new
75
+ resource.valid? # get some errors
76
+ expect(subject).to receive(:index).and_raise(ActiveRecord::RecordInvalid.new(resource))
77
+ get :index, params: { token: 'exception-message' }
78
+ expect(json_response).to eql('exception' => "Validation failed: Name can't be blank, Shipping Category can't be blank, Price can't be blank")
79
+ end
80
+
81
+ it "maps semantic keys to nested_attributes keys" do
82
+ klass = double(nested_attributes_options: { line_items: {},
83
+ bill_address: {} })
84
+ attributes = { 'line_items' => { id: 1 },
85
+ 'bill_address' => { id: 2 },
86
+ 'name' => 'test order' }
87
+
88
+ mapped = subject.map_nested_attributes_keys(klass, attributes)
89
+ expect(mapped.has_key?('line_items_attributes')).to be true
90
+ expect(mapped.has_key?('name')).to be true
91
+ end
92
+
93
+ it "lets a subclass override the product associations that are eager-loaded" do
94
+ expect(controller.respond_to?(:product_includes, true)).to be
95
+ end
96
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Api::V1::AddressesController, type: :controller do
5
+ render_views
6
+
7
+ before do
8
+ stub_authentication!
9
+ @address = create(:address)
10
+ @order = create(:order, bill_address: @address)
11
+ end
12
+
13
+ context "with their own address" do
14
+ before do
15
+ allow_any_instance_of(Order).to receive_messages user: current_api_user
16
+ end
17
+
18
+ it "gets an address" do
19
+ api_get :show, id: @address.id, order_id: @order.number
20
+ expect(json_response['address1']).to eq @address.address1
21
+ end
22
+
23
+ it "updates an address" do
24
+ api_put :update, id: @address.id, order_id: @order.number,
25
+ address: { address1: "123 Test Lane" }
26
+ expect(json_response['address1']).to eq '123 Test Lane'
27
+ end
28
+
29
+ it "receives the errors object if address is invalid" do
30
+ api_put :update, id: @address.id, order_id: @order.number,
31
+ address: { address1: "" }
32
+
33
+ expect(json_response['error']).not_to be_nil
34
+ expect(json_response['errors']).not_to be_nil
35
+ expect(json_response['errors']['address1'].first).to eq "can't be blank"
36
+ end
37
+ end
38
+
39
+ context "on an address that does not belong to this order" do
40
+ before do
41
+ @order.bill_address_id = nil
42
+ @order.ship_address = nil
43
+ end
44
+
45
+ it "cannot retrieve address information" do
46
+ api_get :show, id: @address.id, order_id: @order.number
47
+ assert_unauthorized!
48
+ end
49
+
50
+ it "cannot update address information" do
51
+ api_get :update, id: @address.id, order_id: @order.number
52
+ assert_unauthorized!
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,363 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Api::V1::CheckoutsController, type: :controller do
5
+ render_views
6
+
7
+ shared_examples_for 'action which loads order using load_order_with_lock' do
8
+ before do
9
+ allow(controller).to receive(:load_order).with(true).and_return(true)
10
+ end
11
+
12
+ it 'should invoke load_order_with_lock' do
13
+ expect(controller).to receive(:load_order_with_lock).exactly(1).times
14
+ end
15
+
16
+ it 'should invoke load_order' do
17
+ expect(controller).to receive(:load_order).with(true).exactly(1).times.and_return(true)
18
+ end
19
+
20
+ context 'ensure no double_render_error' do
21
+ before do
22
+ def controller.load_order(*)
23
+ respond_with(@order, default_template: 'spree/api/v1/orders/show', status: 200)
24
+ end
25
+ end
26
+
27
+ it 'should not generate double_render_error' do
28
+ expect(response).to be_success
29
+ end
30
+ end
31
+
32
+ after do
33
+ send_request
34
+ end
35
+ end
36
+
37
+ before(:each) do
38
+ stub_authentication!
39
+ Spree::Config[:track_inventory_levels] = false
40
+ country_zone = create(:zone, name: 'CountryZone')
41
+ @state = create(:state)
42
+ @country = @state.country
43
+ country_zone.members.create(zoneable: @country)
44
+ create(:stock_location)
45
+
46
+ @shipping_method = create(:shipping_method, zones: [country_zone])
47
+ @payment_method = create(:credit_card_payment_method)
48
+ end
49
+
50
+ after do
51
+ Spree::Config[:track_inventory_levels] = true
52
+ end
53
+
54
+ context "PUT 'update'" do
55
+ let(:order) do
56
+ order = create(:order_with_line_items)
57
+ # Order should be in a pristine state
58
+ # Without doing this, the order may transition from 'cart' straight to 'delivery'
59
+ order.shipments.delete_all
60
+ order
61
+ end
62
+
63
+ before(:each) do
64
+ allow_any_instance_of(Order).to receive_messages(confirmation_required?: true)
65
+ allow_any_instance_of(Order).to receive_messages(payment_required?: true)
66
+ end
67
+
68
+ it "should transition a recently created order from cart to address" do
69
+ expect(order.state).to eq "cart"
70
+ expect(order.email).not_to be_nil
71
+ api_put :update, id: order.to_param, order_token: order.guest_token
72
+ expect(order.reload.state).to eq "address"
73
+ end
74
+
75
+ it "should transition a recently created order from cart to address with order token in header" do
76
+ expect(order.state).to eq "cart"
77
+ expect(order.email).not_to be_nil
78
+ request.headers["X-Spree-Order-Token"] = order.guest_token
79
+ api_put :update, id: order.to_param
80
+ expect(order.reload.state).to eq "address"
81
+ end
82
+
83
+ it "can take line_items_attributes as a parameter" do
84
+ line_item = order.line_items.first
85
+ api_put :update, id: order.to_param, order_token: order.guest_token,
86
+ order: { line_items_attributes: { 0 => { id: line_item.id, quantity: 1 } } }
87
+ expect(response.status).to eq(200)
88
+ expect(order.reload.state).to eq "address"
89
+ end
90
+
91
+ it "can take line_items as a parameter" do
92
+ line_item = order.line_items.first
93
+ api_put :update, id: order.to_param, order_token: order.guest_token,
94
+ order: { line_items: { 0 => { id: line_item.id, quantity: 1 } } }
95
+ expect(response.status).to eq(200)
96
+ expect(order.reload.state).to eq "address"
97
+ end
98
+
99
+ it "will return an error if the order cannot transition" do
100
+ skip "not sure if this test is valid"
101
+ order.bill_address = nil
102
+ order.save
103
+ order.update_column(:state, "address")
104
+ api_put :update, id: order.to_param, order_token: order.guest_token
105
+ # Order has not transitioned
106
+ expect(response.status).to eq(422)
107
+ end
108
+
109
+ context "transitioning to delivery" do
110
+ before do
111
+ order.update_column(:state, "address")
112
+ end
113
+
114
+ let(:address) do
115
+ {
116
+ firstname: 'John',
117
+ lastname: 'Doe',
118
+ address1: '7735 Old Georgetown Road',
119
+ city: 'Bethesda',
120
+ phone: '3014445002',
121
+ zipcode: '20814',
122
+ state_id: @state.id,
123
+ country_id: @country.id
124
+ }
125
+ end
126
+
127
+ it "can update addresses and transition from address to delivery" do
128
+ api_put :update,
129
+ id: order.to_param, order_token: order.guest_token,
130
+ order: {
131
+ bill_address_attributes: address,
132
+ ship_address_attributes: address
133
+ }
134
+ expect(json_response['state']).to eq('delivery')
135
+ expect(json_response['bill_address']['firstname']).to eq('John')
136
+ expect(json_response['ship_address']['firstname']).to eq('John')
137
+ expect(response.status).to eq(200)
138
+ end
139
+
140
+ # Regression Spec for #5389 & #5880
141
+ it "can update addresses but not transition to delivery w/o shipping setup" do
142
+ Spree::ShippingMethod.destroy_all
143
+ api_put :update,
144
+ id: order.to_param, order_token: order.guest_token,
145
+ order: {
146
+ bill_address_attributes: address,
147
+ ship_address_attributes: address
148
+ }
149
+ expect(json_response['error']).to eq(I18n.t(:could_not_transition, scope: "spree.api.order"))
150
+ expect(response.status).to eq(422)
151
+ end
152
+
153
+ # Regression test for #4498
154
+ it "does not contain duplicate variant data in delivery return" do
155
+ api_put :update,
156
+ id: order.to_param, order_token: order.guest_token,
157
+ order: {
158
+ bill_address_attributes: address,
159
+ ship_address_attributes: address
160
+ }
161
+ # Shipments manifests should not return the ENTIRE variant
162
+ # This information is already present within the order's line items
163
+ expect(json_response['shipments'].first['manifest'].first['variant']).to be_nil
164
+ expect(json_response['shipments'].first['manifest'].first['variant_id']).to_not be_nil
165
+ end
166
+ end
167
+
168
+ it "can update shipping method and transition from delivery to payment" do
169
+ order.update_column(:state, "delivery")
170
+ shipment = create(:shipment, order: order)
171
+ shipment.refresh_rates
172
+ shipping_rate = shipment.shipping_rates.where(selected: false).first
173
+ api_put :update, id: order.to_param, order_token: order.guest_token,
174
+ order: { shipments_attributes: { "0" => { selected_shipping_rate_id: shipping_rate.id, id: shipment.id } } }
175
+ expect(response.status).to eq(200)
176
+ # Find the correct shipment...
177
+ json_shipment = json_response['shipments'].detect { |s| s["id"] == shipment.id }
178
+ # Find the correct shipping rate for that shipment...
179
+ json_shipping_rate = json_shipment['shipping_rates'].detect { |sr| sr["id"] == shipping_rate.id }
180
+ # ... And finally ensure that it's selected
181
+ expect(json_shipping_rate['selected']).to be true
182
+ # Order should automatically transfer to payment because all criteria are met
183
+ expect(json_response['state']).to eq('payment')
184
+ end
185
+
186
+ it "can update payment method and transition from payment to confirm" do
187
+ order.update_column(:state, "payment")
188
+ api_put :update, id: order.to_param, order_token: order.guest_token,
189
+ order: { payments_attributes: [{ payment_method_id: @payment_method.id }] }
190
+ expect(json_response['state']).to eq('confirm')
191
+ expect(json_response['payments'][0]['payment_method']['name']).to eq(@payment_method.name)
192
+ expect(json_response['payments'][0]['amount']).to eq(order.total.to_s)
193
+ expect(response.status).to eq(200)
194
+ end
195
+
196
+ it "can update payment method with source and transition from payment to confirm" do
197
+ order.update_column(:state, "payment")
198
+ source_attributes = {
199
+ number: "4111111111111111",
200
+ month: 1.month.from_now.month,
201
+ year: 1.month.from_now.year,
202
+ verification_value: "123",
203
+ name: "Spree Commerce"
204
+ }
205
+
206
+ api_put :update, id: order.to_param, order_token: order.guest_token,
207
+ order: { payments_attributes: [{ payment_method_id: @payment_method.id.to_s }],
208
+ payment_source: { @payment_method.id.to_s => source_attributes } }
209
+ expect(json_response['payments'][0]['payment_method']['name']).to eq(@payment_method.name)
210
+ expect(json_response['payments'][0]['amount']).to eq(order.total.to_s)
211
+ expect(response.status).to eq(200)
212
+ end
213
+
214
+ it "returns errors when source is missing attributes" do
215
+ order.update_column(:state, "payment")
216
+ api_put :update, id: order.to_param, order_token: order.guest_token,
217
+ order: {
218
+ payments_attributes: [{ payment_method_id: @payment_method.id }]
219
+ },
220
+ payment_source: {
221
+ @payment_method.id.to_s => { name: "Spree" }
222
+ }
223
+
224
+ expect(response.status).to eq(422)
225
+ cc_errors = json_response['errors']['payments.Credit Card']
226
+ expect(cc_errors).to include("Number can't be blank")
227
+ expect(cc_errors).to include("Month is not a number")
228
+ expect(cc_errors).to include("Year is not a number")
229
+ expect(cc_errors).to include("Verification Value can't be blank")
230
+ end
231
+
232
+ it "allow users to reuse a credit card" do
233
+ order.update_column(:state, "payment")
234
+ credit_card = create(:credit_card, user_id: order.user_id, payment_method_id: @payment_method.id)
235
+
236
+ api_put :update, id: order.to_param, order_token: order.guest_token,
237
+ order: { existing_card: credit_card.id }
238
+
239
+ expect(response.status).to eq 200
240
+ expect(order.credit_cards).to match_array [credit_card]
241
+ end
242
+
243
+ it "can transition from confirm to complete" do
244
+ order.update_columns(state: 'confirm')
245
+ allow_any_instance_of(Spree::Order).to receive_messages(payment_required?: false)
246
+ api_put :update, id: order.to_param, order_token: order.guest_token
247
+ expect(json_response['state']).to eq('complete')
248
+ expect(response.status).to eq(200)
249
+ end
250
+
251
+ it "returns the order if the order is already complete" do
252
+ order.update_columns(completed_at: Time.current, state: 'complete')
253
+ api_put :update, id: order.to_param, order_token: order.guest_token
254
+ expect(json_response['number']).to eq(order.number)
255
+ expect(response.status).to eq(200)
256
+ end
257
+
258
+ # Regression test for #3784
259
+ it "can update the special instructions for an order" do
260
+ instructions = "Don't drop it. (Please)"
261
+ api_put :update, id: order.to_param, order_token: order.guest_token,
262
+ order: { special_instructions: instructions }
263
+ expect(json_response['special_instructions']).to eql(instructions)
264
+ end
265
+
266
+ context "as an admin" do
267
+ sign_in_as_admin!
268
+ it "can assign a user to the order" do
269
+ user = create(:user)
270
+ # Need to pass email as well so that validations succeed
271
+ api_put :update, id: order.to_param, order_token: order.guest_token,
272
+ order: { user_id: user.id, email: "guest@spreecommerce.org" }
273
+ expect(response.status).to eq(200)
274
+ expect(json_response['user_id']).to eq(user.id)
275
+ end
276
+ end
277
+
278
+ it "can assign an email to the order" do
279
+ api_put :update, id: order.to_param, order_token: order.guest_token,
280
+ order: { email: "guest@spreecommerce.org" }
281
+ expect(json_response['email']).to eq("guest@spreecommerce.org")
282
+ expect(response.status).to eq(200)
283
+ end
284
+
285
+ it "can apply a coupon code to an order" do
286
+ skip "ensure that the order totals are properly updated, see frontend orders_controller or checkout_controller as example"
287
+
288
+ order.update_column(:state, "payment")
289
+ expect(PromotionHandler::Coupon).to receive(:new).with(order).and_call_original
290
+ expect_any_instance_of(PromotionHandler::Coupon).to receive(:apply).and_return({ coupon_applied?: true })
291
+ api_put :update, id: order.to_param, order_token: order.guest_token, order: { coupon_code: "foobar" }
292
+ end
293
+
294
+ def send_request
295
+ api_put :update, id: order.to_param, order_token: order.guest_token
296
+ end
297
+
298
+ it_should_behave_like 'action which loads order using load_order_with_lock'
299
+ end
300
+
301
+ context "PUT 'next'" do
302
+ let!(:order) { create(:order_with_line_items) }
303
+ it "cannot transition to address without a line item" do
304
+ order.line_items.delete_all
305
+ order.update_column(:email, "spree@example.com")
306
+ api_put :next, id: order.to_param, order_token: order.guest_token
307
+ expect(response.status).to eq(422)
308
+ expect(json_response["errors"]["base"]).to include(Spree.t(:there_are_no_items_for_this_order))
309
+ end
310
+
311
+ it "can transition an order to the next state" do
312
+ order.update_column(:email, "spree@example.com")
313
+
314
+ api_put :next, id: order.to_param, order_token: order.guest_token
315
+ expect(response.status).to eq(200)
316
+ expect(json_response['state']).to eq('address')
317
+ end
318
+
319
+ it "cannot transition if order email is blank" do
320
+ order.update_columns(
321
+ state: 'address',
322
+ email: nil
323
+ )
324
+
325
+ api_put :next, id: order.to_param, order_token: order.guest_token
326
+ expect(response.status).to eq(422)
327
+ expect(json_response['error']).to match(/could not be transitioned/)
328
+ end
329
+
330
+ it "doesnt advance payment state if order has no payment" do
331
+ order.update_column(:state, "payment")
332
+ api_put :next, id: order.to_param, order_token: order.guest_token, order: {}
333
+ expect(json_response["errors"]["base"]).to include(Spree.t(:no_payment_found))
334
+ end
335
+
336
+ def send_request
337
+ api_put :next, id: order.to_param, order_token: order.guest_token
338
+ end
339
+
340
+ it_should_behave_like 'action which loads order using load_order_with_lock'
341
+ end
342
+
343
+ context "PUT 'advance'" do
344
+ let!(:order) { create(:order_with_line_items) }
345
+
346
+ it 'continues to advance advances an order while it can move forward' do
347
+ expect_any_instance_of(Spree::Order).to receive(:next).exactly(3).times.and_return(true, true, false)
348
+ api_put :advance, id: order.to_param, order_token: order.guest_token
349
+ end
350
+
351
+ it 'returns the order' do
352
+ api_put :advance, id: order.to_param, order_token: order.guest_token
353
+ expect(json_response['id']).to eq(order.id)
354
+ end
355
+
356
+ def send_request
357
+ api_put :advance, id: order.to_param, order_token: order.guest_token
358
+ end
359
+
360
+ it_should_behave_like 'action which loads order using load_order_with_lock'
361
+ end
362
+ end
363
+ end