File size: 5,450 Bytes
05b45a5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
from unittest.mock import mock_open, patch

import pytest
import requests

from ui.lib import api


@pytest.fixture
def mock_response():
    class MockResponse:
        def __init__(self, json_data, status_code=200, content=b"audio data"):
            self._json = json_data
            self.status_code = status_code
            self.content = content

        def json(self):
            return self._json

        def raise_for_status(self):
            if self.status_code != 200:
                raise requests.exceptions.HTTPError(f"HTTP {self.status_code}")

    return MockResponse


def test_check_api_status_success(mock_response):
    """Test successful API status check"""
    mock_data = {"voices": ["voice1", "voice2"]}
    with patch("requests.get", return_value=mock_response(mock_data)):
        status, voices = api.check_api_status()
        assert status is True
        assert voices == ["voice1", "voice2"]


def test_check_api_status_no_voices(mock_response):
    """Test API response with no voices"""
    with patch("requests.get", return_value=mock_response({"voices": []})):
        status, voices = api.check_api_status()
        assert status is False
        assert voices == []


def test_check_api_status_timeout():
    """Test API timeout"""
    with patch("requests.get", side_effect=requests.exceptions.Timeout):
        status, voices = api.check_api_status()
        assert status is False
        assert voices == []


def test_check_api_status_connection_error():
    """Test API connection error"""
    with patch("requests.get", side_effect=requests.exceptions.ConnectionError):
        status, voices = api.check_api_status()
        assert status is False
        assert voices == []


def test_text_to_speech_success(mock_response, tmp_path):
    """Test successful speech generation"""
    with (
        patch("requests.post", return_value=mock_response({})),
        patch("ui.lib.api.OUTPUTS_DIR", str(tmp_path)),
        patch("builtins.open", mock_open()) as mock_file,
    ):
        result = api.text_to_speech("test text", "voice1", "mp3", 1.0)

        assert result is not None
        assert "output_" in result
        assert result.endswith(".mp3")
        mock_file.assert_called_once()


def test_text_to_speech_empty_text():
    """Test speech generation with empty text"""
    result = api.text_to_speech("", "voice1", "mp3", 1.0)
    assert result is None


def test_text_to_speech_timeout():
    """Test speech generation timeout"""
    with patch("requests.post", side_effect=requests.exceptions.Timeout):
        result = api.text_to_speech("test", "voice1", "mp3", 1.0)
        assert result is None


def test_text_to_speech_request_error():
    """Test speech generation request error"""
    with patch("requests.post", side_effect=requests.exceptions.RequestException):
        result = api.text_to_speech("test", "voice1", "mp3", 1.0)
        assert result is None


def test_get_status_html_available():
    """Test status HTML generation for available service"""
    html = api.get_status_html(True)
    assert "green" in html
    assert "Available" in html


def test_get_status_html_unavailable():
    """Test status HTML generation for unavailable service"""
    html = api.get_status_html(False)
    assert "red" in html
    assert "Unavailable" in html


def test_text_to_speech_api_params(mock_response, tmp_path):
    """Test correct API parameters are sent"""
    test_cases = [
        # Single voice as string
        ("voice1", "voice1"),
        # Multiple voices as list
        (["voice1", "voice2"], "voice1+voice2"),
        # Single voice as list
        (["voice1"], "voice1"),
    ]

    for input_voice, expected_voice in test_cases:
        with (
            patch("requests.post") as mock_post,
            patch("ui.lib.api.OUTPUTS_DIR", str(tmp_path)),
            patch("builtins.open", mock_open()),
        ):
            mock_post.return_value = mock_response({})
            api.text_to_speech("test text", input_voice, "mp3", 1.5)

            mock_post.assert_called_once()
            args, kwargs = mock_post.call_args

            # Check request body
            assert kwargs["json"] == {
                "model": "kokoro",
                "input": "test text",
                "voice": expected_voice,
                "response_format": "mp3",
                "speed": 1.5,
            }

            # Check headers and timeout
            assert kwargs["headers"] == {"Content-Type": "application/json"}
            assert kwargs["timeout"] == 300


def test_text_to_speech_output_filename(mock_response, tmp_path):
    """Test output filename contains correct voice identifier"""
    test_cases = [
        # Single voice
        ("voice1", lambda f: "voice-voice1" in f),
        # Multiple voices
        (["voice1", "voice2"], lambda f: "voice-voice1+voice2" in f),
    ]

    for input_voice, filename_check in test_cases:
        with (
            patch("requests.post", return_value=mock_response({})),
            patch("ui.lib.api.OUTPUTS_DIR", str(tmp_path)),
            patch("builtins.open", mock_open()) as mock_file,
        ):
            result = api.text_to_speech("test text", input_voice, "mp3", 1.0)

            assert result is not None
            assert filename_check(result), (
                f"Expected voice pattern not found in filename: {result}"
            )
            mock_file.assert_called_once()