From afca1a31d05f3334342628a93d4d796c95e5f5fe Mon Sep 17 00:00:00 2001 From: Jenny Shen Date: Thu, 20 Jul 2023 01:42:58 -0400 Subject: [rubygems/rubygems] Create MockServer object to test WebAuthn logic to prevent real TCPServers from being created and be leaked into other tests https://github.com/rubygems/rubygems/commit/96d6cb33a2 --- test/rubygems/multifactor_auth_fetcher.rb | 76 ---------------- test/rubygems/multifactor_auth_utilities.rb | 111 +++++++++++++++++++++++ test/rubygems/test_gem_commands_owner_command.rb | 38 +++----- test/rubygems/test_gem_commands_push_command.rb | 32 ++----- test/rubygems/test_gem_commands_yank_command.rb | 30 ++---- test/rubygems/test_gem_gemcutter_utilities.rb | 30 ++---- 6 files changed, 154 insertions(+), 163 deletions(-) delete mode 100644 test/rubygems/multifactor_auth_fetcher.rb create mode 100644 test/rubygems/multifactor_auth_utilities.rb diff --git a/test/rubygems/multifactor_auth_fetcher.rb b/test/rubygems/multifactor_auth_fetcher.rb deleted file mode 100644 index e90a01c5ba..0000000000 --- a/test/rubygems/multifactor_auth_fetcher.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -## -# A MultifactorAuthFetcher is a FakeFetcher that adds paths to data for requests related to -# multi-factor authentication. -# - -require_relative "utilities" -require "json" - -class Gem::MultifactorAuthFetcher < Gem::FakeFetcher - attr_reader :host, :webauthn_url - - # GET /api/v1/webauthn_verification defaults to user does not have any security devices - def initialize(host: nil) - super() - @host = host || Gem.host - @path_token = "odow34b93t6aPCdY" - @webauthn_url = "#{@host}/webauthn_verification/#{@path_token}" - @data["#{@host}/api/v1/webauthn_verification"] = Gem::HTTPResponseFactory.create( - body: "You don't have any security devices", - code: 422, - msg: "Unprocessable Entity" - ) - end - - # given a url, return a response that requires multifactor authentication - def respond_with_require_otp(url, success_body) - response_fail = "You have enabled multifactor authentication" - - @data[url] = proc do - @call_count ||= 0 - if (@call_count += 1).odd? - Gem::HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized") - else - Gem::HTTPResponseFactory.create(body: success_body, code: 200, msg: "OK") - end - end - end - - # GET /api/v1/webauthn_verification returns a webauthn url - # GET /api/v1/webauthn_verification/:token/status.json (polling url) returns pending status - def respond_with_webauthn_url - @data["#{@host}/api/v1/webauthn_verification"] = Gem::HTTPResponseFactory.create(body: @webauthn_url, code: 200, msg: "OK") - @data["#{@host}/api/v1/webauthn_verification/#{@path_token}/status.json"] = Gem::HTTPResponseFactory.create( - body: { status: "pending", message: "Security device authentication is still pending." }.to_json, - code: 200, - msg: "OK" - ) - end - - # GET /api/v1/webauthn_verification/:token/status.json returns success status with OTP code - def respond_with_webauthn_polling(code) - @data["#{@host}/api/v1/webauthn_verification/#{@path_token}/status.json"] = Gem::HTTPResponseFactory.create( - body: { status: "success", code: code }.to_json, - code: 200, - msg: "OK" - ) - end - - # GET /api/v1/webauthn_verification/:token/status.json returns expired status - def respond_with_webauthn_polling_failure - @data["#{@host}/api/v1/webauthn_verification/#{@path_token}/status.json"] = Gem::HTTPResponseFactory.create( - body: { - status: "expired", - message: "The token in the link you used has either expired or been used already.", - }.to_json, - code: 200, - msg: "OK" - ) - end - - def webauthn_url_with_port(port) - "#{@webauthn_url}?port=#{port}" - end -end diff --git a/test/rubygems/multifactor_auth_utilities.rb b/test/rubygems/multifactor_auth_utilities.rb new file mode 100644 index 0000000000..1133131a76 --- /dev/null +++ b/test/rubygems/multifactor_auth_utilities.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +## +# A MultifactorAuthFetcher is a FakeFetcher that adds paths to data for requests related to +# multi-factor authentication. +# + +require_relative "utilities" +require "json" + +class Gem::MultifactorAuthFetcher < Gem::FakeFetcher + attr_reader :host, :webauthn_url + + # GET /api/v1/webauthn_verification defaults to user does not have any security devices + def initialize(host: nil) + super() + @host = host || Gem.host + @path_token = "odow34b93t6aPCdY" + @webauthn_url = "#{@host}/webauthn_verification/#{@path_token}" + @data["#{@host}/api/v1/webauthn_verification"] = Gem::HTTPResponseFactory.create( + body: "You don't have any security devices", + code: 422, + msg: "Unprocessable Entity" + ) + end + + # given a url, return a response that requires multifactor authentication + def respond_with_require_otp(url, success_body) + response_fail = "You have enabled multifactor authentication" + + @data[url] = proc do + @call_count ||= 0 + if (@call_count += 1).odd? + Gem::HTTPResponseFactory.create(body: response_fail, code: 401, msg: "Unauthorized") + else + Gem::HTTPResponseFactory.create(body: success_body, code: 200, msg: "OK") + end + end + end + + # GET /api/v1/webauthn_verification returns a webauthn url + # GET /api/v1/webauthn_verification/:token/status.json (polling url) returns pending status + def respond_with_webauthn_url + @data["#{@host}/api/v1/webauthn_verification"] = Gem::HTTPResponseFactory.create(body: @webauthn_url, code: 200, msg: "OK") + @data["#{@host}/api/v1/webauthn_verification/#{@path_token}/status.json"] = Gem::HTTPResponseFactory.create( + body: { status: "pending", message: "Security device authentication is still pending." }.to_json, + code: 200, + msg: "OK" + ) + end + + # GET /api/v1/webauthn_verification/:token/status.json returns success status with OTP code + def respond_with_webauthn_polling(code) + @data["#{@host}/api/v1/webauthn_verification/#{@path_token}/status.json"] = Gem::HTTPResponseFactory.create( + body: { status: "success", code: code }.to_json, + code: 200, + msg: "OK" + ) + end + + # GET /api/v1/webauthn_verification/:token/status.json returns expired status + def respond_with_webauthn_polling_failure + @data["#{@host}/api/v1/webauthn_verification/#{@path_token}/status.json"] = Gem::HTTPResponseFactory.create( + body: { + status: "expired", + message: "The token in the link you used has either expired or been used already.", + }.to_json, + code: 200, + msg: "OK" + ) + end + + def webauthn_url_with_port(port) + "#{@webauthn_url}?port=#{port}" + end +end + +## +# The MockTCPServer for use in tests or to avoid real TCPServer instances to be created +# when testing code related to the WebAuthn listener. +# +# Example: +# +# server = Gem::MockTCPServer +# port = server.addr[1].to_s +# +# # this mocks waiting for a request by calling sleep +# server.accept +# +# # this mocks the server closing +# server.close + +class Gem::MockTCPServer + attr_reader :port + + def initialize(port = 5678) + @port = port + end + + def close + true + end + + def addr + ["AF_INET6", @port, "::", "::"] + end + + def accept + sleep + end +end diff --git a/test/rubygems/test_gem_commands_owner_command.rb b/test/rubygems/test_gem_commands_owner_command.rb index d737506ad2..d35232df7e 100644 --- a/test/rubygems/test_gem_commands_owner_command.rb +++ b/test/rubygems/test_gem_commands_owner_command.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require_relative "helper" -require_relative "multifactor_auth_fetcher" +require_relative "multifactor_auth_utilities" require "rubygems/commands/owner_command" class TestGemCommandsOwnerCommand < Gem::TestCase @@ -358,8 +358,7 @@ EOF def test_with_webauthn_enabled_success response_success = "Owner added successfully." - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new @stub_fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems/freewill/owners", response_success) @stub_fetcher.respond_with_webauthn_url @@ -370,11 +369,11 @@ EOF @cmd.add_owners("freewill", ["user-new1@example.com"]) end end - ensure - server.close end - assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(port)} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output + assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \ + "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ + "you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output assert_match "You are verified with a security device. You may close the browser window.", @stub_ui.output assert_equal "Uvh6T57tkWuUnWYo", @stub_fetcher.last_request["OTP"] assert_match response_success, @stub_ui.output @@ -382,8 +381,7 @@ EOF def test_with_webauthn_enabled_failure response_success = "Owner added successfully." - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new error = Gem::WebauthnVerificationError.new("Something went wrong") @stub_fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems/freewill/owners", response_success) @@ -395,12 +393,12 @@ EOF @cmd.add_owners("freewill", ["user-new1@example.com"]) end end - ensure - server.close end assert_match @stub_fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key - assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(port)} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output + assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \ + "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ + "you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output assert_match "ERROR: Security device verification failed: Something went wrong", @stub_ui.error refute_match "You are verified with a security device. You may close the browser window.", @stub_ui.output refute_match response_success, @stub_ui.output @@ -408,8 +406,7 @@ EOF def test_with_webauthn_enabled_success_with_polling response_success = "Owner added successfully." - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new @stub_fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems/freewill/owners", response_success) @stub_fetcher.respond_with_webauthn_url @@ -419,12 +416,10 @@ EOF use_ui @stub_ui do @cmd.add_owners("freewill", ["user-new1@example.com"]) end - ensure - server.close end - assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(port)} to authenticate " \ - "via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \ + "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \ "command with the `--otp [your_code]` option.", @stub_ui.output assert_match "You are verified with a security device. You may close the browser window.", @stub_ui.output assert_equal "Uvh6T57tkWuUnWYo", @stub_fetcher.last_request["OTP"] @@ -433,8 +428,7 @@ EOF def test_with_webauthn_enabled_failure_with_polling response_success = "Owner added successfully." - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new @stub_fetcher.respond_with_require_otp( "#{Gem.host}/api/v1/gems/freewill/owners", @@ -447,13 +441,11 @@ EOF use_ui @stub_ui do @cmd.add_owners("freewill", ["user-new1@example.com"]) end - ensure - server.close end assert_match @stub_fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key - assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(port)} to authenticate " \ - "via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \ + "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \ "command with the `--otp [your_code]` option.", @stub_ui.output assert_match "ERROR: Security device verification failed: The token in the link you used has either expired " \ "or been used already.", @stub_ui.error diff --git a/test/rubygems/test_gem_commands_push_command.rb b/test/rubygems/test_gem_commands_push_command.rb index 1f003f6ac6..bb0363da1a 100644 --- a/test/rubygems/test_gem_commands_push_command.rb +++ b/test/rubygems/test_gem_commands_push_command.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require_relative "helper" -require_relative "multifactor_auth_fetcher" +require_relative "multifactor_auth_utilities" require "rubygems/commands/push_command" require "rubygems/config_file" @@ -423,8 +423,7 @@ class TestGemCommandsPushCommand < Gem::TestCase def test_with_webauthn_enabled_success response_success = "Successfully registered gem: freewill (1.0.0)" - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new @fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success) @fetcher.respond_with_webauthn_url @@ -435,11 +434,9 @@ class TestGemCommandsPushCommand < Gem::TestCase @cmd.send_gem(@path) end end - ensure - server.close end - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output assert_match "You are verified with a security device. You may close the browser window.", @ui.output @@ -449,8 +446,7 @@ class TestGemCommandsPushCommand < Gem::TestCase def test_with_webauthn_enabled_failure response_success = "Successfully registered gem: freewill (1.0.0)" - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new error = Gem::WebauthnVerificationError.new("Something went wrong") @fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success) @@ -463,14 +459,12 @@ class TestGemCommandsPushCommand < Gem::TestCase @cmd.send_gem(@path) end end - ensure - server.close end end assert_equal 1, error.exit_code assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output assert_match "ERROR: Security device verification failed: Something went wrong", @ui.error @@ -480,8 +474,7 @@ class TestGemCommandsPushCommand < Gem::TestCase def test_with_webauthn_enabled_success_with_polling response_success = "Successfully registered gem: freewill (1.0.0)" - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new @fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success) @fetcher.respond_with_webauthn_url @@ -491,11 +484,9 @@ class TestGemCommandsPushCommand < Gem::TestCase use_ui @ui do @cmd.send_gem(@path) end - ensure - server.close end - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output assert_match "You are verified with a security device. You may close the browser window.", @ui.output @@ -505,8 +496,7 @@ class TestGemCommandsPushCommand < Gem::TestCase def test_with_webauthn_enabled_failure_with_polling response_success = "Successfully registered gem: freewill (1.0.0)" - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new @fetcher.respond_with_require_otp("#{Gem.host}/api/v1/gems", response_success) @fetcher.respond_with_webauthn_url @@ -517,15 +507,13 @@ class TestGemCommandsPushCommand < Gem::TestCase use_ui @ui do @cmd.send_gem(@path) end - ensure - server.close end end assert_equal 1, error.exit_code assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} to authenticate " \ - "via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ + "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \ "command with the `--otp [your_code]` option.", @ui.output assert_match "ERROR: Security device verification failed: The token in the link you used has either expired " \ "or been used already.", @ui.error diff --git a/test/rubygems/test_gem_commands_yank_command.rb b/test/rubygems/test_gem_commands_yank_command.rb index dcd7ba1a83..bfa0d6421f 100644 --- a/test/rubygems/test_gem_commands_yank_command.rb +++ b/test/rubygems/test_gem_commands_yank_command.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require_relative "helper" -require_relative "multifactor_auth_fetcher" +require_relative "multifactor_auth_utilities" require "rubygems/commands/yank_command" class TestGemCommandsYankCommand < Gem::TestCase @@ -113,8 +113,7 @@ class TestGemCommandsYankCommand < Gem::TestCase end def test_with_webauthn_enabled_success - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new @fetcher.respond_with_require_otp("http://example/api/v1/gems/yank", "Successfully yanked") @fetcher.respond_with_webauthn_url @@ -129,12 +128,10 @@ class TestGemCommandsYankCommand < Gem::TestCase @cmd.execute end end - ensure - server.close end assert_match %r{Yanking gem from http://example}, @ui.output - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output assert_match "You are verified with a security device. You may close the browser window.", @ui.output @@ -143,8 +140,7 @@ class TestGemCommandsYankCommand < Gem::TestCase end def test_with_webauthn_enabled_failure - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new error = Gem::WebauthnVerificationError.new("Something went wrong") @fetcher.respond_with_require_otp("http://example/api/v1/gems/yank", "Successfully yanked") @@ -161,15 +157,13 @@ class TestGemCommandsYankCommand < Gem::TestCase @cmd.execute end end - ensure - server.close end end assert_equal 1, error.exit_code assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key assert_match %r{Yanking gem from http://example}, @ui.output - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output assert_match "ERROR: Security device verification failed: Something went wrong", @ui.error @@ -178,8 +172,7 @@ class TestGemCommandsYankCommand < Gem::TestCase end def test_with_webauthn_enabled_success_with_polling - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new @fetcher.respond_with_require_otp("http://example/api/v1/gems/yank", "Successfully yanked") @fetcher.respond_with_webauthn_url @@ -193,12 +186,10 @@ class TestGemCommandsYankCommand < Gem::TestCase use_ui @ui do @cmd.execute end - ensure - server.close end assert_match %r{Yanking gem from http://example}, @ui.output - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output assert_match "You are verified with a security device. You may close the browser window.", @ui.output @@ -207,8 +198,7 @@ class TestGemCommandsYankCommand < Gem::TestCase end def test_with_webauthn_enabled_failure_with_polling - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new @fetcher.respond_with_require_otp("http://example/api/v1/gems/yank", "Successfully yanked") @fetcher.respond_with_webauthn_url @@ -223,15 +213,13 @@ class TestGemCommandsYankCommand < Gem::TestCase use_ui @ui do @cmd.execute end - ensure - server.close end end assert_equal 1, error.exit_code assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key assert_match %r{Yanking gem from http://example}, @ui.output - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output assert_match "ERROR: Security device verification failed: The token in the link you used has either expired " \ diff --git a/test/rubygems/test_gem_gemcutter_utilities.rb b/test/rubygems/test_gem_gemcutter_utilities.rb index 8282eb641b..6bd6cfd979 100644 --- a/test/rubygems/test_gem_gemcutter_utilities.rb +++ b/test/rubygems/test_gem_gemcutter_utilities.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require_relative "helper" -require_relative "multifactor_auth_fetcher" +require_relative "multifactor_auth_utilities" require "rubygems" require "rubygems/command" require "rubygems/gemcutter_utilities" @@ -223,8 +223,7 @@ class TestGemGemcutterUtilities < Gem::TestCase end def test_sign_in_with_webauthn_enabled - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new @fetcher.respond_with_require_otp @fetcher.respond_with_webauthn_url @@ -232,11 +231,9 @@ class TestGemGemcutterUtilities < Gem::TestCase Gem::GemcutterUtilities::WebauthnListener.stub(:listener_thread, Thread.new { Thread.current[:otp] = "Uvh6T57tkWuUnWYo" }) do util_sign_in end - ensure - server.close end - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output assert_match "You are verified with a security device. You may close the browser window.", @sign_in_ui.output @@ -244,8 +241,7 @@ class TestGemGemcutterUtilities < Gem::TestCase end def test_sign_in_with_webauthn_enabled_with_error - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new error = Gem::WebauthnVerificationError.new("Something went wrong") @fetcher.respond_with_require_otp @@ -255,13 +251,11 @@ class TestGemGemcutterUtilities < Gem::TestCase Gem::GemcutterUtilities::WebauthnListener.stub(:listener_thread, Thread.new { Thread.current[:error] = error }) do util_sign_in end - ensure - server.close end end assert_equal 1, error.exit_code - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output assert_match "ERROR: Security device verification failed: Something went wrong", @sign_in_ui.error @@ -270,19 +264,16 @@ class TestGemGemcutterUtilities < Gem::TestCase end def test_sign_in_with_webauthn_enabled_with_polling - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new @fetcher.respond_with_require_otp @fetcher.respond_with_webauthn_url @fetcher.respond_with_webauthn_polling("Uvh6T57tkWuUnWYo") TCPServer.stub(:new, server) do util_sign_in - ensure - server.close end - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output assert_match "You are verified with a security device. You may close the browser window.", @sign_in_ui.output @@ -290,8 +281,7 @@ class TestGemGemcutterUtilities < Gem::TestCase end def test_sign_in_with_webauthn_enabled_with_polling_failure - port = 5678 - server = TCPServer.new(port) + server = Gem::MockTCPServer.new @fetcher.respond_with_require_otp @fetcher.respond_with_webauthn_url @fetcher.respond_with_webauthn_polling_failure @@ -299,12 +289,10 @@ class TestGemGemcutterUtilities < Gem::TestCase assert_raise Gem::MockGemUi::TermError do TCPServer.stub(:new, server) do util_sign_in - ensure - server.close end end - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output assert_match "ERROR: Security device verification failed: " \ -- cgit v1.2.3