tts-voice-creator-clone-and.../tests/test_magazine_auth.py
mARTin-B78 0551b6651f feat(voices): redesigned My Voices list, working copy, engine-aware TTS availability
My Voices
- Top bar with search, filter toggle (active-count badge), synth-source drawer, group and table buttons;
  filters/sort in a collapsible labelled panel; one-line sort header with select-all/none checkbox
- Groups chip list is a collapsed accordion; bottom bar is Refresh/Sync/Import plus one Actions (N) menu
  (Edit / Audio / Share / Danger), disabled until voices are checked; touch targets stay 44px
- Copy selected works again (copyText was no longer exported), shows "N voice names copied to clipboard"
  and falls back to a copy dialog when the async clipboard is unavailable

TTS / engines
- Health probe works for /v1 card URLs and, behind a LiteLLM gateway, checks the selected model's own
  upstream so stopped containers are not reported as available
- Unhiding a voice publishes a missing entry to the engine's voices.json (no more silent default-voice fallback)
- Status-bar TTS picker also drives the My Voices and STT-test selectors; Use as TTS refreshes the status bar

Includes the engine connect/disconnect, custom TTS, STT footer and magazine auth work that was already
in the working tree, with their tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 04:44:27 +02:00

68 lines
3.2 KiB
Python

"""Vision credentials stay attached to their configured endpoint."""
import os
import pathlib
import tempfile
import unittest
from unittest import mock
os.environ.setdefault("CONFIG_DIR", tempfile.mkdtemp(prefix="ttvc-magazine-auth-test-"))
from core.magazine import BLOCK_FIGURE, describe_visual_region, vision_ocr_page, vision_ocr_page_chain
class VisionAuthTests(unittest.TestCase):
def test_transcription_and_description_use_only_matching_vision_key(self):
settings = {
"vision_ocr_url": "http://primary/",
"vision_ocr_api_key": "primary-test-key",
"vision_ocr_fallback_url": "http://fallback/proxy/v1/",
"vision_ocr_fallback_api_key": "fallback-test-key",
"llm_api_key": "unrelated-test-key",
}
response = mock.Mock()
response.json.return_value = {"choices": [{"message": {"content":
"text [10,10,110,60]A real sentence transcribed from this page."}}]}
for url, expected in (
("http://primary/v1/", {"Authorization": "Bearer primary-test-key"}),
("http://fallback/proxy/v1", {"Authorization": "Bearer fallback-test-key"}),
("http://unconfigured/v1", {}),
("http://primary/other/v1", {}),
):
for describe in (False, True):
with self.subTest(url=url, describe=describe), \
mock.patch("core.config._load_settings", return_value=settings), \
mock.patch("core.magazine.request_with_retries", return_value=response) as request:
if describe:
result = describe_visual_region(pathlib.Path(__file__), BLOCK_FIGURE, url, "model")
else:
result = vision_ocr_page(pathlib.Path(__file__), url, "model")
self.assertIsNotNone(result)
self.assertEqual(request.call_args.kwargs["headers"], expected)
def test_empty_key_and_fallback_never_reuse_another_engine_key(self):
settings = {
"vision_ocr_url": "http://primary/v1",
"vision_ocr_api_key": "primary-test-key",
"vision_ocr_fallback_url": "http://fallback/v1",
"vision_ocr_fallback_api_key": "",
"llm_api_key": "unrelated-test-key",
}
failure = mock.Mock()
failure.json.return_value = {"choices": [{"message": {"content": ""}}]}
success = mock.Mock()
success.json.return_value = {"choices": [{"message": {"content":
"text [10,10,110,60]A real sentence transcribed from this page."}}]}
with mock.patch("core.config._load_settings", return_value=settings), \
mock.patch("core.magazine.request_with_retries", side_effect=[failure, success]) as request:
result = vision_ocr_page_chain(pathlib.Path(__file__), [
("http://primary/v1", "model"), ("http://fallback/v1", "model"),
])
self.assertIsNotNone(result)
self.assertEqual(request.call_args_list[0].kwargs["headers"],
{"Authorization": "Bearer primary-test-key"})
self.assertEqual(request.call_args_list[1].kwargs["headers"], {})
if __name__ == "__main__":
unittest.main()