core/tests/components/buienradar/test_camera.py

229 lines
6.8 KiB
Python

"""The tests for generic camera component."""
import asyncio
from aiohttp.client_exceptions import ClientResponseError
from homeassistant.const import HTTP_INTERNAL_SERVER_ERROR
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
# An infinitesimally small time-delta.
EPSILON_DELTA = 0.0000000001
def radar_map_url(dim: int = 512, country_code: str = "NL") -> str:
"""Build map url, defaulting to 512 wide (as in component)."""
return f"https://api.buienradar.nl/image/1.0/RadarMap{country_code}?w={dim}&h={dim}"
async def test_fetching_url_and_caching(aioclient_mock, hass, hass_client):
"""Test that it fetches the given url."""
aioclient_mock.get(radar_map_url(), text="hello world")
await async_setup_component(
hass, "camera", {"camera": {"name": "config_test", "platform": "buienradar"}}
)
client = await hass_client()
resp = await client.get("/api/camera_proxy/camera.config_test")
assert resp.status == 200
assert aioclient_mock.call_count == 1
body = await resp.text()
assert body == "hello world"
# default delta is 600s -> should be the same when calling immediately
# afterwards.
resp = await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 1
async def test_expire_delta(aioclient_mock, hass, hass_client):
"""Test that the cache expires after delta."""
aioclient_mock.get(radar_map_url(), text="hello world")
await async_setup_component(
hass,
"camera",
{
"camera": {
"name": "config_test",
"platform": "buienradar",
"delta": EPSILON_DELTA,
}
},
)
client = await hass_client()
resp = await client.get("/api/camera_proxy/camera.config_test")
assert resp.status == 200
assert aioclient_mock.call_count == 1
body = await resp.text()
assert body == "hello world"
await asyncio.sleep(EPSILON_DELTA)
# tiny delta has passed -> should immediately call again
resp = await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 2
async def test_only_one_fetch_at_a_time(aioclient_mock, hass, hass_client):
"""Test that it fetches with only one request at the same time."""
aioclient_mock.get(radar_map_url(), text="hello world")
await async_setup_component(
hass, "camera", {"camera": {"name": "config_test", "platform": "buienradar"}}
)
client = await hass_client()
resp_1 = client.get("/api/camera_proxy/camera.config_test")
resp_2 = client.get("/api/camera_proxy/camera.config_test")
resp = await resp_1
resp_2 = await resp_2
assert (await resp.text()) == (await resp_2.text())
assert aioclient_mock.call_count == 1
async def test_dimension(aioclient_mock, hass, hass_client):
"""Test that it actually adheres to the dimension."""
aioclient_mock.get(radar_map_url(700), text="hello world")
await async_setup_component(
hass,
"camera",
{"camera": {"name": "config_test", "platform": "buienradar", "dimension": 700}},
)
client = await hass_client()
await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 1
async def test_belgium_country(aioclient_mock, hass, hass_client):
"""Test that it actually adheres to another country like Belgium."""
aioclient_mock.get(radar_map_url(country_code="BE"), text="hello world")
await async_setup_component(
hass,
"camera",
{
"camera": {
"name": "config_test",
"platform": "buienradar",
"country_code": "BE",
}
},
)
client = await hass_client()
await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 1
async def test_failure_response_not_cached(aioclient_mock, hass, hass_client):
"""Test that it does not cache a failure response."""
aioclient_mock.get(radar_map_url(), text="hello world", status=401)
await async_setup_component(
hass, "camera", {"camera": {"name": "config_test", "platform": "buienradar"}}
)
client = await hass_client()
await client.get("/api/camera_proxy/camera.config_test")
await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 2
async def test_last_modified_updates(aioclient_mock, hass, hass_client):
"""Test that it does respect HTTP not modified."""
# Build Last-Modified header value
now = dt_util.utcnow()
last_modified = now.strftime("%a, %d %m %Y %H:%M:%S GMT")
aioclient_mock.get(
radar_map_url(),
text="hello world",
status=200,
headers={"Last-Modified": last_modified},
)
await async_setup_component(
hass,
"camera",
{
"camera": {
"name": "config_test",
"platform": "buienradar",
"delta": EPSILON_DELTA,
}
},
)
client = await hass_client()
resp_1 = await client.get("/api/camera_proxy/camera.config_test")
# It is not possible to check if header was sent.
assert aioclient_mock.call_count == 1
await asyncio.sleep(EPSILON_DELTA)
# Content has expired, change response to a 304 NOT MODIFIED, which has no
# text, i.e. old value should be kept
aioclient_mock.clear_requests()
# mock call count is now reset as well:
assert aioclient_mock.call_count == 0
aioclient_mock.get(radar_map_url(), text=None, status=304)
resp_2 = await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 1
assert (await resp_1.read()) == (await resp_2.read())
async def test_retries_after_error(aioclient_mock, hass, hass_client):
"""Test that it does retry after an error instead of caching."""
await async_setup_component(
hass, "camera", {"camera": {"name": "config_test", "platform": "buienradar"}}
)
client = await hass_client()
aioclient_mock.get(radar_map_url(), text=None, status=HTTP_INTERNAL_SERVER_ERROR)
# A 404 should not return data and throw:
try:
await client.get("/api/camera_proxy/camera.config_test")
except ClientResponseError:
pass
assert aioclient_mock.call_count == 1
# Change the response to a 200
aioclient_mock.clear_requests()
aioclient_mock.get(radar_map_url(), text="DEADBEEF")
assert aioclient_mock.call_count == 0
# http error should not be cached, immediate retry.
resp_2 = await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 1
# Binary text can not be added as body to `aioclient_mock.get(text=...)`,
# while `resp.read()` returns bytes, encode the value.
assert (await resp_2.read()) == b"DEADBEEF"