aixtools 0.1.9__py3-none-any.whl → 0.1.11__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 aixtools might be problematic. Click here for more details.
- aixtools/_version.py +2 -2
- aixtools/compliance/__init__.py +9 -0
- aixtools/compliance/private_data.py +5 -3
- aixtools/mcp/client.py +102 -1
- aixtools/testing/aix_test_model.py +2 -0
- {aixtools-0.1.9.dist-info → aixtools-0.1.11.dist-info}/METADATA +2 -1
- {aixtools-0.1.9.dist-info → aixtools-0.1.11.dist-info}/RECORD +10 -46
- aixtools-0.1.11.dist-info/top_level.txt +1 -0
- aixtools-0.1.9.dist-info/top_level.txt +0 -5
- docker/mcp-base/Dockerfile +0 -33
- docker/mcp-base/zscaler.crt +0 -28
- notebooks/example_faulty_mcp_server.ipynb +0 -74
- notebooks/example_mcp_server_stdio.ipynb +0 -76
- notebooks/example_raw_mcp_client.ipynb +0 -84
- notebooks/example_tool_doctor.ipynb +0 -65
- scripts/config.sh +0 -28
- scripts/lint.sh +0 -32
- scripts/log_view.sh +0 -18
- scripts/run_example_mcp_server.sh +0 -14
- scripts/run_faulty_mcp_server.sh +0 -13
- scripts/run_server.sh +0 -29
- scripts/test.sh +0 -30
- tests/__init__.py +0 -0
- tests/unit/__init__.py +0 -0
- tests/unit/a2a/__init__.py +0 -0
- tests/unit/a2a/google_sdk/__init__.py +0 -0
- tests/unit/a2a/google_sdk/pydantic_ai_adapter/__init__.py +0 -0
- tests/unit/a2a/google_sdk/pydantic_ai_adapter/test_agent_executor.py +0 -188
- tests/unit/a2a/google_sdk/pydantic_ai_adapter/test_storage.py +0 -156
- tests/unit/a2a/google_sdk/test_card.py +0 -114
- tests/unit/a2a/google_sdk/test_remote_agent_connection.py +0 -413
- tests/unit/a2a/google_sdk/test_utils.py +0 -208
- tests/unit/agents/__init__.py +0 -0
- tests/unit/agents/test_prompt.py +0 -363
- tests/unit/compliance/test_private_data.py +0 -329
- tests/unit/google/__init__.py +0 -1
- tests/unit/google/test_client.py +0 -233
- tests/unit/mcp/__init__.py +0 -0
- tests/unit/mcp/test_client.py +0 -242
- tests/unit/server/__init__.py +0 -0
- tests/unit/server/test_path.py +0 -225
- tests/unit/server/test_utils.py +0 -362
- tests/unit/utils/__init__.py +0 -0
- tests/unit/utils/test_files.py +0 -146
- tests/unit/vault/__init__.py +0 -0
- tests/unit/vault/test_vault.py +0 -246
- {aixtools-0.1.9.dist-info → aixtools-0.1.11.dist-info}/WHEEL +0 -0
- {aixtools-0.1.9.dist-info → aixtools-0.1.11.dist-info}/entry_points.txt +0 -0
tests/unit/server/test_utils.py
DELETED
|
@@ -1,362 +0,0 @@
|
|
|
1
|
-
"""Unit tests for aixtools.server.utils module."""
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
import unittest
|
|
5
|
-
from unittest.mock import AsyncMock, Mock, patch
|
|
6
|
-
|
|
7
|
-
from aixtools.server.utils import (
|
|
8
|
-
get_session_id_from_request,
|
|
9
|
-
get_session_id_str,
|
|
10
|
-
get_session_id_tuple,
|
|
11
|
-
get_user_id_from_request,
|
|
12
|
-
run_in_thread,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class TestGetSessionIdFromRequest(unittest.TestCase):
|
|
17
|
-
"""Test cases for get_session_id_from_request function."""
|
|
18
|
-
|
|
19
|
-
@patch('aixtools.server.utils.dependencies')
|
|
20
|
-
def test_get_session_id_with_context_none(self, mock_dependencies):
|
|
21
|
-
"""Test getting session ID when context is None."""
|
|
22
|
-
mock_request = Mock()
|
|
23
|
-
mock_request.headers.get.return_value = "test-session-123"
|
|
24
|
-
mock_dependencies.get_http_request.return_value = mock_request
|
|
25
|
-
|
|
26
|
-
result = get_session_id_from_request(None)
|
|
27
|
-
|
|
28
|
-
self.assertEqual(result, "test-session-123")
|
|
29
|
-
mock_dependencies.get_http_request.assert_called_once()
|
|
30
|
-
mock_request.headers.get.assert_called_once_with("session-id")
|
|
31
|
-
|
|
32
|
-
def test_get_session_id_with_context_provided(self):
|
|
33
|
-
"""Test getting session ID when context is provided."""
|
|
34
|
-
mock_ctx = Mock()
|
|
35
|
-
mock_request = Mock()
|
|
36
|
-
mock_request.headers.get.return_value = "ctx-session-456"
|
|
37
|
-
mock_ctx.get_http_request.return_value = mock_request
|
|
38
|
-
|
|
39
|
-
result = get_session_id_from_request(mock_ctx)
|
|
40
|
-
|
|
41
|
-
self.assertEqual(result, "ctx-session-456")
|
|
42
|
-
mock_ctx.get_http_request.assert_called_once()
|
|
43
|
-
mock_request.headers.get.assert_called_once_with("session-id")
|
|
44
|
-
|
|
45
|
-
@patch('aixtools.server.utils.dependencies')
|
|
46
|
-
def test_get_session_id_header_not_found(self, mock_dependencies):
|
|
47
|
-
"""Test getting session ID when header is not found."""
|
|
48
|
-
mock_request = Mock()
|
|
49
|
-
mock_request.headers.get.return_value = None
|
|
50
|
-
mock_dependencies.get_http_request.return_value = mock_request
|
|
51
|
-
|
|
52
|
-
result = get_session_id_from_request(None)
|
|
53
|
-
|
|
54
|
-
self.assertIsNone(result)
|
|
55
|
-
|
|
56
|
-
@patch('aixtools.server.utils.dependencies')
|
|
57
|
-
def test_get_session_id_value_error(self, mock_dependencies):
|
|
58
|
-
"""Test getting session ID when ValueError is raised."""
|
|
59
|
-
mock_dependencies.get_http_request.side_effect = ValueError("Request error")
|
|
60
|
-
|
|
61
|
-
result = get_session_id_from_request(None)
|
|
62
|
-
|
|
63
|
-
self.assertIsNone(result)
|
|
64
|
-
|
|
65
|
-
@patch('aixtools.server.utils.dependencies')
|
|
66
|
-
def test_get_session_id_runtime_error(self, mock_dependencies):
|
|
67
|
-
"""Test getting session ID when RuntimeError is raised."""
|
|
68
|
-
mock_dependencies.get_http_request.side_effect = RuntimeError("Runtime error")
|
|
69
|
-
|
|
70
|
-
result = get_session_id_from_request(None)
|
|
71
|
-
|
|
72
|
-
self.assertIsNone(result)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class TestGetUserIdFromRequest(unittest.TestCase):
|
|
76
|
-
"""Test cases for get_user_id_from_request function."""
|
|
77
|
-
|
|
78
|
-
@patch('aixtools.server.utils.dependencies')
|
|
79
|
-
def test_get_user_id_with_context_none(self, mock_dependencies):
|
|
80
|
-
"""Test getting user ID when context is None."""
|
|
81
|
-
mock_request = Mock()
|
|
82
|
-
mock_request.headers.get.return_value = "TEST-USER-123"
|
|
83
|
-
mock_dependencies.get_http_request.return_value = mock_request
|
|
84
|
-
|
|
85
|
-
result = get_user_id_from_request(None)
|
|
86
|
-
|
|
87
|
-
self.assertEqual(result, "test-user-123") # Should be lowercase
|
|
88
|
-
mock_dependencies.get_http_request.assert_called_once()
|
|
89
|
-
mock_request.headers.get.assert_called_once_with("user-id")
|
|
90
|
-
|
|
91
|
-
def test_get_user_id_with_context_provided(self):
|
|
92
|
-
"""Test getting user ID when context is provided."""
|
|
93
|
-
mock_ctx = Mock()
|
|
94
|
-
mock_request = Mock()
|
|
95
|
-
mock_request.headers.get.return_value = "CTX-USER-456"
|
|
96
|
-
mock_ctx.get_http_request.return_value = mock_request
|
|
97
|
-
|
|
98
|
-
result = get_user_id_from_request(mock_ctx)
|
|
99
|
-
|
|
100
|
-
self.assertEqual(result, "ctx-user-456") # Should be lowercase
|
|
101
|
-
mock_ctx.get_http_request.assert_called_once()
|
|
102
|
-
mock_request.headers.get.assert_called_once_with("user-id")
|
|
103
|
-
|
|
104
|
-
@patch('aixtools.server.utils.dependencies')
|
|
105
|
-
def test_get_user_id_header_not_found(self, mock_dependencies):
|
|
106
|
-
"""Test getting user ID when header is not found."""
|
|
107
|
-
mock_request = Mock()
|
|
108
|
-
mock_request.headers.get.return_value = None
|
|
109
|
-
mock_dependencies.get_http_request.return_value = mock_request
|
|
110
|
-
|
|
111
|
-
result = get_user_id_from_request(None)
|
|
112
|
-
|
|
113
|
-
self.assertIsNone(result)
|
|
114
|
-
|
|
115
|
-
@patch('aixtools.server.utils.dependencies')
|
|
116
|
-
def test_get_user_id_empty_string(self, mock_dependencies):
|
|
117
|
-
"""Test getting user ID when header is empty string."""
|
|
118
|
-
mock_request = Mock()
|
|
119
|
-
mock_request.headers.get.return_value = ""
|
|
120
|
-
mock_dependencies.get_http_request.return_value = mock_request
|
|
121
|
-
|
|
122
|
-
result = get_user_id_from_request(None)
|
|
123
|
-
|
|
124
|
-
self.assertIsNone(result)
|
|
125
|
-
|
|
126
|
-
@patch('aixtools.server.utils.dependencies')
|
|
127
|
-
def test_get_user_id_value_error(self, mock_dependencies):
|
|
128
|
-
"""Test getting user ID when ValueError is raised."""
|
|
129
|
-
mock_dependencies.get_http_request.side_effect = ValueError("Request error")
|
|
130
|
-
|
|
131
|
-
result = get_user_id_from_request(None)
|
|
132
|
-
|
|
133
|
-
self.assertIsNone(result)
|
|
134
|
-
|
|
135
|
-
@patch('aixtools.server.utils.dependencies')
|
|
136
|
-
def test_get_user_id_runtime_error(self, mock_dependencies):
|
|
137
|
-
"""Test getting user ID when RuntimeError is raised."""
|
|
138
|
-
mock_dependencies.get_http_request.side_effect = RuntimeError("Runtime error")
|
|
139
|
-
|
|
140
|
-
result = get_user_id_from_request(None)
|
|
141
|
-
|
|
142
|
-
self.assertIsNone(result)
|
|
143
|
-
|
|
144
|
-
@patch('aixtools.server.utils.dependencies')
|
|
145
|
-
def test_get_user_id_attribute_error(self, mock_dependencies):
|
|
146
|
-
"""Test getting user ID when AttributeError is raised."""
|
|
147
|
-
mock_dependencies.get_http_request.side_effect = AttributeError("Attribute error")
|
|
148
|
-
|
|
149
|
-
result = get_user_id_from_request(None)
|
|
150
|
-
|
|
151
|
-
self.assertIsNone(result)
|
|
152
|
-
|
|
153
|
-
@patch('aixtools.server.utils.dependencies')
|
|
154
|
-
def test_get_user_id_case_variations(self, mock_dependencies):
|
|
155
|
-
"""Test that user ID is always returned in lowercase."""
|
|
156
|
-
test_cases = [
|
|
157
|
-
("UPPERCASE", "uppercase"),
|
|
158
|
-
("MixedCase", "mixedcase"),
|
|
159
|
-
("lowercase", "lowercase"),
|
|
160
|
-
("CamelCase", "camelcase"),
|
|
161
|
-
]
|
|
162
|
-
|
|
163
|
-
for input_user_id, expected_output in test_cases:
|
|
164
|
-
with self.subTest(input_user_id=input_user_id):
|
|
165
|
-
mock_request = Mock()
|
|
166
|
-
mock_request.headers.get.return_value = input_user_id
|
|
167
|
-
mock_dependencies.get_http_request.return_value = mock_request
|
|
168
|
-
|
|
169
|
-
result = get_user_id_from_request(None)
|
|
170
|
-
|
|
171
|
-
self.assertEqual(result, expected_output)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
class TestGetSessionIdTuple(unittest.TestCase):
|
|
175
|
-
"""Test cases for get_session_id_tuple function."""
|
|
176
|
-
|
|
177
|
-
@patch('aixtools.server.utils.get_session_id_from_request')
|
|
178
|
-
@patch('aixtools.server.utils.get_user_id_from_request')
|
|
179
|
-
@patch('aixtools.server.utils.session_id_var')
|
|
180
|
-
@patch('aixtools.server.utils.user_id_var')
|
|
181
|
-
def test_get_session_id_tuple_with_headers(self, mock_user_var, mock_session_var,
|
|
182
|
-
mock_get_user, mock_get_session):
|
|
183
|
-
"""Test getting session ID tuple when headers are available."""
|
|
184
|
-
mock_get_user.return_value = "header-user"
|
|
185
|
-
mock_get_session.return_value = "header-session"
|
|
186
|
-
|
|
187
|
-
result = get_session_id_tuple(None)
|
|
188
|
-
|
|
189
|
-
self.assertEqual(result, ("header-user", "header-session"))
|
|
190
|
-
mock_get_user.assert_called_once_with(None)
|
|
191
|
-
mock_get_session.assert_called_once_with(None)
|
|
192
|
-
|
|
193
|
-
@patch('aixtools.server.utils.get_session_id_from_request')
|
|
194
|
-
@patch('aixtools.server.utils.get_user_id_from_request')
|
|
195
|
-
@patch('aixtools.server.utils.session_id_var')
|
|
196
|
-
@patch('aixtools.server.utils.user_id_var')
|
|
197
|
-
def test_get_session_id_tuple_with_fallback(self, mock_user_var, mock_session_var,
|
|
198
|
-
mock_get_user, mock_get_session):
|
|
199
|
-
"""Test getting session ID tuple with fallback to context variables."""
|
|
200
|
-
mock_get_user.return_value = None
|
|
201
|
-
mock_get_session.return_value = None
|
|
202
|
-
mock_user_var.get.return_value = "context-user"
|
|
203
|
-
mock_session_var.get.return_value = "context-session"
|
|
204
|
-
|
|
205
|
-
result = get_session_id_tuple(None)
|
|
206
|
-
|
|
207
|
-
self.assertEqual(result, ("context-user", "context-session"))
|
|
208
|
-
mock_user_var.get.assert_called_once_with("default_user")
|
|
209
|
-
mock_session_var.get.assert_called_once_with("default_session")
|
|
210
|
-
|
|
211
|
-
@patch('aixtools.server.utils.get_session_id_from_request')
|
|
212
|
-
@patch('aixtools.server.utils.get_user_id_from_request')
|
|
213
|
-
@patch('aixtools.server.utils.session_id_var')
|
|
214
|
-
@patch('aixtools.server.utils.user_id_var')
|
|
215
|
-
def test_get_session_id_tuple_mixed_sources(self, mock_user_var, mock_session_var,
|
|
216
|
-
mock_get_user, mock_get_session):
|
|
217
|
-
"""Test getting session ID tuple with mixed sources."""
|
|
218
|
-
mock_get_user.return_value = "header-user"
|
|
219
|
-
mock_get_session.return_value = None
|
|
220
|
-
mock_session_var.get.return_value = "context-session"
|
|
221
|
-
|
|
222
|
-
result = get_session_id_tuple(None)
|
|
223
|
-
|
|
224
|
-
self.assertEqual(result, ("header-user", "context-session"))
|
|
225
|
-
|
|
226
|
-
def test_get_session_id_tuple_with_context(self):
|
|
227
|
-
"""Test getting session ID tuple with provided context."""
|
|
228
|
-
mock_ctx = Mock()
|
|
229
|
-
|
|
230
|
-
with patch('aixtools.server.utils.get_user_id_from_request') as mock_get_user, \
|
|
231
|
-
patch('aixtools.server.utils.get_session_id_from_request') as mock_get_session:
|
|
232
|
-
|
|
233
|
-
mock_get_user.return_value = "ctx-user"
|
|
234
|
-
mock_get_session.return_value = "ctx-session"
|
|
235
|
-
|
|
236
|
-
result = get_session_id_tuple(mock_ctx)
|
|
237
|
-
|
|
238
|
-
self.assertEqual(result, ("ctx-user", "ctx-session"))
|
|
239
|
-
mock_get_user.assert_called_once_with(mock_ctx)
|
|
240
|
-
mock_get_session.assert_called_once_with(mock_ctx)
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
class TestGetSessionIdStr(unittest.TestCase):
|
|
244
|
-
"""Test cases for get_session_id_str function."""
|
|
245
|
-
|
|
246
|
-
@patch('aixtools.server.utils.get_session_id_tuple')
|
|
247
|
-
def test_get_session_id_str(self, mock_get_tuple):
|
|
248
|
-
"""Test getting session ID string."""
|
|
249
|
-
mock_get_tuple.return_value = ("test-user", "test-session")
|
|
250
|
-
|
|
251
|
-
result = get_session_id_str(None)
|
|
252
|
-
|
|
253
|
-
self.assertEqual(result, "test-user:test-session")
|
|
254
|
-
mock_get_tuple.assert_called_once_with(None)
|
|
255
|
-
|
|
256
|
-
@patch('aixtools.server.utils.get_session_id_tuple')
|
|
257
|
-
def test_get_session_id_str_with_context(self, mock_get_tuple):
|
|
258
|
-
"""Test getting session ID string with context."""
|
|
259
|
-
mock_ctx = Mock()
|
|
260
|
-
mock_get_tuple.return_value = ("ctx-user", "ctx-session")
|
|
261
|
-
|
|
262
|
-
result = get_session_id_str(mock_ctx)
|
|
263
|
-
|
|
264
|
-
self.assertEqual(result, "ctx-user:ctx-session")
|
|
265
|
-
mock_get_tuple.assert_called_once_with(mock_ctx)
|
|
266
|
-
|
|
267
|
-
@patch('aixtools.server.utils.get_session_id_tuple')
|
|
268
|
-
def test_get_session_id_str_special_characters(self, mock_get_tuple):
|
|
269
|
-
"""Test getting session ID string with special characters."""
|
|
270
|
-
mock_get_tuple.return_value = ("user@domain.com", "session-123-abc")
|
|
271
|
-
|
|
272
|
-
result = get_session_id_str(None)
|
|
273
|
-
|
|
274
|
-
self.assertEqual(result, "user@domain.com:session-123-abc")
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
class TestRunInThread(unittest.IsolatedAsyncioTestCase):
|
|
278
|
-
"""Test cases for run_in_thread decorator."""
|
|
279
|
-
|
|
280
|
-
async def test_run_in_thread_basic_function(self):
|
|
281
|
-
"""Test run_in_thread decorator with basic function."""
|
|
282
|
-
@run_in_thread
|
|
283
|
-
def sync_function(x, y):
|
|
284
|
-
return x + y
|
|
285
|
-
|
|
286
|
-
result = await sync_function(5, 3) # type: ignore
|
|
287
|
-
|
|
288
|
-
self.assertEqual(result, 8)
|
|
289
|
-
|
|
290
|
-
async def test_run_in_thread_with_kwargs(self):
|
|
291
|
-
"""Test run_in_thread decorator with keyword arguments."""
|
|
292
|
-
@run_in_thread
|
|
293
|
-
def sync_function(x, y, multiplier=1):
|
|
294
|
-
return (x + y) * multiplier
|
|
295
|
-
|
|
296
|
-
result = await sync_function(5, 3, multiplier=2) # type: ignore
|
|
297
|
-
|
|
298
|
-
self.assertEqual(result, 16)
|
|
299
|
-
|
|
300
|
-
async def test_run_in_thread_with_exception(self):
|
|
301
|
-
"""Test run_in_thread decorator when function raises exception."""
|
|
302
|
-
@run_in_thread
|
|
303
|
-
def failing_function():
|
|
304
|
-
raise ValueError("Test error")
|
|
305
|
-
|
|
306
|
-
with self.assertRaises(ValueError) as context:
|
|
307
|
-
await failing_function() # type: ignore
|
|
308
|
-
|
|
309
|
-
self.assertEqual(str(context.exception), "Test error")
|
|
310
|
-
|
|
311
|
-
async def test_run_in_thread_preserves_function_metadata(self):
|
|
312
|
-
"""Test that run_in_thread preserves function metadata."""
|
|
313
|
-
@run_in_thread
|
|
314
|
-
def documented_function(x):
|
|
315
|
-
"""This is a test function."""
|
|
316
|
-
return x * 2
|
|
317
|
-
|
|
318
|
-
self.assertEqual(documented_function.__name__, "documented_function")
|
|
319
|
-
self.assertEqual(documented_function.__doc__, "This is a test function.")
|
|
320
|
-
|
|
321
|
-
async def test_run_in_thread_with_no_args(self):
|
|
322
|
-
"""Test run_in_thread decorator with function that takes no arguments."""
|
|
323
|
-
@run_in_thread
|
|
324
|
-
def no_args_function():
|
|
325
|
-
return "success"
|
|
326
|
-
|
|
327
|
-
result = await no_args_function() # type: ignore
|
|
328
|
-
|
|
329
|
-
self.assertEqual(result, "success")
|
|
330
|
-
|
|
331
|
-
async def test_run_in_thread_with_complex_return_type(self):
|
|
332
|
-
"""Test run_in_thread decorator with complex return type."""
|
|
333
|
-
@run_in_thread
|
|
334
|
-
def complex_function():
|
|
335
|
-
return {"key": "value", "list": [1, 2, 3], "nested": {"inner": True}}
|
|
336
|
-
|
|
337
|
-
result = await complex_function() # type: ignore
|
|
338
|
-
|
|
339
|
-
expected = {"key": "value", "list": [1, 2, 3], "nested": {"inner": True}}
|
|
340
|
-
self.assertEqual(result, expected)
|
|
341
|
-
|
|
342
|
-
@patch('asyncio.to_thread')
|
|
343
|
-
async def test_run_in_thread_calls_asyncio_to_thread(self, mock_to_thread):
|
|
344
|
-
"""Test that run_in_thread actually calls asyncio.to_thread."""
|
|
345
|
-
mock_to_thread.return_value = "mocked_result"
|
|
346
|
-
|
|
347
|
-
@run_in_thread
|
|
348
|
-
def test_function(arg1, arg2, kwarg1=None):
|
|
349
|
-
return f"{arg1}-{arg2}-{kwarg1}"
|
|
350
|
-
|
|
351
|
-
result = await test_function("a", "b", kwarg1="c") # type: ignore
|
|
352
|
-
|
|
353
|
-
self.assertEqual(result, "mocked_result")
|
|
354
|
-
mock_to_thread.assert_called_once()
|
|
355
|
-
# Verify the original function and arguments were passed
|
|
356
|
-
args, kwargs = mock_to_thread.call_args
|
|
357
|
-
self.assertEqual(args[1:], ("a", "b")) # Skip the function itself
|
|
358
|
-
self.assertEqual(kwargs, {"kwarg1": "c"})
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if __name__ == '__main__':
|
|
362
|
-
unittest.main()
|
tests/unit/utils/__init__.py
DELETED
|
File without changes
|
tests/unit/utils/test_files.py
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
"""Unit tests for aixtools.utils.files module."""
|
|
2
|
-
|
|
3
|
-
import unittest
|
|
4
|
-
|
|
5
|
-
from aixtools.utils.files import is_text_content
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestIsTextContent(unittest.TestCase):
|
|
9
|
-
"""Test cases for is_text_content function."""
|
|
10
|
-
|
|
11
|
-
def test_text_mime_types_return_true(self):
|
|
12
|
-
"""Test that text mime types return True."""
|
|
13
|
-
text_mime_types = [
|
|
14
|
-
"text/plain",
|
|
15
|
-
"text/html",
|
|
16
|
-
"text/css",
|
|
17
|
-
"text/javascript",
|
|
18
|
-
"text/csv",
|
|
19
|
-
"text/xml",
|
|
20
|
-
"text/markdown",
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
for mime_type in text_mime_types:
|
|
24
|
-
with self.subTest(mime_type=mime_type):
|
|
25
|
-
result = is_text_content(b"some content", mime_type)
|
|
26
|
-
self.assertTrue(result, f"MIME type {mime_type} should be detected as text")
|
|
27
|
-
|
|
28
|
-
def test_application_text_mime_types_return_true(self):
|
|
29
|
-
"""Test that application text-like mime types return True."""
|
|
30
|
-
app_text_mime_types = [
|
|
31
|
-
"application/json",
|
|
32
|
-
"application/xml",
|
|
33
|
-
"application/javascript",
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
for mime_type in app_text_mime_types:
|
|
37
|
-
with self.subTest(mime_type=mime_type):
|
|
38
|
-
result = is_text_content(b"some content", mime_type)
|
|
39
|
-
self.assertTrue(result, f"MIME type {mime_type} should be detected as text")
|
|
40
|
-
|
|
41
|
-
def test_binary_mime_types_with_valid_utf8_return_true(self):
|
|
42
|
-
"""Test that binary mime types with valid UTF-8 content return True."""
|
|
43
|
-
binary_mime_types = [
|
|
44
|
-
"application/octet-stream",
|
|
45
|
-
"image/png",
|
|
46
|
-
"video/mp4",
|
|
47
|
-
"application/pdf",
|
|
48
|
-
]
|
|
49
|
-
|
|
50
|
-
utf8_content = "Hello, world! 🌍".encode("utf-8")
|
|
51
|
-
|
|
52
|
-
for mime_type in binary_mime_types:
|
|
53
|
-
with self.subTest(mime_type=mime_type):
|
|
54
|
-
result = is_text_content(utf8_content, mime_type)
|
|
55
|
-
self.assertTrue(result, f"Valid UTF-8 content should be detected as text even with {mime_type}")
|
|
56
|
-
|
|
57
|
-
def test_binary_mime_types_with_invalid_utf8_return_false(self):
|
|
58
|
-
"""Test that binary mime types with invalid UTF-8 content return False."""
|
|
59
|
-
binary_mime_types = [
|
|
60
|
-
"application/octet-stream",
|
|
61
|
-
"image/png",
|
|
62
|
-
"video/mp4",
|
|
63
|
-
"application/pdf",
|
|
64
|
-
]
|
|
65
|
-
|
|
66
|
-
# Create invalid UTF-8 bytes
|
|
67
|
-
invalid_utf8_content = b'\x80\x81\x82\x83'
|
|
68
|
-
|
|
69
|
-
for mime_type in binary_mime_types:
|
|
70
|
-
with self.subTest(mime_type=mime_type):
|
|
71
|
-
result = is_text_content(invalid_utf8_content, mime_type)
|
|
72
|
-
self.assertFalse(result, f"Invalid UTF-8 content should not be detected as text with {mime_type}")
|
|
73
|
-
|
|
74
|
-
def test_empty_mime_type_with_valid_utf8_return_true(self):
|
|
75
|
-
"""Test that empty mime type with valid UTF-8 content returns True."""
|
|
76
|
-
utf8_content = "Hello, world!".encode("utf-8")
|
|
77
|
-
|
|
78
|
-
result = is_text_content(utf8_content, "")
|
|
79
|
-
self.assertTrue(result)
|
|
80
|
-
|
|
81
|
-
def test_empty_mime_type_with_invalid_utf8_return_false(self):
|
|
82
|
-
"""Test that empty mime type with invalid UTF-8 content returns False."""
|
|
83
|
-
invalid_utf8_content = b'\x80\x81\x82\x83'
|
|
84
|
-
|
|
85
|
-
result = is_text_content(invalid_utf8_content, "")
|
|
86
|
-
self.assertFalse(result)
|
|
87
|
-
|
|
88
|
-
def test_none_mime_type_with_valid_utf8_return_true(self):
|
|
89
|
-
"""Test that None mime type with valid UTF-8 content returns True."""
|
|
90
|
-
utf8_content = "Hello, world!".encode("utf-8")
|
|
91
|
-
|
|
92
|
-
result = is_text_content(utf8_content, None) # type: ignore
|
|
93
|
-
self.assertTrue(result)
|
|
94
|
-
|
|
95
|
-
def test_none_mime_type_with_invalid_utf8_return_false(self):
|
|
96
|
-
"""Test that None mime type with invalid UTF-8 content returns False."""
|
|
97
|
-
invalid_utf8_content = b'\x80\x81\x82\x83'
|
|
98
|
-
|
|
99
|
-
result = is_text_content(invalid_utf8_content, None) # type: ignore
|
|
100
|
-
self.assertFalse(result)
|
|
101
|
-
|
|
102
|
-
def test_empty_content_with_text_mime_type_return_true(self):
|
|
103
|
-
"""Test that empty content with text mime type returns True."""
|
|
104
|
-
result = is_text_content(b"", "text/plain")
|
|
105
|
-
self.assertTrue(result)
|
|
106
|
-
|
|
107
|
-
def test_empty_content_with_binary_mime_type_return_true(self):
|
|
108
|
-
"""Test that empty content with binary mime type returns True (empty is valid UTF-8)."""
|
|
109
|
-
result = is_text_content(b"", "application/octet-stream")
|
|
110
|
-
self.assertTrue(result)
|
|
111
|
-
|
|
112
|
-
def test_unicode_content_return_true(self):
|
|
113
|
-
"""Test that Unicode content is properly detected as text."""
|
|
114
|
-
unicode_strings = [
|
|
115
|
-
"Hello, 世界!",
|
|
116
|
-
"Café ☕",
|
|
117
|
-
"🚀 Rocket",
|
|
118
|
-
"Здравствуй мир",
|
|
119
|
-
"مرحبا بالعالم",
|
|
120
|
-
]
|
|
121
|
-
|
|
122
|
-
for unicode_str in unicode_strings:
|
|
123
|
-
with self.subTest(content=unicode_str):
|
|
124
|
-
utf8_content = unicode_str.encode("utf-8")
|
|
125
|
-
result = is_text_content(utf8_content, "application/octet-stream")
|
|
126
|
-
self.assertTrue(result, f"Unicode string '{unicode_str}' should be detected as text")
|
|
127
|
-
|
|
128
|
-
def test_binary_image_data_return_false(self):
|
|
129
|
-
"""Test that actual binary image data returns False."""
|
|
130
|
-
# PNG file header
|
|
131
|
-
png_header = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR'
|
|
132
|
-
|
|
133
|
-
result = is_text_content(png_header, "image/png")
|
|
134
|
-
self.assertFalse(result)
|
|
135
|
-
|
|
136
|
-
def test_binary_executable_data_return_false(self):
|
|
137
|
-
"""Test that binary executable data returns False."""
|
|
138
|
-
# Binary data with invalid UTF-8 sequences
|
|
139
|
-
binary_data = b'\x7f\x80\x81\x82\x83\x84\x85\x86'
|
|
140
|
-
|
|
141
|
-
result = is_text_content(binary_data, "application/octet-stream")
|
|
142
|
-
self.assertFalse(result)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if __name__ == '__main__':
|
|
146
|
-
unittest.main()
|
tests/unit/vault/__init__.py
DELETED
|
File without changes
|