|
17 | 17 | AsyncRequestHandle, |
18 | 18 | CDN_URL, |
19 | 19 | Completed, |
| 20 | + DEFAULT_QUEUE_POLL_INTERVAL, |
20 | 21 | FAL_CDN_FALLBACK_URL, |
21 | 22 | FalClientHTTPError, |
22 | 23 | FalClientTimeoutError, |
@@ -1254,7 +1255,11 @@ def test_sync_handle_retries(monkeypatch): |
1254 | 1255 | ) |
1255 | 1256 |
|
1256 | 1257 | # Mock iter_events to skip waiting |
1257 | | - def _iter_events(self, with_logs: bool = False, interval: float = 0.1): |
| 1258 | + def _iter_events( |
| 1259 | + self, |
| 1260 | + with_logs: bool = False, |
| 1261 | + interval: float = DEFAULT_QUEUE_POLL_INTERVAL, |
| 1262 | + ): |
1258 | 1263 | return iter([Completed(logs=[], metrics={})]) |
1259 | 1264 |
|
1260 | 1265 | monkeypatch.setattr(SyncRequestHandle, "iter_events", _iter_events, raising=True) |
@@ -1336,7 +1341,11 @@ async def test_async_handle_retries(monkeypatch): |
1336 | 1341 | ) |
1337 | 1342 |
|
1338 | 1343 | # Mock iter_events to skip waiting |
1339 | | - async def _iter_events(self, with_logs: bool = False, interval: float = 0.1): |
| 1344 | + async def _iter_events( |
| 1345 | + self, |
| 1346 | + with_logs: bool = False, |
| 1347 | + interval: float = DEFAULT_QUEUE_POLL_INTERVAL, |
| 1348 | + ): |
1340 | 1349 | yield Completed(logs=[], metrics={}) |
1341 | 1350 |
|
1342 | 1351 | monkeypatch.setattr(AsyncRequestHandle, "iter_events", _iter_events, raising=True) |
@@ -1418,7 +1427,11 @@ def test_sync_get_does_not_retry_on_500_503(monkeypatch, status_code): |
1418 | 1427 | ) |
1419 | 1428 |
|
1420 | 1429 | # Mock iter_events to skip waiting |
1421 | | - def _iter_events(self, with_logs: bool = False, interval: float = 0.1): |
| 1430 | + def _iter_events( |
| 1431 | + self, |
| 1432 | + with_logs: bool = False, |
| 1433 | + interval: float = DEFAULT_QUEUE_POLL_INTERVAL, |
| 1434 | + ): |
1422 | 1435 | return iter([Completed(logs=[], metrics={})]) |
1423 | 1436 |
|
1424 | 1437 | monkeypatch.setattr(SyncRequestHandle, "iter_events", _iter_events, raising=True) |
@@ -1454,7 +1467,11 @@ async def test_async_get_does_not_retry_on_500_503(monkeypatch, status_code): |
1454 | 1467 | ) |
1455 | 1468 |
|
1456 | 1469 | # Mock iter_events to skip waiting |
1457 | | - async def _iter_events(self, with_logs: bool = False, interval: float = 0.1): |
| 1470 | + async def _iter_events( |
| 1471 | + self, |
| 1472 | + with_logs: bool = False, |
| 1473 | + interval: float = DEFAULT_QUEUE_POLL_INTERVAL, |
| 1474 | + ): |
1458 | 1475 | yield Completed(logs=[], metrics={}) |
1459 | 1476 |
|
1460 | 1477 | monkeypatch.setattr(AsyncRequestHandle, "iter_events", _iter_events, raising=True) |
@@ -1486,7 +1503,11 @@ def test_sync_handle_retries_ingress(monkeypatch): |
1486 | 1503 | ) |
1487 | 1504 |
|
1488 | 1505 | # Mock iter_events to skip waiting |
1489 | | - def _iter_events(self, with_logs: bool = False, interval: float = 0.1): |
| 1506 | + def _iter_events( |
| 1507 | + self, |
| 1508 | + with_logs: bool = False, |
| 1509 | + interval: float = DEFAULT_QUEUE_POLL_INTERVAL, |
| 1510 | + ): |
1490 | 1511 | return iter([Completed(logs=[], metrics={})]) |
1491 | 1512 |
|
1492 | 1513 | monkeypatch.setattr(SyncRequestHandle, "iter_events", _iter_events, raising=True) |
@@ -1574,7 +1595,11 @@ async def test_async_handle_retries_ingress(monkeypatch): |
1574 | 1595 | ) |
1575 | 1596 |
|
1576 | 1597 | # Mock iter_events to skip waiting |
1577 | | - async def _iter_events(self, with_logs: bool = False, interval: float = 0.1): |
| 1598 | + async def _iter_events( |
| 1599 | + self, |
| 1600 | + with_logs: bool = False, |
| 1601 | + interval: float = DEFAULT_QUEUE_POLL_INTERVAL, |
| 1602 | + ): |
1578 | 1603 | yield Completed(logs=[], metrics={}) |
1579 | 1604 |
|
1580 | 1605 | monkeypatch.setattr(AsyncRequestHandle, "iter_events", _iter_events, raising=True) |
@@ -1972,6 +1997,50 @@ def test_sync_client_subscribe_with_start_timeout(): |
1972 | 1997 | assert first_call_kwargs["headers"]["X-Fal-Request-Timeout"] == "90.0" |
1973 | 1998 |
|
1974 | 1999 |
|
| 2000 | +def test_sync_client_subscribe_with_interval(monkeypatch): |
| 2001 | + """Test that subscribe() passes interval through to request polling.""" |
| 2002 | + iter_event_calls = [] |
| 2003 | + |
| 2004 | + def _iter_events( |
| 2005 | + self, |
| 2006 | + *, |
| 2007 | + with_logs: bool = False, |
| 2008 | + interval: float = DEFAULT_QUEUE_POLL_INTERVAL, |
| 2009 | + ): |
| 2010 | + iter_event_calls.append((with_logs, interval)) |
| 2011 | + return iter([Completed(logs=[], metrics={})]) |
| 2012 | + |
| 2013 | + monkeypatch.setattr(SyncRequestHandle, "iter_events", _iter_events, raising=True) |
| 2014 | + |
| 2015 | + with patch("fal_client.client._maybe_retry_request") as mock_request: |
| 2016 | + submit_response = Mock() |
| 2017 | + submit_response.json.return_value = { |
| 2018 | + "request_id": "req-123", |
| 2019 | + "response_url": "http://response", |
| 2020 | + "status_url": "http://status", |
| 2021 | + "cancel_url": "http://cancel", |
| 2022 | + } |
| 2023 | + |
| 2024 | + result_response = Mock() |
| 2025 | + result_response.json.return_value = {"result": "done"} |
| 2026 | + |
| 2027 | + mock_request.side_effect = [submit_response, result_response] |
| 2028 | + |
| 2029 | + queue_updates = [] |
| 2030 | + client = SyncClient(key="test-key") |
| 2031 | + result = client.subscribe( |
| 2032 | + "test-app", |
| 2033 | + {"input": "data"}, |
| 2034 | + interval=0.5, |
| 2035 | + with_logs=True, |
| 2036 | + on_queue_update=queue_updates.append, |
| 2037 | + ) |
| 2038 | + |
| 2039 | + assert result == {"result": "done"} |
| 2040 | + assert len(queue_updates) == 1 |
| 2041 | + assert iter_event_calls == [(True, 0.5), (False, 0.5)] |
| 2042 | + |
| 2043 | + |
1975 | 2044 | @pytest.mark.asyncio |
1976 | 2045 | async def test_async_client_run_with_start_timeout(): |
1977 | 2046 | """Test that start_timeout adds X-Fal-Request-Timeout header in async run().""" |
@@ -2044,6 +2113,53 @@ async def test_async_client_subscribe_with_start_timeout(): |
2044 | 2113 | assert first_call_kwargs["headers"]["X-Fal-Request-Timeout"] == "90.0" |
2045 | 2114 |
|
2046 | 2115 |
|
| 2116 | +@pytest.mark.asyncio |
| 2117 | +async def test_async_client_subscribe_with_interval(monkeypatch): |
| 2118 | + """Test that async subscribe() passes interval through to request polling.""" |
| 2119 | + iter_event_calls = [] |
| 2120 | + |
| 2121 | + async def _iter_events( |
| 2122 | + self, |
| 2123 | + *, |
| 2124 | + with_logs: bool = False, |
| 2125 | + interval: float = DEFAULT_QUEUE_POLL_INTERVAL, |
| 2126 | + ): |
| 2127 | + iter_event_calls.append((with_logs, interval)) |
| 2128 | + yield Completed(logs=[], metrics={}) |
| 2129 | + |
| 2130 | + monkeypatch.setattr(AsyncRequestHandle, "iter_events", _iter_events, raising=True) |
| 2131 | + |
| 2132 | + with patch( |
| 2133 | + "fal_client.client._async_maybe_retry_request", new_callable=AsyncMock |
| 2134 | + ) as mock_request: |
| 2135 | + submit_response = Mock() |
| 2136 | + submit_response.json.return_value = { |
| 2137 | + "request_id": "req-789", |
| 2138 | + "response_url": "http://response", |
| 2139 | + "status_url": "http://status", |
| 2140 | + "cancel_url": "http://cancel", |
| 2141 | + } |
| 2142 | + |
| 2143 | + result_response = Mock() |
| 2144 | + result_response.json.return_value = {"result": "async_done"} |
| 2145 | + |
| 2146 | + mock_request.side_effect = [submit_response, result_response] |
| 2147 | + |
| 2148 | + queue_updates = [] |
| 2149 | + client = AsyncClient(key="test-key") |
| 2150 | + result = await client.subscribe( |
| 2151 | + "test-app", |
| 2152 | + {"input": "data"}, |
| 2153 | + interval=0.5, |
| 2154 | + with_logs=True, |
| 2155 | + on_queue_update=queue_updates.append, |
| 2156 | + ) |
| 2157 | + |
| 2158 | + assert result == {"result": "async_done"} |
| 2159 | + assert len(queue_updates) == 1 |
| 2160 | + assert iter_event_calls == [(True, 0.5), (False, 0.5)] |
| 2161 | + |
| 2162 | + |
2047 | 2163 | def test_sync_client_run_without_start_timeout_no_header(): |
2048 | 2164 | """Test that no timeout header is added when start_timeout is not specified.""" |
2049 | 2165 | with patch("fal_client.client._maybe_retry_request") as mock_request: |
|
0 commit comments