aixtools 0.1.10__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/mcp/client.py +102 -1
- aixtools/testing/aix_test_model.py +2 -0
- {aixtools-0.1.10.dist-info → aixtools-0.1.11.dist-info}/METADATA +2 -1
- {aixtools-0.1.10.dist-info → aixtools-0.1.11.dist-info}/RECORD +8 -45
- aixtools-0.1.11.dist-info/top_level.txt +1 -0
- aixtools-0.1.10.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.10.dist-info → aixtools-0.1.11.dist-info}/WHEEL +0 -0
- {aixtools-0.1.10.dist-info → aixtools-0.1.11.dist-info}/entry_points.txt +0 -0
tests/unit/agents/test_prompt.py
DELETED
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
"""Unit tests for aixtools.agents.prompt module."""
|
|
2
|
-
|
|
3
|
-
import tempfile
|
|
4
|
-
import unittest
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from unittest.mock import Mock, patch, mock_open
|
|
7
|
-
|
|
8
|
-
from pydantic_ai import BinaryContent
|
|
9
|
-
|
|
10
|
-
from aixtools.agents.prompt import (
|
|
11
|
-
CLAUDE_IMAGE_MAX_FILE_SIZE_IN_CONTEXT,
|
|
12
|
-
CLAUDE_MAX_FILE_SIZE_IN_CONTEXT,
|
|
13
|
-
build_user_input,
|
|
14
|
-
file_to_binary_content,
|
|
15
|
-
should_be_included_into_context,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class TestShouldBeIncludedIntoContext(unittest.TestCase):
|
|
20
|
-
"""Test cases for should_be_included_into_context function."""
|
|
21
|
-
|
|
22
|
-
def test_non_binary_content_returns_false(self):
|
|
23
|
-
"""Test that non-BinaryContent returns False."""
|
|
24
|
-
result = should_be_included_into_context("text content", 100)
|
|
25
|
-
self.assertFalse(result)
|
|
26
|
-
|
|
27
|
-
result = should_be_included_into_context(None, 100)
|
|
28
|
-
self.assertFalse(result)
|
|
29
|
-
|
|
30
|
-
def test_text_media_type_returns_false(self):
|
|
31
|
-
"""Test that text media types return False."""
|
|
32
|
-
binary_content = BinaryContent(data=b"test", media_type="text/plain")
|
|
33
|
-
result = should_be_included_into_context(binary_content, 100)
|
|
34
|
-
self.assertFalse(result)
|
|
35
|
-
|
|
36
|
-
binary_content = BinaryContent(data=b"test", media_type="text/html")
|
|
37
|
-
result = should_be_included_into_context(binary_content, 100)
|
|
38
|
-
self.assertFalse(result)
|
|
39
|
-
|
|
40
|
-
def test_archive_types_return_false(self):
|
|
41
|
-
"""Test that archive media types return False."""
|
|
42
|
-
archive_types = [
|
|
43
|
-
"application/zip",
|
|
44
|
-
"application/x-tar",
|
|
45
|
-
"application/gzip",
|
|
46
|
-
"application/x-gzip",
|
|
47
|
-
"application/x-rar-compressed",
|
|
48
|
-
"application/x-7z-compressed",
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
for media_type in archive_types:
|
|
52
|
-
binary_content = BinaryContent(data=b"test", media_type=media_type)
|
|
53
|
-
result = should_be_included_into_context(binary_content, 100)
|
|
54
|
-
self.assertFalse(result, f"Archive type {media_type} should return False")
|
|
55
|
-
|
|
56
|
-
def test_image_within_size_limit_returns_true(self):
|
|
57
|
-
"""Test that images within size limit return True."""
|
|
58
|
-
with patch.object(BinaryContent, 'is_image', new_callable=lambda: True):
|
|
59
|
-
binary_content = BinaryContent(data=b"image_data", media_type="image/png")
|
|
60
|
-
|
|
61
|
-
# Test with size under limit
|
|
62
|
-
result = should_be_included_into_context(binary_content, 1024)
|
|
63
|
-
self.assertTrue(result)
|
|
64
|
-
|
|
65
|
-
def test_image_over_size_limit_returns_false(self):
|
|
66
|
-
"""Test that images over size limit return False."""
|
|
67
|
-
with patch.object(BinaryContent, 'is_image', new_callable=lambda: True):
|
|
68
|
-
binary_content = BinaryContent(data=b"image_data", media_type="image/png")
|
|
69
|
-
|
|
70
|
-
# Test with size over limit
|
|
71
|
-
result = should_be_included_into_context(binary_content, CLAUDE_IMAGE_MAX_FILE_SIZE_IN_CONTEXT + 1)
|
|
72
|
-
self.assertFalse(result)
|
|
73
|
-
|
|
74
|
-
def test_non_image_within_size_limit_returns_true(self):
|
|
75
|
-
"""Test that non-images within size limit return True."""
|
|
76
|
-
with patch.object(BinaryContent, 'is_image', new_callable=lambda: False):
|
|
77
|
-
binary_content = BinaryContent(data=b"pdf_data", media_type="application/pdf")
|
|
78
|
-
|
|
79
|
-
# Test with size under limit
|
|
80
|
-
result = should_be_included_into_context(binary_content, 1024)
|
|
81
|
-
self.assertTrue(result)
|
|
82
|
-
|
|
83
|
-
def test_non_image_over_size_limit_returns_false(self):
|
|
84
|
-
"""Test that non-images over size limit return False."""
|
|
85
|
-
with patch.object(BinaryContent, 'is_image', new_callable=lambda: False):
|
|
86
|
-
binary_content = BinaryContent(data=b"pdf_data", media_type="application/pdf")
|
|
87
|
-
|
|
88
|
-
# Test with size over limit
|
|
89
|
-
result = should_be_included_into_context(binary_content, CLAUDE_MAX_FILE_SIZE_IN_CONTEXT + 1)
|
|
90
|
-
self.assertFalse(result)
|
|
91
|
-
|
|
92
|
-
def test_custom_size_limits(self):
|
|
93
|
-
"""Test with custom size limits."""
|
|
94
|
-
# Test non-image content with custom limits (since image detection is complex)
|
|
95
|
-
pdf_content = BinaryContent(data=b"pdf_data", media_type="application/pdf")
|
|
96
|
-
|
|
97
|
-
# Test non-image over custom file limit
|
|
98
|
-
result = should_be_included_into_context(
|
|
99
|
-
pdf_content, 5000, max_img_size_bytes=1024, max_file_size_bytes=4096
|
|
100
|
-
)
|
|
101
|
-
self.assertFalse(result)
|
|
102
|
-
|
|
103
|
-
# Test non-image under custom file limit
|
|
104
|
-
result = should_be_included_into_context(
|
|
105
|
-
pdf_content, 2000, max_img_size_bytes=1024, max_file_size_bytes=4096
|
|
106
|
-
)
|
|
107
|
-
self.assertTrue(result)
|
|
108
|
-
|
|
109
|
-
# Test with very small custom limits
|
|
110
|
-
result = should_be_included_into_context(
|
|
111
|
-
pdf_content, 100, max_img_size_bytes=50, max_file_size_bytes=80
|
|
112
|
-
)
|
|
113
|
-
self.assertFalse(result)
|
|
114
|
-
|
|
115
|
-
# Test with very large custom limits
|
|
116
|
-
result = should_be_included_into_context(
|
|
117
|
-
pdf_content, 100, max_img_size_bytes=200, max_file_size_bytes=200
|
|
118
|
-
)
|
|
119
|
-
self.assertTrue(result)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
class TestFileToBinaryContent(unittest.TestCase):
|
|
123
|
-
"""Test cases for file_to_binary_content function."""
|
|
124
|
-
|
|
125
|
-
def setUp(self):
|
|
126
|
-
"""Set up test fixtures."""
|
|
127
|
-
self.temp_dir = tempfile.mkdtemp()
|
|
128
|
-
self.temp_path = Path(self.temp_dir)
|
|
129
|
-
|
|
130
|
-
def tearDown(self):
|
|
131
|
-
"""Clean up test fixtures."""
|
|
132
|
-
import shutil
|
|
133
|
-
shutil.rmtree(self.temp_dir)
|
|
134
|
-
|
|
135
|
-
@patch('aixtools.agents.prompt.is_text_content')
|
|
136
|
-
@patch('mimetypes.guess_type')
|
|
137
|
-
def test_text_file_returns_string(self, mock_guess_type, mock_is_text):
|
|
138
|
-
"""Test that text files return decoded strings."""
|
|
139
|
-
mock_guess_type.return_value = ("text/plain", None)
|
|
140
|
-
mock_is_text.return_value = True
|
|
141
|
-
|
|
142
|
-
test_file = self.temp_path / "test.txt"
|
|
143
|
-
test_content = "Hello, world!"
|
|
144
|
-
test_file.write_text(test_content, encoding="utf-8")
|
|
145
|
-
|
|
146
|
-
result = file_to_binary_content(test_file)
|
|
147
|
-
|
|
148
|
-
self.assertEqual(result, test_content)
|
|
149
|
-
mock_is_text.assert_called_once()
|
|
150
|
-
|
|
151
|
-
@patch('aixtools.agents.prompt.is_text_content')
|
|
152
|
-
@patch('mimetypes.guess_type')
|
|
153
|
-
def test_binary_file_returns_binary_content(self, mock_guess_type, mock_is_text):
|
|
154
|
-
"""Test that binary files return BinaryContent."""
|
|
155
|
-
mock_guess_type.return_value = ("image/png", None)
|
|
156
|
-
mock_is_text.return_value = False
|
|
157
|
-
|
|
158
|
-
test_file = self.temp_path / "test.png"
|
|
159
|
-
test_data = b'\x89PNG\r\n\x1a\n'
|
|
160
|
-
test_file.write_bytes(test_data)
|
|
161
|
-
|
|
162
|
-
result = file_to_binary_content(test_file)
|
|
163
|
-
|
|
164
|
-
self.assertIsInstance(result, BinaryContent)
|
|
165
|
-
if isinstance(result, BinaryContent):
|
|
166
|
-
self.assertEqual(result.data, test_data)
|
|
167
|
-
self.assertEqual(result.media_type, "image/png")
|
|
168
|
-
|
|
169
|
-
@patch('aixtools.agents.prompt.is_text_content')
|
|
170
|
-
@patch('mimetypes.guess_type')
|
|
171
|
-
def test_unknown_mime_type_defaults_to_octet_stream(self, mock_guess_type, mock_is_text):
|
|
172
|
-
"""Test that unknown mime types default to application/octet-stream."""
|
|
173
|
-
mock_guess_type.return_value = (None, None)
|
|
174
|
-
mock_is_text.return_value = False
|
|
175
|
-
|
|
176
|
-
test_file = self.temp_path / "test.unknown"
|
|
177
|
-
test_data = b'unknown data'
|
|
178
|
-
test_file.write_bytes(test_data)
|
|
179
|
-
|
|
180
|
-
result = file_to_binary_content(test_file)
|
|
181
|
-
|
|
182
|
-
self.assertIsInstance(result, BinaryContent)
|
|
183
|
-
if isinstance(result, BinaryContent):
|
|
184
|
-
self.assertEqual(result.media_type, "application/octet-stream")
|
|
185
|
-
|
|
186
|
-
@patch('aixtools.agents.prompt.is_text_content')
|
|
187
|
-
def test_explicit_mime_type(self, mock_is_text):
|
|
188
|
-
"""Test with explicitly provided mime type."""
|
|
189
|
-
mock_is_text.return_value = False
|
|
190
|
-
|
|
191
|
-
test_file = self.temp_path / "test.data"
|
|
192
|
-
test_data = b'test data'
|
|
193
|
-
test_file.write_bytes(test_data)
|
|
194
|
-
|
|
195
|
-
result = file_to_binary_content(test_file, "application/custom")
|
|
196
|
-
|
|
197
|
-
self.assertIsInstance(result, BinaryContent)
|
|
198
|
-
if isinstance(result, BinaryContent):
|
|
199
|
-
self.assertEqual(result.media_type, "application/custom")
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
class TestBuildUserInput(unittest.TestCase):
|
|
203
|
-
"""Test cases for build_user_input function."""
|
|
204
|
-
|
|
205
|
-
def setUp(self):
|
|
206
|
-
"""Set up test fixtures."""
|
|
207
|
-
self.session_tuple = ("user123", "session456")
|
|
208
|
-
self.user_text = "Please analyze these files"
|
|
209
|
-
|
|
210
|
-
def test_no_file_paths_returns_text_only(self):
|
|
211
|
-
"""Test that no file paths returns just the user text."""
|
|
212
|
-
result = build_user_input(self.session_tuple, self.user_text, [])
|
|
213
|
-
|
|
214
|
-
self.assertEqual(result, self.user_text)
|
|
215
|
-
|
|
216
|
-
@patch('aixtools.agents.prompt.should_be_included_into_context')
|
|
217
|
-
@patch('aixtools.agents.prompt.file_to_binary_content')
|
|
218
|
-
@patch('aixtools.agents.prompt.container_to_host_path')
|
|
219
|
-
@patch('mimetypes.guess_type')
|
|
220
|
-
def test_single_file_not_included_in_context(
|
|
221
|
-
self, mock_guess_type, mock_container_to_host,
|
|
222
|
-
mock_file_to_binary, mock_should_include
|
|
223
|
-
):
|
|
224
|
-
"""Test with single file that should not be included in context."""
|
|
225
|
-
# Setup mocks
|
|
226
|
-
mock_guess_type.return_value = ("text/plain", None)
|
|
227
|
-
mock_should_include.return_value = False
|
|
228
|
-
|
|
229
|
-
mock_host_path = Mock()
|
|
230
|
-
mock_host_path.stat.return_value.st_size = 1024
|
|
231
|
-
mock_container_to_host.return_value = mock_host_path
|
|
232
|
-
|
|
233
|
-
mock_file_to_binary.return_value = "file content"
|
|
234
|
-
|
|
235
|
-
file_paths = [Path("/workspace/test.txt")]
|
|
236
|
-
|
|
237
|
-
result = build_user_input(self.session_tuple, self.user_text, file_paths)
|
|
238
|
-
|
|
239
|
-
self.assertIsInstance(result, list)
|
|
240
|
-
self.assertEqual(len(result), 1) # Only the prompt, no binary attachments
|
|
241
|
-
self.assertIsInstance(result[0], str)
|
|
242
|
-
prompt_text = str(result[0])
|
|
243
|
-
self.assertIn("Please analyze these files", prompt_text)
|
|
244
|
-
self.assertIn("Attachments:", prompt_text)
|
|
245
|
-
self.assertIn("test.txt", prompt_text)
|
|
246
|
-
self.assertIn("file_size=1024 bytes", prompt_text)
|
|
247
|
-
|
|
248
|
-
@patch('aixtools.agents.prompt.should_be_included_into_context')
|
|
249
|
-
@patch('aixtools.agents.prompt.file_to_binary_content')
|
|
250
|
-
@patch('aixtools.agents.prompt.container_to_host_path')
|
|
251
|
-
@patch('mimetypes.guess_type')
|
|
252
|
-
def test_single_file_included_in_context(
|
|
253
|
-
self, mock_guess_type, mock_container_to_host,
|
|
254
|
-
mock_file_to_binary, mock_should_include
|
|
255
|
-
):
|
|
256
|
-
"""Test with single file that should be included in context."""
|
|
257
|
-
# Setup mocks
|
|
258
|
-
mock_guess_type.return_value = ("image/png", None)
|
|
259
|
-
mock_should_include.return_value = True
|
|
260
|
-
|
|
261
|
-
mock_host_path = Mock()
|
|
262
|
-
mock_host_path.stat.return_value.st_size = 2048
|
|
263
|
-
mock_container_to_host.return_value = mock_host_path
|
|
264
|
-
|
|
265
|
-
mock_binary_content = BinaryContent(data=b"image data", media_type="image/png")
|
|
266
|
-
mock_file_to_binary.return_value = mock_binary_content
|
|
267
|
-
|
|
268
|
-
file_paths = [Path("/workspace/image.png")]
|
|
269
|
-
|
|
270
|
-
result = build_user_input(self.session_tuple, self.user_text, file_paths)
|
|
271
|
-
|
|
272
|
-
self.assertIsInstance(result, list)
|
|
273
|
-
self.assertEqual(len(result), 2) # Prompt + 1 binary attachment
|
|
274
|
-
self.assertIsInstance(result[0], str)
|
|
275
|
-
prompt_text = str(result[0])
|
|
276
|
-
self.assertIn("Please analyze these files", prompt_text)
|
|
277
|
-
self.assertIn("Attachments:", prompt_text)
|
|
278
|
-
self.assertIn("image.png", prompt_text)
|
|
279
|
-
self.assertIn("file_size=2048 bytes", prompt_text)
|
|
280
|
-
self.assertIn("provided to model context at index 0", prompt_text)
|
|
281
|
-
self.assertEqual(result[1], mock_binary_content)
|
|
282
|
-
|
|
283
|
-
@patch('aixtools.agents.prompt.should_be_included_into_context')
|
|
284
|
-
@patch('aixtools.agents.prompt.file_to_binary_content')
|
|
285
|
-
@patch('aixtools.agents.prompt.container_to_host_path')
|
|
286
|
-
@patch('mimetypes.guess_type')
|
|
287
|
-
def test_multiple_files_mixed_inclusion(
|
|
288
|
-
self, mock_guess_type, mock_container_to_host,
|
|
289
|
-
mock_file_to_binary, mock_should_include
|
|
290
|
-
):
|
|
291
|
-
"""Test with multiple files, some included in context, some not."""
|
|
292
|
-
# Setup mocks
|
|
293
|
-
mock_guess_type.side_effect = [("text/plain", None), ("image/png", None)]
|
|
294
|
-
mock_should_include.side_effect = [False, True] # First file not included, second included
|
|
295
|
-
|
|
296
|
-
mock_host_path1 = Mock()
|
|
297
|
-
mock_host_path1.stat.return_value.st_size = 1024
|
|
298
|
-
mock_host_path2 = Mock()
|
|
299
|
-
mock_host_path2.stat.return_value.st_size = 2048
|
|
300
|
-
mock_container_to_host.side_effect = [mock_host_path1, mock_host_path2]
|
|
301
|
-
|
|
302
|
-
mock_text_content = "text content"
|
|
303
|
-
mock_binary_content = BinaryContent(data=b"image data", media_type="image/png")
|
|
304
|
-
mock_file_to_binary.side_effect = [mock_text_content, mock_binary_content]
|
|
305
|
-
|
|
306
|
-
file_paths = [Path("/workspace/text.txt"), Path("/workspace/image.png")]
|
|
307
|
-
|
|
308
|
-
result = build_user_input(self.session_tuple, self.user_text, file_paths)
|
|
309
|
-
|
|
310
|
-
self.assertIsInstance(result, list)
|
|
311
|
-
self.assertEqual(len(result), 2) # Prompt + 1 binary attachment
|
|
312
|
-
self.assertIsInstance(result[0], str)
|
|
313
|
-
prompt_text = str(result[0])
|
|
314
|
-
self.assertIn("Please analyze these files", prompt_text)
|
|
315
|
-
self.assertIn("Attachments:", prompt_text)
|
|
316
|
-
self.assertIn("text.txt", prompt_text)
|
|
317
|
-
self.assertIn("image.png", prompt_text)
|
|
318
|
-
self.assertIn("file_size=1024 bytes", prompt_text)
|
|
319
|
-
self.assertIn("file_size=2048 bytes", prompt_text)
|
|
320
|
-
self.assertIn("provided to model context at index 0", prompt_text)
|
|
321
|
-
self.assertEqual(result[1], mock_binary_content)
|
|
322
|
-
|
|
323
|
-
def test_non_workspace_path_raises_error(self):
|
|
324
|
-
"""Test that non-workspace paths raise ValueError."""
|
|
325
|
-
file_paths = [Path("/invalid/path.txt")]
|
|
326
|
-
|
|
327
|
-
with self.assertRaises(ValueError) as context:
|
|
328
|
-
build_user_input(self.session_tuple, self.user_text, file_paths)
|
|
329
|
-
|
|
330
|
-
self.assertIn(
|
|
331
|
-
"Container path must be a subdir of '/workspace', got '/invalid/path.txt' instead",
|
|
332
|
-
str(context.exception),
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
@patch('aixtools.agents.prompt.should_be_included_into_context')
|
|
336
|
-
@patch('aixtools.agents.prompt.file_to_binary_content')
|
|
337
|
-
@patch('aixtools.agents.prompt.container_to_host_path')
|
|
338
|
-
@patch('mimetypes.guess_type')
|
|
339
|
-
def test_unknown_mime_type_defaults(
|
|
340
|
-
self, mock_guess_type, mock_container_to_host,
|
|
341
|
-
mock_file_to_binary, mock_should_include
|
|
342
|
-
):
|
|
343
|
-
"""Test that unknown mime types default to application/octet-stream."""
|
|
344
|
-
# Setup mocks
|
|
345
|
-
mock_guess_type.return_value = (None, None) # Unknown mime type
|
|
346
|
-
mock_should_include.return_value = False
|
|
347
|
-
|
|
348
|
-
mock_host_path = Mock()
|
|
349
|
-
mock_host_path.stat.return_value.st_size = 1024
|
|
350
|
-
mock_container_to_host.return_value = mock_host_path
|
|
351
|
-
|
|
352
|
-
mock_file_to_binary.return_value = "file content"
|
|
353
|
-
|
|
354
|
-
file_paths = [Path("/workspace/unknown.dat")]
|
|
355
|
-
|
|
356
|
-
build_user_input(self.session_tuple, self.user_text, file_paths)
|
|
357
|
-
|
|
358
|
-
# Verify that file_to_binary_content was called with the default mime type
|
|
359
|
-
mock_file_to_binary.assert_called_once_with(mock_host_path, "application/octet-stream")
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
if __name__ == '__main__':
|
|
363
|
-
unittest.main()
|
|
@@ -1,329 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import unittest
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
from aixtools.compliance.private_data import PrivateData, PRIVATE_DATA_FILE
|
|
6
|
-
from aixtools.server.path import get_workspace_path
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class TestPrivateData(unittest.TestCase):
|
|
10
|
-
"""Test cases for PrivateData class without mocking or patching."""
|
|
11
|
-
|
|
12
|
-
def setUp(self):
|
|
13
|
-
"""Set up test environment with temporary directory."""
|
|
14
|
-
# Create a test user and session context
|
|
15
|
-
self.workspace_path = Path(get_workspace_path())
|
|
16
|
-
self.workspace_path.mkdir(parents=True, exist_ok=True)
|
|
17
|
-
self.private_data_file = self.workspace_path / PRIVATE_DATA_FILE
|
|
18
|
-
|
|
19
|
-
def tearDown(self):
|
|
20
|
-
"""Clean up test environment."""
|
|
21
|
-
self.private_data_file.unlink(missing_ok=True)
|
|
22
|
-
|
|
23
|
-
def test_init_no_existing_file(self):
|
|
24
|
-
"""Test initialization when no private data file exists."""
|
|
25
|
-
private_data = PrivateData()
|
|
26
|
-
self.assertFalse(private_data.has_private_data)
|
|
27
|
-
self.assertEqual(private_data.get_private_datasets(), [])
|
|
28
|
-
self.assertEqual(private_data.get_idap_datasets(), [])
|
|
29
|
-
|
|
30
|
-
def test_init_with_existing_file(self):
|
|
31
|
-
"""Test initialization when private data file exists."""
|
|
32
|
-
# Create a test file
|
|
33
|
-
private_data_path = self.workspace_path / PRIVATE_DATA_FILE
|
|
34
|
-
test_data = {
|
|
35
|
-
"_has_private_data": True,
|
|
36
|
-
"_private_datasets": ["dataset1", "dataset2"],
|
|
37
|
-
"_idap_datasets": ["dataset1"],
|
|
38
|
-
"ctx": None
|
|
39
|
-
}
|
|
40
|
-
with open(private_data_path, "w") as f:
|
|
41
|
-
json.dump(test_data, f)
|
|
42
|
-
|
|
43
|
-
private_data = PrivateData()
|
|
44
|
-
|
|
45
|
-
self.assertTrue(private_data.has_private_data)
|
|
46
|
-
self.assertEqual(set(private_data.get_private_datasets()), {"dataset1", "dataset2"})
|
|
47
|
-
self.assertEqual(private_data.get_idap_datasets(), ["dataset1"])
|
|
48
|
-
|
|
49
|
-
def test_add_private_dataset_new(self):
|
|
50
|
-
"""Test adding a new private dataset."""
|
|
51
|
-
private_data = PrivateData()
|
|
52
|
-
|
|
53
|
-
private_data.add_private_dataset("test_dataset")
|
|
54
|
-
|
|
55
|
-
self.assertTrue(private_data.has_private_data)
|
|
56
|
-
self.assertIn("test_dataset", private_data.get_private_datasets())
|
|
57
|
-
|
|
58
|
-
# Verify data is saved to file
|
|
59
|
-
private_data_path = self.workspace_path / PRIVATE_DATA_FILE
|
|
60
|
-
self.assertTrue(private_data_path.exists())
|
|
61
|
-
|
|
62
|
-
with open(private_data_path, "r") as f:
|
|
63
|
-
saved_data = json.load(f)
|
|
64
|
-
|
|
65
|
-
self.assertTrue(saved_data["_has_private_data"])
|
|
66
|
-
self.assertIn("test_dataset", saved_data["_private_datasets"])
|
|
67
|
-
|
|
68
|
-
def test_add_private_dataset_duplicate(self):
|
|
69
|
-
"""Test adding a duplicate private dataset."""
|
|
70
|
-
private_data = PrivateData()
|
|
71
|
-
|
|
72
|
-
private_data.add_private_dataset("test_dataset")
|
|
73
|
-
initial_count = len(private_data.get_private_datasets())
|
|
74
|
-
|
|
75
|
-
private_data.add_private_dataset("test_dataset")
|
|
76
|
-
final_count = len(private_data.get_private_datasets())
|
|
77
|
-
|
|
78
|
-
self.assertEqual(initial_count, final_count)
|
|
79
|
-
self.assertEqual(private_data.get_private_datasets().count("test_dataset"), 1)
|
|
80
|
-
|
|
81
|
-
def test_add_idap_dataset_new(self):
|
|
82
|
-
"""Test adding a new IDAP dataset."""
|
|
83
|
-
private_data = PrivateData()
|
|
84
|
-
|
|
85
|
-
private_data.add_idap_dataset("idap_dataset")
|
|
86
|
-
|
|
87
|
-
self.assertTrue(private_data.has_private_data)
|
|
88
|
-
self.assertIn("idap_dataset", private_data.get_idap_datasets())
|
|
89
|
-
self.assertIn("idap_dataset", private_data.get_private_datasets())
|
|
90
|
-
|
|
91
|
-
# Verify data is saved to file
|
|
92
|
-
private_data_path = self.workspace_path / PRIVATE_DATA_FILE
|
|
93
|
-
self.assertTrue(private_data_path.exists())
|
|
94
|
-
|
|
95
|
-
with open(private_data_path, "r") as f:
|
|
96
|
-
saved_data = json.load(f)
|
|
97
|
-
|
|
98
|
-
self.assertTrue(saved_data["_has_private_data"])
|
|
99
|
-
self.assertIn("idap_dataset", saved_data["_idap_datasets"])
|
|
100
|
-
self.assertIn("idap_dataset", saved_data["_private_datasets"])
|
|
101
|
-
|
|
102
|
-
def test_add_idap_dataset_existing_private(self):
|
|
103
|
-
"""Test adding IDAP dataset when it already exists as private dataset."""
|
|
104
|
-
private_data = PrivateData()
|
|
105
|
-
|
|
106
|
-
private_data.add_private_dataset("existing_dataset")
|
|
107
|
-
private_data.add_idap_dataset("existing_dataset")
|
|
108
|
-
|
|
109
|
-
self.assertTrue(private_data.has_private_data)
|
|
110
|
-
self.assertIn("existing_dataset", private_data.get_idap_datasets())
|
|
111
|
-
self.assertIn("existing_dataset", private_data.get_private_datasets())
|
|
112
|
-
self.assertEqual(private_data.get_private_datasets().count("existing_dataset"), 1)
|
|
113
|
-
|
|
114
|
-
def test_add_idap_dataset_duplicate(self):
|
|
115
|
-
"""Test adding a duplicate IDAP dataset."""
|
|
116
|
-
private_data = PrivateData()
|
|
117
|
-
|
|
118
|
-
private_data.add_idap_dataset("idap_dataset")
|
|
119
|
-
initial_idap_count = len(private_data.get_idap_datasets())
|
|
120
|
-
initial_private_count = len(private_data.get_private_datasets())
|
|
121
|
-
|
|
122
|
-
private_data.add_idap_dataset("idap_dataset")
|
|
123
|
-
final_idap_count = len(private_data.get_idap_datasets())
|
|
124
|
-
final_private_count = len(private_data.get_private_datasets())
|
|
125
|
-
|
|
126
|
-
self.assertEqual(initial_idap_count, final_idap_count)
|
|
127
|
-
self.assertEqual(initial_private_count, final_private_count)
|
|
128
|
-
|
|
129
|
-
def test_has_private_dataset(self):
|
|
130
|
-
"""Test checking if a private dataset exists."""
|
|
131
|
-
private_data = PrivateData()
|
|
132
|
-
|
|
133
|
-
self.assertFalse(private_data.has_private_dataset("nonexistent"))
|
|
134
|
-
|
|
135
|
-
private_data.add_private_dataset("test_dataset")
|
|
136
|
-
|
|
137
|
-
self.assertTrue(private_data.has_private_dataset("test_dataset"))
|
|
138
|
-
self.assertFalse(private_data.has_private_dataset("nonexistent"))
|
|
139
|
-
|
|
140
|
-
def test_has_idap_dataset(self):
|
|
141
|
-
"""Test checking if an IDAP dataset exists."""
|
|
142
|
-
private_data = PrivateData()
|
|
143
|
-
|
|
144
|
-
self.assertFalse(private_data.has_idap_dataset("nonexistent"))
|
|
145
|
-
|
|
146
|
-
private_data.add_idap_dataset("idap_dataset")
|
|
147
|
-
|
|
148
|
-
self.assertTrue(private_data.has_idap_dataset("idap_dataset"))
|
|
149
|
-
self.assertFalse(private_data.has_idap_dataset("nonexistent"))
|
|
150
|
-
|
|
151
|
-
def test_get_datasets_returns_copies(self):
|
|
152
|
-
"""Test that get methods return copies to prevent external modification."""
|
|
153
|
-
private_data = PrivateData()
|
|
154
|
-
private_data.add_private_dataset("private_dataset")
|
|
155
|
-
private_data.add_idap_dataset("idap_dataset")
|
|
156
|
-
|
|
157
|
-
private_datasets = private_data.get_private_datasets()
|
|
158
|
-
idap_datasets = private_data.get_idap_datasets()
|
|
159
|
-
|
|
160
|
-
# Modify the returned lists
|
|
161
|
-
private_datasets.append("external_modification")
|
|
162
|
-
idap_datasets.append("external_modification")
|
|
163
|
-
|
|
164
|
-
# Verify original data is unchanged
|
|
165
|
-
self.assertNotIn("external_modification", private_data.get_private_datasets())
|
|
166
|
-
self.assertNotIn("external_modification", private_data.get_idap_datasets())
|
|
167
|
-
|
|
168
|
-
def test_has_private_data_setter_true(self):
|
|
169
|
-
"""Test setting has_private_data to True."""
|
|
170
|
-
private_data = PrivateData()
|
|
171
|
-
|
|
172
|
-
private_data.has_private_data = True
|
|
173
|
-
|
|
174
|
-
self.assertTrue(private_data.has_private_data)
|
|
175
|
-
|
|
176
|
-
# Verify data is saved to file
|
|
177
|
-
private_data_path = self.workspace_path / PRIVATE_DATA_FILE
|
|
178
|
-
self.assertTrue(private_data_path.exists())
|
|
179
|
-
|
|
180
|
-
def test_has_private_data_setter_false(self):
|
|
181
|
-
"""Test setting has_private_data to False clears all data."""
|
|
182
|
-
private_data = PrivateData()
|
|
183
|
-
private_data.add_private_dataset("dataset1")
|
|
184
|
-
private_data.add_idap_dataset("dataset2")
|
|
185
|
-
|
|
186
|
-
private_data.has_private_data = False
|
|
187
|
-
|
|
188
|
-
self.assertFalse(private_data.has_private_data)
|
|
189
|
-
self.assertEqual(private_data.get_private_datasets(), [])
|
|
190
|
-
self.assertEqual(private_data.get_idap_datasets(), [])
|
|
191
|
-
|
|
192
|
-
# Verify file is deleted
|
|
193
|
-
private_data_path = self.workspace_path / PRIVATE_DATA_FILE
|
|
194
|
-
self.assertFalse(private_data_path.exists())
|
|
195
|
-
|
|
196
|
-
def test_save_with_no_private_data(self):
|
|
197
|
-
"""Test saving when has_private_data is False deletes the file."""
|
|
198
|
-
private_data = PrivateData()
|
|
199
|
-
private_data.add_private_dataset("test_dataset")
|
|
200
|
-
|
|
201
|
-
# Verify file exists
|
|
202
|
-
private_data_path = self.workspace_path / PRIVATE_DATA_FILE
|
|
203
|
-
self.assertTrue(private_data_path.exists())
|
|
204
|
-
|
|
205
|
-
private_data.has_private_data = False
|
|
206
|
-
|
|
207
|
-
# Verify file is deleted
|
|
208
|
-
self.assertFalse(private_data_path.exists())
|
|
209
|
-
|
|
210
|
-
def test_save_with_private_data(self):
|
|
211
|
-
"""Test saving when has_private_data is True creates/updates the file."""
|
|
212
|
-
private_data = PrivateData()
|
|
213
|
-
private_data.add_private_dataset("dataset1")
|
|
214
|
-
private_data.add_idap_dataset("dataset2")
|
|
215
|
-
|
|
216
|
-
private_data_path = self.workspace_path / PRIVATE_DATA_FILE
|
|
217
|
-
self.assertTrue(private_data_path.exists())
|
|
218
|
-
|
|
219
|
-
with open(private_data_path, "r") as f:
|
|
220
|
-
saved_data = json.load(f)
|
|
221
|
-
|
|
222
|
-
self.assertTrue(saved_data["_has_private_data"])
|
|
223
|
-
self.assertIn("dataset1", saved_data["_private_datasets"])
|
|
224
|
-
self.assertIn("dataset2", saved_data["_private_datasets"])
|
|
225
|
-
self.assertIn("dataset2", saved_data["_idap_datasets"])
|
|
226
|
-
self.assertIsNone(saved_data["ctx"])
|
|
227
|
-
|
|
228
|
-
def test_load_from_corrupted_file(self):
|
|
229
|
-
"""Test loading from a corrupted JSON file."""
|
|
230
|
-
private_data_path = self.workspace_path / PRIVATE_DATA_FILE
|
|
231
|
-
with open(private_data_path, "w") as f:
|
|
232
|
-
f.write("invalid json content")
|
|
233
|
-
|
|
234
|
-
with self.assertRaises(json.JSONDecodeError):
|
|
235
|
-
PrivateData()
|
|
236
|
-
|
|
237
|
-
def test_load_from_file_with_missing_fields(self):
|
|
238
|
-
"""Test loading from a file with missing fields uses defaults."""
|
|
239
|
-
private_data_path = self.workspace_path / PRIVATE_DATA_FILE
|
|
240
|
-
test_data = {"_has_private_data": True} # Missing other fields
|
|
241
|
-
with open(private_data_path, "w") as f:
|
|
242
|
-
json.dump(test_data, f)
|
|
243
|
-
|
|
244
|
-
private_data = PrivateData()
|
|
245
|
-
|
|
246
|
-
self.assertTrue(private_data.has_private_data)
|
|
247
|
-
self.assertEqual(private_data.get_private_datasets(), [])
|
|
248
|
-
self.assertEqual(private_data.get_idap_datasets(), [])
|
|
249
|
-
|
|
250
|
-
def test_multiple_operations_persistence(self):
|
|
251
|
-
"""Test that multiple operations are properly persisted."""
|
|
252
|
-
# Create first instance and add data
|
|
253
|
-
private_data1 = PrivateData()
|
|
254
|
-
private_data1.add_private_dataset("dataset1")
|
|
255
|
-
private_data1.add_idap_dataset("dataset2")
|
|
256
|
-
|
|
257
|
-
# Create second instance and verify data is loaded
|
|
258
|
-
private_data2 = PrivateData()
|
|
259
|
-
self.assertTrue(private_data2.has_private_data)
|
|
260
|
-
self.assertIn("dataset1", private_data2.get_private_datasets())
|
|
261
|
-
self.assertIn("dataset2", private_data2.get_private_datasets())
|
|
262
|
-
self.assertIn("dataset2", private_data2.get_idap_datasets())
|
|
263
|
-
|
|
264
|
-
# Add more data with second instance
|
|
265
|
-
private_data2.add_private_dataset("dataset3")
|
|
266
|
-
|
|
267
|
-
# Create third instance and verify all data is present
|
|
268
|
-
private_data3 = PrivateData()
|
|
269
|
-
expected_private = {"dataset1", "dataset2", "dataset3"}
|
|
270
|
-
expected_idap = {"dataset2"}
|
|
271
|
-
|
|
272
|
-
self.assertEqual(set(private_data3.get_private_datasets()), expected_private)
|
|
273
|
-
self.assertEqual(set(private_data3.get_idap_datasets()), expected_idap)
|
|
274
|
-
|
|
275
|
-
def test_str_and_repr(self):
|
|
276
|
-
"""Test string representation methods."""
|
|
277
|
-
private_data = PrivateData()
|
|
278
|
-
private_data.add_private_dataset("dataset1")
|
|
279
|
-
private_data.add_idap_dataset("dataset2")
|
|
280
|
-
|
|
281
|
-
str_repr = str(private_data)
|
|
282
|
-
repr_repr = repr(private_data)
|
|
283
|
-
|
|
284
|
-
self.assertEqual(str_repr, repr_repr)
|
|
285
|
-
self.assertIn("has_private_data=True", str_repr)
|
|
286
|
-
self.assertIn("dataset1", str_repr)
|
|
287
|
-
self.assertIn("dataset2", str_repr)
|
|
288
|
-
self.assertIn(str(self.workspace_path / PRIVATE_DATA_FILE), str_repr)
|
|
289
|
-
|
|
290
|
-
def test_file_operations_create_directories(self):
|
|
291
|
-
"""Test that file operations create necessary directories."""
|
|
292
|
-
# Remove the workspace directory
|
|
293
|
-
import shutil
|
|
294
|
-
shutil.rmtree(self.workspace_path)
|
|
295
|
-
self.assertFalse(self.workspace_path.exists())
|
|
296
|
-
|
|
297
|
-
# Create PrivateData instance and add data
|
|
298
|
-
private_data = PrivateData()
|
|
299
|
-
private_data.add_private_dataset("test_dataset")
|
|
300
|
-
|
|
301
|
-
# Verify directory and file were created
|
|
302
|
-
self.assertTrue(self.workspace_path.exists())
|
|
303
|
-
private_data_path = self.workspace_path / PRIVATE_DATA_FILE
|
|
304
|
-
self.assertTrue(private_data_path.exists())
|
|
305
|
-
|
|
306
|
-
def test_concurrent_modifications_simulation(self):
|
|
307
|
-
"""Test simulation of concurrent modifications (without actual threading)."""
|
|
308
|
-
# Create two instances with same context
|
|
309
|
-
private_data1 = PrivateData()
|
|
310
|
-
private_data2 = PrivateData()
|
|
311
|
-
|
|
312
|
-
# Add data with first instance
|
|
313
|
-
private_data1.add_private_dataset("dataset1")
|
|
314
|
-
|
|
315
|
-
# Second instance should see the changes when reloaded
|
|
316
|
-
private_data2.load()
|
|
317
|
-
self.assertIn("dataset1", private_data2.get_private_datasets())
|
|
318
|
-
|
|
319
|
-
# Add data with second instance
|
|
320
|
-
private_data2.add_idap_dataset("dataset2")
|
|
321
|
-
|
|
322
|
-
# First instance should see the changes when reloaded
|
|
323
|
-
private_data1.load()
|
|
324
|
-
self.assertIn("dataset2", private_data1.get_idap_datasets())
|
|
325
|
-
self.assertIn("dataset2", private_data1.get_private_datasets())
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if __name__ == "__main__":
|
|
329
|
-
unittest.main()
|