mirror of
https://github.com/home-assistant/core.git
synced 2026-07-04 13:15:29 +01:00
4f8885b40d
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
164 lines
5.1 KiB
Python
164 lines
5.1 KiB
Python
"""Test downloader services."""
|
|
|
|
import asyncio
|
|
from contextlib import AbstractContextManager, nullcontext as does_not_raise
|
|
|
|
import pytest
|
|
import requests
|
|
from requests_mock import Mocker
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.components.downloader.const import DOMAIN
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
|
|
|
|
|
@pytest.mark.usefixtures("setup_integration")
|
|
@pytest.mark.parametrize(
|
|
("subdir", "expected_result"),
|
|
[
|
|
("test", does_not_raise()),
|
|
("test/path", does_not_raise()),
|
|
("~test/path", pytest.raises(ServiceValidationError)),
|
|
("~/../test/path", pytest.raises(ServiceValidationError)),
|
|
("../test/path", pytest.raises(ServiceValidationError)),
|
|
(".../test/path", pytest.raises(ServiceValidationError)),
|
|
("/test/path", pytest.raises(ServiceValidationError)),
|
|
],
|
|
)
|
|
async def test_download_invalid_subdir(
|
|
hass: HomeAssistant,
|
|
download_completed: asyncio.Event,
|
|
download_failed: asyncio.Event,
|
|
download_url: str,
|
|
subdir: str,
|
|
expected_result: AbstractContextManager,
|
|
) -> None:
|
|
"""Test service invalid subdirectory."""
|
|
|
|
async def call_service() -> None:
|
|
"""Call the download service."""
|
|
completed = hass.async_create_task(download_completed.wait())
|
|
failed = hass.async_create_task(download_failed.wait())
|
|
await hass.services.async_call(
|
|
DOMAIN,
|
|
"download_file",
|
|
{
|
|
"url": download_url,
|
|
"subdir": subdir,
|
|
"filename": "file.txt",
|
|
"overwrite": True,
|
|
},
|
|
blocking=True,
|
|
)
|
|
await asyncio.wait((completed, failed), return_when=asyncio.FIRST_COMPLETED)
|
|
|
|
with expected_result:
|
|
await call_service()
|
|
|
|
|
|
@pytest.mark.usefixtures("setup_integration")
|
|
async def test_download_headers_passed_through(
|
|
hass: HomeAssistant,
|
|
requests_mock: Mocker,
|
|
download_completed: asyncio.Event,
|
|
download_url: str,
|
|
) -> None:
|
|
"""Test that custom headers are passed to the HTTP request."""
|
|
await hass.services.async_call(
|
|
DOMAIN,
|
|
"download_file",
|
|
{
|
|
"url": download_url,
|
|
"headers": {"Authorization": "Bearer token123", "X-Custom": "value"},
|
|
},
|
|
blocking=True,
|
|
)
|
|
await download_completed.wait()
|
|
|
|
assert requests_mock.last_request.headers["Authorization"] == "Bearer token123"
|
|
assert requests_mock.last_request.headers["X-Custom"] == "value"
|
|
|
|
|
|
@pytest.mark.usefixtures("setup_integration")
|
|
@pytest.mark.parametrize(
|
|
("headers", "expected_result"),
|
|
[
|
|
(1, pytest.raises(vol.error.Invalid)), # Not a dictionary
|
|
({"Accept": "application/json"}, does_not_raise()),
|
|
({123: 456.789}, does_not_raise()), # Convert numbers to strings
|
|
(
|
|
{"Accept": ["application/json"]},
|
|
pytest.raises(vol.error.MultipleInvalid),
|
|
), # Value is not a string
|
|
({1: None}, pytest.raises(vol.error.MultipleInvalid)), # Value is None
|
|
(
|
|
{None: "application/json"},
|
|
pytest.raises(vol.error.MultipleInvalid),
|
|
), # Key is None
|
|
],
|
|
)
|
|
async def test_download_headers_schema(
|
|
hass: HomeAssistant,
|
|
download_completed: asyncio.Event,
|
|
download_failed: asyncio.Event,
|
|
download_url: str,
|
|
headers: dict[str, str],
|
|
expected_result: AbstractContextManager,
|
|
) -> None:
|
|
"""Test service with headers."""
|
|
|
|
async def call_service() -> None:
|
|
"""Call the download service."""
|
|
completed = hass.async_create_task(download_completed.wait())
|
|
failed = hass.async_create_task(download_failed.wait())
|
|
await hass.services.async_call(
|
|
DOMAIN,
|
|
"download_file",
|
|
{
|
|
"url": download_url,
|
|
"headers": headers,
|
|
"subdir": "test",
|
|
"filename": "file.txt",
|
|
"overwrite": True,
|
|
},
|
|
blocking=True,
|
|
)
|
|
await asyncio.wait((completed, failed), return_when=asyncio.FIRST_COMPLETED)
|
|
|
|
with expected_result:
|
|
await call_service()
|
|
|
|
|
|
@pytest.mark.usefixtures("setup_integration")
|
|
@pytest.mark.parametrize(
|
|
("exception", "expected_error"),
|
|
[
|
|
pytest.param(
|
|
requests.exceptions.ConnectionError,
|
|
HomeAssistantError,
|
|
id="connection_error",
|
|
),
|
|
pytest.param(
|
|
ValueError,
|
|
ServiceValidationError,
|
|
id="value_error",
|
|
),
|
|
],
|
|
)
|
|
async def test_download_exceptions(
|
|
hass: HomeAssistant,
|
|
requests_mock: Mocker,
|
|
download_url: str,
|
|
download_failed: asyncio.Event,
|
|
exception: type[Exception],
|
|
expected_error: type[Exception],
|
|
) -> None:
|
|
"""Test that exceptions during download propagate to the caller."""
|
|
requests_mock.get(download_url, exc=exception)
|
|
with pytest.raises(expected_error):
|
|
await hass.services.async_call(
|
|
DOMAIN, "download_file", {"url": download_url}, blocking=True
|
|
)
|
|
await download_failed.wait()
|