Improve type hints in generic tests (#121166)

This commit is contained in:
epenet 2024-07-04 10:32:00 +02:00 committed by GitHub
parent 1eec49696a
commit cf96084ea3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 99 additions and 58 deletions

View file

@ -1,7 +1,9 @@
"""Test fixtures for the generic component."""
from __future__ import annotations
from io import BytesIO
from unittest.mock import AsyncMock, Mock, patch
from unittest.mock import AsyncMock, MagicMock, Mock, _patch, patch
from PIL import Image
import pytest
@ -9,12 +11,14 @@ import respx
from homeassistant import config_entries
from homeassistant.components.generic.const import DOMAIN
from homeassistant.config_entries import ConfigFlowResult
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
@pytest.fixture(scope="package")
def fakeimgbytes_png():
def fakeimgbytes_png() -> bytes:
"""Fake image in RAM for testing."""
buf = BytesIO()
Image.new("RGB", (1, 1)).save(buf, format="PNG")
@ -22,7 +26,7 @@ def fakeimgbytes_png():
@pytest.fixture(scope="package")
def fakeimgbytes_jpg():
def fakeimgbytes_jpg() -> bytes:
"""Fake image in RAM for testing."""
buf = BytesIO() # fake image in ram for testing.
Image.new("RGB", (1, 1)).save(buf, format="jpeg")
@ -30,7 +34,7 @@ def fakeimgbytes_jpg():
@pytest.fixture(scope="package")
def fakeimgbytes_svg():
def fakeimgbytes_svg() -> bytes:
"""Fake image in RAM for testing."""
return bytes(
'<svg xmlns="http://www.w3.org/2000/svg"><circle r="50"/></svg>',
@ -39,7 +43,7 @@ def fakeimgbytes_svg():
@pytest.fixture(scope="package")
def fakeimgbytes_gif():
def fakeimgbytes_gif() -> bytes:
"""Fake image in RAM for testing."""
buf = BytesIO() # fake image in ram for testing.
Image.new("RGB", (1, 1)).save(buf, format="gif")
@ -47,19 +51,19 @@ def fakeimgbytes_gif():
@pytest.fixture
def fakeimg_png(fakeimgbytes_png):
def fakeimg_png(fakeimgbytes_png: bytes) -> None:
"""Set up respx to respond to test url with fake image bytes."""
respx.get("http://127.0.0.1/testurl/1").respond(stream=fakeimgbytes_png)
@pytest.fixture
def fakeimg_gif(fakeimgbytes_gif):
def fakeimg_gif(fakeimgbytes_gif: bytes) -> None:
"""Set up respx to respond to test url with fake image bytes."""
respx.get("http://127.0.0.1/testurl/1").respond(stream=fakeimgbytes_gif)
@pytest.fixture(scope="package")
def mock_create_stream():
def mock_create_stream() -> _patch[MagicMock]:
"""Mock create stream."""
mock_stream = Mock()
mock_provider = Mock()
@ -75,7 +79,7 @@ def mock_create_stream():
@pytest.fixture
async def user_flow(hass):
async def user_flow(hass: HomeAssistant) -> ConfigFlowResult:
"""Initiate a user flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -87,7 +91,7 @@ async def user_flow(hass):
@pytest.fixture(name="config_entry")
def config_entry_fixture(hass):
def config_entry_fixture(hass: HomeAssistant) -> MockConfigEntry:
"""Define a config entry fixture."""
entry = MockConfigEntry(
domain=DOMAIN,
@ -112,7 +116,9 @@ def config_entry_fixture(hass):
@pytest.fixture
async def setup_entry(hass, config_entry):
async def setup_entry(
hass: HomeAssistant, config_entry: MockConfigEntry
) -> MockConfigEntry:
"""Set up a config entry ready to be used in tests."""
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()

View file

@ -73,7 +73,7 @@ async def help_setup_mock_config_entry(
async def test_fetching_url(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
fakeimgbytes_png,
fakeimgbytes_png: bytes,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test that it fetches the given url."""
@ -132,7 +132,7 @@ async def test_image_caching(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
freezer: FrozenDateTimeFactory,
fakeimgbytes_png,
fakeimgbytes_png: bytes,
) -> None:
"""Test that the image is cached and not fetched more often than the framerate indicates."""
respx.get("http://example.com").respond(stream=fakeimgbytes_png)
@ -197,7 +197,7 @@ async def test_image_caching(
@respx.mock
async def test_fetching_without_verify_ssl(
hass: HomeAssistant, hass_client: ClientSessionGenerator, fakeimgbytes_png
hass: HomeAssistant, hass_client: ClientSessionGenerator, fakeimgbytes_png: bytes
) -> None:
"""Test that it fetches the given url when ssl verify is off."""
respx.get("https://example.com").respond(stream=fakeimgbytes_png)
@ -221,7 +221,7 @@ async def test_fetching_without_verify_ssl(
@respx.mock
async def test_fetching_url_with_verify_ssl(
hass: HomeAssistant, hass_client: ClientSessionGenerator, fakeimgbytes_png
hass: HomeAssistant, hass_client: ClientSessionGenerator, fakeimgbytes_png: bytes
) -> None:
"""Test that it fetches the given url when ssl verify is explicitly on."""
respx.get("https://example.com").respond(stream=fakeimgbytes_png)
@ -247,8 +247,8 @@ async def test_fetching_url_with_verify_ssl(
async def test_limit_refetch(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
fakeimgbytes_png,
fakeimgbytes_jpg,
fakeimgbytes_png: bytes,
fakeimgbytes_jpg: bytes,
) -> None:
"""Test that it fetches the given url."""
respx.get("http://example.com/0a").respond(stream=fakeimgbytes_png)
@ -319,7 +319,7 @@ async def test_stream_source(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
hass_ws_client: WebSocketGenerator,
fakeimgbytes_png,
fakeimgbytes_png: bytes,
) -> None:
"""Test that the stream source is rendered."""
respx.get("http://example.com").respond(stream=fakeimgbytes_png)
@ -376,7 +376,7 @@ async def test_stream_source_error(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
hass_ws_client: WebSocketGenerator,
fakeimgbytes_png,
fakeimgbytes_png: bytes,
) -> None:
"""Test that the stream source has an error."""
respx.get("http://example.com").respond(stream=fakeimgbytes_png)
@ -418,7 +418,7 @@ async def test_stream_source_error(
@respx.mock
async def test_setup_alternative_options(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, fakeimgbytes_png
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, fakeimgbytes_png: bytes
) -> None:
"""Test that the stream source is setup with different config options."""
respx.get("https://example.com").respond(stream=fakeimgbytes_png)
@ -442,7 +442,7 @@ async def test_no_stream_source(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
hass_ws_client: WebSocketGenerator,
fakeimgbytes_png,
fakeimgbytes_png: bytes,
) -> None:
"""Test a stream request without stream source option set."""
respx.get("https://example.com").respond(stream=fakeimgbytes_png)
@ -482,8 +482,8 @@ async def test_no_stream_source(
async def test_camera_content_type(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
fakeimgbytes_svg,
fakeimgbytes_jpg,
fakeimgbytes_svg: bytes,
fakeimgbytes_jpg: bytes,
) -> None:
"""Test generic camera with custom content_type."""
urlsvg = "https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg"
@ -532,8 +532,8 @@ async def test_camera_content_type(
async def test_timeout_cancelled(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
fakeimgbytes_png,
fakeimgbytes_jpg,
fakeimgbytes_png: bytes,
fakeimgbytes_jpg: bytes,
) -> None:
"""Test that timeouts and cancellations return last image."""

View file

@ -1,11 +1,13 @@
"""Test The generic (IP Camera) config flow."""
from __future__ import annotations
import contextlib
import errno
from http import HTTPStatus
import os.path
from pathlib import Path
from unittest.mock import AsyncMock, PropertyMock, patch
from unittest.mock import AsyncMock, MagicMock, PropertyMock, _patch, patch
import httpx
import pytest
@ -28,7 +30,7 @@ from homeassistant.components.stream import (
CONF_USE_WALLCLOCK_AS_TIMESTAMPS,
)
from homeassistant.components.stream.worker import StreamWorkerError
from homeassistant.config_entries import ConfigEntryState
from homeassistant.config_entries import ConfigEntryState, ConfigFlowResult
from homeassistant.const import (
CONF_AUTHENTICATION,
CONF_NAME,
@ -68,10 +70,10 @@ TESTDATA_YAML = {
@respx.mock
async def test_form(
hass: HomeAssistant,
fakeimgbytes_png,
fakeimgbytes_png: bytes,
hass_client: ClientSessionGenerator,
user_flow,
mock_create_stream,
user_flow: ConfigFlowResult,
mock_create_stream: _patch[MagicMock],
) -> None:
"""Test the form with a normal set of settings."""
@ -122,8 +124,9 @@ async def test_form(
@respx.mock
@pytest.mark.usefixtures("fakeimg_png")
async def test_form_only_stillimage(
hass: HomeAssistant, fakeimg_png, user_flow
hass: HomeAssistant, user_flow: ConfigFlowResult
) -> None:
"""Test we complete ok if the user wants still images only."""
result = await hass.config_entries.flow.async_init(
@ -164,7 +167,10 @@ async def test_form_only_stillimage(
@respx.mock
async def test_form_reject_still_preview(
hass: HomeAssistant, fakeimgbytes_png, mock_create_stream, user_flow
hass: HomeAssistant,
fakeimgbytes_png: bytes,
mock_create_stream: _patch[MagicMock],
user_flow: ConfigFlowResult,
) -> None:
"""Test we go back to the config screen if the user rejects the still preview."""
respx.get("http://127.0.0.1/testurl/1").respond(stream=fakeimgbytes_png)
@ -184,11 +190,11 @@ async def test_form_reject_still_preview(
@respx.mock
@pytest.mark.usefixtures("fakeimg_png")
async def test_form_still_preview_cam_off(
hass: HomeAssistant,
fakeimg_png,
mock_create_stream,
user_flow,
mock_create_stream: _patch[MagicMock],
user_flow: ConfigFlowResult,
hass_client: ClientSessionGenerator,
) -> None:
"""Test camera errors are triggered during preview."""
@ -213,8 +219,9 @@ async def test_form_still_preview_cam_off(
@respx.mock
@pytest.mark.usefixtures("fakeimg_gif")
async def test_form_only_stillimage_gif(
hass: HomeAssistant, fakeimg_gif, user_flow
hass: HomeAssistant, user_flow: ConfigFlowResult
) -> None:
"""Test we complete ok if the user wants a gif."""
data = TESTDATA.copy()
@ -237,7 +244,7 @@ async def test_form_only_stillimage_gif(
@respx.mock
async def test_form_only_svg_whitespace(
hass: HomeAssistant, fakeimgbytes_svg, user_flow
hass: HomeAssistant, fakeimgbytes_svg: bytes, user_flow: ConfigFlowResult
) -> None:
"""Test we complete ok if svg starts with whitespace, issue #68889."""
fakeimgbytes_wspace_svg = bytes(" \n ", encoding="utf-8") + fakeimgbytes_svg
@ -271,7 +278,7 @@ async def test_form_only_svg_whitespace(
],
)
async def test_form_only_still_sample(
hass: HomeAssistant, user_flow, image_file
hass: HomeAssistant, user_flow: ConfigFlowResult, image_file
) -> None:
"""Test various sample images #69037."""
image_path = os.path.join(os.path.dirname(__file__), image_file)
@ -333,8 +340,8 @@ async def test_form_only_still_sample(
)
async def test_still_template(
hass: HomeAssistant,
user_flow,
fakeimgbytes_png,
user_flow: ConfigFlowResult,
fakeimgbytes_png: bytes,
template,
url,
expected_result,
@ -359,8 +366,11 @@ async def test_still_template(
@respx.mock
@pytest.mark.usefixtures("fakeimg_png")
async def test_form_rtsp_mode(
hass: HomeAssistant, fakeimg_png, user_flow, mock_create_stream
hass: HomeAssistant,
user_flow: ConfigFlowResult,
mock_create_stream: _patch[MagicMock],
) -> None:
"""Test we complete ok if the user enters a stream url."""
data = TESTDATA.copy()
@ -399,7 +409,10 @@ async def test_form_rtsp_mode(
async def test_form_only_stream(
hass: HomeAssistant, fakeimgbytes_jpg, user_flow, mock_create_stream
hass: HomeAssistant,
fakeimgbytes_jpg: bytes,
user_flow: ConfigFlowResult,
mock_create_stream: _patch[MagicMock],
) -> None:
"""Test we complete ok if the user wants stream only."""
data = TESTDATA.copy()
@ -435,7 +448,7 @@ async def test_form_only_stream(
async def test_form_still_and_stream_not_provided(
hass: HomeAssistant, user_flow
hass: HomeAssistant, user_flow: ConfigFlowResult
) -> None:
"""Test we show a suitable error if neither still or stream URL are provided."""
result2 = await hass.config_entries.flow.async_configure(
@ -482,7 +495,11 @@ async def test_form_still_and_stream_not_provided(
],
)
async def test_form_image_http_exceptions(
side_effect, expected_message, hass: HomeAssistant, user_flow, mock_create_stream
side_effect,
expected_message,
hass: HomeAssistant,
user_flow: ConfigFlowResult,
mock_create_stream: _patch[MagicMock],
) -> None:
"""Test we handle image http exceptions."""
respx.get("http://127.0.0.1/testurl/1").side_effect = [
@ -502,7 +519,9 @@ async def test_form_image_http_exceptions(
@respx.mock
async def test_form_stream_invalidimage(
hass: HomeAssistant, user_flow, mock_create_stream
hass: HomeAssistant,
user_flow: ConfigFlowResult,
mock_create_stream: _patch[MagicMock],
) -> None:
"""Test we handle invalid image when a stream is specified."""
respx.get("http://127.0.0.1/testurl/1").respond(stream=b"invalid")
@ -519,7 +538,9 @@ async def test_form_stream_invalidimage(
@respx.mock
async def test_form_stream_invalidimage2(
hass: HomeAssistant, user_flow, mock_create_stream
hass: HomeAssistant,
user_flow: ConfigFlowResult,
mock_create_stream: _patch[MagicMock],
) -> None:
"""Test we handle invalid image when a stream is specified."""
respx.get("http://127.0.0.1/testurl/1").respond(content=None)
@ -536,7 +557,9 @@ async def test_form_stream_invalidimage2(
@respx.mock
async def test_form_stream_invalidimage3(
hass: HomeAssistant, user_flow, mock_create_stream
hass: HomeAssistant,
user_flow: ConfigFlowResult,
mock_create_stream: _patch[MagicMock],
) -> None:
"""Test we handle invalid image when a stream is specified."""
respx.get("http://127.0.0.1/testurl/1").respond(content=bytes([0xFF]))
@ -552,7 +575,10 @@ async def test_form_stream_invalidimage3(
@respx.mock
async def test_form_stream_timeout(hass: HomeAssistant, fakeimg_png, user_flow) -> None:
@pytest.mark.usefixtures("fakeimg_png")
async def test_form_stream_timeout(
hass: HomeAssistant, user_flow: ConfigFlowResult
) -> None:
"""Test we handle invalid auth."""
with patch(
"homeassistant.components.generic.config_flow.create_stream"
@ -571,8 +597,9 @@ async def test_form_stream_timeout(hass: HomeAssistant, fakeimg_png, user_flow)
@respx.mock
@pytest.mark.usefixtures("fakeimg_png")
async def test_form_stream_worker_error(
hass: HomeAssistant, fakeimg_png, user_flow
hass: HomeAssistant, user_flow: ConfigFlowResult
) -> None:
"""Test we handle a StreamWorkerError and pass the message through."""
with patch(
@ -589,7 +616,7 @@ async def test_form_stream_worker_error(
@respx.mock
async def test_form_stream_permission_error(
hass: HomeAssistant, fakeimgbytes_png, user_flow
hass: HomeAssistant, fakeimgbytes_png: bytes, user_flow: ConfigFlowResult
) -> None:
"""Test we handle permission error."""
respx.get("http://127.0.0.1/testurl/1").respond(stream=fakeimgbytes_png)
@ -606,8 +633,9 @@ async def test_form_stream_permission_error(
@respx.mock
@pytest.mark.usefixtures("fakeimg_png")
async def test_form_no_route_to_host(
hass: HomeAssistant, fakeimg_png, user_flow
hass: HomeAssistant, user_flow: ConfigFlowResult
) -> None:
"""Test we handle no route to host."""
with patch(
@ -623,8 +651,9 @@ async def test_form_no_route_to_host(
@respx.mock
@pytest.mark.usefixtures("fakeimg_png")
async def test_form_stream_io_error(
hass: HomeAssistant, fakeimg_png, user_flow
hass: HomeAssistant, user_flow: ConfigFlowResult
) -> None:
"""Test we handle no io error when setting up stream."""
with patch(
@ -640,7 +669,8 @@ async def test_form_stream_io_error(
@respx.mock
async def test_form_oserror(hass: HomeAssistant, fakeimg_png, user_flow) -> None:
@pytest.mark.usefixtures("fakeimg_png")
async def test_form_oserror(hass: HomeAssistant, user_flow: ConfigFlowResult) -> None:
"""Test we handle OS error when setting up stream."""
with (
patch(
@ -657,7 +687,7 @@ async def test_form_oserror(hass: HomeAssistant, fakeimg_png, user_flow) -> None
@respx.mock
async def test_options_template_error(
hass: HomeAssistant, fakeimgbytes_png, mock_create_stream
hass: HomeAssistant, fakeimgbytes_png: bytes, mock_create_stream: _patch[MagicMock]
) -> None:
"""Test the options flow with a template error."""
respx.get("http://127.0.0.1/testurl/1").respond(stream=fakeimgbytes_png)
@ -755,7 +785,7 @@ async def test_slug(hass: HomeAssistant, caplog: pytest.LogCaptureFixture) -> No
@respx.mock
async def test_options_only_stream(
hass: HomeAssistant, fakeimgbytes_png, mock_create_stream
hass: HomeAssistant, fakeimgbytes_png: bytes, mock_create_stream: _patch[MagicMock]
) -> None:
"""Test the options flow without a still_image_url."""
respx.get("http://127.0.0.1/testurl/2").respond(stream=fakeimgbytes_png)
@ -792,7 +822,8 @@ async def test_options_only_stream(
assert result3["data"][CONF_CONTENT_TYPE] == "image/jpeg"
async def test_unload_entry(hass: HomeAssistant, fakeimg_png) -> None:
@pytest.mark.usefixtures("fakeimg_png")
async def test_unload_entry(hass: HomeAssistant) -> None:
"""Test unloading the generic IP Camera entry."""
mock_entry = MockConfigEntry(domain=DOMAIN, options=TESTDATA)
mock_entry.add_to_hass(hass)
@ -862,8 +893,9 @@ async def test_migrate_existing_ids(
@respx.mock
@pytest.mark.usefixtures("fakeimg_png")
async def test_use_wallclock_as_timestamps_option(
hass: HomeAssistant, fakeimg_png, mock_create_stream
hass: HomeAssistant, mock_create_stream: _patch[MagicMock]
) -> None:
"""Test the use_wallclock_as_timestamps option flow."""

View file

@ -6,12 +6,15 @@ from homeassistant.components.diagnostics import REDACTED
from homeassistant.components.generic.diagnostics import redact_url
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
async def test_entry_diagnostics(
hass: HomeAssistant, hass_client: ClientSessionGenerator, setup_entry
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
setup_entry: MockConfigEntry,
) -> None:
"""Test config entry diagnostics."""