@@ -1447,3 +1447,149 @@ def delete_password(self, servicename, username):
1447
1447
return None
1448
1448
1449
1449
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