From f61e6d97cc1d54a7fbb5a2bfa6eba98eaacd8f4b Mon Sep 17 00:00:00 2001 From: marlena-b Date: Thu, 22 Aug 2024 19:13:17 +0200 Subject: [PATCH] Prevent empty order submission --- ecommerce/ordering/lib/ordering/order.rb | 6 ++++ ecommerce/ordering/test/order_test.rb | 8 +++-- ecommerce/ordering/test/submit_order_test.rb | 8 +++++ .../controllers/client/orders_controller.rb | 11 +++--- .../app/controllers/orders_controller.rb | 2 ++ .../client_orders/order_cancelled_test.rb | 13 ++++++- .../test/client_orders/order_paid_test.rb | 11 ++++++ .../update_paid_orders_summary_test.rb | 30 ++++++++++++++-- .../update_paid_orders_summary_test.rb | 23 ++++++++++++ .../test/integration/client_orders_test.rb | 14 ++++++++ .../test/integration/orders_test.rb | 35 ++++++++++--------- .../test/orders/order_cancelled_test.rb | 13 ++++++- .../test/orders/order_paid_test.rb | 13 ++++++- 13 files changed, 158 insertions(+), 29 deletions(-) diff --git a/ecommerce/ordering/lib/ordering/order.rb b/ecommerce/ordering/lib/ordering/order.rb index b6ccdd0e..d6b7afe5 100644 --- a/ecommerce/ordering/lib/ordering/order.rb +++ b/ecommerce/ordering/lib/ordering/order.rb @@ -6,6 +6,7 @@ class Order AlreadySubmitted = Class.new(InvalidState) NotPlaced = Class.new(InvalidState) OrderHasExpired = Class.new(InvalidState) + IsEmpty = Class.new(StandardError) def initialize(id) @id = id @@ -16,6 +17,7 @@ def initialize(id) def submit(order_number) raise OrderHasExpired if @state.expired? raise AlreadySubmitted unless @state.draft? + raise IsEmpty if @basket.empty? apply OrderSubmitted.new( data: { order_id: @id, @@ -111,6 +113,10 @@ def order_lines def quantity(product_id) order_lines[product_id] end + + def empty? + order_lines.empty? + end end class State diff --git a/ecommerce/ordering/test/order_test.rb b/ecommerce/ordering/test/order_test.rb index b398e0ae..b715c36d 100644 --- a/ecommerce/ordering/test/order_test.rb +++ b/ecommerce/ordering/test/order_test.rb @@ -11,12 +11,14 @@ def setup @customer_id = SecureRandom.uuid end - def test_order_lines_are_empty_after_adding_and_removing + def test_order_is_empty_after_adding_and_removing order = Order.new(@order_id) order.add_item(@product_id) order.remove_item(@product_id) - order.submit(NumberGenerator.new.call) - assert_equal({}, order.unpublished_events.to_a.last.data[:order_lines]) + + assert_raises(Order::IsEmpty) do + order.submit(NumberGenerator.new.call) + end end def test_order_lines_with_the_same_product_twice diff --git a/ecommerce/ordering/test/submit_order_test.rb b/ecommerce/ordering/test/submit_order_test.rb index a9a57959..32343827 100644 --- a/ecommerce/ordering/test/submit_order_test.rb +++ b/ecommerce/ordering/test/submit_order_test.rb @@ -71,5 +71,13 @@ def test_expired_order_could_not_be_created act(SubmitOrder.new(order_id: aggregate_id)) end end + + def test_empty_order_cannot_be_submitted + aggregate_id = SecureRandom.uuid + + assert_raises(Order::IsEmpty) do + act(SubmitOrder.new(order_id: aggregate_id)) + end + end end end diff --git a/rails_application/app/controllers/client/orders_controller.rb b/rails_application/app/controllers/client/orders_controller.rb index a093c12d..970243cb 100644 --- a/rails_application/app/controllers/client/orders_controller.rb +++ b/rails_application/app/controllers/client/orders_controller.rb @@ -12,10 +12,13 @@ def new end def create - command_bus.(Ordering::SubmitOrder.new(order_id: params[:order_id])) - command_bus.(Crm::AssignCustomerToOrder.new(customer_id: cookies[:client_id], order_id: params[:order_id])) - redirect_to client_order_path(params[:order_id]), - notice: "Your order is being submitted" + ActiveRecord::Base.transaction do + command_bus.(Ordering::SubmitOrder.new(order_id: params[:order_id])) + command_bus.(Crm::AssignCustomerToOrder.new(customer_id: cookies[:client_id], order_id: params[:order_id])) + end + redirect_to client_order_path(params[:order_id]), notice: "Your order is being submitted" + rescue Ordering::Order::IsEmpty + redirect_to edit_client_order_path(params[:order_id]), alert: "You can't submit an empty order" end def show diff --git a/rails_application/app/controllers/orders_controller.rb b/rails_application/app/controllers/orders_controller.rb index d07da1fe..0ca77e2a 100644 --- a/rails_application/app/controllers/orders_controller.rb +++ b/rails_application/app/controllers/orders_controller.rb @@ -76,6 +76,8 @@ def remove_item def create ApplicationRecord.transaction { submit_order(params[:order_id], params[:customer_id]) } redirect_to order_path(params[:order_id]), notice: "Your order is being submitted" + rescue Ordering::Order::IsEmpty + redirect_to edit_order_path(params[:order_id]), alert: "You can't submit an empty order" rescue Crm::Customer::NotExists redirect_to order_path(params[:order_id]), alert: "Order can not be submitted! Customer does not exist." end diff --git a/rails_application/test/client_orders/order_cancelled_test.rb b/rails_application/test/client_orders/order_cancelled_test.rb index 70e474fc..f18d54a4 100644 --- a/rails_application/test/client_orders/order_cancelled_test.rb +++ b/rails_application/test/client_orders/order_cancelled_test.rb @@ -15,6 +15,7 @@ def test_order_confirmed customer_id = SecureRandom.uuid order_id = SecureRandom.uuid order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER + product_id = SecureRandom.uuid event_store.publish(Crm::CustomerRegistered.new( data: { @@ -23,6 +24,8 @@ def test_order_confirmed } )) + create_product(product_id, "Async Remote", 30) + run_command(Ordering::AddItemToBasket.new(order_id: order_id, product_id: product_id)) run_command(Ordering::SubmitOrder.new(order_id: order_id, order_number: order_number)) event_store.publish( @@ -39,5 +42,13 @@ def test_order_confirmed assert_equal(order_number, orders.first.number) assert_equal("Cancelled", orders.first.state) end + + private + + def create_product(product_id, name, price) + run_command(ProductCatalog::RegisterProduct.new(product_id: product_id)) + run_command(ProductCatalog::NameProduct.new(product_id: product_id, name: name)) + run_command(Pricing::SetPrice.new(product_id: product_id, price: price)) + end end -end \ No newline at end of file +end diff --git a/rails_application/test/client_orders/order_paid_test.rb b/rails_application/test/client_orders/order_paid_test.rb index cb126741..94ead995 100644 --- a/rails_application/test/client_orders/order_paid_test.rb +++ b/rails_application/test/client_orders/order_paid_test.rb @@ -15,8 +15,11 @@ def test_order_confirmed customer_id = SecureRandom.uuid order_id = SecureRandom.uuid order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER + product_id = SecureRandom.uuid run_command(Crm::RegisterCustomer.new(customer_id: customer_id, name: "John Doe")) + create_product(product_id, "Async Remote", 30) + run_command(Ordering::AddItemToBasket.new(order_id: order_id, product_id: product_id)) event_store.publish( Pricing::OrderTotalValueCalculated.new( @@ -48,5 +51,13 @@ def test_order_confirmed assert_equal(order_number, orders.first.number) assert_equal("Paid", orders.first.state) end + + private + + def create_product(product_id, name, price) + run_command(ProductCatalog::RegisterProduct.new(product_id: product_id)) + run_command(ProductCatalog::NameProduct.new(product_id: product_id, name: name)) + run_command(Pricing::SetPrice.new(product_id: product_id, price: price)) + end end end diff --git a/rails_application/test/client_orders/update_paid_orders_summary_test.rb b/rails_application/test/client_orders/update_paid_orders_summary_test.rb index 6090e1c8..be18faa0 100644 --- a/rails_application/test/client_orders/update_paid_orders_summary_test.rb +++ b/rails_application/test/client_orders/update_paid_orders_summary_test.rb @@ -9,21 +9,26 @@ def setup Client.destroy_all end - def test_update_orders_cummary - event_store = Rails.configuration.event_store + def test_update_orders_summary customer_id = SecureRandom.uuid other_customer_id = SecureRandom.uuid product_id = SecureRandom.uuid order_id = SecureRandom.uuid + register_product(product_id) + name_product(product_id, "Async Remote") + set_price_to_product(product_id, 3) register_customer(other_customer_id) register_customer(customer_id) + add_item_to_basket(order_id, product_id) confirm_order(customer_id, order_id, 3) customer = Client.find_by(uid: customer_id) assert_equal 3.to_d, customer.paid_orders_summary order_id = SecureRandom.uuid + add_item_to_basket(order_id, product_id) + add_item_to_basket(order_id, product_id) confirm_order(customer_id, order_id, 6) customer = Client.find_by(uid: customer_id) @@ -36,6 +41,27 @@ def register_customer(customer_id) run_command(Crm::RegisterCustomer.new(customer_id: customer_id, name: "John Doe")) end + def register_product(product_id) + run_command(ProductCatalog::RegisterProduct.new(product_id: product_id)) + end + + def name_product(product_id, name) + run_command( + ProductCatalog::NameProduct.new( + product_id: product_id, + name: name + ) + ) + end + + def set_price_to_product(product_id, price) + run_command(Pricing::SetPrice.new(product_id: product_id, price: price)) + end + + def add_item_to_basket(order_id, product_id) + run_command(Ordering::AddItemToBasket.new(order_id: order_id, product_id: product_id)) + end + def confirm_order(customer_id, order_id, total_amount) order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER event_store.publish( diff --git a/rails_application/test/customers/update_paid_orders_summary_test.rb b/rails_application/test/customers/update_paid_orders_summary_test.rb index cf1db6f4..32353782 100644 --- a/rails_application/test/customers/update_paid_orders_summary_test.rb +++ b/rails_application/test/customers/update_paid_orders_summary_test.rb @@ -13,15 +13,22 @@ def test_update_orders_summary customer_id = SecureRandom.uuid other_customer_id = SecureRandom.uuid order_id = SecureRandom.uuid + product_id = SecureRandom.uuid register_customer(other_customer_id) register_customer(customer_id) + register_product(product_id) + name_product(product_id, "Async Remote") + set_price_to_product(product_id, 3) + add_item_to_basket(order_id, product_id) confirm_order(customer_id, order_id, 3) customer = Customer.find(customer_id) assert_equal 3.to_d, customer.paid_orders_summary order_id = SecureRandom.uuid + add_item_to_basket(order_id, product_id) + add_item_to_basket(order_id, product_id) confirm_order(customer_id, order_id, 6) customer = Customer.find(customer_id) @@ -34,6 +41,22 @@ def register_customer(customer_id) run_command(Crm::RegisterCustomer.new(customer_id: customer_id, name: "John Doe")) end + def register_product(product_id) + run_command(ProductCatalog::RegisterProduct.new(product_id: product_id)) + end + + def name_product(product_id, name) + run_command(ProductCatalog::NameProduct.new(product_id: product_id, name: "Async Remote")) + end + + def set_price_to_product(product_id, price) + run_command(Pricing::SetPrice.new(product_id: product_id, price: price)) + end + + def add_item_to_basket(order_id, product_id) + run_command(Ordering::AddItemToBasket.new(order_id: order_id, product_id: product_id)) + end + def confirm_order(customer_id, order_id, total_amount) order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER event_store.publish( diff --git a/rails_application/test/integration/client_orders_test.rb b/rails_application/test/integration/client_orders_test.rb index 66bcc846..226f2b5f 100644 --- a/rails_application/test/integration/client_orders_test.rb +++ b/rails_application/test/integration/client_orders_test.rb @@ -154,6 +154,20 @@ def test_adding_product_which_is_not_available_in_requested_quantity assert_equal "Product not available in requested quantity!", flash[:alert] end + def test_empty_order_cannot_be_submitted + customer_id = register_customer("Customer Shop") + + login(customer_id) + visit_client_orders + + order_id = SecureRandom.uuid + assert_no_changes -> { ClientOrders::Order.count } do + as_client_submit_order_for_customer(order_id) + end + + assert_select "#alert", "You can't submit an empty order" + end + private def submit_order_for_customer(customer_id, order_id) diff --git a/rails_application/test/integration/orders_test.rb b/rails_application/test/integration/orders_test.rb index 5dfc6d76..8c3c1610 100644 --- a/rails_application/test/integration/orders_test.rb +++ b/rails_application/test/integration/orders_test.rb @@ -8,23 +8,6 @@ def setup add_available_vat_rate(10) end - def test_submitting_empty_order - arkency_id = register_customer("Arkency") - - get "/" - assert_select "h1", "Orders" - get "/orders/new" - follow_redirect! - assert_select "h1", "Order" - post "/orders", - params: { - "authenticity_token" => "[FILTERED]", - "order_id" => SecureRandom.uuid, - "customer_id" => arkency_id, - "commit" => "Submit order" - } - end - def test_happy_path shopify_id = register_customer("Shopify") @@ -202,6 +185,24 @@ def test_discount_is_applied_for_new_order assert_select("td", "10.0%") end + def test_empty_order_cannot_be_submitted + order_id = SecureRandom.uuid + shopify_id = register_customer("Shopify") + + assert_no_changes -> { Orders::Order.count } do + post "/orders", + params: { + "authenticity_token" => "[FILTERED]", + "order_id" => order_id, + "customer_id" => shopify_id, + "commit" => "Submit order" + } + end + follow_redirect! + + assert_select "#alert", "You can't submit an empty order" + end + private def assert_remove_buttons_visible(async_remote_id, fearless_id, order_id) diff --git a/rails_application/test/orders/order_cancelled_test.rb b/rails_application/test/orders/order_cancelled_test.rb index e075fd79..67cb67dd 100644 --- a/rails_application/test/orders/order_cancelled_test.rb +++ b/rails_application/test/orders/order_cancelled_test.rb @@ -15,6 +15,7 @@ def test_cancel_confirmed_order customer_id = SecureRandom.uuid order_id = SecureRandom.uuid order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER + product_id = SecureRandom.uuid event_store.publish(Crm::CustomerRegistered.new( data: { @@ -23,6 +24,8 @@ def test_cancel_confirmed_order } )) + create_product(product_id, "Async Remote", 30) + run_command(Ordering::AddItemToBasket.new(order_id: order_id, product_id: product_id)) run_command(Ordering::SubmitOrder.new(order_id: order_id, order_number: order_number)) order_cancelled = Fulfillment::OrderCancelled.new( @@ -40,5 +43,13 @@ def test_cancel_confirmed_order assert_equal("Cancelled", orders.first.state) assert event_store.event_in_stream?(order_cancelled.event_id, "Orders$all") end + + private + + def create_product(product_id, name, price) + run_command(ProductCatalog::RegisterProduct.new(product_id: product_id)) + run_command(ProductCatalog::NameProduct.new(product_id: product_id, name: name)) + run_command(Pricing::SetPrice.new(product_id: product_id, price: price)) + end end -end \ No newline at end of file +end diff --git a/rails_application/test/orders/order_paid_test.rb b/rails_application/test/orders/order_paid_test.rb index c28553ad..fb917e6c 100644 --- a/rails_application/test/orders/order_paid_test.rb +++ b/rails_application/test/orders/order_paid_test.rb @@ -15,8 +15,11 @@ def test_order_confirmed customer_id = SecureRandom.uuid order_id = SecureRandom.uuid order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER + product_id = SecureRandom.uuid + create_product(product_id, "Async Remote", 10) run_command(Crm::RegisterCustomer.new(customer_id: customer_id, name: "John Doe")) + run_command(Ordering::AddItemToBasket.new(order_id: order_id, product_id: product_id)) run_command(Ordering::SubmitOrder.new(order_id: order_id, order_number: order_number)) run_command( Crm::AssignCustomerToOrder.new(customer_id: customer_id, order_id: order_id) @@ -37,5 +40,13 @@ def test_order_confirmed assert_equal("Paid", orders.first.state) assert event_store.event_in_stream?(order_confirmed.event_id, "Orders$all") end + + private + + def create_product(product_id, name, price) + run_command(ProductCatalog::RegisterProduct.new(product_id: product_id)) + run_command(ProductCatalog::NameProduct.new(product_id: product_id, name: name)) + run_command(Pricing::SetPrice.new(product_id: product_id, price: price)) + end end -end \ No newline at end of file +end