@@ -1447,3 +1447,149 @@ def delete_password(self, servicename, username):
14471447 return None
14481448
14491449 os .remove (file_path )
1450+
1451+
1452+ @mock .patch ("trino.client.TrinoRequest.http" )
1453+ def test_trinoquery_heartbeat_success (mock_requests , sample_post_response_data , sample_get_response_data ):
1454+ """Test that heartbeat is sent periodically and does not stop on success."""
1455+ head_call_count = 0
1456+ def fake_head (url , timeout = 10 ):
1457+ nonlocal head_call_count
1458+ head_call_count += 1
1459+ class Resp :
1460+ status_code = 200
1461+ return Resp ()
1462+ mock_requests .head .side_effect = fake_head
1463+ mock_requests .Response .return_value .json .return_value = sample_post_response_data
1464+ mock_requests .get .return_value .json .return_value = sample_get_response_data
1465+ mock_requests .post .return_value .json .return_value = sample_post_response_data
1466+ req = TrinoRequest (
1467+ host = "coordinator" ,
1468+ port = 8080 ,
1469+ client_session = ClientSession (user = "test" ),
1470+ http_scheme = "http" ,
1471+ )
1472+ query = TrinoQuery (request = req , query = "SELECT 1" , heartbeat_interval = 0.1 )
1473+ def finish_query (* args , ** kwargs ):
1474+ query ._finished = True
1475+ return []
1476+ query .fetch = finish_query
1477+ query ._next_uri = "http://coordinator/v1/statement/next"
1478+ query ._row_mapper = mock .Mock (map = lambda x : [])
1479+ query ._start_heartbeat ()
1480+ time .sleep (0.3 )
1481+ query ._stop_heartbeat ()
1482+ assert head_call_count >= 2
1483+
1484+ @mock .patch ("trino.client.TrinoRequest.http" )
1485+ def test_trinoquery_heartbeat_failure_stops (mock_requests , sample_post_response_data , sample_get_response_data ):
1486+ """Test that heartbeat stops after 3 consecutive failures."""
1487+ def fake_head (url , timeout = 10 ):
1488+ class Resp :
1489+ status_code = 500
1490+ return Resp ()
1491+ mock_requests .head .side_effect = fake_head
1492+ mock_requests .Response .return_value .json .return_value = sample_post_response_data
1493+ mock_requests .get .return_value .json .return_value = sample_get_response_data
1494+ mock_requests .post .return_value .json .return_value = sample_post_response_data
1495+ req = TrinoRequest (
1496+ host = "coordinator" ,
1497+ port = 8080 ,
1498+ client_session = ClientSession (user = "test" ),
1499+ http_scheme = "http" ,
1500+ )
1501+ query = TrinoQuery (request = req , query = "SELECT 1" , heartbeat_interval = 0.05 )
1502+ query ._next_uri = "http://coordinator/v1/statement/next"
1503+ query ._row_mapper = mock .Mock (map = lambda x : [])
1504+ query ._start_heartbeat ()
1505+ time .sleep (0.3 )
1506+ assert not query ._heartbeat_enabled
1507+ query ._stop_heartbeat ()
1508+
1509+ @mock .patch ("trino.client.TrinoRequest.http" )
1510+ def test_trinoquery_heartbeat_404_405_stops (mock_requests , sample_post_response_data , sample_get_response_data ):
1511+ """Test that heartbeat stops if server returns 404 or 405."""
1512+ for code in (404 , 405 ):
1513+ def fake_head (url , timeout = 10 , code = code ):
1514+ class Resp :
1515+ status_code = code
1516+ return Resp ()
1517+ mock_requests .head .side_effect = fake_head
1518+ mock_requests .Response .return_value .json .return_value = sample_post_response_data
1519+ mock_requests .get .return_value .json .return_value = sample_get_response_data
1520+ mock_requests .post .return_value .json .return_value = sample_post_response_data
1521+ req = TrinoRequest (
1522+ host = "coordinator" ,
1523+ port = 8080 ,
1524+ client_session = ClientSession (user = "test" ),
1525+ http_scheme = "http" ,
1526+ )
1527+ query = TrinoQuery (request = req , query = "SELECT 1" , heartbeat_interval = 0.05 )
1528+ query ._next_uri = "http://coordinator/v1/statement/next"
1529+ query ._row_mapper = mock .Mock (map = lambda x : [])
1530+ query ._start_heartbeat ()
1531+ time .sleep (0.2 )
1532+ assert not query ._heartbeat_enabled
1533+ query ._stop_heartbeat ()
1534+
1535+ @mock .patch ("trino.client.TrinoRequest.http" )
1536+ def test_trinoquery_heartbeat_stops_on_finish (mock_requests , sample_post_response_data , sample_get_response_data ):
1537+ """Test that heartbeat stops when the query is finished."""
1538+ head_call_count = 0
1539+ def fake_head (url , timeout = 10 ):
1540+ nonlocal head_call_count
1541+ head_call_count += 1
1542+ class Resp :
1543+ status_code = 200
1544+ return Resp ()
1545+ mock_requests .head .side_effect = fake_head
1546+ mock_requests .Response .return_value .json .return_value = sample_post_response_data
1547+ mock_requests .get .return_value .json .return_value = sample_get_response_data
1548+ mock_requests .post .return_value .json .return_value = sample_post_response_data
1549+ req = TrinoRequest (
1550+ host = "coordinator" ,
1551+ port = 8080 ,
1552+ client_session = ClientSession (user = "test" ),
1553+ http_scheme = "http" ,
1554+ )
1555+ query = TrinoQuery (request = req , query = "SELECT 1" , heartbeat_interval = 0.05 )
1556+ query ._next_uri = "http://coordinator/v1/statement/next"
1557+ query ._row_mapper = mock .Mock (map = lambda x : [])
1558+ query ._start_heartbeat ()
1559+ time .sleep (0.1 )
1560+ query ._finished = True
1561+ time .sleep (0.1 )
1562+ query ._stop_heartbeat ()
1563+ # Heartbeat should have stopped after query finished
1564+ assert head_call_count >= 1
1565+
1566+ @mock .patch ("trino.client.TrinoRequest.http" )
1567+ def test_trinoquery_heartbeat_stops_on_cancel (mock_requests , sample_post_response_data , sample_get_response_data ):
1568+ """Test that heartbeat stops when the query is cancelled."""
1569+ head_call_count = 0
1570+ def fake_head (url , timeout = 10 ):
1571+ nonlocal head_call_count
1572+ head_call_count += 1
1573+ class Resp :
1574+ status_code = 200
1575+ return Resp ()
1576+ mock_requests .head .side_effect = fake_head
1577+ mock_requests .Response .return_value .json .return_value = sample_post_response_data
1578+ mock_requests .get .return_value .json .return_value = sample_get_response_data
1579+ mock_requests .post .return_value .json .return_value = sample_post_response_data
1580+ req = TrinoRequest (
1581+ host = "coordinator" ,
1582+ port = 8080 ,
1583+ client_session = ClientSession (user = "test" ),
1584+ http_scheme = "http" ,
1585+ )
1586+ query = TrinoQuery (request = req , query = "SELECT 1" , heartbeat_interval = 0.05 )
1587+ query ._next_uri = "http://coordinator/v1/statement/next"
1588+ query ._row_mapper = mock .Mock (map = lambda x : [])
1589+ query ._start_heartbeat ()
1590+ time .sleep (0.1 )
1591+ query ._cancelled = True
1592+ time .sleep (0.1 )
1593+ query ._stop_heartbeat ()
1594+ # Heartbeat should have stopped after query cancelled
1595+ assert head_call_count >= 1
0 commit comments