core/tests/components/local_file/test_camera.py

288 lines
8.3 KiB
Python

"""The tests for local file camera component."""
from http import HTTPStatus
from typing import Any
from unittest.mock import Mock, mock_open, patch
import pytest
from homeassistant.components.local_file.const import (
DEFAULT_NAME,
DOMAIN,
SERVICE_UPDATE_FILE_PATH,
)
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import ATTR_ENTITY_ID, CONF_FILE_PATH
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import issue_registry as ir
from homeassistant.setup import async_setup_component
from homeassistant.util import slugify
from tests.common import MockConfigEntry
from tests.typing import ClientSessionGenerator
async def test_loading_file(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
loaded_entry: MockConfigEntry,
) -> None:
"""Test that it loads image from disk."""
client = await hass_client()
m_open = mock_open(read_data=b"hello")
with patch("homeassistant.components.local_file.camera.open", m_open, create=True):
resp = await client.get("/api/camera_proxy/camera.local_file")
assert resp.status == HTTPStatus.OK
body = await resp.text()
assert body == "hello"
async def test_file_not_readable_after_setup(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
caplog: pytest.LogCaptureFixture,
loaded_entry: MockConfigEntry,
) -> None:
"""Test a warning is shown setup when file is not readable."""
client = await hass_client()
with patch(
"homeassistant.components.local_file.camera.open", side_effect=FileNotFoundError
):
resp = await client.get("/api/camera_proxy/camera.local_file")
assert resp.status == HTTPStatus.INTERNAL_SERVER_ERROR
assert "Could not read camera Local File image from file: mock.file" in caplog.text
@pytest.mark.parametrize(
("config", "url", "content_type"),
[
(
{
"name": "test_jpg",
"file_path": "/path/to/image.jpg",
},
"/api/camera_proxy/camera.test_jpg",
"image/jpeg",
),
(
{
"name": "test_png",
"file_path": "/path/to/image.png",
},
"/api/camera_proxy/camera.test_png",
"image/png",
),
(
{
"name": "test_svg",
"file_path": "/path/to/image.svg",
},
"/api/camera_proxy/camera.test_svg",
"image/svg+xml",
),
(
{
"name": "test_no_ext",
"file_path": "/path/to/image",
},
"/api/camera_proxy/camera.test_no_ext",
"image/jpeg",
),
],
)
async def test_camera_content_type(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
config: dict[str, Any],
url: str,
content_type: str,
) -> None:
"""Test local_file camera content_type."""
config_entry = MockConfigEntry(
domain=DOMAIN,
source=SOURCE_USER,
options=config,
entry_id="1",
)
config_entry.add_to_hass(hass)
with (
patch("os.path.isfile", Mock(return_value=True)),
patch("os.access", Mock(return_value=True)),
):
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
client = await hass_client()
image = "hello"
m_open = mock_open(read_data=image.encode())
with patch("homeassistant.components.local_file.camera.open", m_open, create=True):
resp_1 = await client.get(url)
assert resp_1.status == HTTPStatus.OK
assert resp_1.content_type == content_type
body = await resp_1.text()
assert body == image
@pytest.mark.parametrize(
"get_config",
[
{
"name": DEFAULT_NAME,
"file_path": "mock/path.jpg",
}
],
)
async def test_update_file_path(
hass: HomeAssistant, loaded_entry: MockConfigEntry
) -> None:
"""Test update_file_path service."""
# Setup platform
config_entry = MockConfigEntry(
domain=DOMAIN,
source=SOURCE_USER,
options={
"name": "local_file_camera_2",
"file_path": "mock/path_2.jpg",
},
entry_id="2",
)
config_entry.add_to_hass(hass)
with (
patch("os.path.isfile", Mock(return_value=True)),
patch("os.access", Mock(return_value=True)),
patch(
"homeassistant.components.local_file.camera.mimetypes.guess_type",
Mock(return_value=(None, None)),
),
):
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
# Fetch state and check motion detection attribute
state = hass.states.get("camera.local_file")
assert state.attributes.get("friendly_name") == "Local File"
assert state.attributes.get("file_path") == "mock/path.jpg"
service_data = {"entity_id": "camera.local_file", "file_path": "new/path.jpg"}
with (
patch("os.path.isfile", Mock(return_value=True)),
patch("os.access", Mock(return_value=True)),
patch(
"homeassistant.components.local_file.camera.mimetypes.guess_type",
Mock(return_value=(None, None)),
),
):
await hass.services.async_call(
DOMAIN,
SERVICE_UPDATE_FILE_PATH,
service_data,
blocking=True,
)
state = hass.states.get("camera.local_file")
assert state.attributes.get("file_path") == "new/path.jpg"
# Check that local_file_camera_2 file_path is still as configured
state = hass.states.get("camera.local_file_camera_2")
assert state.attributes.get("file_path") == "mock/path_2.jpg"
# Assert it fails if file is not readable
service_data = {
ATTR_ENTITY_ID: "camera.local_file",
CONF_FILE_PATH: "new/path2.jpg",
}
with pytest.raises(
ServiceValidationError, match="Path new/path2.jpg is not accessible"
):
await hass.services.async_call(
DOMAIN,
SERVICE_UPDATE_FILE_PATH,
service_data,
blocking=True,
)
async def test_import_from_yaml_success(
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test import."""
with (
patch("os.path.isfile", Mock(return_value=True)),
patch("os.access", Mock(return_value=True)),
patch(
"homeassistant.components.local_file.camera.mimetypes.guess_type",
Mock(return_value=(None, None)),
),
):
await async_setup_component(
hass,
"camera",
{
"camera": {
"name": "config_test",
"platform": "local_file",
"file_path": "mock.file",
}
},
)
await hass.async_block_till_done()
assert hass.config_entries.async_has_entries(DOMAIN)
state = hass.states.get("camera.config_test")
assert state.attributes.get("file_path") == "mock.file"
issue = issue_registry.async_get_issue(
HOMEASSISTANT_DOMAIN, f"deprecated_yaml_{DOMAIN}"
)
assert issue
assert issue.translation_key == "deprecated_yaml"
async def test_import_from_yaml_fails(
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test import fails due to not accessible file."""
with (
patch("os.path.isfile", Mock(return_value=True)),
patch("os.access", Mock(return_value=False)),
patch(
"homeassistant.components.local_file.camera.mimetypes.guess_type",
Mock(return_value=(None, None)),
),
):
await async_setup_component(
hass,
"camera",
{
"camera": {
"name": "config_test",
"platform": "local_file",
"file_path": "mock.file",
}
},
)
await hass.async_block_till_done()
assert not hass.config_entries.async_has_entries(DOMAIN)
assert not hass.states.get("camera.config_test")
issue = issue_registry.async_get_issue(
DOMAIN, f"no_access_path_{slugify("mock.file")}"
)
assert issue
assert issue.translation_key == "no_access_path"