@@ -1523,182 +1523,166 @@ def test_mssql_pymssql_connection_factory():
15231523 del sys .modules ["pymssql" ]
15241524
15251525
1526- def test_mssql_cursor_init_datetimeoffset_handling ():
1527- """Test that the MSSQL cursor init properly handles DATETIMEOFFSET conversion."""
1526+ def test_mssql_pyodbc_connection_datetimeoffset_handling ():
1527+ """Test that the MSSQL pyodbc connection properly handles DATETIMEOFFSET conversion."""
15281528 from datetime import datetime , timezone , timedelta
15291529 import struct
1530- from unittest .mock import Mock
1530+ from unittest .mock import Mock , patch
15311531
1532- config = MSSQLConnectionConfig (
1533- host = "localhost" ,
1534- driver = "pyodbc" , # DATETIMEOFFSET handling is pyodbc-specific
1535- check_import = False ,
1536- )
1532+ with patch ("pyodbc.connect" ) as mock_pyodbc_connect :
1533+ # Track calls to add_output_converter
1534+ converter_calls = []
15371535
1538- # Get the cursor init function
1539- cursor_init = config ._cursor_init
1540- assert cursor_init is not None
1541-
1542- # Create a mock cursor and connection
1543- mock_connection = Mock ()
1544- mock_cursor = Mock ()
1545- mock_cursor .connection = mock_connection
1546-
1547- # Track calls to add_output_converter
1548- converter_calls = []
1549-
1550- def mock_add_output_converter (sql_type , converter_func ):
1551- converter_calls .append ((sql_type , converter_func ))
1552-
1553- mock_connection .add_output_converter = mock_add_output_converter
1554-
1555- # Call the cursor init function
1556- cursor_init (mock_cursor )
1557-
1558- # Verify that add_output_converter was called for SQL type -155 (DATETIMEOFFSET)
1559- assert len (converter_calls ) == 1
1560- sql_type , converter_func = converter_calls [0 ]
1561- assert sql_type == - 155
1562-
1563- # Test the converter function with actual DATETIMEOFFSET binary data
1564- # Create a test DATETIMEOFFSET value: 2023-12-25 15:30:45.123456789 +05:30
1565- year , month , day = 2023 , 12 , 25
1566- hour , minute , second = 15 , 30 , 45
1567- nanoseconds = 123456789
1568- tz_hour_offset , tz_minute_offset = 5 , 30
1569-
1570- # Pack the binary data according to the DATETIMEOFFSET format
1571- binary_data = struct .pack (
1572- "<6hI2h" ,
1573- year ,
1574- month ,
1575- day ,
1576- hour ,
1577- minute ,
1578- second ,
1579- nanoseconds ,
1580- tz_hour_offset ,
1581- tz_minute_offset ,
1582- )
1536+ def mock_add_output_converter (sql_type , converter_func ):
1537+ converter_calls .append ((sql_type , converter_func ))
15831538
1584- # Convert using the registered converter
1585- result = converter_func (binary_data )
1586-
1587- # Verify the result
1588- expected_dt = datetime (
1589- 2023 ,
1590- 12 ,
1591- 25 ,
1592- 15 ,
1593- 30 ,
1594- 45 ,
1595- 123456 , # microseconds = nanoseconds // 1000
1596- timezone (timedelta (hours = 5 , minutes = 30 )),
1597- )
1598- assert result == expected_dt
1599- assert result .tzinfo == timezone (timedelta (hours = 5 , minutes = 30 ))
1539+ # Create a mock connection that will be returned by pyodbc.connect
1540+ mock_connection = Mock ()
1541+ mock_connection .add_output_converter = mock_add_output_converter
1542+ mock_pyodbc_connect .return_value = mock_connection
1543+
1544+ config = MSSQLConnectionConfig (
1545+ host = "localhost" ,
1546+ driver = "pyodbc" , # DATETIMEOFFSET handling is pyodbc-specific
1547+ check_import = False ,
1548+ )
16001549
1550+ # Get the connection factory and call it
1551+ factory_with_kwargs = config ._connection_factory_with_kwargs
1552+ connection = factory_with_kwargs ()
1553+
1554+ # Verify that add_output_converter was called for SQL type -155 (DATETIMEOFFSET)
1555+ assert len (converter_calls ) == 1
1556+ sql_type , converter_func = converter_calls [0 ]
1557+ assert sql_type == - 155
1558+
1559+ # Test the converter function with actual DATETIMEOFFSET binary data
1560+ # Create a test DATETIMEOFFSET value: 2023-12-25 15:30:45.123456789 +05:30
1561+ year , month , day = 2023 , 12 , 25
1562+ hour , minute , second = 15 , 30 , 45
1563+ nanoseconds = 123456789
1564+ tz_hour_offset , tz_minute_offset = 5 , 30
1565+
1566+ # Pack the binary data according to the DATETIMEOFFSET format
1567+ binary_data = struct .pack (
1568+ "<6hI2h" ,
1569+ year ,
1570+ month ,
1571+ day ,
1572+ hour ,
1573+ minute ,
1574+ second ,
1575+ nanoseconds ,
1576+ tz_hour_offset ,
1577+ tz_minute_offset ,
1578+ )
1579+
1580+ # Convert using the registered converter
1581+ result = converter_func (binary_data )
1582+
1583+ # Verify the result
1584+ expected_dt = datetime (
1585+ 2023 ,
1586+ 12 ,
1587+ 25 ,
1588+ 15 ,
1589+ 30 ,
1590+ 45 ,
1591+ 123456 , # microseconds = nanoseconds // 1000
1592+ timezone (timedelta (hours = 5 , minutes = 30 )),
1593+ )
1594+ assert result == expected_dt
1595+ assert result .tzinfo == timezone (timedelta (hours = 5 , minutes = 30 ))
16011596
1602- def test_mssql_cursor_init_negative_timezone_offset ():
1603- """Test DATETIMEOFFSET handling with negative timezone offset."""
1597+
1598+ def test_mssql_pyodbc_connection_negative_timezone_offset ():
1599+ """Test DATETIMEOFFSET handling with negative timezone offset at connection level."""
16041600 from datetime import datetime , timezone , timedelta
16051601 import struct
1606- from unittest .mock import Mock
1602+ from unittest .mock import Mock , patch
16071603
1608- config = MSSQLConnectionConfig (
1609- host = "localhost" ,
1610- driver = "pyodbc" , # DATETIMEOFFSET handling is pyodbc-specific
1611- check_import = False ,
1612- )
1604+ with patch ("pyodbc.connect" ) as mock_pyodbc_connect :
1605+ converter_calls = []
16131606
1614- cursor_init = config ._cursor_init
1615- mock_connection = Mock ()
1616- mock_cursor = Mock ()
1617- mock_cursor .connection = mock_connection
1618-
1619- converter_calls = []
1620-
1621- def mock_add_output_converter (sql_type , converter_func ):
1622- converter_calls .append ((sql_type , converter_func ))
1623-
1624- mock_connection .add_output_converter = mock_add_output_converter
1625- cursor_init (mock_cursor )
1626-
1627- # Get the converter function
1628- _ , converter_func = converter_calls [0 ]
1629-
1630- # Test with negative timezone offset: 2023-01-01 12:00:00.0 -08:00
1631- year , month , day = 2023 , 1 , 1
1632- hour , minute , second = 12 , 0 , 0
1633- nanoseconds = 0
1634- tz_hour_offset , tz_minute_offset = - 8 , 0
1635-
1636- binary_data = struct .pack (
1637- "<6hI2h" ,
1638- year ,
1639- month ,
1640- day ,
1641- hour ,
1642- minute ,
1643- second ,
1644- nanoseconds ,
1645- tz_hour_offset ,
1646- tz_minute_offset ,
1647- )
1607+ def mock_add_output_converter (sql_type , converter_func ):
1608+ converter_calls .append ((sql_type , converter_func ))
16481609
1649- result = converter_func (binary_data )
1610+ mock_connection = Mock ()
1611+ mock_connection .add_output_converter = mock_add_output_converter
1612+ mock_pyodbc_connect .return_value = mock_connection
16501613
1651- expected_dt = datetime (2023 , 1 , 1 , 12 , 0 , 0 , 0 , timezone (timedelta (hours = - 8 , minutes = 0 )))
1652- assert result == expected_dt
1653- assert result .tzinfo == timezone (timedelta (hours = - 8 ))
1614+ config = MSSQLConnectionConfig (
1615+ host = "localhost" ,
1616+ driver = "pyodbc" , # DATETIMEOFFSET handling is pyodbc-specific
1617+ check_import = False ,
1618+ )
16541619
1620+ factory_with_kwargs = config ._connection_factory_with_kwargs
1621+ connection = factory_with_kwargs ()
16551622
1656- def test_mssql_cursor_init_no_add_output_converter ():
1657- """Test that cursor init gracefully handles connections without add_output_converter."""
1658- from unittest .mock import Mock
1623+ # Get the converter function
1624+ _ , converter_func = converter_calls [0 ]
1625+
1626+ # Test with negative timezone offset: 2023-01-01 12:00:00.0 -08:00
1627+ year , month , day = 2023 , 1 , 1
1628+ hour , minute , second = 12 , 0 , 0
1629+ nanoseconds = 0
1630+ tz_hour_offset , tz_minute_offset = - 8 , 0
1631+
1632+ binary_data = struct .pack (
1633+ "<6hI2h" ,
1634+ year ,
1635+ month ,
1636+ day ,
1637+ hour ,
1638+ minute ,
1639+ second ,
1640+ nanoseconds ,
1641+ tz_hour_offset ,
1642+ tz_minute_offset ,
1643+ )
16591644
1660- config = MSSQLConnectionConfig (
1661- host = "localhost" ,
1662- driver = "pyodbc" , # DATETIMEOFFSET handling is pyodbc-specific
1663- check_import = False ,
1664- )
1645+ result = converter_func (binary_data )
16651646
1666- cursor_init = config ._cursor_init
1667- assert cursor_init is not None
1647+ expected_dt = datetime (2023 , 1 , 1 , 12 , 0 , 0 , 0 , timezone (timedelta (hours = - 8 , minutes = 0 )))
1648+ assert result == expected_dt
1649+ assert result .tzinfo == timezone (timedelta (hours = - 8 ))
16681650
1669- # Create a mock cursor and connection without add_output_converter
1670- mock_connection = Mock ()
1671- mock_cursor = Mock ()
1672- mock_cursor .connection = mock_connection
16731651
1674- # Remove the add_output_converter attribute
1675- if hasattr ( mock_connection , " add_output_converter" ):
1676- delattr ( mock_connection , "add_output_converter" )
1652+ def test_mssql_pyodbc_connection_no_add_output_converter ():
1653+ """Test that connection gracefully handles pyodbc without add_output_converter."""
1654+ from unittest . mock import Mock , patch
16771655
1678- # This should not raise an exception
1679- cursor_init (mock_cursor )
1656+ with patch ("pyodbc.connect" ) as mock_pyodbc_connect :
1657+ # Create a mock connection without add_output_converter
1658+ mock_connection = Mock ()
1659+ # Remove the add_output_converter attribute
1660+ if hasattr (mock_connection , "add_output_converter" ):
1661+ delattr (mock_connection , "add_output_converter" )
1662+ mock_pyodbc_connect .return_value = mock_connection
16801663
1664+ config = MSSQLConnectionConfig (
1665+ host = "localhost" ,
1666+ driver = "pyodbc" , # DATETIMEOFFSET handling is pyodbc-specific
1667+ check_import = False ,
1668+ )
16811669
1682- def test_mssql_cursor_init_returns_callable_for_pyodbc ():
1683- """Test that _cursor_init returns a callable function for pyodbc driver."""
1684- config = MSSQLConnectionConfig (
1685- host = "localhost" ,
1686- driver = "pyodbc" ,
1687- check_import = False ,
1688- )
1670+ # This should not raise an exception
1671+ factory_with_kwargs = config ._connection_factory_with_kwargs
1672+ connection = factory_with_kwargs ()
16891673
1690- cursor_init = config ._cursor_init
1691- assert cursor_init is not None
1692- assert callable (cursor_init )
1674+ # Verify we get the connection back
1675+ assert connection is mock_connection
16931676
16941677
1695- def test_mssql_cursor_init_returns_none_for_pymssql ():
1696- """Test that _cursor_init returns None for pymssql driver."""
1678+ def test_mssql_no_cursor_init_for_pymssql ():
1679+ """Test that _cursor_init is not needed for pymssql driver."""
16971680 config = MSSQLConnectionConfig (
16981681 host = "localhost" ,
16991682 driver = "pymssql" ,
17001683 check_import = False ,
17011684 )
17021685
1703- cursor_init = config ._cursor_init
1704- assert cursor_init is None
1686+ # Since we moved output converter setup to connection level,
1687+ # there's no cursor init needed for any driver
1688+ assert not hasattr (config , "_cursor_init" ) or config ._cursor_init is None
0 commit comments