1- import json
2- from unittest .mock import Mock , mock_open , patch
3-
4- import pytest
5-
6- from zstash .globus import globus_block_wait , globus_transfer
7- from zstash .globus_utils import load_tokens
8-
91"""
102# Run all tests
113pytest test_globus_refresh.py -v
2012pytest test_globus_refresh.py -v -s
2113"""
2214
23- # Mock Token Expiration #######################################################
15+ import json
16+ from unittest .mock import Mock , mock_open , patch
2417
18+ import pytest
2519
20+ from zstash .globus import globus_block_wait , globus_transfer
21+ from zstash .globus_utils import load_tokens
22+
23+
24+ # Verifies that globus_transfer() calls endpoint_autoactivate for both endpoints
2625def test_globus_transfer_refreshes_tokens ():
2726 """Test that globus_transfer calls endpoint_autoactivate"""
2827 with patch ("zstash.globus.transfer_client" ) as mock_client , patch (
2928 "zstash.globus.local_endpoint" , "local-uuid"
30- ), patch ("zstash.globus.remote_endpoint" , "remote-uuid" ):
29+ ), patch ("zstash.globus.remote_endpoint" , "remote-uuid" ), patch (
30+ "zstash.globus.task_id" , None
31+ ), patch (
32+ "zstash.globus.transfer_data" , None
33+ ):
3134
3235 mock_client .endpoint_autoactivate = Mock ()
3336 mock_client .operation_ls = Mock (return_value = [])
@@ -46,6 +49,7 @@ def test_globus_transfer_refreshes_tokens():
4649 assert any ("if_expires_in=86400" in str (call ) for call in calls )
4750
4851
52+ # Confirms periodic refresh during long waits
4953def test_globus_block_wait_refreshes_periodically ():
5054 """Test that globus_block_wait refreshes tokens on each retry"""
5155 with patch ("zstash.globus.transfer_client" ) as mock_client , patch (
@@ -63,11 +67,11 @@ def test_globus_block_wait_refreshes_periodically():
6367 assert mock_client .endpoint_autoactivate .call_count >= 1
6468
6569
66- # Mock Time to Simulate Expiration ############################################
67-
68-
70+ # Validates expiration detection logic
6971def test_load_tokens_detects_expiration (caplog ):
7072 """Test that load_tokens detects soon-to-expire tokens"""
73+ import time as time_module
74+
7175 # Create a token file with expiration in 30 minutes
7276 current_time = 1000000
7377 expires_at = current_time + 1800 # 30 minutes from now
@@ -80,21 +84,20 @@ def test_load_tokens_detects_expiration(caplog):
8084 }
8185 }
8286
83- with patch ( "time. time" , return_value = current_time ), patch (
87+ with patch . object ( time_module , " time" , return_value = current_time ), patch (
8488 "builtins.open" , mock_open (read_data = json .dumps (tokens ))
8589 ), patch ("os.path.exists" , return_value = True ):
8690
87- result = load_tokens ()
91+ with caplog .at_level ("INFO" ):
92+ result = load_tokens ()
8893
8994 # Check that warning was logged
9095 assert "expiring soon" in caplog .text
9196 assert result == tokens
9297
9398
94- # Integration Test with Short Timeout #########################################
95-
96-
97- @pytest .mark .integration # Mark as integration test
99+ @pytest .mark .integration
100+ @pytest .mark .skip (reason = "Requires real Globus credentials" )
98101def test_refresh_mechanism_with_short_token ():
99102 """
100103 Integration test: Authenticate, manually expire token, verify refresh works.
@@ -105,8 +108,9 @@ def test_refresh_mechanism_with_short_token():
105108 # Set up with real credentials (skip if no credentials available)
106109 pytest .importorskip ("globus_sdk" )
107110
108- endpoint1 = "your-test-endpoint-1"
109- endpoint2 = "your-test-endpoint-2"
111+ # Use actual endpoint UUIDs if running this test
112+ endpoint1 = "your-actual-endpoint-uuid-1"
113+ endpoint2 = "your-actual-endpoint-uuid-2"
110114
111115 transfer_client = get_transfer_client_with_auth ([endpoint1 , endpoint2 ])
112116
@@ -120,9 +124,7 @@ def test_refresh_mechanism_with_short_token():
120124 assert result is not None
121125
122126
123- # Stress Test with Rapid Calls ###############################################
124-
125-
127+ # Ensures no issues with many rapid refresh calls
126128def test_multiple_rapid_refreshes ():
127129 """Test that calling refresh many times doesn't break"""
128130 with patch ("zstash.globus.transfer_client" ) as mock_client :
@@ -136,15 +138,19 @@ def test_multiple_rapid_refreshes():
136138 assert mock_client .endpoint_autoactivate .call_count == 100
137139
138140
139- # End-to-End Test with Short Transfer #########################################
140-
141-
141+ # End-to-end test with mocked transfer
142142def test_small_transfer_with_refresh_enabled ():
143143 """
144144 Functional test: Transfer a small file and verify refresh calls were made.
145- Uses real Globus but completes in seconds.
146145 """
147- with patch ("zstash.globus.transfer_client" ) as mock_client :
146+ with patch ("zstash.globus.transfer_client" ) as mock_client , patch (
147+ "zstash.globus.local_endpoint" , "local-uuid"
148+ ), patch ("zstash.globus.remote_endpoint" , "remote-uuid" ), patch (
149+ "zstash.globus.task_id" , None
150+ ), patch (
151+ "zstash.globus.transfer_data" , None
152+ ):
153+
148154 # Set up mock to track calls
149155 mock_client .endpoint_autoactivate = Mock ()
150156 mock_client .submit_transfer = Mock (return_value = {"task_id" : "test-123" })
@@ -158,56 +164,89 @@ def test_small_transfer_with_refresh_enabled():
158164 assert mock_client .endpoint_autoactivate .called
159165
160166
161- # Parametrized Test for Different Scenarios ###################################
162-
163-
167+ # Tests blocking PUT mode
168+ # Tests non-blocking PUT mode
164169@pytest .mark .parametrize (
165170 "transfer_type,non_blocking" ,
166171 [
167172 ("put" , False ),
168173 ("put" , True ),
169- ("get" , False ),
170174 ],
171175)
172176def test_globus_transfer_refreshes_in_all_modes (transfer_type , non_blocking ):
173177 """Test that token refresh works for all transfer types"""
174178 with patch ("zstash.globus.transfer_client" ) as mock_client , patch (
175179 "zstash.globus.local_endpoint" , "local-uuid"
176180 ), patch ("zstash.globus.remote_endpoint" , "remote-uuid" ), patch (
181+ "zstash.globus.task_id" , None
182+ ), patch (
183+ "zstash.globus.transfer_data" , None
184+ ), patch (
177185 "zstash.globus.archive_directory_listing" , [{"name" : "file.tar" }]
178186 ):
179187
180188 mock_client .endpoint_autoactivate = Mock ()
181189 mock_client .operation_ls = Mock (return_value = [{"name" : "file.tar" }])
182- mock_client .submit_transfer = Mock (return_value = {"task_id" : "test-123" })
190+ # Need to return a complete task dict to avoid KeyError
191+ mock_client .submit_transfer = Mock (
192+ return_value = {
193+ "task_id" : "test-123" ,
194+ "source_endpoint_id" : "src-uuid" ,
195+ "destination_endpoint_id" : "dst-uuid" ,
196+ "label" : "test transfer" ,
197+ }
198+ )
183199 mock_client .task_wait = Mock (return_value = True )
184- mock_client .get_task = Mock (return_value = {"status" : "SUCCEEDED" })
200+ mock_client .get_task = Mock (
201+ return_value = {
202+ "status" : "SUCCEEDED" ,
203+ "source_endpoint_id" : "src-uuid" ,
204+ "destination_endpoint_id" : "dst-uuid" ,
205+ "label" : "test transfer" ,
206+ }
207+ )
185208
186209 globus_transfer ("remote-ep" , "/path" , "file.tar" , transfer_type , non_blocking )
187210
188211 # Verify refresh was called
189212 assert mock_client .endpoint_autoactivate .called
190213
191214
192- # Fixture for Common Setup ####################################################
193-
194-
195215@pytest .fixture
196216def mock_globus_client ():
197217 """Fixture to set up a mock Globus client"""
198218 with patch ("zstash.globus.transfer_client" ) as mock_client , patch (
199219 "zstash.globus.local_endpoint" , "local-uuid"
200- ), patch ("zstash.globus.remote_endpoint" , "remote-uuid" ):
220+ ), patch ("zstash.globus.remote_endpoint" , "remote-uuid" ), patch (
221+ "zstash.globus.task_id" , None
222+ ), patch (
223+ "zstash.globus.transfer_data" , None
224+ ):
201225
202226 mock_client .endpoint_autoactivate = Mock ()
203227 mock_client .operation_ls = Mock (return_value = [])
204- mock_client .submit_transfer = Mock (return_value = {"task_id" : "test-123" })
228+ mock_client .submit_transfer = Mock (
229+ return_value = {
230+ "task_id" : "test-123" ,
231+ "source_endpoint_id" : "src-uuid" ,
232+ "destination_endpoint_id" : "dst-uuid" ,
233+ "label" : "test transfer" ,
234+ }
235+ )
205236 mock_client .task_wait = Mock (return_value = True )
206- mock_client .get_task = Mock (return_value = {"status" : "SUCCEEDED" })
237+ mock_client .get_task = Mock (
238+ return_value = {
239+ "status" : "SUCCEEDED" ,
240+ "source_endpoint_id" : "src-uuid" ,
241+ "destination_endpoint_id" : "dst-uuid" ,
242+ "label" : "test transfer" ,
243+ }
244+ )
207245
208246 yield mock_client
209247
210248
249+ # Demonstrates reusable fixture pattern
211250def test_with_fixture (mock_globus_client ):
212251 """Test using the fixture"""
213252 globus_transfer ("remote-ep" , "/path" , "file.tar" , "put" , False )
0 commit comments