mito-ai 0.1.37__py3-none-any.whl → 0.1.39__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mito-ai might be problematic. Click here for more details.
- mito_ai/__init__.py +17 -1
- mito_ai/_version.py +1 -1
- mito_ai/app_builder/handlers.py +43 -38
- mito_ai/app_builder/models.py +1 -1
- mito_ai/completions/handlers.py +1 -1
- mito_ai/completions/prompt_builders/agent_system_message.py +18 -45
- mito_ai/completions/prompt_builders/chat_name_prompt.py +6 -6
- mito_ai/log/handlers.py +10 -3
- mito_ai/log/urls.py +3 -3
- mito_ai/openai_client.py +1 -1
- mito_ai/streamlit_conversion/agent_utils.py +116 -0
- mito_ai/streamlit_conversion/prompts/prompt_constants.py +59 -0
- mito_ai/streamlit_conversion/prompts/prompt_utils.py +10 -0
- mito_ai/streamlit_conversion/prompts/streamlit_app_creation_prompt.py +45 -0
- mito_ai/streamlit_conversion/prompts/streamlit_error_correction_prompt.py +28 -0
- mito_ai/streamlit_conversion/prompts/streamlit_finish_todo_prompt.py +44 -0
- mito_ai/streamlit_conversion/streamlit_agent_handler.py +90 -44
- mito_ai/streamlit_conversion/streamlit_system_prompt.py +30 -17
- mito_ai/streamlit_conversion/streamlit_utils.py +48 -8
- mito_ai/streamlit_conversion/validate_streamlit_app.py +116 -0
- mito_ai/streamlit_preview/__init__.py +7 -0
- mito_ai/streamlit_preview/handlers.py +164 -0
- mito_ai/streamlit_preview/manager.py +159 -0
- mito_ai/streamlit_preview/urls.py +22 -0
- mito_ai/tests/streamlit_conversion/test_streamlit_agent_handler.py +166 -78
- mito_ai/tests/streamlit_conversion/test_streamlit_utils.py +4 -5
- mito_ai/tests/streamlit_conversion/test_validate_streamlit_app.py +119 -0
- mito_ai/tests/streamlit_preview/test_streamlit_preview_manager.py +302 -0
- mito_ai/tests/utils/test_anthropic_utils.py +2 -2
- mito_ai/utils/anthropic_utils.py +4 -4
- mito_ai/utils/open_ai_utils.py +0 -4
- mito_ai/utils/telemetry_utils.py +28 -1
- {mito_ai-0.1.37.data → mito_ai-0.1.39.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +1 -1
- {mito_ai-0.1.37.data → mito_ai-0.1.39.data}/data/share/jupyter/labextensions/mito_ai/package.json +2 -2
- {mito_ai-0.1.37.data → mito_ai-0.1.39.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +1 -1
- {mito_ai-0.1.37.data → mito_ai-0.1.39.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +6 -1
- mito_ai-0.1.37.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.831f63b48760c7119b9b.js → mito_ai-0.1.39.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.16b532b655cd2906e04a.js +799 -116
- mito_ai-0.1.39.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.16b532b655cd2906e04a.js.map +1 -0
- mito_ai-0.1.37.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.93ecc9bc0edba61535cc.js → mito_ai-0.1.39.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.606207904e6aaa42b1bf.js +5 -5
- mito_ai-0.1.37.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.93ecc9bc0edba61535cc.js.map → mito_ai-0.1.39.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.606207904e6aaa42b1bf.js.map +1 -1
- {mito_ai-0.1.37.dist-info → mito_ai-0.1.39.dist-info}/METADATA +4 -1
- {mito_ai-0.1.37.dist-info → mito_ai-0.1.39.dist-info}/RECORD +53 -42
- mito_ai/streamlit_conversion/validate_and_run_streamlit_code.py +0 -207
- mito_ai/tests/streamlit_conversion/test_validate_and_run_streamlit_code.py +0 -418
- mito_ai-0.1.37.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.831f63b48760c7119b9b.js.map +0 -1
- {mito_ai-0.1.37.data → mito_ai-0.1.39.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
- {mito_ai-0.1.37.data → mito_ai-0.1.39.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
- {mito_ai-0.1.37.data → mito_ai-0.1.39.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js +0 -0
- {mito_ai-0.1.37.data → mito_ai-0.1.39.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js.map +0 -0
- {mito_ai-0.1.37.data → mito_ai-0.1.39.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.9795f79265ddb416864b.js +0 -0
- {mito_ai-0.1.37.data → mito_ai-0.1.39.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.9795f79265ddb416864b.js.map +0 -0
- {mito_ai-0.1.37.data → mito_ai-0.1.39.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +0 -0
- {mito_ai-0.1.37.data → mito_ai-0.1.39.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js.map +0 -0
- {mito_ai-0.1.37.dist-info → mito_ai-0.1.39.dist-info}/WHEEL +0 -0
- {mito_ai-0.1.37.dist-info → mito_ai-0.1.39.dist-info}/entry_points.txt +0 -0
- {mito_ai-0.1.37.dist-info → mito_ai-0.1.39.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
# Copyright (c) Saga Inc.
|
|
2
|
-
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
-
|
|
4
|
-
import pytest
|
|
5
|
-
import tempfile
|
|
6
|
-
import os
|
|
7
|
-
import subprocess
|
|
8
|
-
import time
|
|
9
|
-
import ast
|
|
10
|
-
import importlib.util
|
|
11
|
-
from unittest.mock import patch, MagicMock, mock_open
|
|
12
|
-
from mito_ai.streamlit_conversion.validate_and_run_streamlit_code import (
|
|
13
|
-
StreamlitValidator,
|
|
14
|
-
streamlit_code_validator
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class TestStreamlitValidator:
|
|
19
|
-
"""Test cases for StreamlitValidator class"""
|
|
20
|
-
|
|
21
|
-
def test_validate_syntax_valid_code(self):
|
|
22
|
-
"""Test syntax validation with valid Python code"""
|
|
23
|
-
validator = StreamlitValidator()
|
|
24
|
-
code = "import streamlit\nst.title('Hello World')"
|
|
25
|
-
|
|
26
|
-
is_valid, message = validator.validate_syntax(code)
|
|
27
|
-
|
|
28
|
-
assert is_valid is True
|
|
29
|
-
assert "Syntax is valid" in message
|
|
30
|
-
|
|
31
|
-
def test_validate_syntax_invalid_code(self):
|
|
32
|
-
"""Test syntax validation with invalid Python code"""
|
|
33
|
-
validator = StreamlitValidator()
|
|
34
|
-
code = "import streamlit\nst.title('Hello World' # Missing closing parenthesis"
|
|
35
|
-
|
|
36
|
-
is_valid, message = validator.validate_syntax(code)
|
|
37
|
-
|
|
38
|
-
assert is_valid is False
|
|
39
|
-
assert "Syntax error" in message
|
|
40
|
-
|
|
41
|
-
def test_validate_syntax_empty_code(self):
|
|
42
|
-
"""Test syntax validation with empty code"""
|
|
43
|
-
validator = StreamlitValidator()
|
|
44
|
-
code = ""
|
|
45
|
-
|
|
46
|
-
is_valid, message = validator.validate_syntax(code)
|
|
47
|
-
|
|
48
|
-
assert is_valid is True
|
|
49
|
-
assert "Syntax is valid" in message
|
|
50
|
-
|
|
51
|
-
def test_create_temp_app(self):
|
|
52
|
-
"""Test creating temporary app file"""
|
|
53
|
-
validator = StreamlitValidator()
|
|
54
|
-
code = "import streamlit\nst.title('Test')"
|
|
55
|
-
|
|
56
|
-
app_path = validator.create_temp_app(code)
|
|
57
|
-
|
|
58
|
-
assert validator.temp_dir is not None
|
|
59
|
-
assert os.path.exists(validator.temp_dir)
|
|
60
|
-
assert app_path.endswith("app.py")
|
|
61
|
-
assert os.path.exists(app_path)
|
|
62
|
-
|
|
63
|
-
# Check file content
|
|
64
|
-
with open(app_path, 'r') as f:
|
|
65
|
-
content = f.read()
|
|
66
|
-
assert content == code
|
|
67
|
-
|
|
68
|
-
# Cleanup
|
|
69
|
-
validator.cleanup()
|
|
70
|
-
|
|
71
|
-
def test_create_temp_app_empty_code(self):
|
|
72
|
-
"""Test creating temporary app file with empty code"""
|
|
73
|
-
validator = StreamlitValidator()
|
|
74
|
-
code = ""
|
|
75
|
-
|
|
76
|
-
app_path = validator.create_temp_app(code)
|
|
77
|
-
|
|
78
|
-
assert os.path.exists(app_path)
|
|
79
|
-
|
|
80
|
-
with open(app_path, 'r') as f:
|
|
81
|
-
content = f.read()
|
|
82
|
-
assert content == ""
|
|
83
|
-
|
|
84
|
-
# Cleanup
|
|
85
|
-
validator.cleanup()
|
|
86
|
-
|
|
87
|
-
@patch('subprocess.Popen')
|
|
88
|
-
def test_start_streamlit_app_success(self, mock_popen):
|
|
89
|
-
"""Test successful Streamlit app startup"""
|
|
90
|
-
validator = StreamlitValidator(port=8502)
|
|
91
|
-
app_path = "/tmp/test/app.py"
|
|
92
|
-
|
|
93
|
-
# Mock successful subprocess
|
|
94
|
-
mock_process = MagicMock()
|
|
95
|
-
mock_popen.return_value = mock_process
|
|
96
|
-
|
|
97
|
-
success, message = validator.start_streamlit_app(app_path)
|
|
98
|
-
|
|
99
|
-
assert success is True
|
|
100
|
-
assert "Streamlit app started" in message
|
|
101
|
-
assert validator.process == mock_process
|
|
102
|
-
|
|
103
|
-
# Verify subprocess was called with correct arguments
|
|
104
|
-
mock_popen.assert_called_once()
|
|
105
|
-
call_args = mock_popen.call_args[0][0]
|
|
106
|
-
assert "streamlit" in call_args
|
|
107
|
-
assert "run" in call_args
|
|
108
|
-
assert app_path in call_args
|
|
109
|
-
assert "8502" in call_args
|
|
110
|
-
|
|
111
|
-
@patch('subprocess.Popen')
|
|
112
|
-
def test_start_streamlit_app_failure(self, mock_popen):
|
|
113
|
-
"""Test Streamlit app startup failure"""
|
|
114
|
-
validator = StreamlitValidator()
|
|
115
|
-
app_path = "/tmp/test/app.py"
|
|
116
|
-
|
|
117
|
-
# Mock subprocess failure
|
|
118
|
-
mock_popen.side_effect = Exception("Failed to start process")
|
|
119
|
-
|
|
120
|
-
success, message = validator.start_streamlit_app(app_path)
|
|
121
|
-
|
|
122
|
-
assert success is False
|
|
123
|
-
assert "Failed to start Streamlit" in message
|
|
124
|
-
assert validator.process is None
|
|
125
|
-
|
|
126
|
-
@patch('requests.get')
|
|
127
|
-
def test_wait_for_app_success(self, mock_get):
|
|
128
|
-
"""Test waiting for app to be ready successfully"""
|
|
129
|
-
validator = StreamlitValidator(port=8502, timeout=5)
|
|
130
|
-
|
|
131
|
-
# Mock successful HTTP response
|
|
132
|
-
mock_response = MagicMock()
|
|
133
|
-
mock_response.status_code = 200
|
|
134
|
-
mock_get.return_value = mock_response
|
|
135
|
-
|
|
136
|
-
success, message = validator.wait_for_app()
|
|
137
|
-
|
|
138
|
-
assert success is True
|
|
139
|
-
assert "App is running successfully" in message
|
|
140
|
-
mock_get.assert_called_with("http://localhost:8502", timeout=5)
|
|
141
|
-
|
|
142
|
-
@patch('requests.get')
|
|
143
|
-
def test_wait_for_app_http_error(self, mock_get):
|
|
144
|
-
"""Test waiting for app with HTTP error"""
|
|
145
|
-
validator = StreamlitValidator(port=8501, timeout=5)
|
|
146
|
-
|
|
147
|
-
# Mock HTTP error response
|
|
148
|
-
mock_response = MagicMock()
|
|
149
|
-
mock_response.status_code = 500
|
|
150
|
-
mock_get.return_value = mock_response
|
|
151
|
-
|
|
152
|
-
success, message = validator.wait_for_app()
|
|
153
|
-
|
|
154
|
-
assert success is False
|
|
155
|
-
assert "App failed to start within timeout" in message
|
|
156
|
-
|
|
157
|
-
@patch('subprocess.Popen')
|
|
158
|
-
def test_check_for_errors_process_running(self, mock_popen):
|
|
159
|
-
"""Test error checking when process is running"""
|
|
160
|
-
validator = StreamlitValidator()
|
|
161
|
-
|
|
162
|
-
# Mock running process
|
|
163
|
-
mock_process = MagicMock()
|
|
164
|
-
mock_process.poll.return_value = None # Process is running
|
|
165
|
-
validator.process = mock_process
|
|
166
|
-
|
|
167
|
-
success, message = validator.check_for_errors()
|
|
168
|
-
|
|
169
|
-
assert success is True
|
|
170
|
-
assert "App is running without errors" in message
|
|
171
|
-
|
|
172
|
-
@patch('subprocess.Popen')
|
|
173
|
-
def test_check_for_errors_process_crashed(self, mock_popen):
|
|
174
|
-
"""Test error checking when process has crashed"""
|
|
175
|
-
validator = StreamlitValidator()
|
|
176
|
-
|
|
177
|
-
# Mock crashed process
|
|
178
|
-
mock_process = MagicMock()
|
|
179
|
-
mock_process.poll.return_value = 1 # Process has exited
|
|
180
|
-
mock_process.communicate.return_value = ("stdout", "stderr error message")
|
|
181
|
-
validator.process = mock_process
|
|
182
|
-
|
|
183
|
-
success, message = validator.check_for_errors()
|
|
184
|
-
|
|
185
|
-
assert success is False
|
|
186
|
-
assert "App crashed" in message
|
|
187
|
-
assert "stderr error message" in message
|
|
188
|
-
|
|
189
|
-
@patch('subprocess.Popen')
|
|
190
|
-
def test_check_for_errors_process_crashed_with_warnings(self, mock_popen):
|
|
191
|
-
"""Test error checking when process crashed but only has warnings"""
|
|
192
|
-
validator = StreamlitValidator()
|
|
193
|
-
|
|
194
|
-
# Mock crashed process with only warnings
|
|
195
|
-
mock_process = MagicMock()
|
|
196
|
-
mock_process.poll.return_value = 1
|
|
197
|
-
mock_process.communicate.return_value = ("stdout", "missing ScriptRunContext warning")
|
|
198
|
-
validator.process = mock_process
|
|
199
|
-
|
|
200
|
-
success, message = validator.check_for_errors()
|
|
201
|
-
|
|
202
|
-
assert success is True
|
|
203
|
-
assert "App is running without errors" in message
|
|
204
|
-
|
|
205
|
-
def test_check_for_errors_no_process(self):
|
|
206
|
-
"""Test error checking when no process exists"""
|
|
207
|
-
validator = StreamlitValidator()
|
|
208
|
-
|
|
209
|
-
success, message = validator.check_for_errors()
|
|
210
|
-
|
|
211
|
-
assert success is False
|
|
212
|
-
assert "No process found" in message
|
|
213
|
-
|
|
214
|
-
@patch('subprocess.Popen')
|
|
215
|
-
def test_cleanup_with_process(self, mock_popen):
|
|
216
|
-
"""Test cleanup with running process"""
|
|
217
|
-
validator = StreamlitValidator()
|
|
218
|
-
|
|
219
|
-
# Mock process
|
|
220
|
-
mock_process = MagicMock()
|
|
221
|
-
validator.process = mock_process
|
|
222
|
-
validator.temp_dir = "/tmp/test_dir"
|
|
223
|
-
|
|
224
|
-
# Mock directory exists
|
|
225
|
-
with patch('os.path.exists', return_value=True):
|
|
226
|
-
with patch('shutil.rmtree') as mock_rmtree:
|
|
227
|
-
validator.cleanup()
|
|
228
|
-
|
|
229
|
-
mock_process.terminate.assert_called_once()
|
|
230
|
-
mock_process.wait.assert_called_once()
|
|
231
|
-
mock_rmtree.assert_called_once_with("/tmp/test_dir")
|
|
232
|
-
|
|
233
|
-
assert validator.process is None
|
|
234
|
-
assert validator.temp_dir is None # type: ignore[unreachable]
|
|
235
|
-
|
|
236
|
-
def test_cleanup_without_process(self):
|
|
237
|
-
"""Test cleanup without process"""
|
|
238
|
-
validator = StreamlitValidator()
|
|
239
|
-
|
|
240
|
-
# Should not raise any exceptions
|
|
241
|
-
validator.cleanup()
|
|
242
|
-
|
|
243
|
-
assert validator.process is None
|
|
244
|
-
assert validator.temp_dir is None
|
|
245
|
-
|
|
246
|
-
@patch('subprocess.Popen')
|
|
247
|
-
@patch('requests.get')
|
|
248
|
-
def test_validate_app_success(self, mock_get, mock_popen):
|
|
249
|
-
"""Test complete validation pipeline success"""
|
|
250
|
-
validator = StreamlitValidator(port=8501, timeout=5)
|
|
251
|
-
code = "import streamlit\nst.title('Hello World')"
|
|
252
|
-
|
|
253
|
-
# Mock successful subprocess
|
|
254
|
-
mock_process = MagicMock()
|
|
255
|
-
mock_process.poll.return_value = None
|
|
256
|
-
mock_popen.return_value = mock_process
|
|
257
|
-
|
|
258
|
-
# Mock successful HTTP response
|
|
259
|
-
mock_response = MagicMock()
|
|
260
|
-
mock_response.status_code = 200
|
|
261
|
-
mock_get.return_value = mock_response
|
|
262
|
-
|
|
263
|
-
results = validator.validate_app(code)
|
|
264
|
-
|
|
265
|
-
assert results['syntax_valid'] is True
|
|
266
|
-
assert results['app_starts'] is True
|
|
267
|
-
assert results['app_responsive'] is True
|
|
268
|
-
assert len(results['errors']) == 0
|
|
269
|
-
|
|
270
|
-
def test_validate_app_syntax_error(self):
|
|
271
|
-
"""Test validation pipeline with syntax error"""
|
|
272
|
-
validator = StreamlitValidator()
|
|
273
|
-
code = "import streamlit\nst.title('Hello World' # Missing parenthesis"
|
|
274
|
-
|
|
275
|
-
results = validator.validate_app(code)
|
|
276
|
-
|
|
277
|
-
assert results['syntax_valid'] is False
|
|
278
|
-
assert results['app_starts'] is False
|
|
279
|
-
assert results['app_responsive'] is False
|
|
280
|
-
assert len(results['errors']) == 1
|
|
281
|
-
assert "Syntax error" in results['errors'][0]
|
|
282
|
-
|
|
283
|
-
@patch('subprocess.Popen')
|
|
284
|
-
def test_validate_app_startup_failure(self, mock_popen):
|
|
285
|
-
"""Test validation pipeline with startup failure"""
|
|
286
|
-
validator = StreamlitValidator()
|
|
287
|
-
code = "import streamlit\nst.title('Hello World')"
|
|
288
|
-
|
|
289
|
-
# Mock startup failure
|
|
290
|
-
mock_popen.side_effect = Exception("Failed to start")
|
|
291
|
-
|
|
292
|
-
results = validator.validate_app(code)
|
|
293
|
-
|
|
294
|
-
assert results['syntax_valid'] is True
|
|
295
|
-
assert results['app_starts'] is False
|
|
296
|
-
assert results['app_responsive'] is False
|
|
297
|
-
assert len(results['errors']) == 1
|
|
298
|
-
assert "Failed to start Streamlit" in results['errors'][0]
|
|
299
|
-
|
|
300
|
-
@patch('subprocess.Popen')
|
|
301
|
-
@patch('requests.get')
|
|
302
|
-
def test_validate_app_runtime_error(self, mock_get, mock_popen):
|
|
303
|
-
"""Test validation pipeline with runtime error"""
|
|
304
|
-
validator = StreamlitValidator(port=8501, timeout=5)
|
|
305
|
-
code = "import streamlit\nst.title('Hello World')"
|
|
306
|
-
|
|
307
|
-
# Mock successful startup
|
|
308
|
-
mock_process = MagicMock()
|
|
309
|
-
mock_process.poll.return_value = 1 # Process crashed
|
|
310
|
-
mock_process.communicate.return_value = ("stdout", "Runtime error occurred")
|
|
311
|
-
mock_popen.return_value = mock_process
|
|
312
|
-
|
|
313
|
-
# Mock successful HTTP response
|
|
314
|
-
mock_response = MagicMock()
|
|
315
|
-
mock_response.status_code = 200
|
|
316
|
-
mock_get.return_value = mock_response
|
|
317
|
-
|
|
318
|
-
results = validator.validate_app(code)
|
|
319
|
-
|
|
320
|
-
assert results['syntax_valid'] is True
|
|
321
|
-
assert results['app_starts'] is True
|
|
322
|
-
assert results['app_responsive'] is True
|
|
323
|
-
assert len(results['errors']) == 1
|
|
324
|
-
assert "App crashed" in results['errors'][0]
|
|
325
|
-
|
|
326
|
-
def test_validate_app_exception_handling(self):
|
|
327
|
-
"""Test validation pipeline exception handling"""
|
|
328
|
-
validator = StreamlitValidator()
|
|
329
|
-
code = "import streamlit\nst.title('Hello World')"
|
|
330
|
-
|
|
331
|
-
# Mock an exception during validation
|
|
332
|
-
with patch.object(validator, 'validate_syntax', side_effect=Exception("Unexpected error")):
|
|
333
|
-
results = validator.validate_app(code)
|
|
334
|
-
|
|
335
|
-
assert results['syntax_valid'] is False
|
|
336
|
-
assert results['app_starts'] is False
|
|
337
|
-
assert results['app_responsive'] is False
|
|
338
|
-
assert len(results['errors']) == 1
|
|
339
|
-
assert "Validation error" in results['errors'][0]
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
class TestStreamlitCodeValidator:
|
|
343
|
-
"""Test cases for streamlit_code_validator function"""
|
|
344
|
-
|
|
345
|
-
def test_streamlit_code_validator_success(self):
|
|
346
|
-
"""Test successful code validation"""
|
|
347
|
-
code = "import streamlit\nst.title('Hello World')"
|
|
348
|
-
|
|
349
|
-
with patch('mito_ai.streamlit_conversion.validate_and_run_streamlit_code.StreamlitValidator') as mock_validator_class:
|
|
350
|
-
mock_validator = MagicMock()
|
|
351
|
-
mock_validator_class.return_value = mock_validator
|
|
352
|
-
|
|
353
|
-
mock_validator.validate_app.return_value = {
|
|
354
|
-
'syntax_valid': True,
|
|
355
|
-
'app_starts': True,
|
|
356
|
-
'app_responsive': True,
|
|
357
|
-
'errors': []
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
has_error, message = streamlit_code_validator(code)
|
|
361
|
-
|
|
362
|
-
assert has_error is False
|
|
363
|
-
assert "Errors found" not in message
|
|
364
|
-
mock_validator.validate_app.assert_called_once_with(code)
|
|
365
|
-
|
|
366
|
-
def test_streamlit_code_validator_error_in_code(self):
|
|
367
|
-
"""Test code validation when code contains 'error'"""
|
|
368
|
-
code = "error in the code"
|
|
369
|
-
|
|
370
|
-
has_error, message = streamlit_code_validator(code)
|
|
371
|
-
|
|
372
|
-
assert has_error is True
|
|
373
|
-
assert "Errors found" in message
|
|
374
|
-
|
|
375
|
-
def test_streamlit_code_validator_empty_code(self):
|
|
376
|
-
"""Test code validation with empty code"""
|
|
377
|
-
code = ""
|
|
378
|
-
|
|
379
|
-
with patch('mito_ai.streamlit_conversion.validate_and_run_streamlit_code.StreamlitValidator') as mock_validator_class:
|
|
380
|
-
mock_validator = MagicMock()
|
|
381
|
-
mock_validator_class.return_value = mock_validator
|
|
382
|
-
|
|
383
|
-
mock_validator.validate_app.return_value = {
|
|
384
|
-
'syntax_valid': True,
|
|
385
|
-
'app_starts': True,
|
|
386
|
-
'app_responsive': True,
|
|
387
|
-
'errors': []
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
has_error, message = streamlit_code_validator(code)
|
|
391
|
-
|
|
392
|
-
assert has_error is False
|
|
393
|
-
assert "Errors found" not in message
|
|
394
|
-
|
|
395
|
-
def test_streamlit_code_validator_multiple_errors(self):
|
|
396
|
-
"""Test code validation with multiple errors"""
|
|
397
|
-
code = "import streamlit\nst.title('Hello World')"
|
|
398
|
-
|
|
399
|
-
with patch('mito_ai.streamlit_conversion.validate_and_run_streamlit_code.StreamlitValidator') as mock_validator_class:
|
|
400
|
-
mock_validator = MagicMock()
|
|
401
|
-
mock_validator_class.return_value = mock_validator
|
|
402
|
-
|
|
403
|
-
mock_validator.validate_app.return_value = {
|
|
404
|
-
'syntax_valid': False,
|
|
405
|
-
'app_starts': False,
|
|
406
|
-
'app_responsive': False,
|
|
407
|
-
'errors': [
|
|
408
|
-
'Syntax error: invalid syntax',
|
|
409
|
-
'App failed to start'
|
|
410
|
-
]
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
has_error, message = streamlit_code_validator(code)
|
|
414
|
-
|
|
415
|
-
assert has_error is True
|
|
416
|
-
assert "Errors found" in message
|
|
417
|
-
assert "Syntax error" in message
|
|
418
|
-
assert "App failed to start" in message
|