mocha gem과 관련된 Ruby 3 지원

Ruby 3 지원과 관련하여 또 다른 주제가 있습니다.

재현 단계

$ bundle exec rspec ./spec/lib/backup_restore/database_restorer_multisite_spec.rb:15

Ruby 3.0.2에서의 실제 결과

$ ruby -v
ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux]
$ bundle exec rspec ./spec/lib/backup_restore/database_restorer_multisite_spec.rb:15
Run options: include {:locations=>{"./spec/lib/backup_restore/database_restorer_multisite_spec.rb"=>[15]}}

Randomized with seed 43732
F

Failures:

  1) BackupRestore::DatabaseRestorer#restore database connection reconnects to the correct database
     Failure/Error:
       log Discourse::Utils.execute_command(
         {
           "SKIP_POST_DEPLOYMENT_MIGRATIONS" => "0",
           "SKIP_OPTIMIZE_ICONS" => "1",
           "DISABLE_TRANSLATION_OVERRIDES" => "1"
         },
         "rake", "db:migrate",
         failure_message: "Failed to migrate database.",
         chdir: Rails.root
       )

     Mocha::ExpectationError:
       unexpected invocation: Discourse::Utils.execute_command({"SKIP_POST_DEPLOYMENT_MIGRATIONS" => "0", "SKIP_OPTIMIZE_ICONS" => "1", "DISABLE_TRANSLATION_OVERRIDES" => "1"}, "rake", "db:migrate", {:failure_message => "Failed to migrate database.", :chdir => #<Pathname:0xd548>})
       unsatisfied expectations:
       - expected exactly once, invoked never: Discourse::Utils.execute_command()
       satisfied expectations:
       - expected exactly once, invoked once: BackupRestore.move_tables_between_schemas("public", "backup")
       - expected at least once, invoked 77 times: Migration::BaseDropper.create_readonly_function(any_parameters)
       - expected exactly once, invoked once: #<Mock:psql status>.exitstatus(any_parameters)
       - expected exactly once, invoked once: Process.last_status(any_parameters)
       - expected exactly twice, invoked twice: #<Mock:psql>.readline(any_parameters)
       - expected exactly once, invoked once: IO.popen(any_parameters)
     # ./lib/backup_restore/database_restorer.rb:138:in `migrate_database'
     # ./lib/backup_restore/database_restorer.rb:27:in `restore'
     # ./spec/lib/backup_restore/shared_context_for_backup_restore.rb:59:in `execute_stubbed_restore'
     # ./spec/lib/backup_restore/database_restorer_multisite_spec.rb:17:in `block (4 levels) in <top (required)>'
     # ./spec/rails_helper.rb:279:in `block (2 levels) in <top (required)>'

Finished in 0.28437 seconds (files took 2.37 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/lib/backup_restore/database_restorer_multisite_spec.rb:15 # BackupRestore::DatabaseRestorer#restore database connection reconnects to the correct database

Randomized with seed 43732

$

Ruby 2.7.4에서의 실제 결과

$ ruby -v
ruby 2.7.4p191 (2021-07-07 revision a21a3b7d23) [x86_64-linux]
$ bundle exec rspec ./spec/lib/backup_restore/database_restorer_multisite_spec.rb:15
Run options: include {:locations=>{"./spec/lib/backup_restore/database_restorer_multisite_spec.rb"=>[15]}}

Randomized with seed 37771
.

Finished in 0.63589 seconds (files took 2.63 seconds to load)
1 example, 0 failures

Randomized with seed 37771

$

지금까지 발견한 내용

실제로 이는 Ruby 3의 키워드 인자(keyword arguments) 지원이 필요한 또 다른 사례입니다.

  • Ruby 2.7.4에서 -w 옵션을 활성화하여 실행하면 warning: Using the last argument as keyword parameters is deprecated 경고를 발견할 수 있습니다.
$ bundle exec rspec -w ./spec/lib/backup_restore/database_restorer_multisite_spec.rb:15 2>&1 | grep keyword
/home/yahonda/.rbenv/versions/2.7.4/lib/ruby/gems/2.7.0/gems/mocha-1.13.0/lib/mocha/parameters_matcher.rb:13: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
  • 다음은 디버그 출력을 위한 임시 수정안입니다.
$ git diff 191823e8696881ab8c72948165834eca18315cb3...8141f2f0eca4bedcf59e34f31dd9ca35e39c2a25
diff --git a/lib/mocha/parameters_matcher.rb b/lib/mocha/parameters_matcher.rb
index 2b6ed84..26c4a25 100644
--- a/lib/mocha/parameters_matcher.rb
+++ b/lib/mocha/parameters_matcher.rb
@@ -10,7 +10,11 @@ module Mocha

     def match?(actual_parameters = [])
       if @matching_block
-        @matching_block.call(*actual_parameters)
+        p actual_parameters
+        if actual_parameters.last.is_a? Hash
+          kwargs = actual_parameters.pop
+        end
+        @matching_block.call(*actual_parameters, **kwargs)
       else
         parameters_match?(actual_parameters)
       end
$

수정된 mocha 코드 실행 결과

$ bundle exec rspec ./spec/lib/backup_restore/database_restorer_multisite_spec.rb:15
Run options: include {:locations=>{"./spec/lib/backup_restore/database_restorer_multisite_spec.rb"=>[15]}}

Randomized with seed 27573
[{"SKIP_POST_DEPLOYMENT_MIGRATIONS"=>"0", "SKIP_OPTIMIZE_ICONS"=>"1", "DISABLE_TRANSLATION_OVERRIDES"=>"1"}, "rake", "db:migrate", {:failure_message=>"Failed to migrate database.", :chdir=>#<Pathname:/home/yahonda/src/github.com/discourse/discourse>}]
.

Finished in 0.40263 seconds (files took 2.88 seconds to load)
1 example, 0 failures

Randomized with seed 27573

$

mocha에 대한 최소 재현 사례(minimum case)를 생성해야 할 수도 있습니다. 조언이 있으신 분은 알려주시면 감사하겠습니다.

네, mocha에 대한 수정인 것 같습니다. GitHub에서 이 문제에 대해 버그 리포트를 열었나요?

아직은요. 이번 주에 좀 더 자세히 살펴볼 거예요.

비슷한 문제가 보고된 사례를 찾았고, 우회 방법이 설명되어 있습니다.

**kwargs 대신 kwargs를 사용하면, kwargs가 해시(Hash)가 되어 어설션(assertion)에서 사용할 수 있고 경고가 생성되지 않습니다…

제시된 우회 방법을 적용하면 실패하던 스펙이 통과합니다.

https://github.com/yahonda/discourse/commit/356f15b02cb4a0bb869d19adf2a2018d07ee79bf

$ git diff main
diff --git a/spec/lib/backup_restore/shared_context_for_backup_restore.rb b/spec/lib/backup_restore/shared_context_for_backup_restore.rb
index 2ff78176ea..0fbc8ade15 100644
--- a/spec/lib/backup_restore/shared_context_for_backup_restore.rb
+++ b/spec/lib/backup_restore/shared_context_for_backup_restore.rb
@@ -33,7 +33,7 @@ shared_context "shared stuff" do
   end

   def expect_db_migrate
-    Discourse::Utils.expects(:execute_command).with do |env, *command, **options|
+    Discourse::Utils.expects(:execute_command).with do |env, *command, options|
       env["SKIP_POST_DEPLOYMENT_MIGRATIONS"] == "0" &&
         env["SKIP_OPTIMIZE_ICONS"] == "1" &&
         env["DISABLE_TRANSLATION_OVERRIDES"] == "1" &&
$

풀 리퀘스트를 열어도 좋습니다.

FIX: `BackupRestore::DatabaseRestorer` failures with Ruby 3 - Pull Request #14580 - discourse/discourse - GitHub 열림