Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v3.0] Fix app and tests to work with ActiveRecord.has_many_inverse #4098

Conversation

waiting-for-dev
Copy link
Contributor

@waiting-for-dev waiting-for-dev commented Jun 10, 2021

Backports 55bd0e4

Rails 6.1 added a new default has_many_inversing (see
rails/rails#34533 &
rails/rails#37429), which is enabled by default
on new applications. This setting broke some tests and created a couple
of bugs on Solidus. This commit should fix all of this. CI will only
examine the case when the new setting is enabled, but a local run
confirmed the suite is green when it's off.

A couple of the modifications introduced are attributable to two
different bugs in Rails discovered during the process:

We also fixed two bugs on our side:

  • Spree::Variant#product Rails association was configured as the
    inverse of Spree::Product#variants. However,
    Spree::Product#variants has a custom scope which filters out master
    variants
    (
    -> { where(is_master: false).order(:position) },
    ).
    Instead, Spree::Product#variants_including_master is now used.
  • Spree::Stock::SimpleCoordinator#build_shipments is meant to build
    shipments presented as an option to the user. To calculate their
    associated shipping rates, it delegates the shipments to
    Spree::Stock::Estimator. This service needs to know the order
    instance those shipments would be assigned to. With the current
    implementation, this information is taken from the shipment itself as
    it's already associated to that order when it's initialized on
    Spree::Stock::Package
    ( ).
    Before the has_many_inversing feature, this initialization wasn't
    reflected in the inverse association Spree::Order#shipments, but
    it's no longer the case. For this reason, after we have calculated the
    shipping rates, we need to remove the shipping instances through
    order.shipments = order.shipments - shipment. Follow-up work could
    clean this up if we pass the order as a parameter to the estimator.
    Still, it would have backward compatibility issues for user-created
    estimators as the interface would change. The following snippet
    isolates the issue:
require 'bundler/inline'

gemfile(true) do
  source 'https://rubygems.org'
  gem 'activerecord', '6.1.3.1'
  gem 'sqlite3'
end

require 'active_record'

ActiveRecord::Base.has_many_inversing = true
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 'bug.db')

ActiveRecord::Schema.define do
  create_table :parents, force: true do |t|
    t.timestamps
  end
  create_table :nodes, force: true do |t|
    t.references :parent, foreign_key: false
    t.timestamps
  end
end

class Parent < ActiveRecord::Base
  has_many :nodes, inverse_of: :parent
end

class Node < ActiveRecord::Base
  belongs_to :parent, inverse_of: :nodes
end

require 'minitest/autorun'

class BugTest < Minitest::Test
  def test_with_has_many_inversing # IT SUCCEEDS
    parent = Parent.new
    parent.save

    Node.new(parent: parent)

    assert_equal(
      1,
      parent.nodes.size
    )
  end

  def test_without_has_many_inversing # IT FAILS
    ActiveRecord::Base.has_many_inversing = false

    parent = Parent.new
    parent.save

    Node.new(parent: parent)

    assert_equal(
      1,
      parent.nodes.size
    )

    ActiveRecord::Base.has_many_inversing = true
  end
end

Other than that, other minor modifications done in the test suite
include:

  • A few#reload calls are needed to refresh the new and improved cache.
  • Some parent records need to be persisted before creating one of their
    children. We haven't investigated this point further. It could be due
    to some undocumented change of behavior in Rails or another minor bug.

Rails 6.1 added a new default `has_many_inversing` (see
rails/rails#34533 &
rails/rails#37429), which is enabled by default
on new applications. This setting broke some tests and created a couple
of bugs on Solidus. This commit should fix all of this. CI will only
examine the case when the new setting is enabled, but a local run
confirmed the suite is green when it's off.

A couple of the modifications introduced are attributable to two
different bugs in Rails discovered during the process:

- rails/rails#42102 forces us to use
  `Spree::Order#shipments#push` instead of `Spree::Order#shipments#=` when
  the proposed shipments get created from the order.
- rails/rails#42094 only applies to a rare
  corner case, but it hit us in the test case `takes into account a
  passed in scope` for `Spree::Core::Search::Variant`.

We also fixed two bugs on our side:

- `Spree::Variant#product` Rails association was configured as the
  inverse of `Spree::Product#variants`. However,
  `Spree::Product#variants` has a custom scope which filters out master
  variants
  (https://github.com/solidusio/solidus/blob/2ea829645b00fcedd5bfd69e045bddab7f40beb9/core/app/models/spree/product.rb#L47).
  Instead, `Spree::Product#variants_including_master` is now used.
- `Spree::Stock::SimpleCoordinator#build_shipments` is meant to build
  shipments presented as an option to the user. To calculate their
  associated shipping rates, it delegates the shipments to
  `Spree::Stock::Estimator`. This service needs to know the order
  instance those shipments would be assigned to. With the current
  implementation, this information is taken from the shipment itself as
  it's already associated to that order when it's initialized on
  `Spree::Stock::Package`
  (https://github.com/solidusio/solidus/blob/2ea829645b00fcedd5bfd69e045bddab7f40beb9/core/app/models/spree/stock/package.rb#L127).
  Before the `has_many_inversing` feature, this initialization wasn't
  reflected in the inverse association `Spree::Order#shipments`, but
  it's no longer the case. For this reason, after we have calculated the
  shipping rates, we need to remove the shipping instances through
  `order.shipments = order.shipments - shipment`. Follow-up work could
  clean this up if we pass the order as a parameter to the estimator.
  Still, it would have backward compatibility issues for user-created
  estimators as the interface would change. The following snippet
  isolates the issue:

```ruby
require 'bundler/inline'

gemfile(true) do
  source 'https://rubygems.org'
  gem 'activerecord', '6.1.3.1'
  gem 'sqlite3'
end

require 'active_record'

ActiveRecord::Base.has_many_inversing = true
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 'bug.db')

ActiveRecord::Schema.define do
  create_table :parents, force: true do |t|
    t.timestamps
  end
  create_table :nodes, force: true do |t|
    t.references :parent, foreign_key: false
    t.timestamps
  end
end

class Parent < ActiveRecord::Base
  has_many :nodes, inverse_of: :parent
end

class Node < ActiveRecord::Base
  belongs_to :parent, inverse_of: :nodes
end

require 'minitest/autorun'

class BugTest < Minitest::Test
  def test_with_has_many_inversing # IT SUCCEEDS
    parent = Parent.new
    parent.save

    Node.new(parent: parent)

    assert_equal(
      1,
      parent.nodes.size
    )
  end

  def test_without_has_many_inversing # IT FAILS
    ActiveRecord::Base.has_many_inversing = false

    parent = Parent.new
    parent.save

    Node.new(parent: parent)

    assert_equal(
      1,
      parent.nodes.size
    )

    ActiveRecord::Base.has_many_inversing = true
  end
end
```

Other than that, other minor modifications done in the test suite
include:

- A few`#reload` calls are needed to refresh the new and improved cache.
- Some parent records need to be persisted before creating one of their
  children. We haven't investigated this point further. It could be due
  to some undocumented change of behavior in Rails or another minor bug.
@waiting-for-dev waiting-for-dev force-pushed the waiting-for-dev/backtrack_has_many_inverse_fix_to_3_0 branch from bef6550 to b097d39 Compare June 10, 2021 10:25
@kennyadsl kennyadsl changed the title Fix app and tests to work with ActiveRecord.has_many_inverse [v3.0] Fix app and tests to work with ActiveRecord.has_many_inverse Jun 10, 2021
@kennyadsl kennyadsl merged commit 63c9374 into solidusio:v3.0 Jun 10, 2021
@kennyadsl kennyadsl deleted the waiting-for-dev/backtrack_has_many_inverse_fix_to_3_0 branch June 10, 2021 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants