From fd2a3631b1e23557b94e532616f97b1aaca4acfb Mon Sep 17 00:00:00 2001 From: Johnny Shields <27655+johnnyshields@users.noreply.github.com> Date: Sat, 10 May 2025 05:02:10 +0900 Subject: [PATCH 1/6] Remove reference to ancient driver in tests --- spec/support/expectations.rb | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/spec/support/expectations.rb b/spec/support/expectations.rb index 30084a6a0d..19fd089b38 100644 --- a/spec/support/expectations.rb +++ b/spec/support/expectations.rb @@ -3,15 +3,6 @@ module Mongoid module Expectations - def connection_class - if defined?(Mongo::Server::ConnectionBase) - Mongo::Server::ConnectionBase - else - # Pre-2.8 drivers - Mongo::Server::Connection - end - end - def expect_query(number) if %i[ sharded load-balanced ].include?(ClusterConfig.instance.topology) && number > 0 skip 'This spec requires replica set or standalone topology' @@ -19,9 +10,9 @@ def expect_query(number) rv = nil RSpec::Mocks.with_temporary_scope do if number > 0 - expect_any_instance_of(connection_class).to receive(:command_started).exactly(number).times.and_call_original + expect_any_instance_of(Mongo::Server::ConnectionBase).to receive(:command_started).exactly(number).times.and_call_original else - expect_any_instance_of(connection_class).not_to receive(:command_started) + expect_any_instance_of(Mongo::Server::ConnectionBase).not_to receive(:command_started) end rv = yield end From e0c6c789b46fd9ae2b75be3963c0aacd01aaf66f Mon Sep 17 00:00:00 2001 From: Johnny Shields <27655+johnnyshields@users.noreply.github.com> Date: Sat, 10 May 2025 05:44:32 +0900 Subject: [PATCH 2/6] Fix expect_query helper for Ruby 3.3+ --- spec/support/expectations.rb | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/spec/support/expectations.rb b/spec/support/expectations.rb index 19fd089b38..e3beb1164c 100644 --- a/spec/support/expectations.rb +++ b/spec/support/expectations.rb @@ -1,5 +1,4 @@ # frozen_string_literal: true -# rubocop:todo all module Mongoid module Expectations @@ -7,16 +6,24 @@ def expect_query(number) if %i[ sharded load-balanced ].include?(ClusterConfig.instance.topology) && number > 0 skip 'This spec requires replica set or standalone topology' end - rv = nil + RSpec::Mocks.with_temporary_scope do + klass = Mongo::Server::ConnectionBase + if number > 0 - expect_any_instance_of(Mongo::Server::ConnectionBase).to receive(:command_started).exactly(number).times.and_call_original + # Due to changes in Ruby 3.3, RSpec's #and_call_original (which wraps the target + # method) causes infinite recursion. We can achieve the same behavior with #bind. + original_method = klass.instance_method(:command_started) + expect_any_instance_of(klass).to receive(:command_started).exactly(number).times do |*args, **kwargs| + instance = args.shift + original_method.bind(instance).call(*args, **kwargs) + end else - expect_any_instance_of(Mongo::Server::ConnectionBase).not_to receive(:command_started) + expect_any_instance_of(klass).not_to receive(:command_started) end - rv = yield + + yield end - rv end def expect_no_queries(&block) From c63ffbaaedd9800ad4dd544b13bf4f751784a1c2 Mon Sep 17 00:00:00 2001 From: Johnny Shields <27655+johnnyshields@users.noreply.github.com> Date: Sat, 10 May 2025 05:55:31 +0900 Subject: [PATCH 3/6] Fix Rubocop --- spec/support/expectations.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/support/expectations.rb b/spec/support/expectations.rb index e3beb1164c..e96c09b108 100644 --- a/spec/support/expectations.rb +++ b/spec/support/expectations.rb @@ -2,6 +2,8 @@ module Mongoid module Expectations + # rubocop:disable Metrics/AbcSize + # rubocop:disable RSpec/AnyInstance def expect_query(number) if %i[ sharded load-balanced ].include?(ClusterConfig.instance.topology) && number > 0 skip 'This spec requires replica set or standalone topology' @@ -12,11 +14,10 @@ def expect_query(number) if number > 0 # Due to changes in Ruby 3.3, RSpec's #and_call_original (which wraps the target - # method) causes infinite recursion. We can achieve the same behavior with #bind. + # method) causes infinite recursion. We can achieve the same behavior with binding. original_method = klass.instance_method(:command_started) expect_any_instance_of(klass).to receive(:command_started).exactly(number).times do |*args, **kwargs| - instance = args.shift - original_method.bind(instance).call(*args, **kwargs) + original_method.bind_call(*args, **kwargs) end else expect_any_instance_of(klass).not_to receive(:command_started) @@ -25,6 +26,8 @@ def expect_query(number) yield end end + # rubocop:enable Metrics/AbcSize + # rubocop:enable RSpec/AnyInstance def expect_no_queries(&block) expect_query(0, &block) From 3ce960cb04abd600aaf1a7900c05369e2242cd2b Mon Sep 17 00:00:00 2001 From: Johnny Shields <27655+johnnyshields@users.noreply.github.com> Date: Sat, 10 May 2025 06:48:40 +0900 Subject: [PATCH 4/6] Move method assignment outside RSpec temporary scope block --- spec/support/expectations.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/support/expectations.rb b/spec/support/expectations.rb index e96c09b108..8060efa453 100644 --- a/spec/support/expectations.rb +++ b/spec/support/expectations.rb @@ -9,13 +9,13 @@ def expect_query(number) skip 'This spec requires replica set or standalone topology' end - RSpec::Mocks.with_temporary_scope do - klass = Mongo::Server::ConnectionBase + klass = Mongo::Server::ConnectionBase + original_method = klass.instance_method(:command_started) + RSpec::Mocks.with_temporary_scope do if number > 0 # Due to changes in Ruby 3.3, RSpec's #and_call_original (which wraps the target # method) causes infinite recursion. We can achieve the same behavior with binding. - original_method = klass.instance_method(:command_started) expect_any_instance_of(klass).to receive(:command_started).exactly(number).times do |*args, **kwargs| original_method.bind_call(*args, **kwargs) end From 920ce45f50951f2a87e74f6cfcefe7f2db5ec8e6 Mon Sep 17 00:00:00 2001 From: Johnny Shields <27655+johnnyshields@users.noreply.github.com> Date: Sat, 10 May 2025 18:58:38 +0900 Subject: [PATCH 5/6] Update expectations.rb --- spec/support/expectations.rb | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/spec/support/expectations.rb b/spec/support/expectations.rb index 8060efa453..354d68cfc5 100644 --- a/spec/support/expectations.rb +++ b/spec/support/expectations.rb @@ -3,7 +3,6 @@ module Mongoid module Expectations # rubocop:disable Metrics/AbcSize - # rubocop:disable RSpec/AnyInstance def expect_query(number) if %i[ sharded load-balanced ].include?(ClusterConfig.instance.topology) && number > 0 skip 'This spec requires replica set or standalone topology' @@ -11,23 +10,23 @@ def expect_query(number) klass = Mongo::Server::ConnectionBase original_method = klass.instance_method(:command_started) + query_count = 0 - RSpec::Mocks.with_temporary_scope do - if number > 0 - # Due to changes in Ruby 3.3, RSpec's #and_call_original (which wraps the target - # method) causes infinite recursion. We can achieve the same behavior with binding. - expect_any_instance_of(klass).to receive(:command_started).exactly(number).times do |*args, **kwargs| - original_method.bind_call(*args, **kwargs) - end - else - expect_any_instance_of(klass).not_to receive(:command_started) + begin + klass.define_method(:command_started) do |*args, **kwargs| + query_count += 1 + original_method.bind(self).call(*args, **kwargs) end - yield + result = yield + expect(query_count).to eq(number) + result + ensure + klass.remove_method(:command_started) + klass.define_method(:command_started, original_method) end end # rubocop:enable Metrics/AbcSize - # rubocop:enable RSpec/AnyInstance def expect_no_queries(&block) expect_query(0, &block) From 82e19b738eab14fdfd7e31aec373ae884d2dda8d Mon Sep 17 00:00:00 2001 From: Johnny Shields <27655+johnnyshields@users.noreply.github.com> Date: Sat, 10 May 2025 19:07:42 +0900 Subject: [PATCH 6/6] Update expectations.rb --- spec/support/expectations.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/support/expectations.rb b/spec/support/expectations.rb index 354d68cfc5..fb40419965 100644 --- a/spec/support/expectations.rb +++ b/spec/support/expectations.rb @@ -2,7 +2,8 @@ module Mongoid module Expectations - # rubocop:disable Metrics/AbcSize + # Previously this method used RSpec::Mocks with .exactly.times(n).and_call_original, + # which stopped working reliably in Ruby 3.3. Now we directly wrap the target method. def expect_query(number) if %i[ sharded load-balanced ].include?(ClusterConfig.instance.topology) && number > 0 skip 'This spec requires replica set or standalone topology' @@ -15,7 +16,7 @@ def expect_query(number) begin klass.define_method(:command_started) do |*args, **kwargs| query_count += 1 - original_method.bind(self).call(*args, **kwargs) + original_method.bind_call(self, *args, **kwargs) end result = yield @@ -26,7 +27,6 @@ def expect_query(number) klass.define_method(:command_started, original_method) end end - # rubocop:enable Metrics/AbcSize def expect_no_queries(&block) expect_query(0, &block)