From 7efca794f887898bf263881eeafa19939a9be36d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pacana?= Date: Mon, 27 Apr 2026 10:24:42 +0200 Subject: [PATCH] Harden adapter mutation coverage - Verify pg adapter mocks during raised transactions so rollback expectations cannot be skipped. - Cover Sequel error unwrapping branches for PG subclasses, Sequel subclasses, non-PG wrapped errors, plain errors, and missing Sequel. - Bring adapter mutation coverage back to 100%. --- test/test_pg_adapter.rb | 3 +- test/test_sequel_adapter.rb | 94 +++++++++++++++++++++++++++++++++---- 2 files changed, 88 insertions(+), 9 deletions(-) diff --git a/test/test_pg_adapter.rb b/test/test_pg_adapter.rb index b3ba434..6784fd3 100644 --- a/test/test_pg_adapter.rb +++ b/test/test_pg_adapter.rb @@ -79,8 +79,9 @@ module En57 end, ) do yield connection, PgAdapter.new(connection_uri), -> { connection_count } + ensure + connection.verify end - connection.verify end end end diff --git a/test/test_sequel_adapter.rb b/test/test_sequel_adapter.rb index 44b2178..2d559b5 100644 --- a/test/test_sequel_adapter.rb +++ b/test/test_sequel_adapter.rb @@ -47,19 +47,22 @@ module En57 end def test_with_serializable_transaction_unwraps_pg_errors + assert_unwraps_pg_error(PG::Error.new("boom")) + end + + def test_with_serializable_transaction_unwraps_pg_error_subclasses + assert_unwraps_pg_error(PG::TRSerializationFailure.new("boom")) + end + + def test_with_serializable_transaction_unwraps_sequel_error_subclasses pg_error = PG::Error.new("boom") - sequel_error = Sequel::DatabaseError.new("wrapped") + sequel_error = Class.new(Sequel::DatabaseError).new("wrapped") sequel_error.wrapped_exception = pg_error raised = assert_raises(PG::Error) do with_mock_adapter do |database, _connection, adapter| - database.expect(:transaction, nil) do |options, &block| - assert_equal({ isolation: :serializable }, options) - block.call - true - end - database.expect(:synchronize, nil) { raise sequel_error } + expect_failed_transaction(database, sequel_error) adapter.with_serializable_transaction { flunk "not yielded" } end @@ -68,16 +71,91 @@ module En57 assert_same pg_error, raised end + def test_with_serializable_transaction_reraises_non_pg_sequel_errors + error = RuntimeError.new("boom") + sequel_error = Sequel::DatabaseError.new("wrapped") + sequel_error.wrapped_exception = error + + raised = + assert_raises(Sequel::DatabaseError) do + with_mock_adapter do |database, _connection, adapter| + expect_failed_transaction(database, sequel_error) + + adapter.with_serializable_transaction { flunk "not yielded" } + end + end + + assert_same sequel_error, raised + end + + def test_with_serializable_transaction_reraises_plain_errors + error = RuntimeError.new("boom") + + raised = + assert_raises(RuntimeError) do + with_mock_adapter do |database, _connection, adapter| + expect_failed_transaction(database, error) + + adapter.with_serializable_transaction { flunk "not yielded" } + end + end + + assert_same error, raised + end + + def test_with_serializable_transaction_reraises_without_sequel_loaded + error = RuntimeError.new("boom") + sequel = Object.send(:remove_const, :Sequel) + + raised = + assert_raises(RuntimeError) do + with_mock_adapter do |database, _connection, adapter| + expect_failed_transaction(database, error) + + adapter.with_serializable_transaction { flunk "not yielded" } + end + end + + assert_same error, raised + ensure + Object.const_set(:Sequel, sequel) if sequel + end + private + def assert_unwraps_pg_error(pg_error) + sequel_error = Sequel::DatabaseError.new("wrapped") + sequel_error.wrapped_exception = pg_error + + raised = + assert_raises(PG::Error) do + with_mock_adapter do |database, _connection, adapter| + expect_failed_transaction(database, sequel_error) + + adapter.with_serializable_transaction { flunk "not yielded" } + end + end + + assert_same pg_error, raised + end + def with_mock_adapter database = Minitest::Mock.new connection = Minitest::Mock.new yield database, connection, SequelAdapter.new(database) - + ensure database.verify connection.verify end + + def expect_failed_transaction(database, error) + database.expect(:transaction, nil) do |options, &block| + assert_equal({ isolation: :serializable }, options) + block.call + true + end + database.expect(:synchronize, nil) { raise error } + end end end -- 2.51.2