mirror of https://github.com/home-assistant/core
317 lines
9.3 KiB
Python
317 lines
9.3 KiB
Python
"""Define fixtures available for all tests."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Generator
|
|
from pathlib import Path
|
|
import re
|
|
import tempfile
|
|
from unittest.mock import patch
|
|
|
|
from nio import (
|
|
AsyncClient,
|
|
ErrorResponse,
|
|
JoinError,
|
|
JoinResponse,
|
|
LocalProtocolError,
|
|
LoginError,
|
|
LoginResponse,
|
|
Response,
|
|
RoomResolveAliasError,
|
|
RoomResolveAliasResponse,
|
|
UploadResponse,
|
|
WhoamiError,
|
|
WhoamiResponse,
|
|
)
|
|
from PIL import Image
|
|
import pytest
|
|
|
|
from homeassistant.components.matrix import (
|
|
CONF_COMMANDS,
|
|
CONF_EXPRESSION,
|
|
CONF_HOMESERVER,
|
|
CONF_ROOMS,
|
|
CONF_WORD,
|
|
EVENT_MATRIX_COMMAND,
|
|
MatrixBot,
|
|
RoomAlias,
|
|
RoomAnyID,
|
|
RoomID,
|
|
)
|
|
from homeassistant.components.matrix.const import DOMAIN as MATRIX_DOMAIN
|
|
from homeassistant.components.matrix.notify import CONF_DEFAULT_ROOM
|
|
from homeassistant.components.notify import DOMAIN as NOTIFY_DOMAIN
|
|
from homeassistant.const import (
|
|
CONF_NAME,
|
|
CONF_PASSWORD,
|
|
CONF_PLATFORM,
|
|
CONF_USERNAME,
|
|
CONF_VERIFY_SSL,
|
|
)
|
|
from homeassistant.core import Event, HomeAssistant
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
from tests.common import async_capture_events
|
|
|
|
TEST_NOTIFIER_NAME = "matrix_notify"
|
|
|
|
TEST_HOMESERVER = "example.com"
|
|
TEST_DEFAULT_ROOM = RoomID("!DefaultNotificationRoom:example.com")
|
|
TEST_ROOM_A_ID = RoomID("!RoomA-ID:example.com")
|
|
TEST_ROOM_B_ID = RoomID("!RoomB-ID:example.com")
|
|
TEST_ROOM_B_ALIAS = RoomAlias("#RoomB-Alias:example.com")
|
|
TEST_ROOM_C_ID = RoomID("!RoomC-ID:example.com")
|
|
TEST_JOINABLE_ROOMS: dict[RoomAnyID, RoomID] = {
|
|
TEST_ROOM_A_ID: TEST_ROOM_A_ID,
|
|
TEST_ROOM_B_ALIAS: TEST_ROOM_B_ID,
|
|
TEST_ROOM_C_ID: TEST_ROOM_C_ID,
|
|
}
|
|
TEST_BAD_ROOM = "!UninvitedRoom:example.com"
|
|
TEST_MXID = "@user:example.com"
|
|
TEST_DEVICE_ID = "FAKEID"
|
|
TEST_PASSWORD = "password"
|
|
TEST_TOKEN = "access_token"
|
|
|
|
NIO_IMPORT_PREFIX = "homeassistant.components.matrix.nio."
|
|
|
|
|
|
class _MockAsyncClient(AsyncClient):
|
|
"""Mock class to simulate MatrixBot._client's I/O methods."""
|
|
|
|
async def close(self):
|
|
return None
|
|
|
|
async def room_resolve_alias(self, room_alias: RoomAnyID):
|
|
if room_id := TEST_JOINABLE_ROOMS.get(room_alias):
|
|
return RoomResolveAliasResponse(
|
|
room_alias=room_alias, room_id=room_id, servers=[TEST_HOMESERVER]
|
|
)
|
|
return RoomResolveAliasError(message=f"Could not resolve {room_alias}")
|
|
|
|
async def join(self, room_id: RoomID):
|
|
if room_id in TEST_JOINABLE_ROOMS.values():
|
|
return JoinResponse(room_id=room_id)
|
|
return JoinError(message="Not allowed to join this room.")
|
|
|
|
async def login(self, *args, **kwargs):
|
|
if kwargs.get("password") == TEST_PASSWORD or kwargs.get("token") == TEST_TOKEN:
|
|
self.access_token = TEST_TOKEN
|
|
return LoginResponse(
|
|
access_token=TEST_TOKEN,
|
|
device_id="test_device",
|
|
user_id=TEST_MXID,
|
|
)
|
|
self.access_token = ""
|
|
return LoginError(message="LoginError", status_code="status_code")
|
|
|
|
async def logout(self, *args, **kwargs):
|
|
self.access_token = ""
|
|
|
|
async def whoami(self):
|
|
if self.access_token == TEST_TOKEN:
|
|
self.user_id = TEST_MXID
|
|
self.device_id = TEST_DEVICE_ID
|
|
return WhoamiResponse(
|
|
user_id=TEST_MXID, device_id=TEST_DEVICE_ID, is_guest=False
|
|
)
|
|
self.access_token = ""
|
|
return WhoamiError(
|
|
message="Invalid access token passed.", status_code="M_UNKNOWN_TOKEN"
|
|
)
|
|
|
|
async def room_send(self, *args, **kwargs):
|
|
if not self.logged_in:
|
|
raise LocalProtocolError
|
|
if kwargs["room_id"] not in TEST_JOINABLE_ROOMS.values():
|
|
return ErrorResponse(message="Cannot send a message in this room.")
|
|
return Response()
|
|
|
|
async def sync(self, *args, **kwargs):
|
|
return None
|
|
|
|
async def sync_forever(self, *args, **kwargs):
|
|
return None
|
|
|
|
async def upload(self, *args, **kwargs):
|
|
return UploadResponse(content_uri="mxc://example.com/randomgibberish"), None
|
|
|
|
|
|
MOCK_CONFIG_DATA = {
|
|
MATRIX_DOMAIN: {
|
|
CONF_HOMESERVER: "https://matrix.example.com",
|
|
CONF_USERNAME: TEST_MXID,
|
|
CONF_PASSWORD: TEST_PASSWORD,
|
|
CONF_VERIFY_SSL: True,
|
|
CONF_ROOMS: list(TEST_JOINABLE_ROOMS),
|
|
CONF_COMMANDS: [
|
|
{
|
|
CONF_WORD: "WordTrigger",
|
|
CONF_NAME: "WordTriggerEventName",
|
|
},
|
|
{
|
|
CONF_EXPRESSION: "My name is (?P<name>.*)",
|
|
CONF_NAME: "ExpressionTriggerEventName",
|
|
},
|
|
{
|
|
CONF_WORD: "WordTriggerSubset",
|
|
CONF_NAME: "WordTriggerSubsetEventName",
|
|
CONF_ROOMS: [TEST_ROOM_B_ALIAS, TEST_ROOM_C_ID],
|
|
},
|
|
{
|
|
CONF_EXPRESSION: "Your name is (?P<name>.*)",
|
|
CONF_NAME: "ExpressionTriggerSubsetEventName",
|
|
CONF_ROOMS: [TEST_ROOM_B_ALIAS, TEST_ROOM_C_ID],
|
|
},
|
|
],
|
|
},
|
|
NOTIFY_DOMAIN: {
|
|
CONF_NAME: TEST_NOTIFIER_NAME,
|
|
CONF_PLATFORM: MATRIX_DOMAIN,
|
|
CONF_DEFAULT_ROOM: TEST_DEFAULT_ROOM,
|
|
},
|
|
}
|
|
|
|
MOCK_WORD_COMMANDS = {
|
|
TEST_ROOM_A_ID: {
|
|
"WordTrigger": {
|
|
"word": "WordTrigger",
|
|
"name": "WordTriggerEventName",
|
|
"rooms": list(TEST_JOINABLE_ROOMS.values()),
|
|
}
|
|
},
|
|
TEST_ROOM_B_ID: {
|
|
"WordTrigger": {
|
|
"word": "WordTrigger",
|
|
"name": "WordTriggerEventName",
|
|
"rooms": list(TEST_JOINABLE_ROOMS.values()),
|
|
},
|
|
"WordTriggerSubset": {
|
|
"word": "WordTriggerSubset",
|
|
"name": "WordTriggerSubsetEventName",
|
|
"rooms": [TEST_ROOM_B_ID, TEST_ROOM_C_ID],
|
|
},
|
|
},
|
|
TEST_ROOM_C_ID: {
|
|
"WordTrigger": {
|
|
"word": "WordTrigger",
|
|
"name": "WordTriggerEventName",
|
|
"rooms": list(TEST_JOINABLE_ROOMS.values()),
|
|
},
|
|
"WordTriggerSubset": {
|
|
"word": "WordTriggerSubset",
|
|
"name": "WordTriggerSubsetEventName",
|
|
"rooms": [TEST_ROOM_B_ID, TEST_ROOM_C_ID],
|
|
},
|
|
},
|
|
}
|
|
|
|
MOCK_EXPRESSION_COMMANDS = {
|
|
TEST_ROOM_A_ID: [
|
|
{
|
|
"expression": re.compile("My name is (?P<name>.*)"),
|
|
"name": "ExpressionTriggerEventName",
|
|
"rooms": list(TEST_JOINABLE_ROOMS.values()),
|
|
}
|
|
],
|
|
TEST_ROOM_B_ID: [
|
|
{
|
|
"expression": re.compile("My name is (?P<name>.*)"),
|
|
"name": "ExpressionTriggerEventName",
|
|
"rooms": list(TEST_JOINABLE_ROOMS.values()),
|
|
},
|
|
{
|
|
"expression": re.compile("Your name is (?P<name>.*)"),
|
|
"name": "ExpressionTriggerSubsetEventName",
|
|
"rooms": [TEST_ROOM_B_ID, TEST_ROOM_C_ID],
|
|
},
|
|
],
|
|
TEST_ROOM_C_ID: [
|
|
{
|
|
"expression": re.compile("My name is (?P<name>.*)"),
|
|
"name": "ExpressionTriggerEventName",
|
|
"rooms": list(TEST_JOINABLE_ROOMS.values()),
|
|
},
|
|
{
|
|
"expression": re.compile("Your name is (?P<name>.*)"),
|
|
"name": "ExpressionTriggerSubsetEventName",
|
|
"rooms": [TEST_ROOM_B_ID, TEST_ROOM_C_ID],
|
|
},
|
|
],
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_client():
|
|
"""Return mocked AsyncClient."""
|
|
with patch("homeassistant.components.matrix.AsyncClient", _MockAsyncClient) as mock:
|
|
yield mock
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_save_json():
|
|
"""Prevent saving test access_tokens."""
|
|
with patch("homeassistant.components.matrix.save_json") as mock:
|
|
yield mock
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_load_json():
|
|
"""Mock loading access_tokens from a file."""
|
|
with patch(
|
|
"homeassistant.components.matrix.load_json_object",
|
|
return_value={TEST_MXID: TEST_TOKEN},
|
|
) as mock:
|
|
yield mock
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_allowed_path():
|
|
"""Allow using NamedTemporaryFile for mock image."""
|
|
with patch(
|
|
"homeassistant.core_config.Config.is_allowed_path", return_value=True
|
|
) as mock:
|
|
yield mock
|
|
|
|
|
|
@pytest.fixture
|
|
async def matrix_bot(
|
|
hass: HomeAssistant, mock_client, mock_save_json, mock_allowed_path
|
|
) -> MatrixBot:
|
|
"""Set up Matrix and Notify component.
|
|
|
|
The resulting MatrixBot will have a mocked _client.
|
|
"""
|
|
|
|
assert await async_setup_component(hass, MATRIX_DOMAIN, MOCK_CONFIG_DATA)
|
|
assert await async_setup_component(hass, NOTIFY_DOMAIN, MOCK_CONFIG_DATA)
|
|
await hass.async_block_till_done()
|
|
|
|
# Accessing hass.data in tests is not desirable, but all the tests here
|
|
# currently do this.
|
|
assert isinstance(matrix_bot := hass.data[MATRIX_DOMAIN], MatrixBot)
|
|
|
|
await hass.async_start()
|
|
|
|
return matrix_bot
|
|
|
|
|
|
@pytest.fixture
|
|
def matrix_events(hass: HomeAssistant) -> list[Event]:
|
|
"""Track event calls."""
|
|
return async_capture_events(hass, MATRIX_DOMAIN)
|
|
|
|
|
|
@pytest.fixture
|
|
def command_events(hass: HomeAssistant) -> list[Event]:
|
|
"""Track event calls."""
|
|
return async_capture_events(hass, EVENT_MATRIX_COMMAND)
|
|
|
|
|
|
@pytest.fixture
|
|
def image_path(tmp_path: Path) -> Generator[tempfile._TemporaryFileWrapper]:
|
|
"""Provide the Path to a mock image."""
|
|
image = Image.new("RGBA", size=(50, 50), color=(256, 0, 0))
|
|
with tempfile.NamedTemporaryFile(dir=tmp_path) as image_file:
|
|
image.save(image_file, "PNG")
|
|
yield image_file
|