"""STT footer metadata uses card names and effective endpoints.""" import asyncio import os import tempfile import unittest from unittest.mock import patch from fastapi import HTTPException os.environ.setdefault('CONFIG_DIR', tempfile.mkdtemp(prefix='ttvc-stt-footer-tests-')) from routes import stt class SttFooterMetadataTests(unittest.TestCase): def test_custom_models_use_own_legacy_name_with_stable_id_priority(self): settings = { 'custom_engine_cards': [ {'id': 'first', 'name': 'First speech', 'url': 'http://shared', 'role': 'stt'}, {'id': 'second', 'name': 'Second speech', 'url': 'http://shared', 'role': 'stt'}, ], 'engine_models': {'First speech': 'first-legacy', 'Second speech': 'second-legacy'}, } self.assertEqual(stt._stt_backend_model('custom:first', settings), 'first-legacy') self.assertEqual(stt._stt_backend_model('custom:second', settings), 'second-legacy') settings['engine_models']['first'] = 'first-current' self.assertEqual(stt._stt_backend_model('custom:first', settings), 'first-current') settings['engine_models']['first'] = '' self.assertEqual(stt._stt_backend_model('custom:first', settings), 'large-v3') def test_native_selected_credentials_preserve_only_matching_global_fallback(self): settings = { 'engine_active': {'faster_whisper_url': 'faster-whisper'}, 'faster_whisper_url': 'http://gateway/v1/', 'whisper_url': 'http://gateway', 'whisper_api_key': 'global-test-key', 'engine_api_keys': {'faster-whisper': 'own-test-key', 'other': 'other-test-key'}, 'custom_engine_cards': [{'id': 'other', 'url': 'http://gateway', 'role': 'stt'}], } self.assertEqual(stt._stt_backend_api_key(settings, 'faster_whisper'), 'own-test-key') del settings['engine_api_keys']['faster-whisper'] self.assertEqual(stt._stt_backend_api_key(settings, 'faster_whisper'), 'global-test-key') settings['faster_whisper_url'] = 'http://unrelated' self.assertEqual(stt._stt_backend_api_key(settings, 'faster_whisper'), '') settings['engine_active']['faster_whisper_url'] = 'other' settings['faster_whisper_url'] = 'http://gateway' del settings['engine_api_keys']['other'] self.assertEqual(stt._stt_backend_api_key(settings, 'faster_whisper'), '') @patch('routes.stt._stt_backend_health', return_value={'ok': True, 'models': ['discovered']}) @patch('routes.stt._load_settings') def test_disabled_native_does_not_disable_selected_custom_on_shared_url(self, load, health): load.return_value = { 'whisper_url': 'http://shared/v1', 'nvidia_router_url': 'http://shared/v1', 'engine_active': {'whisper_url': 'realtime', 'nvidia_router_url': 'realtime'}, 'engine_disabled': {'nvidia-parakeet-asr': True}, 'custom_engine_cards': [{'id': 'realtime', 'name': 'Realtime', 'role': 'stt', 'url': 'http://shared/v1'}], } items = {item['id']: item for item in asyncio.run(stt.stt_backends())['backends']} self.assertFalse(items['nvidia_router']['available']) self.assertFalse(items['nvidia_router']['connected']) self.assertTrue(items['custom:realtime']['available']) self.assertTrue(items['custom:realtime']['connected']) self.assertEqual(items['configured']['engine_id'], 'realtime') self.assertEqual(items['custom:realtime']['engine_id'], 'realtime') self.assertEqual(sum(call.args[0] == 'http://shared/v1' for call in health.call_args_list), 2) with self.assertRaises(HTTPException) as caught: stt._transcribe_audio(None, load.return_value, 'nvidia_router') self.assertEqual(caught.exception.status_code, 409) @patch('routes.stt._stt_backend_health', return_value={'ok': True, 'models': ['discovered']}) @patch('routes.stt._load_settings') def test_legacy_native_slot_uses_known_endpoint_identity(self, load, health): load.return_value = { 'nvidia_asr_url': 'http://native:8090', 'engine_disabled': {'nvidia-parakeet-asr': True}, } items = {item['id']: item for item in asyncio.run(stt.stt_backends())['backends']} item = items['nvidia_parakeet'] self.assertEqual(item['title'], 'NVIDIA Parakeet Router ASR') self.assertEqual(item['engine_id'], 'nvidia-parakeet-asr') self.assertFalse(item['available']) self.assertFalse(item['connected']) self.assertNotIn('http://native:8090', [call.args[0] for call in health.call_args_list]) with self.assertRaises(HTTPException) as caught: stt._transcribe_audio(None, load.return_value, 'nvidia_parakeet') self.assertEqual(caught.exception.status_code, 409) @patch('routes.stt._stt_backend_health', return_value={'ok': True, 'models': ['discovered']}) @patch('routes.stt._load_settings') def test_selected_alternate_native_uses_its_own_disconnect_state(self, load, health): load.return_value = { 'nvidia_asr_url': 'http://rnnt:8092', 'engine_active': {'nvidia_asr_url': 'nvidia-parakeet-rnnt-nim'}, 'engine_disabled': {'parakeet-stt-multi-9011': True}, } items = {item['id']: item for item in asyncio.run(stt.stt_backends())['backends']} self.assertTrue(items['nvidia_parakeet']['available']) self.assertTrue(items['nvidia_parakeet']['connected']) self.assertEqual(items['nvidia_parakeet']['engine_id'], 'nvidia-parakeet-rnnt-nim') self.assertFalse(stt._stt_disconnected(load.return_value, 'nvidia_parakeet', 'http://rnnt:8092')) load.return_value['engine_disabled']['nvidia-parakeet-rnnt-nim'] = True self.assertTrue(stt._stt_disconnected(load.return_value, 'nvidia_parakeet', 'http://rnnt:8092')) @patch('routes.stt._stt_backend_health', return_value={'ok': True, 'models': ['discovered']}) @patch('routes.stt._load_settings') def test_card_titles_stable_identity_and_effective_endpoint(self, load, health): load.return_value = { 'faster_whisper_url': 'http://native:9000/v1', 'engine_active': {'faster_whisper_url': 'stt-whisperx-blackwell'}, 'custom_engine_cards': [ {'id': 'first', 'name': 'Renamed speech', 'url': 'http://stale', 'role': 'stt'}, {'id': 'second', 'label': 'Other speech', 'url': 'http://shared/v1', 'role': 'stt'}, {'id': 'off', 'name': 'Disconnected', 'url': 'http://off', 'role': 'stt'}, ], 'engine_local_urls': {'first': 'http://old', 'dc-first': 'http://shared/v1'}, 'engine_api_keys': {'first': 'first-test-key', 'second': 'second-test-key'}, 'engine_models': {'first': 'first-model', 'second': 'second-model'}, 'engine_disabled': {'off': True}, } items = {item['id']: item for item in asyncio.run(stt.stt_backends())['backends']} self.assertEqual(items['faster_whisper']['title'], 'WhisperX ยท faster-whisper (Blackwell GPU)') self.assertEqual(items['custom:first']['title'], 'Renamed speech') self.assertEqual(items['custom:second']['title'], 'Other speech') self.assertEqual(items['custom:first']['url'], 'http://shared/v1') self.assertEqual(stt._stt_backend_url(load.return_value, 'custom:first'), 'http://shared/v1') self.assertEqual(items['custom:first']['model'], 'first-model') self.assertEqual(items['custom:second']['model'], 'second-model') self.assertTrue(items['custom:first']['available']) health.assert_any_call('http://shared/v1', 'first-test-key') health.assert_any_call('http://shared/v1', 'second-test-key') self.assertFalse(items['custom:off']['available']) self.assertFalse(items['custom:off']['connected']) self.assertNotIn('http://off', [call.args[0] for call in health.call_args_list]) if __name__ == '__main__': unittest.main()