Skip to content

Commit 5fe7403

Browse files
Remove sleep from tests
1 parent b97d63b commit 5fe7403

9 files changed

Lines changed: 104 additions & 39 deletions

File tree

noxfile.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,19 @@
33
nox.options.stop_on_first_error = True
44
nox.options.reuse_existing_virtualenvs = False
55

6-
nox.options.sessions = ["test", "lint"]
6+
# Default sessions - all tests, but not packaging
7+
nox.options.sessions = [
8+
"black_lint",
9+
"pytest",
10+
"semgrep_src",
11+
"mypy",
12+
"pyflakes_src",
13+
"pyflakes_examples",
14+
"pyflakes_tests",
15+
"pylint_src",
16+
"pylint_examples",
17+
"pylint_tests",
18+
]
719

820
_DEFAULT_PYTHON = "3.13"
921
_ALL_PYTHON = ["3.9", "3.10", "3.11", "3.12", "3.13"]

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ examples = [
5555
tests = [
5656
"black",
5757
"coverage[toml]",
58+
"freezegun",
5859
"mypy",
5960
"nox",
6061
"pyflakes",

src/planet_auth/oidc/api_clients/token_api_client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def _polling_checked_call(self, token_params, timeout, poll_interval, auth_enric
7575
else:
7676
raise oe
7777
if sleep_counter < timeout:
78+
# FIXME: a weakness here is we are not counting the time spent in the HTTP call against the timeout.
7879
time.sleep(poll_interval)
7980
sleep_counter += poll_interval
8081
else:

tests/test_planet_auth/unit/auth/auth_clients/oidc/api_clients/test_token_api_client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from planet_auth.oidc.api_clients.token_api_client import TokenApiClient, TokenApiException
2525
from planet_auth.oidc.util import create_pkce_challenge_verifier_pair
2626

27+
from tests.test_planet_auth.util import mock_sleep_skip
28+
2729
TEST_API_ENDPOINT = "https://blackhole.unittest.planet.com/api"
2830
TEST_CLIENT_ID = "__test_client_id__"
2931
TEST_REDIRECT_URI = "__test_redirect_uri__"
@@ -224,6 +226,7 @@ def test_token_from_device_code(self, mock_post):
224226
)
225227
self.assertEqual(API_RESPONSE_VALID, token_response)
226228

229+
@mock.patch("time.sleep", mock_sleep_skip)
227230
@mock.patch("requests.sessions.Session.post", side_effect=mocked_response_pending_then_valid)
228231
def test_token_from_device_code_pending_then_ok(self, mock_post):
229232
under_test = TokenApiClient(token_uri=TEST_API_ENDPOINT)
@@ -237,8 +240,11 @@ def test_token_from_device_code_pending_then_ok(self, mock_post):
237240
self.assertEqual(API_RESPONSE_VALID, token_response)
238241
self.assertEqual(mock_post.call_count, 2)
239242

243+
@mock.patch("time.sleep", mock_sleep_skip)
240244
@mock.patch("requests.sessions.Session.post", side_effect=mocked_response_slow_down)
241245
def test_token_from_device_code_slow_down(self, mock_post):
246+
# FIXME: we get away with only mocking sleep() and not the
247+
# the clock because the timout implementation is a little weak.
242248
under_test = TokenApiClient(token_uri=TEST_API_ENDPOINT)
243249
with self.assertRaises(TokenApiException):
244250
under_test.poll_for_token_from_device_code(
@@ -255,6 +261,7 @@ def test_token_from_device_code_slow_down(self, mock_post):
255261
self.assertEqual(mock_post.call_count, 3)
256262
# TODO check wall-clock time?
257263

264+
@mock.patch("time.sleep", mock_sleep_skip)
258265
@mock.patch("requests.sessions.Session.post", side_effect=mocked_response_pending)
259266
def test_token_from_device_code_client_provided_timeout(self, mock_post):
260267
under_test = TokenApiClient(token_uri=TEST_API_ENDPOINT)

tests/test_planet_auth/unit/auth/auth_clients/oidc/auth_clients/test_device_code.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
DeviceCodeAuthClientException,
2424
)
2525
from tests.test_planet_auth.unit.auth.util import StubOidcClientConfig, StubOidcAuthClient
26-
from tests.test_planet_auth.util import tdata_resource_file_path
26+
from tests.test_planet_auth.util import tdata_resource_file_path, mock_sleep_skip
2727

2828
TEST_AUTH_SERVER = "https://blackhole.unittest.planet.com/fake_authserver"
2929
TEST_CLIENT_ID = "fake_test_client_id"
@@ -86,6 +86,7 @@ def setUp(self):
8686
)
8787
)
8888

89+
@mock.patch("time.sleep", mock_sleep_skip)
8990
@mock.patch(
9091
"planet_auth.oidc.api_clients.token_api_client.TokenApiClient.poll_for_token_from_device_code",
9192
mocked_tokenapi_from_device_code,
@@ -108,6 +109,7 @@ def test_login_1(self):
108109
self.assertIsInstance(test_result, FileBackedOidcCredential)
109110
# self.assertEqual(MOCK_TOKEN, test_result.data())
110111

112+
@mock.patch("time.sleep", mock_sleep_skip)
111113
@mock.patch(
112114
"planet_auth.oidc.api_clients.token_api_client.TokenApiClient.poll_for_token_from_device_code",
113115
mocked_tokenapi_from_device_code,

tests/test_planet_auth/unit/auth/auth_clients/oidc/test_oidc_request_authenticator.py

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import pathlib
1818
import tempfile
19-
import time
19+
import freezegun
2020
import unittest
2121

2222
from planet_auth.oidc.oidc_credential import FileBackedOidcCredential
@@ -164,7 +164,8 @@ def mock_api_call(under_test):
164164
# pre_request_hook()
165165
under_test.pre_request_hook()
166166

167-
def test_happy_path_1(self):
167+
@freezegun.freeze_time(as_kwarg="frozen_time")
168+
def test_happy_path_1(self, frozen_time):
168169
# The first API call should trigger a token load from disk.
169170
# If the token is current, the auth client should be untouched.
170171
under_test = self.under_test_happy_path()
@@ -185,13 +186,14 @@ def test_happy_path_1(self):
185186

186187
# When the token reaches the 3/4 life, the authenticator should
187188
# attempt a token refresh
188-
time.sleep(((3 * TEST_TOKEN_TTL) / 4) + 2)
189+
frozen_time.tick(((3 * TEST_TOKEN_TTL) / 4) + 2)
189190
self.mock_api_call(under_test)
190191
self.assertEqual(1, under_test._auth_client.refresh.call_count)
191192
access_token_t3 = under_test._credential.access_token()
192193
self.assertNotEqual(access_token_t1, access_token_t3)
193194

194-
def test_happy_path_1_in_memory(self):
195+
@freezegun.freeze_time(as_kwarg="frozen_time")
196+
def test_happy_path_1_in_memory(self, frozen_time):
195197
# The first API call should trigger a token load from disk.
196198
# If the token is current, the auth client should be untouched.
197199
under_test = self.under_test_happy_path_in_memory()
@@ -212,24 +214,26 @@ def test_happy_path_1_in_memory(self):
212214

213215
# When the token reaches the 3/4 life, the authenticator should
214216
# attempt a token refresh
215-
time.sleep(((3 * TEST_TOKEN_TTL) / 4) + 2)
217+
frozen_time.tick(((3 * TEST_TOKEN_TTL) / 4) + 2)
216218
self.mock_api_call(under_test)
217219
self.assertEqual(1, under_test._auth_client.refresh.call_count)
218220
access_token_t3 = under_test._credential.access_token()
219221
self.assertNotEqual(access_token_t1, access_token_t3)
220222

221-
def test_happy_path_2(self):
223+
@freezegun.freeze_time(as_kwarg="frozen_time")
224+
def test_happy_path_2(self, frozen_time):
222225
# The first API call should trigger a token load.
223226
# If the token is past the refresh time, a token refresh should be
224227
# attempted before the first use
225228
under_test = self.under_test_happy_path()
226229

227-
time.sleep(TEST_TOKEN_TTL + 2)
230+
frozen_time.tick(TEST_TOKEN_TTL + 2)
228231
self.mock_api_call(under_test)
229232
self.assertIsNotNone(under_test._credential.data())
230233
self.assertEqual(1, under_test._auth_client.refresh.call_count)
231234

232-
def test_refresh_fails(self):
235+
@freezegun.freeze_time(as_kwarg="frozen_time")
236+
def test_refresh_fails(self, frozen_time):
233237
# Refresh could fail. If it does, this should be buried and we
234238
# should try to carry on with the token we have.
235239
# (this is why we refresh before expiry, so transient failures
@@ -242,15 +246,16 @@ def test_refresh_fails(self):
242246
self.assertEqual(0, under_test._auth_client.refresh.call_count)
243247
access_token_t1 = under_test._credential.access_token()
244248

245-
time.sleep(TEST_TOKEN_TTL + 2)
249+
frozen_time.tick(TEST_TOKEN_TTL + 2)
246250

247251
self.mock_api_call(under_test)
248252
self.assertEqual(1, under_test._auth_client.refresh.call_count)
249253
access_token_t2 = under_test._credential.access_token()
250254

251255
self.assertEqual(access_token_t1, access_token_t2)
252256

253-
def test_no_refresh_token(self):
257+
@freezegun.freeze_time(as_kwarg="frozen_time")
258+
def test_no_refresh_token(self, frozen_time):
254259
# if we have no refresh token, what happens when we expect to
255260
# refresh? It's not expected that a user use the refreshing
256261
# authenticator when not using a refresh token (there is another
@@ -265,22 +270,23 @@ def test_no_refresh_token(self):
265270
access_token_t1 = under_test._credential.access_token()
266271
self.assertEqual(0, under_test._auth_client.refresh.call_count)
267272

268-
time.sleep(TEST_TOKEN_TTL + 2)
273+
frozen_time.tick(TEST_TOKEN_TTL + 2)
269274

270275
self.mock_api_call(under_test)
271276
self.assertEqual(1, under_test._auth_client.refresh.call_count)
272277
access_token_t2 = under_test._credential.access_token()
273278
self.assertEqual(access_token_t1, access_token_t2)
274279

275-
def test_no_auth_client(self):
280+
@freezegun.freeze_time(as_kwarg="frozen_time")
281+
def test_no_auth_client(self, frozen_time):
276282
# when no auth client is provided, just authenticate with what we
277283
# have.
278284
under_test = self.under_test_no_auth_client()
279285

280286
self.mock_api_call(under_test)
281287
access_token_t1 = under_test._credential.access_token()
282288

283-
time.sleep(TEST_TOKEN_TTL + 2)
289+
frozen_time.tick(TEST_TOKEN_TTL + 2)
284290

285291
self.mock_api_call(under_test)
286292
access_token_t2 = under_test._credential.access_token()
@@ -304,7 +310,8 @@ def test_no_access_token(self):
304310
self.mock_api_call(under_test)
305311
self.assertEqual(1, under_test._auth_client.refresh.call_count)
306312

307-
def test_out_of_band_update_1(self):
313+
@freezegun.freeze_time(as_kwarg="frozen_time")
314+
def test_out_of_band_update_1(self, frozen_time):
308315
# Out of band credential update. We expect the refresher to reload
309316
# the credential file without attempting a refresh.
310317
under_test = self.under_test_happy_path()
@@ -315,7 +322,7 @@ def test_out_of_band_update_1(self):
315322
self.assertEqual(0, under_test._auth_client.refresh.call_count)
316323
access_token_t1 = under_test._credential.access_token()
317324

318-
time.sleep(TEST_TOKEN_TTL + 2)
325+
frozen_time.tick(TEST_TOKEN_TTL + 2)
319326

320327
credential_t1 = under_test._credential
321328
oob_credential = self.stub_auth_client.refresh(credential_t1.refresh_token())
@@ -330,7 +337,8 @@ def test_out_of_band_update_1(self):
330337
self.assertNotEqual(access_token_t1, access_token_t2)
331338
self.assertEqual(access_token_oob, access_token_t2)
332339

333-
def test_out_of_band_update_2(self):
340+
@freezegun.freeze_time(as_kwarg="frozen_time")
341+
def test_out_of_band_update_2(self, frozen_time):
334342
# Out of band credential update. Something updated the credential
335343
# on disk underneath us. We expect the refresher to reload
336344
# the credential file without attempting a refresh.
@@ -348,7 +356,7 @@ def test_out_of_band_update_2(self):
348356
oob_credential.save()
349357
access_token_oob = oob_credential.access_token()
350358

351-
time.sleep(TEST_TOKEN_TTL + 2)
359+
frozen_time.tick(TEST_TOKEN_TTL + 2)
352360

353361
# The new token is outside TTL, even after the token is reloaded, this
354362
# should be detected and a refresh should still occur.
@@ -358,7 +366,8 @@ def test_out_of_band_update_2(self):
358366
self.assertNotEqual(access_token_t1, access_token_t2)
359367
self.assertNotEqual(access_token_oob, access_token_t2)
360368

361-
def test_side_band_update_credential(self):
369+
@freezegun.freeze_time(as_kwarg="frozen_time")
370+
def test_side_band_update_credential(self, frozen_time):
362371
# a side-band update of the credential should reset our "refresh at" time,
363372
# cascading behavior from there. Where the above happens when someone
364373
# modifies a credential on disk, this happens when something in memory
@@ -378,7 +387,7 @@ def test_side_band_update_credential(self):
378387
self.assertEqual(0, under_test._auth_client.refresh.call_count)
379388

380389
# Get the sideband credential when we would have expected a refresh to probe behavior.
381-
time.sleep(TEST_TOKEN_TTL + 2)
390+
frozen_time.tick(TEST_TOKEN_TTL + 2)
382391
sideband_credential_path = self.tmp_dir_path / "test_sideband.json"
383392
sideband_credential = self.mock_auth_login_and_command_initialize(
384393
credential_path=sideband_credential_path, auth_client=self.stub_auth_client
@@ -461,7 +470,8 @@ def mock_api_call(self, under_test):
461470
# pre_request_hook()
462471
under_test.pre_request_hook()
463472

464-
def test_refresh_token_calls_refresh(self):
473+
@freezegun.freeze_time(as_kwarg="frozen_time")
474+
def test_refresh_token_calls_refresh(self, frozen_time):
465475
under_test = self.under_test_with_refresh_token()
466476

467477
self.assertIsNone(under_test._credential.data())
@@ -471,15 +481,16 @@ def test_refresh_token_calls_refresh(self):
471481
self.assertEqual(0, under_test._auth_client.login.call_count)
472482
access_token_t1 = under_test._credential.access_token()
473483

474-
time.sleep(TEST_TOKEN_TTL + 2)
484+
frozen_time.tick(TEST_TOKEN_TTL + 2)
475485

476486
self.mock_api_call(under_test)
477487
self.assertEqual(1, under_test._auth_client.refresh.call_count)
478488
self.assertEqual(0, under_test._auth_client.login.call_count)
479489
access_token_t2 = under_test._credential.access_token()
480490
self.assertNotEqual(access_token_t1, access_token_t2)
481491

482-
def test_no_refresh_token_calls_login(self):
492+
@freezegun.freeze_time(as_kwarg="frozen_time")
493+
def test_no_refresh_token_calls_login(self, frozen_time):
483494
under_test = self.under_test_without_refresh_token()
484495

485496
self.assertIsNone(under_test._credential.data())
@@ -489,23 +500,24 @@ def test_no_refresh_token_calls_login(self):
489500
self.assertEqual(0, under_test._auth_client.login.call_count)
490501
access_token_t1 = under_test._credential.access_token()
491502

492-
time.sleep(TEST_TOKEN_TTL + 2)
503+
frozen_time.tick(TEST_TOKEN_TTL + 2)
493504

494505
self.mock_api_call(under_test)
495506
self.assertEqual(0, under_test._auth_client.refresh.call_count)
496507
self.assertEqual(1, under_test._auth_client.login.call_count)
497508
access_token_t2 = under_test._credential.access_token()
498509
self.assertNotEqual(access_token_t1, access_token_t2)
499510

500-
def test_no_auth_client(self):
511+
@freezegun.freeze_time(as_kwarg="frozen_time")
512+
def test_no_auth_client(self, frozen_time):
501513
# when no auth client is provided, just authenticate with what we
502514
# have.
503515
under_test = self.under_test_no_auth_client()
504516

505517
self.mock_api_call(under_test)
506518
access_token_t1 = under_test._credential.access_token()
507519

508-
time.sleep(TEST_TOKEN_TTL + 2)
520+
frozen_time.tick(TEST_TOKEN_TTL + 2)
509521

510522
self.mock_api_call(under_test)
511523
access_token_t2 = under_test._credential.access_token()

tests/test_planet_auth/unit/auth/auth_clients/oidc/test_token_validator.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import json
1616
import jwt.utils
1717
import secrets
18-
import time
18+
import freezegun
1919
import unittest
2020
from unittest.mock import MagicMock
2121

@@ -447,13 +447,14 @@ def test_access_token_multiple_audiences(self):
447447
token_str=access_token, issuer=self.token_builder_1.issuer, audience="extra_audience_3"
448448
)
449449

450-
def test_access_token_expired(self):
450+
@freezegun.freeze_time(as_kwarg="frozen_time")
451+
def test_access_token_expired(self, frozen_time):
451452
# QE TC2
452453
under_test = self.under_test_1
453454
access_token = self.token_builder_1.construct_oidc_access_token_rfc8693(
454455
username=TEST_TOKEN_USER, requested_scopes=TEST_TOKEN_SCOPES, ttl=3
455456
)
456-
time.sleep(5)
457+
frozen_time.tick(5)
457458
with self.assertRaises(ExpiredTokenException):
458459
under_test.validate_token(
459460
access_token,
@@ -631,7 +632,8 @@ def test_validate_id_token_multiple_audiences(self):
631632
with self.assertRaises(InvalidTokenException):
632633
under_test.validate_id_token(id_token, issuer=self.token_builder_1.issuer, client_id="test_client_id")
633634

634-
def test_min_jwks_fetch_interval(self):
635+
@freezegun.freeze_time(as_kwarg="frozen_time")
636+
def test_min_jwks_fetch_interval(self, frozen_time):
635637
under_test = self.under_test_2
636638
token_known_signer = self.token_builder_1.construct_oidc_access_token_rfc8693(
637639
username=TEST_TOKEN_USER, requested_scopes=TEST_TOKEN_SCOPES, ttl=TEST_TOKEN_TTL
@@ -680,7 +682,7 @@ def test_min_jwks_fetch_interval(self):
680682
# t4 - after the interval has passed, a we should reload for any
681683
# request. If a miss comes in before the next interval, it should not
682684
# trigger a reload. Also, it's not automatic, it's on demand.
683-
time.sleep(self.MIN_JWKS_FETCH_INTERVAL + 2)
685+
frozen_time.tick(self.MIN_JWKS_FETCH_INTERVAL + 2)
684686
self.assertEqual(1, under_test._jwks_client.jwks.call_count)
685687
under_test.validate_token(
686688
token_known_signer,

0 commit comments

Comments
 (0)