Skip to content

Commit

Permalink
Fix issues with tests using freezetime that doesn't work with timezon…
Browse files Browse the repository at this point in the history
…es/utc
  • Loading branch information
allenporter authored and frenck committed Jun 27, 2022
1 parent 0b24661 commit 063da90
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 22 deletions.
17 changes: 9 additions & 8 deletions homeassistant/components/google/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,6 @@ def __init__(
self._device_flow_info: DeviceFlowInfo = device_flow_info
self._exchange_task_unsub: CALLBACK_TYPE | None = None
self._timeout_unsub: CALLBACK_TYPE | None = None
max_timeout = dt.utcnow() + datetime.timedelta(seconds=EXCHANGE_TIMEOUT_SECONDS)
# For some reason, oauth.step1_get_device_and_user_codes() returns a datetime
# object without tzinfo. For the comparison below to work, it needs one.
user_code_expiry = device_flow_info.user_code_expiry.replace(
tzinfo=datetime.timezone.utc
)
self._expiration_time = min(user_code_expiry, max_timeout)
self._listener: CALLBACK_TYPE | None = None
self._creds: Credentials | None = None

Expand Down Expand Up @@ -115,13 +108,21 @@ def creds(self) -> Credentials | None:
def async_start_exchange(self) -> None:
"""Start the device auth exchange flow polling."""
_LOGGER.debug("Starting exchange flow")
max_timeout = dt.utcnow() + datetime.timedelta(seconds=EXCHANGE_TIMEOUT_SECONDS)
# For some reason, oauth.step1_get_device_and_user_codes() returns a datetime
# object without tzinfo. For the comparison below to work, it needs one.
user_code_expiry = self._device_flow_info.user_code_expiry.replace(
tzinfo=datetime.timezone.utc
)
expiration_time = min(user_code_expiry, max_timeout)

self._exchange_task_unsub = async_track_time_interval(
self._hass,
self._async_poll_attempt,
datetime.timedelta(seconds=self._device_flow_info.interval),
)
self._timeout_unsub = async_track_point_in_utc_time(
self._hass, self._async_timeout, self._expiration_time
self._hass, self._async_timeout, expiration_time
)

async def _async_poll_attempt(self, now: datetime.datetime) -> None:
Expand Down
36 changes: 22 additions & 14 deletions tests/components/google/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from aiohttp.client_exceptions import ClientError
from freezegun.api import FrozenDateTimeFactory
from oauth2client.client import (
DeviceFlowInfo,
FlowExchangeError,
OAuth2Credentials,
OAuth2DeviceCodeError,
Expand Down Expand Up @@ -59,18 +60,26 @@ async def mock_code_flow(
) -> YieldFixture[Mock]:
"""Fixture for initiating OAuth flow."""
with patch(
"oauth2client.client.OAuth2WebServerFlow.step1_get_device_and_user_codes",
"homeassistant.components.google.api.OAuth2WebServerFlow.step1_get_device_and_user_codes",
) as mock_flow:
mock_flow.return_value.user_code_expiry = utcnow() + code_expiration_delta
mock_flow.return_value.interval = CODE_CHECK_INTERVAL
mock_flow.return_value = DeviceFlowInfo.FromResponse(
{
"device_code": "4/4-GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8",
"user_code": "GQVQ-JKEC",
"verification_url": "https://www.google.com/device",
"expires_in": code_expiration_delta.total_seconds(),
"interval": CODE_CHECK_INTERVAL,
}
)
yield mock_flow


@pytest.fixture
async def mock_exchange(creds: OAuth2Credentials) -> YieldFixture[Mock]:
"""Fixture for mocking out the exchange for credentials."""
with patch(
"oauth2client.client.OAuth2WebServerFlow.step2_exchange", return_value=creds
"homeassistant.components.google.api.OAuth2WebServerFlow.step2_exchange",
return_value=creds,
) as mock:
yield mock

Expand Down Expand Up @@ -108,7 +117,6 @@ async def fire_alarm(hass, point_in_time):
await hass.async_block_till_done()


@pytest.mark.freeze_time("2022-06-03 15:19:59-00:00")
async def test_full_flow_yaml_creds(
hass: HomeAssistant,
mock_code_flow: Mock,
Expand All @@ -131,9 +139,8 @@ async def test_full_flow_yaml_creds(
"homeassistant.components.google.async_setup_entry", return_value=True
) as mock_setup:
# Run one tick to invoke the credential exchange check
freezer.tick(CODE_CHECK_ALARM_TIMEDELTA)
await fire_alarm(hass, datetime.datetime.utcnow())
await hass.async_block_till_done()
now = utcnow()
await fire_alarm(hass, now + CODE_CHECK_ALARM_TIMEDELTA)
result = await hass.config_entries.flow.async_configure(
flow_id=result["flow_id"]
)
Expand All @@ -143,11 +150,12 @@ async def test_full_flow_yaml_creds(
assert "data" in result
data = result["data"]
assert "token" in data
assert 0 < data["token"]["expires_in"] <= 60 * 60
assert (
data["token"]["expires_in"]
== 60 * 60 - CODE_CHECK_ALARM_TIMEDELTA.total_seconds()
datetime.datetime.now().timestamp()
<= data["token"]["expires_at"]
< (datetime.datetime.now() + datetime.timedelta(days=8)).timestamp()
)
assert data["token"]["expires_at"] == 1654273199.0
data["token"].pop("expires_at")
data["token"].pop("expires_in")
assert data == {
Expand Down Expand Up @@ -238,7 +246,7 @@ async def test_code_error(
assert await component_setup()

with patch(
"oauth2client.client.OAuth2WebServerFlow.step1_get_device_and_user_codes",
"homeassistant.components.google.api.OAuth2WebServerFlow.step1_get_device_and_user_codes",
side_effect=OAuth2DeviceCodeError("Test Failure"),
):
result = await hass.config_entries.flow.async_init(
Expand Down Expand Up @@ -267,7 +275,7 @@ async def test_expired_after_exchange(

# Fail first attempt then advance clock past exchange timeout
with patch(
"oauth2client.client.OAuth2WebServerFlow.step2_exchange",
"homeassistant.components.google.api.OAuth2WebServerFlow.step2_exchange",
side_effect=FlowExchangeError(),
):
now = utcnow()
Expand Down Expand Up @@ -299,7 +307,7 @@ async def test_exchange_error(
# Run one tick to invoke the credential exchange check
now = utcnow()
with patch(
"oauth2client.client.OAuth2WebServerFlow.step2_exchange",
"homeassistant.components.google.api.OAuth2WebServerFlow.step2_exchange",
side_effect=FlowExchangeError(),
):
now += CODE_CHECK_ALARM_TIMEDELTA
Expand Down

0 comments on commit 063da90

Please sign in to comment.