import unittest from unittest.mock import patch import numpy as np from fastapi import HTTPException import app class SpokenRussianTests(unittest.TestCase): def test_address_forms_are_expanded_for_speech(self): text = 'В г. Иваново, на ул. Ленина, д. 4 и корп. 2.' self.assertEqual( app.normalize_spoken_russian(text), 'В городе Иваново, на улице Ленина, дом 4 и корпус 2.', ) def test_route_units_are_expanded(self): text = 'Маршрут: 3 км, 250 м, 21 мин, скорость 12 км/ч.' self.assertEqual( app.normalize_spoken_russian(text), 'Маршрут: 3 километра, 250 метров, 21 минута, скорость 12 километров в час.', ) def test_request_over_limit_fails_instead_of_truncating(self): with self.assertRaises(HTTPException): app.normalize_tts_text('а' * (app.TTS_MAX_REQUEST_CHARS + 1)) class PiperChunkTests(unittest.TestCase): def test_long_text_keeps_tail_in_last_chunk(self): text = 'Первая фраза достаточно длинная. Финальная фраза должна прозвучать полностью.' chunks = app.split_tts_chunks(text, 35) self.assertGreater(len(chunks), 1) self.assertIn('полностью.', chunks[-1]) self.assertEqual(' '.join(chunks), text) def test_multiple_chunks_are_returned_as_one_audio_array(self): with patch.object(app, 'TTS_CHUNK_MAX_CHARS', 30), patch.object( app, 'synthesize_piper_chunk', side_effect=[(np.array([0.1, 0.2], dtype='float32'), 100), (np.array([0.3], dtype='float32'), 100)], ) as synthesizer: audio, sample_rate = app.synthesize_with_piper( 'Первая длинная реплика. Вторая длинная реплика.', 1.0, 'ru_RU-irina-medium', ) self.assertEqual(synthesizer.call_count, 2) self.assertEqual(sample_rate, 100) self.assertGreater(len(audio), 3) self.assertAlmostEqual(float(audio[-1]), 0.3, places=6) if __name__ == '__main__': unittest.main()