From 0f44beb477be6071327973fe4c77e7b75e822954 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 5 Feb 2019 19:25:03 +0000 Subject: [PATCH 1/5] Delete all Spree::Admin::LineItemsController customizations as they are no longer used --- .../admin/line_items_controller_decorator.rb | 60 ---------- .../spree/admin/line_items/create.js.erb | 6 - app/views/spree/admin/orders/edit.html.haml | 5 +- app/views/spree/admin/orders/new.html.haml | 7 +- .../admin/orders/set_distribution.html.haml | 2 +- .../spree/admin/line_items_controller_spec.rb | 113 ------------------ 6 files changed, 6 insertions(+), 187 deletions(-) delete mode 100644 app/controllers/spree/admin/line_items_controller_decorator.rb delete mode 100644 app/views/spree/admin/line_items/create.js.erb delete mode 100644 spec/controllers/spree/admin/line_items_controller_spec.rb diff --git a/app/controllers/spree/admin/line_items_controller_decorator.rb b/app/controllers/spree/admin/line_items_controller_decorator.rb deleted file mode 100644 index 3ef85234df..0000000000 --- a/app/controllers/spree/admin/line_items_controller_decorator.rb +++ /dev/null @@ -1,60 +0,0 @@ -require 'open_food_network/scope_variant_to_hub' - -Spree::Admin::LineItemsController.class_eval do - prepend_before_filter :load_order, except: :index - around_filter :apply_enterprise_fees_with_lock, only: :update - - def create - variant = Spree::Variant.find(params[:line_item][:variant_id]) - OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant) - - @line_item = @order.add_variant(variant, params[:line_item][:quantity].to_i) - - if @order.save - respond_with(@line_item) do |format| - format.html { render :partial => 'spree/admin/orders/form', :locals => { :order => @order.reload } } - end - else - respond_with(@line_item) do |format| - format.js { render :action => 'create', :locals => { :order => @order.reload } } - end - end - end - - # TODO: simplify this, 3 formats per action is too much - # we need `js` format for admin/orders/edit (jquery-rails gem) - # we don't know if `html` format is needed - def update - respond_to do |format| - format.html { render_order_form } - format.js { - if @line_item.update_attributes(params[:line_item]) - render nothing: true, status: 204 # No Content, does not trigger ng resource auto-update - else - render json: { errors: @line_item.errors }, status: 412 - end - } - end - end - - private - - def render_order_form - respond_to do |format| - format.html { render partial: 'spree/admin/orders/form', locals: {order: @order.reload} } - end - end - - def load_order - @order = Spree::Order.find_by_number!(params[:order_id]) - authorize! :update, @order - end - - def apply_enterprise_fees_with_lock - authorize! :read, @order - @order.with_lock do - yield - @order.update_distribution_charge! - end - end -end diff --git a/app/views/spree/admin/line_items/create.js.erb b/app/views/spree/admin/line_items/create.js.erb deleted file mode 100644 index f1644d2203..0000000000 --- a/app/views/spree/admin/line_items/create.js.erb +++ /dev/null @@ -1,6 +0,0 @@ -// The admin order form contains angular directives, so it needs to be -// compiled before insertion into the DOM -var scope = angular.element("#order-form-wrapper").scope(); -$("#order-form-wrapper").html(scope.$compile('<%= escape_javascript(render :partial => "spree/admin/orders/form") %>')(scope)); -scope.$apply(); -$('select.select2').select2({allowClear: true}); diff --git a/app/views/spree/admin/orders/edit.html.haml b/app/views/spree/admin/orders/edit.html.haml index c4ff3793fc..854f074d17 100644 --- a/app/views/spree/admin/orders/edit.html.haml +++ b/app/views/spree/admin/orders/edit.html.haml @@ -26,9 +26,8 @@ .no-objects-found = Spree.t(:your_order_is_empty_add_product) - %div{"data-hook" => "admin_order_edit_form"} - #order-form-wrapper - = render :partial => 'form', :locals => { :order => @order } + %div + = render :partial => 'form', :locals => { :order => @order } - content_for :head do = javascript_tag 'var expand_variants = true;' diff --git a/app/views/spree/admin/orders/new.html.haml b/app/views/spree/admin/orders/new.html.haml index b653439230..a78e4892a8 100644 --- a/app/views/spree/admin/orders/new.html.haml +++ b/app/views/spree/admin/orders/new.html.haml @@ -11,7 +11,7 @@ = csrf_meta_tags -%div{"data-hook" => "admin_order_new_header"} +%div = render 'spree/shared/error_messages', :target => @order %div{"ng-app" => "admin.orders", "ng-controller" => "orderCtrl"} @@ -19,9 +19,8 @@ = render 'add_product' - unless @order.line_items.any? - %div{"data-hook" => "admin_order_new_form"} - #order-form-wrapper - = render 'form' + %div + = render 'form' - content_for :head do = javascript_tag 'var expand_variants = true;' diff --git a/app/views/spree/admin/orders/set_distribution.html.haml b/app/views/spree/admin/orders/set_distribution.html.haml index 89d036b93a..f5e259321e 100644 --- a/app/views/spree/admin/orders/set_distribution.html.haml +++ b/app/views/spree/admin/orders/set_distribution.html.haml @@ -11,7 +11,7 @@ = csrf_meta_tags -%div{"data-hook" => "admin_order_new_header"} +%div = render 'spree/shared/error_messages', :target => @order %div{"ng-app" => "admin.orders", "ng-controller" => "orderCtrl"} diff --git a/spec/controllers/spree/admin/line_items_controller_spec.rb b/spec/controllers/spree/admin/line_items_controller_spec.rb deleted file mode 100644 index a2ede41a83..0000000000 --- a/spec/controllers/spree/admin/line_items_controller_spec.rb +++ /dev/null @@ -1,113 +0,0 @@ -require 'spec_helper' - -describe Spree::Admin::LineItemsController, type: :controller do - include AuthenticationWorkflow - - describe "#create" do - let!(:variant) { create(:variant, price: 88) } - let!(:vo) { create(:variant_override, hub: distributor, variant: variant, price: 11.11) } - let!(:distributor) { create(:distributor_enterprise) } - let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], variants: [variant]) } - let!(:order) { create(:order, distributor: distributor, order_cycle: order_cycle) } - let(:params) { { order_id: order.number, line_item: { variant_id: variant.id, quantity: 1 } } } - - before { login_as_admin } - - it "takes variant overrides into account for price" do - spree_post :create, params - - order.line_items(:reload).last.price.should == 11.11 - end - end - - describe '#update' do - let(:supplier) { create(:supplier_enterprise) } - let(:distributor1) { create(:distributor_enterprise) } - let(:coordinator) { create(:distributor_enterprise) } - let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator) } - let!(:order1) { FactoryBot.create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.zone.now, distributor: distributor1, billing_address: FactoryBot.create(:address) ) } - let!(:line_item1) { FactoryBot.create(:line_item_with_shipment, order: order1, product: FactoryBot.create(:product, supplier: supplier)) } - let(:line_item_params) { { quantity: 3, final_weight_volume: 3000, price: 3.00 } } - let(:params) { { id: line_item1.id, order_id: order1.number, line_item: line_item_params } } - - context "as an enterprise user" do - context "producer enterprise" do - before do - controller.stub spree_current_user: supplier.owner - spree_put :update, params - end - - it "does not allow access" do - expect(response).to redirect_to spree.unauthorized_path - end - end - - context "coordinator enterprise" do - render_views - - before do - controller.stub spree_current_user: coordinator.owner - end - - # Used in admin/orders/edit - context 'when the request is JS/XHR (jquery-rails gem)' do - it "updates the line item" do - xhr :put, :update, params - line_item1.reload - expect(line_item1.quantity).to eq 3 - expect(line_item1.final_weight_volume).to eq 3000 - expect(line_item1.price).to eq 3.00 - end - - it "returns an empty JSON response" do - xhr :put, :update, params - expect(response.body).to eq ' ' - end - - it 'returns a 204 response' do - xhr :put, :update, params - expect(response.status).to eq 204 - end - - context 'when the line item params are not correct' do - let(:line_item_params) { { price: 'hola' } } - let(:errors) { { 'price' => ['is not a number'] } } - - it 'returns a JSON with the errors' do - xhr :put, :update, params - expect(JSON.parse(response.body)['errors']).to eq(errors) - end - - it 'returns a 412 response' do - xhr :put, :update, params - expect(response.status).to eq 412 - end - end - end - - context 'when the request is HTML' do - before { params[:format] = :html } - - it 'returns an HTML response with the order form' do - spree_put :update, params - expect(response.body).to match(/edit_order/) - end - end - end - - context "hub enterprise" do - before do - controller.stub spree_current_user: distributor1.owner - xhr :put, :update, params - end - - it "updates the line item" do - line_item1.reload - expect(line_item1.quantity).to eq 3 - expect(line_item1.final_weight_volume).to eq 3000 - expect(line_item1.price).to eq 3.00 - end - end - end - end -end From 77b390a836c24f1bd1b68de71ad9ae67e1da9e21 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 5 Feb 2019 20:17:16 +0000 Subject: [PATCH 2/5] Decorate spree api shipments controller to scope variants as they are added/removed from shipments --- .../api/shipments_controller_decorator.rb | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 app/controllers/spree/api/shipments_controller_decorator.rb diff --git a/app/controllers/spree/api/shipments_controller_decorator.rb b/app/controllers/spree/api/shipments_controller_decorator.rb new file mode 100644 index 0000000000..6d63d0d412 --- /dev/null +++ b/app/controllers/spree/api/shipments_controller_decorator.rb @@ -0,0 +1,39 @@ +require 'open_food_network/scope_variant_to_hub' + +Spree::Api::ShipmentsController.class_eval do + def create + variant = Spree::Variant.find(params[:variant_id]) + OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant) + + quantity = params[:quantity].to_i + @shipment = @order.shipments.create(:stock_location_id => params[:stock_location_id]) + @order.contents.add(variant, quantity, nil, @shipment) + + @shipment.refresh_rates + @shipment.save! + + respond_with(@shipment.reload, :default_template => :show) + end + + def add + variant = Spree::Variant.find(params[:variant_id]) + OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant) + + quantity = params[:quantity].to_i + + @order.contents.add(variant, quantity, nil, @shipment) + + respond_with(@shipment, :default_template => :show) + end + + def remove + variant = Spree::Variant.find(params[:variant_id]) + OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant) + + quantity = params[:quantity].to_i + + @order.contents.remove(variant, quantity, @shipment) + @shipment.reload if @shipment.persisted? + respond_with(@shipment, :default_template => :show) + end +end From 58ea28ebb48c148a615c83c8bbd26f91749876d8 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Sat, 9 Feb 2019 23:16:59 +0000 Subject: [PATCH 3/5] Make Api::ShipmentsController#create re-use order.shipment if it exists Improve code and add specs to this controller --- .../api/shipments_controller_decorator.rb | 34 ++++-- .../spree/api/shipments_controller_spec.rb | 113 ++++++++++++++++++ 2 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 spec/controllers/spree/api/shipments_controller_spec.rb diff --git a/app/controllers/spree/api/shipments_controller_decorator.rb b/app/controllers/spree/api/shipments_controller_decorator.rb index 6d63d0d412..6fb5e242ec 100644 --- a/app/controllers/spree/api/shipments_controller_decorator.rb +++ b/app/controllers/spree/api/shipments_controller_decorator.rb @@ -2,38 +2,46 @@ require 'open_food_network/scope_variant_to_hub' Spree::Api::ShipmentsController.class_eval do def create - variant = Spree::Variant.find(params[:variant_id]) - OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant) - + variant = scoped_variant(params[:variant_id]) quantity = params[:quantity].to_i - @shipment = @order.shipments.create(:stock_location_id => params[:stock_location_id]) + @shipment = get_or_create_shipment(params[:stock_location_id]) + @order.contents.add(variant, quantity, nil, @shipment) @shipment.refresh_rates @shipment.save! - respond_with(@shipment.reload, :default_template => :show) + respond_with(@shipment.reload, default_template: :show) end def add - variant = Spree::Variant.find(params[:variant_id]) - OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant) - + variant = scoped_variant(params[:variant_id]) quantity = params[:quantity].to_i @order.contents.add(variant, quantity, nil, @shipment) - respond_with(@shipment, :default_template => :show) + respond_with(@shipment, default_template: :show) end def remove - variant = Spree::Variant.find(params[:variant_id]) - OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant) - + variant = scoped_variant(params[:variant_id]) quantity = params[:quantity].to_i @order.contents.remove(variant, quantity, @shipment) @shipment.reload if @shipment.persisted? - respond_with(@shipment, :default_template => :show) + + respond_with(@shipment, default_template: :show) + end + + private + + def scoped_variant(variant_id) + variant = Spree::Variant.find(variant_id) + OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant) + variant + end + + def get_or_create_shipment(stock_location_id) + @order.shipment || @order.shipments.create(stock_location_id: stock_location_id) end end diff --git a/spec/controllers/spree/api/shipments_controller_spec.rb b/spec/controllers/spree/api/shipments_controller_spec.rb new file mode 100644 index 0000000000..184cd49444 --- /dev/null +++ b/spec/controllers/spree/api/shipments_controller_spec.rb @@ -0,0 +1,113 @@ +require 'spec_helper' + +describe Spree::Api::ShipmentsController, type: :controller do + render_views + + let!(:shipment) { create(:shipment) } + let!(:attributes) { [:id, :tracking, :number, :cost, :shipped_at, :stock_location_name, :order_id, :shipping_rates, :shipping_method, :inventory_units] } + + before do + allow(controller).to receive(:spree_current_user) { current_api_user } + end + + context "as an admin" do + let!(:order) { shipment.order } + let(:order_ship_address) { create(:address) } + let!(:stock_location) { create(:stock_location_with_items) } + let!(:variant) { create(:variant) } + let(:params) do + { quantity: 2, + variant_id: stock_location.stock_items.first.variant.to_param, + order_id: order.number, + stock_location_id: stock_location.to_param, + format: :json } + end + + before do + order.update_attribute(:ship_address_id, order_ship_address.id) + end + + sign_in_as_admin! + + context '#create' do + it 'creates a shipment if order does not have a shipment' do + order.shipment.destroy + order.reload + + spree_post :create, params + + validate_response + expect(json_response["inventory_units"].size).to eq 2 + expect(order.reload.line_items.first.variant.price).to eq(variant.price) + end + + it 'updates and returns exiting shipment, if order already has a shipment' do + original_shipment_id = order.shipment.id + + spree_post :create, params + + expect(json_response["id"]). to eq(original_shipment_id) + validate_response + expect(json_response["inventory_units"].size).to eq 2 + expect(order.reload.line_items.first.variant.price).to eq(variant.price) + end + + it 'updates existing shipment with variant override if an VO is sent' do + hub = create(:distributor_enterprise) + order.update_attribute(:distributor, hub) + variant_override = create(:variant_override, hub: hub, variant: variant) + + spree_post :create, params + + validate_response + expect(json_response["inventory_units"].size).to eq 2 + expect(order.reload.line_items.first.price).to eq(variant_override.price) + end + end + + context 'for a completed order with shipment' do + let(:order) { create :completed_order_with_totals } + + before { params[:id] = order.shipments.first.to_param } + + it 'adds a variant to the shipment' do + spree_put :add, params + + validate_response + expect(inventory_units_for_variant(json_response["inventory_units"], variant).size).to eq 2 + end + + it 'removes a variant from the shipment' do + params[:variant_id] = order.line_items.first.variant.to_param + params[:quantity] = 1 + + spree_put :remove, params + + validate_response + expect(inventory_units_for_variant(json_response["inventory_units"], variant).size).to eq 0 + end + + it 'adds a variant override to the shipment' do + hub = create(:distributor_enterprise) + order.update_attribute(:distributor, hub) + variant_override = create(:variant_override, hub: hub, variant: variant) + + spree_put :add, params + + validate_response + expect(inventory_units_for_variant(json_response["inventory_units"], variant).size).to eq 2 + expect(order.reload.line_items.last.price).to eq(variant_override.price) + end + + def inventory_units_for_variant(inventory_units, variant) + inventory_units.select { |unit| unit['variant_id'] == variant.id } + end + end + + def validate_response + expect(response.status).to eq 200 + attributes.all?{ |attr| json_response.key? attr.to_s } + expect(json_response["shipping_method"]["name"]).to eq order.shipping_method.name + end + end +end From ba8096ff85c24ce11a96d914c3f52b11f8b51e8a Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 12 Feb 2019 17:31:58 +0000 Subject: [PATCH 4/5] Improve method names in shipments_controller_spec --- .../spree/api/shipments_controller_spec.rb | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/spec/controllers/spree/api/shipments_controller_spec.rb b/spec/controllers/spree/api/shipments_controller_spec.rb index 184cd49444..d5ca983622 100644 --- a/spec/controllers/spree/api/shipments_controller_spec.rb +++ b/spec/controllers/spree/api/shipments_controller_spec.rb @@ -36,7 +36,7 @@ describe Spree::Api::ShipmentsController, type: :controller do spree_post :create, params - validate_response + expect_valid_response expect(json_response["inventory_units"].size).to eq 2 expect(order.reload.line_items.first.variant.price).to eq(variant.price) end @@ -47,7 +47,7 @@ describe Spree::Api::ShipmentsController, type: :controller do spree_post :create, params expect(json_response["id"]). to eq(original_shipment_id) - validate_response + expect_valid_response expect(json_response["inventory_units"].size).to eq 2 expect(order.reload.line_items.first.variant.price).to eq(variant.price) end @@ -59,7 +59,7 @@ describe Spree::Api::ShipmentsController, type: :controller do spree_post :create, params - validate_response + expect_valid_response expect(json_response["inventory_units"].size).to eq 2 expect(order.reload.line_items.first.price).to eq(variant_override.price) end @@ -73,8 +73,8 @@ describe Spree::Api::ShipmentsController, type: :controller do it 'adds a variant to the shipment' do spree_put :add, params - validate_response - expect(inventory_units_for_variant(json_response["inventory_units"], variant).size).to eq 2 + expect_valid_response + expect(inventory_units_for(json_response["inventory_units"], variant).size).to eq 2 end it 'removes a variant from the shipment' do @@ -83,8 +83,8 @@ describe Spree::Api::ShipmentsController, type: :controller do spree_put :remove, params - validate_response - expect(inventory_units_for_variant(json_response["inventory_units"], variant).size).to eq 0 + expect_valid_response + expect(inventory_units_for(json_response["inventory_units"], variant).size).to eq 0 end it 'adds a variant override to the shipment' do @@ -94,17 +94,17 @@ describe Spree::Api::ShipmentsController, type: :controller do spree_put :add, params - validate_response - expect(inventory_units_for_variant(json_response["inventory_units"], variant).size).to eq 2 + expect_valid_response + expect(inventory_units_for(json_response["inventory_units"], variant).size).to eq 2 expect(order.reload.line_items.last.price).to eq(variant_override.price) end - def inventory_units_for_variant(inventory_units, variant) + def inventory_units_for(inventory_units, variant) inventory_units.select { |unit| unit['variant_id'] == variant.id } end end - def validate_response + def expect_valid_response expect(response.status).to eq 200 attributes.all?{ |attr| json_response.key? attr.to_s } expect(json_response["shipping_method"]["name"]).to eq order.shipping_method.name From 5ccbf7b3cac37e4ee89a9df733101328a3c4c087 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 12 Feb 2019 17:33:33 +0000 Subject: [PATCH 5/5] Add specs for error cases in shipments_controller_spec --- .../spree/api/shipments_controller_spec.rb | 93 +++++++++++++------ 1 file changed, 67 insertions(+), 26 deletions(-) diff --git a/spec/controllers/spree/api/shipments_controller_spec.rb b/spec/controllers/spree/api/shipments_controller_spec.rb index d5ca983622..88ff8f6ad6 100644 --- a/spec/controllers/spree/api/shipments_controller_spec.rb +++ b/spec/controllers/spree/api/shipments_controller_spec.rb @@ -17,11 +17,12 @@ describe Spree::Api::ShipmentsController, type: :controller do let!(:variant) { create(:variant) } let(:params) do { quantity: 2, - variant_id: stock_location.stock_items.first.variant.to_param, + variant_id: variant.to_param, order_id: order.number, stock_location_id: stock_location.to_param, format: :json } end + let(:error_message) { "broken shipments creation" } before do order.update_attribute(:ship_address_id, order_ship_address.id) @@ -63,6 +64,14 @@ describe Spree::Api::ShipmentsController, type: :controller do expect(json_response["inventory_units"].size).to eq 2 expect(order.reload.line_items.first.price).to eq(variant_override.price) end + + it 'returns error code when adding to order contents fails' do + make_order_contents_fail + + spree_post :create, params + + expect_error_response + end end context 'for a completed order with shipment' do @@ -70,38 +79,60 @@ describe Spree::Api::ShipmentsController, type: :controller do before { params[:id] = order.shipments.first.to_param } - it 'adds a variant to the shipment' do - spree_put :add, params + context '#add' do + it 'adds a variant to the shipment' do + spree_put :add, params - expect_valid_response - expect(inventory_units_for(json_response["inventory_units"], variant).size).to eq 2 + expect_valid_response + expect(inventory_units_for(json_response["inventory_units"], variant).size).to eq 2 + end + + it 'returns error code when adding to order contents fails' do + make_order_contents_fail + + spree_put :add, params + + expect_error_response + end + + it 'adds a variant override to the shipment' do + hub = create(:distributor_enterprise) + order.update_attribute(:distributor, hub) + variant_override = create(:variant_override, hub: hub, variant: variant) + + spree_put :add, params + + expect_valid_response + expect(inventory_units_for(json_response["inventory_units"], variant).size).to eq 2 + expect(order.reload.line_items.last.price).to eq(variant_override.price) + end end - it 'removes a variant from the shipment' do - params[:variant_id] = order.line_items.first.variant.to_param - params[:quantity] = 1 + context '#remove' do + before do + params[:variant_id] = order.line_items.first.variant.to_param + params[:quantity] = 1 + end - spree_put :remove, params + it 'removes a variant from the shipment' do + spree_put :remove, params - expect_valid_response - expect(inventory_units_for(json_response["inventory_units"], variant).size).to eq 0 + expect_valid_response + expect(inventory_units_for(json_response["inventory_units"], variant).size).to eq 0 + end + + it 'returns error code when removing from order contents fails' do + make_order_contents_fail + + spree_put :remove, params + + expect_error_response + end end + end - it 'adds a variant override to the shipment' do - hub = create(:distributor_enterprise) - order.update_attribute(:distributor, hub) - variant_override = create(:variant_override, hub: hub, variant: variant) - - spree_put :add, params - - expect_valid_response - expect(inventory_units_for(json_response["inventory_units"], variant).size).to eq 2 - expect(order.reload.line_items.last.price).to eq(variant_override.price) - end - - def inventory_units_for(inventory_units, variant) - inventory_units.select { |unit| unit['variant_id'] == variant.id } - end + def inventory_units_for(inventory_units, variant) + inventory_units.select { |unit| unit['variant_id'] == variant.id } end def expect_valid_response @@ -109,5 +140,15 @@ describe Spree::Api::ShipmentsController, type: :controller do attributes.all?{ |attr| json_response.key? attr.to_s } expect(json_response["shipping_method"]["name"]).to eq order.shipping_method.name end + + def make_order_contents_fail + expect(Spree::Order).to receive(:find_by_number!) { order } + expect(order).to receive(:contents) { raise error_message } + end + + def expect_error_response + expect(response.status).to eq 422 + expect(json_response["exception"]).to eq error_message + end end end