The code here runs from a callback which was originally designed to make sure the checkout page was set up correctly in the "normal" checkout workflow. It wasn't really designed to be run when the page is being loaded a second time due to the user being redirected back from Stripe (with SCA). The things it's doing here are necessary in the former case, but a really bad idea in the latter (potentially messing up the order's ship and bill addresses in certain cases like guest checkout).
The test setup left the object with unsaved changes, which doesn't work nicely with the new order locking added recently on the edit action.
Fixes ~5 specs including:
5) CheckoutController redirection to cart and stripe redirects when some items are out of stock
Failure/Error:
order.with_lock do
lock_variants_of(order)
yield
end
RuntimeError:
Locking a record with unpersisted changes is not supported. Use `save` to persist the changes, or `reload` to discard them explicitly.
# ./app/services/current_order_locker.rb:22:in `lock_order_and_variants'
# ./app/services/current_order_locker.rb:11:in `around'
# ./spec/controllers/checkout_controller_spec.rb:57:in `block (3 levels) in <top (required)>'
The spec was setting the order's state to "complete" but didn't save
that state to the database. The new locking mechanism is was reloading
the order which loaded the cart state again. And since the order.next
method was mocked to just return true, the controller was trying to do
that in an infinite loop.
When two people tried to buy the same item at the same time, it was
possible to oversell the item and end up with negative stock.
Parallel checkouts could also lead to other random failures. This spec
is testing that scenario by starting two threads which would run into a
race condition unless they use effective synchronisation. The added spec
fails if the synchronisation is removed from the CheckoutController.
OrderUpdater#shipping_address_from_distributor uses order.address_from_distributor to set order.ship_address when order is not delivery: this will clear the ship address as it was done previously without setting an empty address like Spree::Address.default