Fix, improve input validation and add tests to ClickSend tts (#76669)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
CharlB 2022-10-12 11:27:46 +02:00 committed by GitHub
parent d03e0380bb
commit 107e1ed16c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 136 additions and 9 deletions

View file

@ -9,6 +9,7 @@ import voluptuous as vol
from homeassistant.components.notify import PLATFORM_SCHEMA, BaseNotificationService
from homeassistant.const import (
CONF_API_KEY,
CONF_NAME,
CONF_RECIPIENT,
CONF_USERNAME,
CONTENT_TYPE_JSON,
@ -23,20 +24,27 @@ HEADERS = {"Content-Type": CONTENT_TYPE_JSON}
CONF_LANGUAGE = "language"
CONF_VOICE = "voice"
CONF_CALLER = "caller"
MALE_VOICE = "male"
FEMALE_VOICE = "female"
DEFAULT_NAME = "clicksend_tts"
DEFAULT_LANGUAGE = "en-us"
DEFAULT_VOICE = "female"
DEFAULT_VOICE = FEMALE_VOICE
TIMEOUT = 5
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_API_KEY): cv.string,
vol.Required(CONF_RECIPIENT): cv.string,
vol.Required(CONF_RECIPIENT): vol.All(
cv.string, vol.Match(r"^\+?[1-9]\d{1,14}$")
),
vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): cv.string,
vol.Optional(CONF_VOICE, default=DEFAULT_VOICE): cv.string,
vol.Optional(CONF_CALLER): cv.string,
vol.Optional(CONF_VOICE, default=DEFAULT_VOICE): vol.In(
[MALE_VOICE, FEMALE_VOICE]
),
}
)
@ -60,9 +68,6 @@ class ClicksendNotificationService(BaseNotificationService):
self.recipient = config[CONF_RECIPIENT]
self.language = config[CONF_LANGUAGE]
self.voice = config[CONF_VOICE]
self.caller = config.get(CONF_CALLER)
if self.caller is None:
self.caller = self.recipient
def send_message(self, message="", **kwargs):
"""Send a voice call to a user."""
@ -70,7 +75,6 @@ class ClicksendNotificationService(BaseNotificationService):
"messages": [
{
"source": "hass.notify",
"from": self.caller,
"to": self.recipient,
"body": message,
"lang": self.language,

View file

@ -0,0 +1 @@
"""Tests for the ClickSend TTS component."""

View file

@ -0,0 +1,122 @@
"""The test for the Facebook notify module."""
import base64
from http import HTTPStatus
import logging
from unittest.mock import patch
import pytest
import requests_mock
from homeassistant.components import notify
import homeassistant.components.clicksend_tts.notify as cs_tts
from homeassistant.setup import async_setup_component
from tests.common import assert_setup_component
# Infos from https://developers.clicksend.com/docs/rest/v3/#testing
TEST_USERNAME = "nocredit"
TEST_API_KEY = "D83DED51-9E35-4D42-9BB9-0E34B7CA85AE"
TEST_VOICE_NUMBER = "+61411111111"
TEST_VOICE = "male"
TEST_LANGUAGE = "fr-fr"
TEST_MESSAGE = "Just a test message!"
CONFIG = {
notify.DOMAIN: {
"platform": "clicksend_tts",
cs_tts.CONF_USERNAME: TEST_USERNAME,
cs_tts.CONF_API_KEY: TEST_API_KEY,
cs_tts.CONF_RECIPIENT: TEST_VOICE_NUMBER,
cs_tts.CONF_LANGUAGE: TEST_LANGUAGE,
cs_tts.CONF_VOICE: TEST_VOICE,
}
}
@pytest.fixture
def mock_clicksend_tts_notify():
"""Mock Clicksend TTS notify service."""
with patch(
"homeassistant.components.clicksend_tts.notify.get_service", autospec=True
) as ns:
yield ns
async def setup_notify(hass):
"""Test setup."""
with assert_setup_component(1, notify.DOMAIN) as config:
assert await async_setup_component(hass, notify.DOMAIN, CONFIG)
assert config[notify.DOMAIN]
await hass.async_block_till_done()
async def test_no_notify_service(hass, mock_clicksend_tts_notify, caplog):
"""Test missing platform notify service instance."""
caplog.set_level(logging.ERROR)
mock_clicksend_tts_notify.return_value = None
await setup_notify(hass)
await hass.async_block_till_done()
assert mock_clicksend_tts_notify.called
assert "Failed to initialize notification service clicksend_tts" in caplog.text
async def test_send_simple_message(hass):
"""Test sending a simple message with success."""
with requests_mock.Mocker() as mock:
# Mocking authentication endpoint
mock.get(
f"{cs_tts.BASE_API_URL}/account",
status_code=HTTPStatus.OK,
)
# Mocking TTS endpoint
mock.post(
f"{cs_tts.BASE_API_URL}/voice/send",
status_code=HTTPStatus.OK,
)
# Setting up integration
await setup_notify(hass)
# Sending message
data = {
notify.ATTR_MESSAGE: TEST_MESSAGE,
}
await hass.services.async_call(
notify.DOMAIN, cs_tts.DEFAULT_NAME, data, blocking=True
)
# Checking if everything went well
assert mock.called
assert mock.call_count == 2
expected_body = {
"messages": [
{
"source": "hass.notify",
"to": TEST_VOICE_NUMBER,
"body": TEST_MESSAGE,
"lang": TEST_LANGUAGE,
"voice": TEST_VOICE,
}
]
}
assert mock.last_request.json() == expected_body
expected_content_type = "application/json"
assert (
"Content-Type" in mock.last_request.headers.keys()
and mock.last_request.headers["Content-Type"] == expected_content_type
)
encoded_auth = base64.b64encode(
f"{TEST_USERNAME}:{TEST_API_KEY}".encode()
).decode()
expected_auth = f"Basic {encoded_auth}"
assert (
"Authorization" in mock.last_request.headers
and mock.last_request.headers["Authorization"] == expected_auth
)