spree_api 3.1.14 → 3.2.0.rc1

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