This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new adfecd99f97 Fix flaky databricks token-expiry test by freezing 
time_machine (#66660)
adfecd99f97 is described below

commit adfecd99f97819607ee80b81139bd355f150b3fa
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sun May 10 19:36:49 2026 +0200

    Fix flaky databricks token-expiry test by freezing time_machine (#66660)
    
    The Compat 3.0.6 ARM scheduled run failed on
    test_get_federated_token_with_projected_volume with an off-by-one
    on the expected token expiry timestamp:
    
        assert hook.oauth_tokens[resource]["expires_on"] == expiry_date
        E   assert 1752325201 == 1752325200
    
    The test sets up a static expected via
        int((datetime(2025, 7, 12, 12, 0, 0) + timedelta(minutes=60))
           .timestamp())
    while the production code under test computes
        int(time.time() + jsn["expires_in"])
    
    `@time_machine.travel("2025-07-12 12:00:00")` defaults to
    `tick=True`, so the patched clock keeps advancing during the test.
    By the time `time.time()` is sampled the float has crept past the
    travel anchor by enough that `int(...)` rounds up by one second,
    and the assertion blows up. The other 19 `time_machine.travel(...)`
    sites in the same file have the same latent race — none of them
    have caught it yet, but each is one slow CI run away from the same
    flake.
    
    Add `tick=False` to every `time_machine.travel(...)` decorator in
    the file so the clock is frozen at the anchor for the whole test
    body. No production-code change.
    
    Generated-by: Claude Code (Opus 4.7)
---
 .../unit/databricks/hooks/test_databricks_base.py  | 42 +++++++++++-----------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git 
a/providers/databricks/tests/unit/databricks/hooks/test_databricks_base.py 
b/providers/databricks/tests/unit/databricks/hooks/test_databricks_base.py
index 088c3fc27bc..090a3e34c78 100644
--- a/providers/databricks/tests/unit/databricks/hooks/test_databricks_base.py
+++ b/providers/databricks/tests/unit/databricks/hooks/test_databricks_base.py
@@ -111,7 +111,7 @@ class TestBaseDatabricksHook:
         assert BaseDatabricksHook._parse_host(input_url) == expected_host
 
     @mock.patch("requests.post")
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_get_sp_token(self, mock_post):
         mock_response = mock.Mock()
         expiry_date = int((datetime(2025, 7, 12, 12, 0, 0) + 
timedelta(minutes=60)).timestamp())
@@ -187,7 +187,7 @@ class TestBaseDatabricksHook:
             hook._get_sp_token(resource)
 
     @pytest.mark.asyncio
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     @mock.patch("aiohttp.ClientSession.post")
     async def test_a_get_sp_token(self, mock_post):
         expiry_date = int((datetime(2025, 7, 12, 12, 0, 0) + 
timedelta(minutes=60)).timestamp())
@@ -286,21 +286,21 @@ class TestBaseDatabricksHook:
             assert token == "cached_token"
             mock_post.assert_not_called()
 
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_valid_token_not_expired(self):
         expiry_data = int((datetime(2025, 7, 12, 12, 0, 0) + 
timedelta(minutes=60)).timestamp())
         token = {"access_token": "valid_token", "token_type": "Bearer", 
"expires_on": expiry_data}
         hook = BaseDatabricksHook()
         assert hook._is_oauth_token_valid(token)
 
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_valid_token_expired(self):
         expiry_data = int((datetime(2025, 7, 12, 12, 0, 0) - 
timedelta(minutes=60)).timestamp())
         token = {"access_token": "valid_token", "token_type": "Bearer", 
"expires_on": expiry_data}
         hook = BaseDatabricksHook()
         assert not hook._is_oauth_token_valid(token)
 
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_token_expires_within_lead_time(self):
         expires_on = int((datetime(2025, 7, 12, 12, 0, 0) + 
timedelta(seconds=60)).timestamp())
         token = {
@@ -311,7 +311,7 @@ class TestBaseDatabricksHook:
         hook = BaseDatabricksHook()
         assert hook._is_oauth_token_valid(token) is False
 
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_missing_access_token_raises_exception(self):
         expiry_date = int((datetime(2025, 7, 12, 12, 0, 0) + 
timedelta(minutes=60)).timestamp())
         token = {"token_type": "Bearer", "expires_on": expiry_date}
@@ -319,7 +319,7 @@ class TestBaseDatabricksHook:
         with pytest.raises(AirflowException, match="Can't get necessary data 
from OAuth token"):
             hook._is_oauth_token_valid(token)
 
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_wrong_token_type_raises_exception(self):
         expiry_date = int((datetime(2025, 7, 12, 12, 0, 0) + 
timedelta(minutes=60)).timestamp())
         token = {"access_token": "valid_token", "token_type": "Basic", 
"expires_on": expiry_date}
@@ -327,7 +327,7 @@ class TestBaseDatabricksHook:
         with pytest.raises(AirflowException, match="Can't get necessary data 
from OAuth token"):
             hook._is_oauth_token_valid(token)
 
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_missing_token_type_raises_exception(self):
         expiry_date = int((datetime(2025, 7, 12, 12, 0, 0) + 
timedelta(minutes=60)).timestamp())
         token = {"access_token": "valid_token", "expires_on": expiry_date}
@@ -341,7 +341,7 @@ class TestBaseDatabricksHook:
         with pytest.raises(AirflowException, match="Can't get necessary data 
from OAuth token"):
             hook._is_oauth_token_valid(token)
 
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_custom_time_key(self):
         expiry_data = int((datetime(2025, 7, 12, 12, 0, 0) + 
timedelta(minutes=60)).timestamp())
         token = {"access_token": "valid_token", "token_type": "Bearer", 
"custom_expires": expiry_data}
@@ -354,14 +354,14 @@ class TestBaseDatabricksHook:
         with pytest.raises(AirflowException, match="Can't get necessary data 
from OAuth token"):
             hook._is_oauth_token_valid(token)
 
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_string_expiration_time(self):
         expiry_data = int((datetime(2025, 7, 12, 12, 0, 0) + 
timedelta(minutes=60)).timestamp())
         token = {"access_token": "valid_token", "token_type": "Bearer", 
"expires_on": expiry_data}
         hook = BaseDatabricksHook()
         assert hook._is_oauth_token_valid(token)
 
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_exact_boundary_conditions(self):
         expiry_data = int((datetime(2025, 7, 12, 12, 0, 0)).timestamp())
         hook = BaseDatabricksHook()
@@ -804,7 +804,7 @@ class TestBaseDatabricksHook:
         assert hook._get_error_code(exception) == "INVALID_REQUEST"
 
     @mock.patch("requests.get")
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_check_azure_metadata_service_normal(self, mock_get):
         travel_time = int(datetime(2025, 7, 12, 12, 0, 0).timestamp())
         hook = BaseDatabricksHook()
@@ -817,7 +817,7 @@ class TestBaseDatabricksHook:
         assert int(hook._metadata_expiry) == travel_time + hook._metadata_ttl
 
     @mock.patch("requests.get")
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_check_azure_metadata_service_cached(self, mock_get):
         travel_time = int(datetime(2025, 7, 12, 12, 0, 0).timestamp())
         hook = BaseDatabricksHook()
@@ -881,7 +881,7 @@ class TestBaseDatabricksHook:
 
     @pytest.mark.asyncio
     @mock.patch("aiohttp.ClientSession.get")
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     async def test_a_check_azure_metadata_service_cached(self, mock_get):
         travel_time = int(datetime(2025, 7, 12, 12, 0, 0).timestamp())
         hook = BaseDatabricksHook()
@@ -953,7 +953,9 @@ class TestBaseDatabricksHook:
         ],
     )
     @mock.patch("requests.post")
-    @time_machine.travel("2025-07-12 12:00:00")  # mock current timestamp for 
token expiry calculation
+    @time_machine.travel(
+        "2025-07-12 12:00:00", tick=False
+    )  # mock current timestamp for token expiry calculation
     def test_get_federated_token(self, mock_post, mock_file):
         """Test Kubernetes OIDC token federation flow."""
         # Mock K8s TokenRequest API response
@@ -1023,7 +1025,7 @@ class TestBaseDatabricksHook:
         assert db_call_args[1]["data"]["scope"] == "all-apis"
         assert db_call_args[1]["data"]["client_id"] == "test-client-id"
 
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_get_federated_token_cached_valid(self):
         """Test that cached valid token is returned without fetching new 
one."""
         mock_conn = mock.Mock()
@@ -1288,7 +1290,7 @@ class TestBaseDatabricksHook:
 
     @mock.patch("builtins.open", 
mock.mock_open(read_data="projected_k8s_jwt_token"))
     @mock.patch("requests.post")
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     def test_get_federated_token_with_projected_volume(self, mock_post):
         """Test end-to-end federated token flow using projected volume."""
         # Mock Databricks token exchange response
@@ -1414,7 +1416,7 @@ class TestBaseDatabricksHook:
 
     @pytest.mark.asyncio
     @mock.patch("aiohttp.ClientSession.post")
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     @mock.patch("ssl.create_default_context")
     async def test_a_get_federated_token(self, _mock_ssl_ctx, mock_post):
         """Test async version of federated token exchange."""
@@ -1476,7 +1478,7 @@ class TestBaseDatabricksHook:
         assert mock_post.call_count == 2
 
     @pytest.mark.asyncio
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     async def test_a_get_federated_token_cached_valid(self):
         """Test that async version returns cached valid token without fetching 
new one."""
         mock_conn = mock.Mock()
@@ -1753,7 +1755,7 @@ class TestBaseDatabricksHook:
         assert call_kwargs["ssl"] is fake_ctx
 
     @pytest.mark.asyncio
-    @time_machine.travel("2025-07-12 12:00:00")
+    @time_machine.travel("2025-07-12 12:00:00", tick=False)
     async def test_a_get_federated_token_with_projected_volume(self):
         """Test async end-to-end federated token flow using projected 
volume."""
         # Mock Databricks token exchange response

Reply via email to