@@ -5187,31 +5187,103 @@ def test_getinfo_basic_driver_info(db_connection):
51875187
51885188def test_getinfo_string_encoding_utf16 (db_connection ):
51895189 """Test that string values from getinfo are properly decoded from UTF-16."""
5190-
5190+
51915191 # Test string info types that should not contain null bytes
51925192 string_info_types = [
51935193 ("SQL_DRIVER_VER" , sql_const .SQL_DRIVER_VER .value ),
51945194 ("SQL_DRIVER_NAME" , sql_const .SQL_DRIVER_NAME .value ),
51955195 ("SQL_DRIVER_ODBC_VER" , sql_const .SQL_DRIVER_ODBC_VER .value ),
51965196 ("SQL_SERVER_NAME" , sql_const .SQL_SERVER_NAME .value ),
51975197 ]
5198-
5198+
51995199 for name , info_type in string_info_types :
52005200 result = db_connection .getinfo (info_type )
5201-
5201+
52025202 if result is not None :
52035203 # Verify it's a string
5204- assert isinstance (result , str ), \
5205- f"{ name } : Expected str, got { type (result ).__name__ } "
5206-
5204+ assert isinstance (result , str ), f"{ name } : Expected str, got { type (result ).__name__ } "
5205+
52075206 # Verify no null bytes (indicates UTF-16 decoded as UTF-8 bug)
5208- assert '\x00 ' not in result , \
5209- f"{ name } contains null bytes, likely UTF-16/UTF-8 encoding mismatch: { repr (result )} "
5210-
5207+ assert (
5208+ "\x00 " not in result
5209+ ), f"{ name } contains null bytes, likely UTF-16/UTF-8 encoding mismatch: { repr (result )} "
5210+
52115211 # Verify it's not empty (optional, but good sanity check)
52125212 assert len (result ) > 0 , f"{ name } returned empty string"
52135213
52145214
5215+ def test_getinfo_string_decoding_utf8_fallback (db_connection ):
5216+ """Test that getinfo falls back to UTF-8 when UTF-16LE decoding fails.
5217+
5218+ This test verifies the fallback path in the encoding loop where
5219+ UTF-16LE fails but UTF-8 succeeds.
5220+ """
5221+ from unittest .mock import patch
5222+
5223+ # UTF-8 encoded "Hello" - this is valid UTF-8 but NOT valid UTF-16LE
5224+ # (odd number of bytes would fail UTF-16LE decode)
5225+ utf8_data = "Hello" .encode ("utf-8" ) # b'Hello' - 5 bytes, odd length
5226+
5227+ mock_result = {"data" : utf8_data , "length" : len (utf8_data )}
5228+
5229+ # Use a string-type info_type (SQL_DRIVER_NAME = 6 is in string_type_constants)
5230+ info_type = sql_const .SQL_DRIVER_NAME .value
5231+
5232+ with patch .object (db_connection ._conn , "get_info" , return_value = mock_result ):
5233+ result = db_connection .getinfo (info_type )
5234+
5235+ assert result == "Hello" , f"Expected 'Hello', got { repr (result )} "
5236+ assert isinstance (result , str ), f"Expected str, got { type (result ).__name__ } "
5237+
5238+
5239+ def test_getinfo_string_decoding_all_fail_returns_none (db_connection ):
5240+ """Test that getinfo returns None when all decoding attempts fail.
5241+
5242+ This test verifies that when both UTF-16LE and UTF-8 decoding fail,
5243+ the method returns None to avoid silent data corruption.
5244+ """
5245+ from unittest .mock import patch
5246+
5247+ # Invalid byte sequence that cannot be decoded as UTF-16LE or UTF-8
5248+ # 0xFF 0xFE is a BOM, but followed by invalid continuation bytes for UTF-8
5249+ # and odd length makes it invalid UTF-16LE
5250+ invalid_data = bytes ([0x80 , 0x81 , 0x82 ]) # Invalid for both encodings
5251+
5252+ mock_result = {"data" : invalid_data , "length" : len (invalid_data )}
5253+
5254+ # Use a string-type info_type (SQL_DRIVER_NAME = 6 is in string_type_constants)
5255+ info_type = sql_const .SQL_DRIVER_NAME .value
5256+
5257+ with patch .object (db_connection ._conn , "get_info" , return_value = mock_result ):
5258+ result = db_connection .getinfo (info_type )
5259+
5260+ # Should return None when all decoding fails
5261+ assert result is None , f"Expected None for invalid encoding, got { repr (result )} "
5262+
5263+
5264+ def test_getinfo_string_encoding_utf16_primary (db_connection ):
5265+ """Test that getinfo correctly decodes valid UTF-16LE data.
5266+
5267+ This test verifies the primary (expected) encoding path where
5268+ UTF-16LE decoding succeeds on first try.
5269+ """
5270+ from unittest .mock import patch
5271+
5272+ # Valid UTF-16LE encoded "Test" with null terminator
5273+ utf16_data = "Test" .encode ("utf-16-le" ) + b"\x00 \x00 "
5274+
5275+ mock_result = {"data" : utf16_data , "length" : len (utf16_data )}
5276+
5277+ # Use a string-type info_type
5278+ info_type = sql_const .SQL_DRIVER_NAME .value
5279+
5280+ with patch .object (db_connection ._conn , "get_info" , return_value = mock_result ):
5281+ result = db_connection .getinfo (info_type )
5282+
5283+ assert result == "Test" , f"Expected 'Test', got { repr (result )} "
5284+ assert "\x00 " not in result , f"Result contains null bytes: { repr (result )} "
5285+
5286+
52155287def test_getinfo_sql_support (db_connection ):
52165288 """Test SQL support and conformance info types."""
52175289
0 commit comments