266 lines
7.8 KiB
Python
266 lines
7.8 KiB
Python
"""Tests for the Mastodon services."""
|
|
|
|
from unittest.mock import AsyncMock, Mock, patch
|
|
|
|
from mastodon.Mastodon import MastodonAPIError, MediaAttachment
|
|
import pytest
|
|
|
|
from homeassistant.components.mastodon.const import (
|
|
ATTR_CONFIG_ENTRY_ID,
|
|
ATTR_CONTENT_WARNING,
|
|
ATTR_MEDIA,
|
|
ATTR_MEDIA_DESCRIPTION,
|
|
ATTR_STATUS,
|
|
ATTR_VISIBILITY,
|
|
DOMAIN,
|
|
)
|
|
from homeassistant.components.mastodon.services import SERVICE_POST
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
|
|
|
from . import setup_integration
|
|
|
|
from tests.common import MockConfigEntry
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("payload", "kwargs"),
|
|
[
|
|
(
|
|
{
|
|
ATTR_STATUS: "test toot",
|
|
},
|
|
{
|
|
"status": "test toot",
|
|
"spoiler_text": None,
|
|
"visibility": None,
|
|
"media_ids": None,
|
|
"sensitive": None,
|
|
},
|
|
),
|
|
(
|
|
{ATTR_STATUS: "test toot", ATTR_VISIBILITY: "private"},
|
|
{
|
|
"status": "test toot",
|
|
"spoiler_text": None,
|
|
"visibility": "private",
|
|
"media_ids": None,
|
|
"sensitive": None,
|
|
},
|
|
),
|
|
(
|
|
{
|
|
ATTR_STATUS: "test toot",
|
|
ATTR_CONTENT_WARNING: "Spoiler",
|
|
ATTR_VISIBILITY: "private",
|
|
},
|
|
{
|
|
"status": "test toot",
|
|
"spoiler_text": "Spoiler",
|
|
"visibility": "private",
|
|
"media_ids": None,
|
|
"sensitive": None,
|
|
},
|
|
),
|
|
(
|
|
{
|
|
ATTR_STATUS: "test toot",
|
|
ATTR_CONTENT_WARNING: "Spoiler",
|
|
ATTR_MEDIA: "/image.jpg",
|
|
},
|
|
{
|
|
"status": "test toot",
|
|
"spoiler_text": "Spoiler",
|
|
"visibility": None,
|
|
"media_ids": "1",
|
|
"sensitive": None,
|
|
},
|
|
),
|
|
(
|
|
{
|
|
ATTR_STATUS: "test toot",
|
|
ATTR_CONTENT_WARNING: "Spoiler",
|
|
ATTR_MEDIA: "/image.jpg",
|
|
ATTR_MEDIA_DESCRIPTION: "A test image",
|
|
},
|
|
{
|
|
"status": "test toot",
|
|
"spoiler_text": "Spoiler",
|
|
"visibility": None,
|
|
"media_ids": "1",
|
|
"sensitive": None,
|
|
},
|
|
),
|
|
],
|
|
)
|
|
async def test_service_post(
|
|
hass: HomeAssistant,
|
|
mock_mastodon_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
payload: dict[str, str],
|
|
kwargs: dict[str, str | None],
|
|
) -> None:
|
|
"""Test the post service."""
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
with (
|
|
patch.object(hass.config, "is_allowed_path", return_value=True),
|
|
patch.object(
|
|
mock_mastodon_client, "media_post", return_value=MediaAttachment(id="1")
|
|
),
|
|
):
|
|
await hass.services.async_call(
|
|
DOMAIN,
|
|
SERVICE_POST,
|
|
{
|
|
ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
|
|
}
|
|
| payload,
|
|
blocking=True,
|
|
return_response=False,
|
|
)
|
|
|
|
mock_mastodon_client.status_post.assert_called_with(**kwargs)
|
|
|
|
mock_mastodon_client.status_post.reset_mock()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("payload", "kwargs"),
|
|
[
|
|
(
|
|
{
|
|
ATTR_STATUS: "test toot",
|
|
},
|
|
{"status": "test toot", "spoiler_text": None, "visibility": None},
|
|
),
|
|
(
|
|
{
|
|
ATTR_STATUS: "test toot",
|
|
ATTR_CONTENT_WARNING: "Spoiler",
|
|
ATTR_MEDIA: "/image.jpg",
|
|
},
|
|
{
|
|
"status": "test toot",
|
|
"spoiler_text": "Spoiler",
|
|
"visibility": None,
|
|
"media_ids": "1",
|
|
"media_description": None,
|
|
"sensitive": None,
|
|
},
|
|
),
|
|
],
|
|
)
|
|
async def test_post_service_failed(
|
|
hass: HomeAssistant,
|
|
mock_mastodon_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
payload: dict[str, str],
|
|
kwargs: dict[str, str | None],
|
|
) -> None:
|
|
"""Test the post service raising an error."""
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
hass.config.is_allowed_path = Mock(return_value=True)
|
|
mock_mastodon_client.media_post.return_value = MediaAttachment(id="1")
|
|
|
|
mock_mastodon_client.status_post.side_effect = MastodonAPIError
|
|
|
|
with pytest.raises(HomeAssistantError, match="Unable to send message"):
|
|
await hass.services.async_call(
|
|
DOMAIN,
|
|
SERVICE_POST,
|
|
{ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id} | payload,
|
|
blocking=True,
|
|
return_response=False,
|
|
)
|
|
|
|
|
|
async def test_post_media_upload_failed(
|
|
hass: HomeAssistant,
|
|
mock_mastodon_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test the post service raising an error because media upload fails."""
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
payload = {"status": "test toot", "media": "/fail.jpg"}
|
|
|
|
mock_mastodon_client.media_post.side_effect = MastodonAPIError
|
|
|
|
with (
|
|
patch.object(hass.config, "is_allowed_path", return_value=True),
|
|
pytest.raises(HomeAssistantError, match="Unable to upload image /fail.jpg"),
|
|
):
|
|
await hass.services.async_call(
|
|
DOMAIN,
|
|
SERVICE_POST,
|
|
{ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id} | payload,
|
|
blocking=True,
|
|
return_response=False,
|
|
)
|
|
|
|
|
|
async def test_post_path_not_whitelisted(
|
|
hass: HomeAssistant,
|
|
mock_mastodon_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test the post service raising an error because the file path is not whitelisted."""
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
payload = {"status": "test toot", "media": "/fail.jpg"}
|
|
|
|
with pytest.raises(
|
|
HomeAssistantError, match="/fail.jpg is not a whitelisted directory"
|
|
):
|
|
await hass.services.async_call(
|
|
DOMAIN,
|
|
SERVICE_POST,
|
|
{ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id} | payload,
|
|
blocking=True,
|
|
return_response=False,
|
|
)
|
|
|
|
|
|
async def test_service_entry_availability(
|
|
hass: HomeAssistant,
|
|
mock_mastodon_client: AsyncMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test the services without valid entry."""
|
|
mock_config_entry.add_to_hass(hass)
|
|
mock_config_entry2 = MockConfigEntry(domain=DOMAIN)
|
|
mock_config_entry2.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
payload = {"status": "test toot"}
|
|
|
|
with pytest.raises(ServiceValidationError, match="Mock Title is not loaded"):
|
|
await hass.services.async_call(
|
|
DOMAIN,
|
|
SERVICE_POST,
|
|
{ATTR_CONFIG_ENTRY_ID: mock_config_entry2.entry_id} | payload,
|
|
blocking=True,
|
|
return_response=False,
|
|
)
|
|
|
|
with pytest.raises(
|
|
ServiceValidationError, match='Integration "mastodon" not found in registry'
|
|
):
|
|
await hass.services.async_call(
|
|
DOMAIN,
|
|
SERVICE_POST,
|
|
{ATTR_CONFIG_ENTRY_ID: "bad-config_id"} | payload,
|
|
blocking=True,
|
|
return_response=False,
|
|
)
|