Skip to content
Snippets Groups Projects
Unverified Commit 781bc5b3 authored by Allen Porter's avatar Allen Porter Committed by GitHub
Browse files

Add tests for fitbit integration (#100765)

* Add tests for fitbit integration

* Update coveragerc

* Update test requirements
parent 71aef4e9
No related branches found
No related tags found
No related merge requests found
...@@ -389,7 +389,6 @@ omit = ...@@ -389,7 +389,6 @@ omit =
homeassistant/components/firmata/pin.py homeassistant/components/firmata/pin.py
homeassistant/components/firmata/sensor.py homeassistant/components/firmata/sensor.py
homeassistant/components/firmata/switch.py homeassistant/components/firmata/switch.py
homeassistant/components/fitbit/*
homeassistant/components/fivem/__init__.py homeassistant/components/fivem/__init__.py
homeassistant/components/fivem/binary_sensor.py homeassistant/components/fivem/binary_sensor.py
homeassistant/components/fivem/coordinator.py homeassistant/components/fivem/coordinator.py
......
...@@ -12,6 +12,8 @@ from homeassistant.const import ( ...@@ -12,6 +12,8 @@ from homeassistant.const import (
UnitOfVolume, UnitOfVolume,
) )
DOMAIN: Final = "fitbit"
ATTR_ACCESS_TOKEN: Final = "access_token" ATTR_ACCESS_TOKEN: Final = "access_token"
ATTR_REFRESH_TOKEN: Final = "refresh_token" ATTR_REFRESH_TOKEN: Final = "refresh_token"
ATTR_LAST_SAVED_AT: Final = "last_saved_at" ATTR_LAST_SAVED_AT: Final = "last_saved_at"
......
...@@ -629,6 +629,9 @@ feedparser==6.0.10 ...@@ -629,6 +629,9 @@ feedparser==6.0.10
# homeassistant.components.file # homeassistant.components.file
file-read-backwards==2.0.0 file-read-backwards==2.0.0
# homeassistant.components.fitbit
fitbit==0.3.1
# homeassistant.components.fivem # homeassistant.components.fivem
fivem-api==0.1.2 fivem-api==0.1.2
......
"""Tests for fitbit component."""
"""Test fixtures for fitbit."""
from collections.abc import Awaitable, Callable, Generator
import datetime
from http import HTTPStatus
import time
from typing import Any
from unittest.mock import patch
import pytest
from requests_mock.mocker import Mocker
from homeassistant.components.fitbit.const import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
CLIENT_ID = "1234"
CLIENT_SECRET = "5678"
PROFILE_USER_ID = "fitbit-api-user-id-1"
FAKE_TOKEN = "some-token"
FAKE_REFRESH_TOKEN = "some-refresh-token"
PROFILE_API_URL = "https://api.fitbit.com/1/user/-/profile.json"
DEVICES_API_URL = "https://api.fitbit.com/1/user/-/devices.json"
TIMESERIES_API_URL_FORMAT = (
"https://api.fitbit.com/1/user/-/{resource}/date/today/7d.json"
)
@pytest.fixture(name="token_expiration_time")
def mcok_token_expiration_time() -> float:
"""Fixture for expiration time of the config entry auth token."""
return time.time() + 86400
@pytest.fixture(name="fitbit_config_yaml")
def mock_fitbit_config_yaml(token_expiration_time: float) -> dict[str, Any]:
"""Fixture for the yaml fitbit.conf file contents."""
return {
"access_token": FAKE_TOKEN,
"refresh_token": FAKE_REFRESH_TOKEN,
"last_saved_at": token_expiration_time,
}
@pytest.fixture(name="fitbit_config_setup", autouse=True)
def mock_fitbit_config_setup(
fitbit_config_yaml: dict[str, Any],
) -> Generator[None, None, None]:
"""Fixture to mock out fitbit.conf file data loading and persistence."""
with patch(
"homeassistant.components.fitbit.sensor.os.path.isfile", return_value=True
), patch(
"homeassistant.components.fitbit.sensor.load_json_object",
return_value=fitbit_config_yaml,
), patch(
"homeassistant.components.fitbit.sensor.save_json",
):
yield
@pytest.fixture(name="monitored_resources")
def mock_monitored_resources() -> list[str] | None:
"""Fixture for the fitbit yaml config monitored_resources field."""
return None
@pytest.fixture(name="sensor_platform_config")
def mock_sensor_platform_config(
monitored_resources: list[str] | None,
) -> dict[str, Any]:
"""Fixture for the fitbit sensor platform configuration data in configuration.yaml."""
config = {}
if monitored_resources is not None:
config["monitored_resources"] = monitored_resources
return config
@pytest.fixture(name="sensor_platform_setup")
async def mock_sensor_platform_setup(
hass: HomeAssistant,
sensor_platform_config: dict[str, Any],
) -> Callable[[], Awaitable[bool]]:
"""Fixture to set up the integration."""
async def run() -> bool:
result = await async_setup_component(
hass,
"sensor",
{
"sensor": [
{
"platform": DOMAIN,
**sensor_platform_config,
}
]
},
)
await hass.async_block_till_done()
return result
return run
@pytest.fixture(name="profile_id")
async def mock_profile_id() -> str:
"""Fixture for the profile id returned from the API response."""
return PROFILE_USER_ID
@pytest.fixture(name="profile", autouse=True)
async def mock_profile(requests_mock: Mocker, profile_id: str) -> None:
"""Fixture to setup fake requests made to Fitbit API during config flow."""
requests_mock.register_uri(
"GET",
PROFILE_API_URL,
status_code=HTTPStatus.OK,
json={
"user": {
"encodedId": profile_id,
"fullName": "My name",
"locale": "en_US",
},
},
)
@pytest.fixture(name="devices_response")
async def mock_device_response() -> list[dict[str, Any]]:
"""Return the list of devices."""
return []
@pytest.fixture(autouse=True)
async def mock_devices(requests_mock: Mocker, devices_response: dict[str, Any]) -> None:
"""Fixture to setup fake device responses."""
requests_mock.register_uri(
"GET",
DEVICES_API_URL,
status_code=HTTPStatus.OK,
json=devices_response,
)
def timeseries_response(resource: str, value: str) -> dict[str, Any]:
"""Create a timeseries response value."""
return {
resource: [{"dateTime": datetime.datetime.today().isoformat(), "value": value}]
}
@pytest.fixture(name="register_timeseries")
async def mock_register_timeseries(
requests_mock: Mocker,
) -> Callable[[str, dict[str, Any]], None]:
"""Fixture to setup fake timeseries API responses."""
def register(resource: str, response: dict[str, Any]) -> None:
requests_mock.register_uri(
"GET",
TIMESERIES_API_URL_FORMAT.format(resource=resource),
status_code=HTTPStatus.OK,
json=response,
)
return register
"""Tests for the fitbit sensor platform."""
from collections.abc import Awaitable, Callable
from typing import Any
import pytest
from homeassistant.core import HomeAssistant
from .conftest import timeseries_response
DEVICE_RESPONSE_CHARGE_2 = {
"battery": "Medium",
"batteryLevel": 60,
"deviceVersion": "Charge 2",
"id": "816713257",
"lastSyncTime": "2019-11-07T12:00:58.000",
"mac": "16ADD56D54GD",
"type": "TRACKER",
}
DEVICE_RESPONSE_ARIA_AIR = {
"battery": "High",
"batteryLevel": 95,
"deviceVersion": "Aria Air",
"id": "016713257",
"lastSyncTime": "2019-11-07T12:00:58.000",
"mac": "06ADD56D54GD",
"type": "SCALE",
}
@pytest.mark.parametrize(
"monitored_resources",
[["activities/steps"]],
)
async def test_step_sensor(
hass: HomeAssistant,
sensor_platform_setup: Callable[[], Awaitable[bool]],
register_timeseries: Callable[[str, dict[str, Any]], None],
) -> None:
"""Test battery level sensor."""
register_timeseries(
"activities/steps", timeseries_response("activities-steps", "5600")
)
await sensor_platform_setup()
state = hass.states.get("sensor.steps")
assert state
assert state.state == "5600"
assert state.attributes == {
"attribution": "Data provided by Fitbit.com",
"friendly_name": "Steps",
"icon": "mdi:walk",
"unit_of_measurement": "steps",
}
@pytest.mark.parametrize(
("devices_response", "monitored_resources"),
[([DEVICE_RESPONSE_CHARGE_2, DEVICE_RESPONSE_ARIA_AIR], ["devices/battery"])],
)
async def test_device_battery_level(
hass: HomeAssistant,
sensor_platform_setup: Callable[[], Awaitable[bool]],
) -> None:
"""Test battery level sensor for devices."""
await sensor_platform_setup()
state = hass.states.get("sensor.charge_2_battery")
assert state
assert state.state == "Medium"
assert state.attributes == {
"attribution": "Data provided by Fitbit.com",
"friendly_name": "Charge 2 Battery",
"icon": "mdi:battery-50",
"model": "Charge 2",
"type": "tracker",
}
state = hass.states.get("sensor.aria_air_battery")
assert state
assert state.state == "High"
assert state.attributes == {
"attribution": "Data provided by Fitbit.com",
"friendly_name": "Aria Air Battery",
"icon": "mdi:battery",
"model": "Aria Air",
"type": "scale",
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment