2
2
3
3
from unittest .mock import (
4
4
MagicMock ,
5
+ call ,
5
6
mock_open ,
6
7
patch ,
7
- call ,
8
8
)
9
9
import zipfile
10
10
41
41
42
42
class TestPrepareDependencyArchive :
43
43
# Shared expected commands
44
- EXPECTED_DOCKER_IMAGES_CMD = "docker images -q datacloud-custom-code-dependency-builder"
45
- EXPECTED_BUILD_CMD = "DOCKER_DEFAULT_PLATFORM=linux/amd64 docker build -t datacloud-custom-code-dependency-builder -f Dockerfile.dependencies ."
44
+ EXPECTED_DOCKER_IMAGES_CMD = (
45
+ "docker images -q datacloud-custom-code-dependency-builder"
46
+ )
47
+ EXPECTED_BUILD_CMD = (
48
+ "DOCKER_DEFAULT_PLATFORM=linux/amd64 docker build "
49
+ "-t datacloud-custom-code-dependency-builder -f Dockerfile.dependencies ."
50
+ )
46
51
EXPECTED_DOCKER_RUN_CMD = (
47
52
"DOCKER_DEFAULT_PLATFORM=linux/amd64 docker run --rm "
48
53
"-v /tmp/test_dir:/workspace "
@@ -89,7 +94,10 @@ def test_prepare_dependency_archive_image_exists(
89
94
mock_makedirs .assert_called_once_with ("payload/archives" , exist_ok = True )
90
95
91
96
# Verify archive was copied back
92
- mock_copy .assert_any_call ("/tmp/test_dir/native_dependencies.tar.gz" , "payload/archives/native_dependencies.tar.gz" )
97
+ mock_copy .assert_any_call (
98
+ "/tmp/test_dir/native_dependencies.tar.gz" ,
99
+ "payload/archives/native_dependencies.tar.gz" ,
100
+ )
93
101
94
102
@patch ("datacustomcode.deploy.cmd_output" )
95
103
@patch ("datacustomcode.deploy.shutil.copy" )
@@ -132,7 +140,10 @@ def test_prepare_dependency_archive_build_image(
132
140
mock_makedirs .assert_called_once_with ("payload/archives" , exist_ok = True )
133
141
134
142
# Verify archive was copied back
135
- mock_copy .assert_any_call ("/tmp/test_dir/native_dependencies.tar.gz" , "payload/archives/native_dependencies.tar.gz" )
143
+ mock_copy .assert_any_call (
144
+ "/tmp/test_dir/native_dependencies.tar.gz" ,
145
+ "payload/archives/native_dependencies.tar.gz" ,
146
+ )
136
147
137
148
@patch ("datacustomcode.deploy.cmd_output" )
138
149
@patch ("datacustomcode.deploy.shutil.copy" )
@@ -151,9 +162,12 @@ def test_prepare_dependency_archive_docker_build_failure(
151
162
152
163
# Mock cmd_output to return None for image check, then raise exception for build
153
164
from datacustomcode .cmd import CalledProcessError
165
+
154
166
mock_cmd_output .side_effect = [
155
167
None , # Image doesn't exist
156
- CalledProcessError (1 , ("docker" , "build" ), b"Build failed" , b"Error" ), # Build fails
168
+ CalledProcessError (
169
+ 1 , ("docker" , "build" ), b"Build failed" , b"Error"
170
+ ), # Build fails
157
171
]
158
172
159
173
with pytest .raises (CalledProcessError , match = "Build failed" ):
@@ -182,9 +196,12 @@ def test_prepare_dependency_archive_docker_run_failure(
182
196
183
197
# Mock cmd_output to return image ID, then raise exception for run
184
198
from datacustomcode .cmd import CalledProcessError
199
+
185
200
mock_cmd_output .side_effect = [
186
201
"abc123" , # Image exists
187
- CalledProcessError (1 , ("docker" , "run" ), b"Run failed" , b"Error" ), # Run fails
202
+ CalledProcessError (
203
+ 1 , ("docker" , "run" ), b"Run failed" , b"Error"
204
+ ), # Run fails
188
205
]
189
206
190
207
with pytest .raises (CalledProcessError , match = "Run failed" ):
@@ -234,35 +251,51 @@ def test_prepare_dependency_archive_file_copy_failure(
234
251
class TestHasNonemptyRequirementsFile :
235
252
@patch ("datacustomcode.deploy.os.path.dirname" )
236
253
@patch ("datacustomcode.deploy.os.path.isfile" )
237
- @patch ("builtins.open" , new_callable = mock_open , read_data = "numpy==1.21.0\n pandas==1.3.0" )
254
+ @patch (
255
+ "builtins.open" ,
256
+ new_callable = mock_open ,
257
+ read_data = "numpy==1.21.0\n pandas==1.3.0" ,
258
+ )
238
259
def test_has_nonempty_requirements_file_with_dependencies (
239
260
self , mock_file , mock_isfile , mock_dirname
240
261
):
241
- """Test has_nonempty_requirements_file when requirements.txt has dependencies."""
262
+ """
263
+ Test has_nonempty_requirements_file when requirements.txt has dependencies.
264
+ """
242
265
mock_dirname .return_value = "/parent/dir"
243
266
mock_isfile .return_value = True
244
267
245
268
result = has_nonempty_requirements_file ("/test/dir" )
246
269
247
270
assert result is True
248
271
mock_isfile .assert_called_once_with ("/parent/dir/requirements.txt" )
249
- mock_file .assert_called_once_with ("/parent/dir/requirements.txt" , "r" , encoding = "utf-8" )
272
+ mock_file .assert_called_once_with (
273
+ "/parent/dir/requirements.txt" , "r" , encoding = "utf-8"
274
+ )
250
275
251
276
@patch ("datacustomcode.deploy.os.path.dirname" )
252
277
@patch ("datacustomcode.deploy.os.path.isfile" )
253
- @patch ("builtins.open" , new_callable = mock_open , read_data = "# This is a comment\n \n # Another comment" )
278
+ @patch (
279
+ "builtins.open" ,
280
+ new_callable = mock_open ,
281
+ read_data = "# This is a comment\n \n # Another comment" ,
282
+ )
254
283
def test_has_nonempty_requirements_file_only_comments (
255
284
self , mock_file , mock_isfile , mock_dirname
256
285
):
257
- """Test has_nonempty_requirements_file when requirements.txt has only comments."""
286
+ """
287
+ Test has_nonempty_requirements_file when requirements.txt has only comments.
288
+ """
258
289
mock_dirname .return_value = "/parent/dir"
259
290
mock_isfile .return_value = True
260
291
261
292
result = has_nonempty_requirements_file ("/test/dir" )
262
293
263
294
assert result is False
264
295
mock_isfile .assert_called_once_with ("/parent/dir/requirements.txt" )
265
- mock_file .assert_called_once_with ("/parent/dir/requirements.txt" , "r" , encoding = "utf-8" )
296
+ mock_file .assert_called_once_with (
297
+ "/parent/dir/requirements.txt" , "r" , encoding = "utf-8"
298
+ )
266
299
267
300
@patch ("datacustomcode.deploy.os.path.dirname" )
268
301
@patch ("datacustomcode.deploy.os.path.isfile" )
@@ -278,13 +311,13 @@ def test_has_nonempty_requirements_file_empty_file(
278
311
279
312
assert result is False
280
313
mock_isfile .assert_called_once_with ("/parent/dir/requirements.txt" )
281
- mock_file .assert_called_once_with ("/parent/dir/requirements.txt" , "r" , encoding = "utf-8" )
314
+ mock_file .assert_called_once_with (
315
+ "/parent/dir/requirements.txt" , "r" , encoding = "utf-8"
316
+ )
282
317
283
318
@patch ("datacustomcode.deploy.os.path.dirname" )
284
319
@patch ("datacustomcode.deploy.os.path.isfile" )
285
- def test_has_nonempty_requirements_file_not_exists (
286
- self , mock_isfile , mock_dirname
287
- ):
320
+ def test_has_nonempty_requirements_file_not_exists (self , mock_isfile , mock_dirname ):
288
321
"""Test has_nonempty_requirements_file when requirements.txt doesn't exist."""
289
322
mock_dirname .return_value = "/parent/dir"
290
323
mock_isfile .return_value = False
@@ -308,11 +341,17 @@ def test_has_nonempty_requirements_file_permission_error(
308
341
309
342
assert result is False
310
343
mock_isfile .assert_called_once_with ("/parent/dir/requirements.txt" )
311
- mock_file .assert_called_once_with ("/parent/dir/requirements.txt" , "r" , encoding = "utf-8" )
344
+ mock_file .assert_called_once_with (
345
+ "/parent/dir/requirements.txt" , "r" , encoding = "utf-8"
346
+ )
312
347
313
348
@patch ("datacustomcode.deploy.os.path.dirname" )
314
349
@patch ("datacustomcode.deploy.os.path.isfile" )
315
- @patch ("builtins.open" , new_callable = mock_open , read_data = "numpy==1.21.0\n # Comment\n pandas==1.3.0" )
350
+ @patch (
351
+ "builtins.open" ,
352
+ new_callable = mock_open ,
353
+ read_data = "numpy==1.21.0\n # Comment\n pandas==1.3.0" ,
354
+ )
316
355
def test_has_nonempty_requirements_file_mixed_content (
317
356
self , mock_file , mock_isfile , mock_dirname
318
357
):
@@ -324,7 +363,9 @@ def test_has_nonempty_requirements_file_mixed_content(
324
363
325
364
assert result is True
326
365
mock_isfile .assert_called_once_with ("/parent/dir/requirements.txt" )
327
- mock_file .assert_called_once_with ("/parent/dir/requirements.txt" , "r" , encoding = "utf-8" )
366
+ mock_file .assert_called_once_with (
367
+ "/parent/dir/requirements.txt" , "r" , encoding = "utf-8"
368
+ )
328
369
329
370
330
371
class TestMakeApiCall :
0 commit comments