Hide TTS filename behind random token (#131192)

* Hide TTS filename behind random token

* Clean up and fix test snapshots

* Fix tests

* Fix cloud tests
This commit is contained in:
Michael Hansen 2024-11-24 19:52:21 -06:00 committed by GitHub
parent cb4636ada1
commit d4071e7123
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 694 additions and 657 deletions

View File

@ -13,6 +13,7 @@ import logging
import mimetypes
import os
import re
import secrets
import subprocess
import tempfile
from typing import Any, Final, TypedDict, final
@ -540,6 +541,10 @@ class SpeechManager:
self.file_cache: dict[str, str] = {}
self.mem_cache: dict[str, TTSCache] = {}
# filename <-> token
self.filename_to_token: dict[str, str] = {}
self.token_to_filename: dict[str, str] = {}
def _init_cache(self) -> dict[str, str]:
"""Init cache folder and fetch files."""
try:
@ -656,7 +661,17 @@ class SpeechManager:
engine_instance, cache_key, message, use_cache, language, options
)
return f"/api/tts_proxy/{filename}"
# Use a randomly generated token instead of exposing the filename
token = self.filename_to_token.get(filename)
if not token:
# Keep extension (.mp3, etc.)
token = secrets.token_urlsafe(16) + os.path.splitext(filename)[1]
# Map token <-> filename
self.filename_to_token[filename] = token
self.token_to_filename[token] = filename
return f"/api/tts_proxy/{token}"
async def async_get_tts_audio(
self,
@ -910,11 +925,15 @@ class SpeechManager:
),
)
async def async_read_tts(self, filename: str) -> tuple[str | None, bytes]:
async def async_read_tts(self, token: str) -> tuple[str | None, bytes]:
"""Read a voice file and return binary.
This method is a coroutine.
"""
filename = self.token_to_filename.get(token)
if not filename:
raise HomeAssistantError(f"{token} was not recognized!")
if not (record := _RE_VOICE_FILE.match(filename.lower())) and not (
record := _RE_LEGACY_VOICE_FILE.match(filename.lower())
):
@ -1076,6 +1095,7 @@ class TextToSpeechView(HomeAssistantView):
async def get(self, request: web.Request, filename: str) -> web.Response:
"""Start a get request."""
try:
# filename is actually token, but we keep its name for compatibility
content, data = await self.tts.async_read_tts(filename)
except HomeAssistantError as err:
_LOGGER.error("Error on load tts: %s", err)

View File

@ -77,7 +77,7 @@
'tts_output': dict({
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&tts_options=%7B%22voice%22:%22james_earl_jones%22%7D",
'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
'url': '/api/tts_proxy/test_token.mp3',
}),
}),
'type': <PipelineEventType.TTS_END: 'tts-end'>,
@ -166,7 +166,7 @@
'tts_output': dict({
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&tts_options=%7B%22voice%22:%22Arnold+Schwarzenegger%22%7D",
'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_2657c1a8ee_test.mp3',
'url': '/api/tts_proxy/test_token.mp3',
}),
}),
'type': <PipelineEventType.TTS_END: 'tts-end'>,
@ -255,7 +255,7 @@
'tts_output': dict({
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&tts_options=%7B%22voice%22:%22Arnold+Schwarzenegger%22%7D",
'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_2657c1a8ee_test.mp3',
'url': '/api/tts_proxy/test_token.mp3',
}),
}),
'type': <PipelineEventType.TTS_END: 'tts-end'>,
@ -368,7 +368,7 @@
'tts_output': dict({
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&tts_options=%7B%22voice%22:%22james_earl_jones%22%7D",
'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
'url': '/api/tts_proxy/test_token.mp3',
}),
}),
'type': <PipelineEventType.TTS_END: 'tts-end'>,

View File

@ -73,7 +73,7 @@
'tts_output': dict({
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&tts_options=%7B%22voice%22:%22james_earl_jones%22%7D",
'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
'url': '/api/tts_proxy/test_token.mp3',
}),
})
# ---
@ -154,7 +154,7 @@
'tts_output': dict({
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&tts_options=%7B%22voice%22:%22james_earl_jones%22%7D",
'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
'url': '/api/tts_proxy/test_token.mp3',
}),
})
# ---
@ -247,7 +247,7 @@
'tts_output': dict({
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&tts_options=%7B%22voice%22:%22james_earl_jones%22%7D",
'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
'url': '/api/tts_proxy/test_token.mp3',
}),
})
# ---
@ -350,7 +350,7 @@
'tts_output': dict({
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&tts_options=%7B%22voice%22:%22james_earl_jones%22%7D",
'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
'url': '/api/tts_proxy/test_token.mp3',
}),
})
# ---

View File

@ -70,6 +70,9 @@ async def test_pipeline_from_audio_stream_auto(
yield make_10ms_chunk(b"part2")
yield b""
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
await assist_pipeline.async_pipeline_from_audio_stream(
hass,
context=Context(),
@ -133,6 +136,9 @@ async def test_pipeline_from_audio_stream_legacy(
assert msg["success"]
pipeline_id = msg["result"]["id"]
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
# Use the created pipeline
await assist_pipeline.async_pipeline_from_audio_stream(
hass,
@ -198,6 +204,9 @@ async def test_pipeline_from_audio_stream_entity(
assert msg["success"]
pipeline_id = msg["result"]["id"]
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
# Use the created pipeline
await assist_pipeline.async_pipeline_from_audio_stream(
hass,
@ -362,6 +371,9 @@ async def test_pipeline_from_audio_stream_wake_word(
yield b""
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
await assist_pipeline.async_pipeline_from_audio_stream(
hass,
context=Context(),

View File

@ -119,6 +119,9 @@ async def test_audio_pipeline(
events = []
client = await hass_ws_client(hass)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
await client.send_json_auto_id(
{
"type": "assist_pipeline/run",
@ -210,6 +213,9 @@ async def test_audio_pipeline_with_wake_word_timeout(
events = []
client = await hass_ws_client(hass)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
await client.send_json_auto_id(
{
"type": "assist_pipeline/run",
@ -265,6 +271,9 @@ async def test_audio_pipeline_with_wake_word_no_timeout(
events = []
client = await hass_ws_client(hass)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
await client.send_json_auto_id(
{
"type": "assist_pipeline/run",
@ -1540,6 +1549,9 @@ async def test_audio_pipeline_debug(
events = []
client = await hass_ws_client(hass)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
await client.send_json_auto_id(
{
"type": "assist_pipeline/run",
@ -1787,6 +1799,9 @@ async def test_audio_pipeline_with_enhancements(
events = []
client = await hass_ws_client(hass)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
await client.send_json_auto_id(
{
"type": "assist_pipeline/run",

View File

@ -227,6 +227,9 @@ async def test_get_tts_audio(
await on_start_callback()
client = await hass_client()
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
url = "/api/tts_get_url"
data |= {"message": "There is someone at the door."}
@ -235,15 +238,8 @@ async def test_get_tts_audio(
response = await req.json()
assert response == {
"url": (
"http://example.local:8123/api/tts_proxy/"
"42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_en-us_6e8b81ac47_{expected_url_suffix}.mp3"
),
"path": (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_en-us_6e8b81ac47_{expected_url_suffix}.mp3"
),
"url": ("http://example.local:8123/api/tts_proxy/test_token.mp3"),
"path": ("/api/tts_proxy/test_token.mp3"),
}
await hass.async_block_till_done()
@ -280,6 +276,9 @@ async def test_get_tts_audio_logged_out(
await hass.async_block_till_done()
client = await hass_client()
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
url = "/api/tts_get_url"
data |= {"message": "There is someone at the door."}
@ -288,15 +287,8 @@ async def test_get_tts_audio_logged_out(
response = await req.json()
assert response == {
"url": (
"http://example.local:8123/api/tts_proxy/"
"42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_en-us_6e8b81ac47_{expected_url_suffix}.mp3"
),
"path": (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_en-us_6e8b81ac47_{expected_url_suffix}.mp3"
),
"url": ("http://example.local:8123/api/tts_proxy/test_token.mp3"),
"path": ("/api/tts_proxy/test_token.mp3"),
}
await hass.async_block_till_done()
@ -342,6 +334,9 @@ async def test_tts_entity(
assert state
assert state.state == STATE_UNKNOWN
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
url = "/api/tts_get_url"
data = {
"engine_id": entity_id,
@ -353,15 +348,8 @@ async def test_tts_entity(
response = await req.json()
assert response == {
"url": (
"http://example.local:8123/api/tts_proxy/"
"42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_en-us_6e8b81ac47_{entity_id}.mp3"
),
"path": (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_en-us_6e8b81ac47_{entity_id}.mp3"
),
"url": ("http://example.local:8123/api/tts_proxy/test_token.mp3"),
"path": ("/api/tts_proxy/test_token.mp3"),
}
await hass.async_block_till_done()
@ -482,6 +470,9 @@ async def test_deprecated_voice(
client = await hass_client()
# Test with non deprecated voice.
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
url = "/api/tts_get_url"
data |= {
"message": "There is someone at the door.",
@ -494,15 +485,8 @@ async def test_deprecated_voice(
response = await req.json()
assert response == {
"url": (
"http://example.local:8123/api/tts_proxy/"
"42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_{language.lower()}_87567e3e29_{expected_url_suffix}.mp3"
),
"path": (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_{language.lower()}_87567e3e29_{expected_url_suffix}.mp3"
),
"url": ("http://example.local:8123/api/tts_proxy/test_token.mp3"),
"path": ("/api/tts_proxy/test_token.mp3"),
}
await hass.async_block_till_done()
@ -522,20 +506,16 @@ async def test_deprecated_voice(
# Test with deprecated voice.
data["options"] = {"voice": deprecated_voice}
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
req = await client.post(url, json=data)
assert req.status == HTTPStatus.OK
response = await req.json()
assert response == {
"url": (
"http://example.local:8123/api/tts_proxy/"
"42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_{language.lower()}_13646b7d32_{expected_url_suffix}.mp3"
),
"path": (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_{language.lower()}_13646b7d32_{expected_url_suffix}.mp3"
),
"url": ("http://example.local:8123/api/tts_proxy/test_token.mp3"),
"path": ("/api/tts_proxy/test_token.mp3"),
}
await hass.async_block_till_done()
@ -631,6 +611,9 @@ async def test_deprecated_gender(
client = await hass_client()
# Test without deprecated gender option.
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
url = "/api/tts_get_url"
data |= {
"message": "There is someone at the door.",
@ -642,15 +625,8 @@ async def test_deprecated_gender(
response = await req.json()
assert response == {
"url": (
"http://example.local:8123/api/tts_proxy/"
"42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_{language.lower()}_6e8b81ac47_{expected_url_suffix}.mp3"
),
"path": (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_{language.lower()}_6e8b81ac47_{expected_url_suffix}.mp3"
),
"url": ("http://example.local:8123/api/tts_proxy/test_token.mp3"),
"path": ("/api/tts_proxy/test_token.mp3"),
}
await hass.async_block_till_done()
@ -667,20 +643,16 @@ async def test_deprecated_gender(
# Test with deprecated gender option.
data["options"] = {"gender": gender_option}
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
req = await client.post(url, json=data)
assert req.status == HTTPStatus.OK
response = await req.json()
assert response == {
"url": (
"http://example.local:8123/api/tts_proxy/"
"42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_{language.lower()}_dd0e95eb04_{expected_url_suffix}.mp3"
),
"path": (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_{language.lower()}_dd0e95eb04_{expected_url_suffix}.mp3"
),
"url": ("http://example.local:8123/api/tts_proxy/test_token.mp3"),
"path": ("/api/tts_proxy/test_token.mp3"),
}
await hass.async_block_till_done()

View File

@ -204,13 +204,15 @@ async def test_service(
blocking=True,
)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
assert len(calls) == 1
assert calls[0].data[ATTR_MEDIA_ANNOUNCE] is True
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MediaType.MUSIC
assert await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) == (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_en-us_-_{expected_url_suffix}.mp3"
)
assert await get_media_source_url(
hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]
) == ("/api/tts_proxy/test_token.mp3")
await hass.async_block_till_done()
assert (
mock_tts_cache_dir
@ -266,10 +268,13 @@ async def test_service_default_language(
)
assert len(calls) == 1
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MediaType.MUSIC
assert await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) == (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_de-de_-_{expected_url_suffix}.mp3"
)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
assert await get_media_source_url(
hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]
) == ("/api/tts_proxy/test_token.mp3")
await hass.async_block_till_done()
assert (
mock_tts_cache_dir
@ -327,10 +332,13 @@ async def test_service_default_special_language(
)
assert len(calls) == 1
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MediaType.MUSIC
assert await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) == (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_en-us_-_{expected_url_suffix}.mp3"
)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
assert await get_media_source_url(
hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]
) == ("/api/tts_proxy/test_token.mp3")
await hass.async_block_till_done()
assert (
mock_tts_cache_dir
@ -384,10 +392,13 @@ async def test_service_language(
)
assert len(calls) == 1
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MediaType.MUSIC
assert await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) == (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_de-de_-_{expected_url_suffix}.mp3"
)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
assert await get_media_source_url(
hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]
) == ("/api/tts_proxy/test_token.mp3")
await hass.async_block_till_done()
assert (
mock_tts_cache_dir
@ -497,10 +508,13 @@ async def test_service_options(
assert len(calls) == 1
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MediaType.MUSIC
assert await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) == (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_de-de_{opt_hash}_{expected_url_suffix}.mp3"
)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
assert await get_media_source_url(
hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]
) == ("/api/tts_proxy/test_token.mp3")
await hass.async_block_till_done()
assert (
mock_tts_cache_dir
@ -578,10 +592,13 @@ async def test_service_default_options(
assert len(calls) == 1
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MediaType.MUSIC
assert await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) == (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_de-de_{opt_hash}_{expected_url_suffix}.mp3"
)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
assert await get_media_source_url(
hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]
) == ("/api/tts_proxy/test_token.mp3")
await hass.async_block_till_done()
assert (
mock_tts_cache_dir
@ -649,10 +666,13 @@ async def test_merge_default_service_options(
assert len(calls) == 1
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MediaType.MUSIC
assert await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) == (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_de-de_{opt_hash}_{expected_url_suffix}.mp3"
)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
assert await get_media_source_url(
hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]
) == ("/api/tts_proxy/test_token.mp3")
await hass.async_block_till_done()
assert (
mock_tts_cache_dir
@ -1065,9 +1085,13 @@ async def test_setup_legacy_cache_dir(
)
assert len(calls) == 1
assert await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) == (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491_en-us_-_test.mp3"
)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
assert await get_media_source_url(
hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]
) == ("/api/tts_proxy/test_token.mp3")
await hass.async_block_till_done()
@ -1100,9 +1124,12 @@ async def test_setup_cache_dir(
)
assert len(calls) == 1
assert await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) == (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491_en-us_-_tts.test.mp3"
)
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
assert await get_media_source_url(
hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]
) == ("/api/tts_proxy/test_token.mp3")
await hass.async_block_till_done()
@ -1176,13 +1203,13 @@ async def test_service_get_tts_error(
)
async def test_load_cache_legacy_retrieve_without_mem_cache(
async def test_legacy_cannot_retrieve_without_token(
hass: HomeAssistant,
mock_provider: MockTTSProvider,
mock_tts_cache_dir: Path,
hass_client: ClientSessionGenerator,
) -> None:
"""Set up component and load cache and get without mem cache."""
"""Verify that a TTS cannot be retrieved by filename directly."""
tts_data = b""
cache_file = (
mock_tts_cache_dir / "42f18378fd4393d18c8dd11d03fa9563c1e54491_en_-_test.mp3"
@ -1196,17 +1223,16 @@ async def test_load_cache_legacy_retrieve_without_mem_cache(
url = "/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491_en_-_test.mp3"
req = await client.get(url)
assert req.status == HTTPStatus.OK
assert await req.read() == tts_data
assert req.status == HTTPStatus.NOT_FOUND
async def test_load_cache_retrieve_without_mem_cache(
async def test_cannot_retrieve_without_token(
hass: HomeAssistant,
mock_tts_entity: MockTTSEntity,
mock_tts_cache_dir: Path,
hass_client: ClientSessionGenerator,
) -> None:
"""Set up component and load cache and get without mem cache."""
"""Verify that a TTS cannot be retrieved by filename directly."""
tts_data = b""
cache_file = mock_tts_cache_dir / (
"42f18378fd4393d18c8dd11d03fa9563c1e54491_en-us_-_tts.test.mp3"
@ -1220,28 +1246,27 @@ async def test_load_cache_retrieve_without_mem_cache(
url = "/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491_en-us_-_tts.test.mp3"
req = await client.get(url)
assert req.status == HTTPStatus.OK
assert await req.read() == tts_data
assert req.status == HTTPStatus.NOT_FOUND
@pytest.mark.parametrize(
("setup", "data", "expected_url_suffix"),
("setup", "data"),
[
("mock_setup", {"platform": "test"}, "test"),
("mock_setup", {"engine_id": "test"}, "test"),
("mock_config_entry_setup", {"engine_id": "tts.test"}, "tts.test"),
("mock_setup", {"platform": "test"}),
("mock_setup", {"engine_id": "test"}),
("mock_config_entry_setup", {"engine_id": "tts.test"}),
],
indirect=["setup"],
)
async def test_web_get_url(
hass_client: ClientSessionGenerator,
setup: str,
data: dict[str, Any],
expected_url_suffix: str,
hass_client: ClientSessionGenerator, setup: str, data: dict[str, Any]
) -> None:
"""Set up a TTS platform and receive file from web."""
client = await hass_client()
with patch(
"homeassistant.components.tts.secrets.token_urlsafe", return_value="test_token"
):
url = "/api/tts_get_url"
data |= {"message": "There is someone at the door."}
@ -1249,15 +1274,8 @@ async def test_web_get_url(
assert req.status == HTTPStatus.OK
response = await req.json()
assert response == {
"url": (
"http://example.local:8123/api/tts_proxy/"
"42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_en-us_-_{expected_url_suffix}.mp3"
),
"path": (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_en-us_-_{expected_url_suffix}.mp3"
),
"url": ("http://example.local:8123/api/tts_proxy/test_token.mp3"),
"path": ("/api/tts_proxy/test_token.mp3"),
}