@@ -93,7 +93,7 @@ def test_prepare_dependency_archive_image_exists(
9393 mock_docker_build_cmd .return_value = "mock build command"
9494 mock_docker_run_cmd .return_value = "mock run command"
9595
96- prepare_dependency_archive ("/test/dir" , "default" )
96+ prepare_dependency_archive ("/test/dir" , "default" , "script" )
9797
9898 # Verify docker images command was called
9999 mock_cmd_output .assert_any_call (self .EXPECTED_DOCKER_IMAGES_CMD )
@@ -153,7 +153,7 @@ def test_prepare_dependency_archive_build_image(
153153 mock_docker_build_cmd .return_value = "mock build command"
154154 mock_docker_run_cmd .return_value = "mock run command"
155155
156- prepare_dependency_archive ("/test/dir" , "default" )
156+ prepare_dependency_archive ("/test/dir" , "default" , "script" )
157157
158158 # Verify docker images command was called
159159 mock_cmd_output .assert_any_call (self .EXPECTED_DOCKER_IMAGES_CMD )
@@ -214,7 +214,7 @@ def test_prepare_dependency_archive_docker_build_failure(
214214 ]
215215
216216 with pytest .raises (CalledProcessError , match = "Build failed" ):
217- prepare_dependency_archive ("/test/dir" , "default" )
217+ prepare_dependency_archive ("/test/dir" , "default" , "script" )
218218
219219 # Verify docker images command was called
220220 mock_cmd_output .assert_any_call (self .EXPECTED_DOCKER_IMAGES_CMD )
@@ -257,7 +257,7 @@ def test_prepare_dependency_archive_docker_run_failure(
257257 ]
258258
259259 with pytest .raises (CalledProcessError , match = "Run failed" ):
260- prepare_dependency_archive ("/test/dir" , "default" )
260+ prepare_dependency_archive ("/test/dir" , "default" , "script" )
261261
262262 # Verify docker images command was called
263263 mock_cmd_output .assert_any_call (self .EXPECTED_DOCKER_IMAGES_CMD )
@@ -300,14 +300,150 @@ def test_prepare_dependency_archive_file_copy_failure(
300300 mock_copy .side_effect = FileNotFoundError ("File not found" )
301301
302302 with pytest .raises (FileNotFoundError , match = "File not found" ):
303- prepare_dependency_archive ("/test/dir" , "default" )
303+ prepare_dependency_archive ("/test/dir" , "default" , "script" )
304304
305305 # Verify docker images command was called
306306 mock_cmd_output .assert_any_call (self .EXPECTED_DOCKER_IMAGES_CMD )
307307
308308 # Verify files were attempted to be copied
309309 mock_copy .assert_any_call ("requirements.txt" , "/tmp/test_dir" )
310310
311+ @patch ("datacustomcode.deploy.cmd_output" )
312+ @patch ("datacustomcode.deploy.shutil.copytree" )
313+ @patch ("datacustomcode.deploy.shutil.rmtree" )
314+ @patch ("datacustomcode.deploy.shutil.copy" )
315+ @patch ("datacustomcode.deploy.tempfile.TemporaryDirectory" )
316+ @patch ("datacustomcode.deploy.os.path.exists" )
317+ @patch ("datacustomcode.deploy.os.path.join" )
318+ @patch ("datacustomcode.deploy.os.makedirs" )
319+ @patch ("datacustomcode.deploy.docker_build_cmd" )
320+ @patch ("datacustomcode.deploy.docker_run_cmd" )
321+ def test_prepare_dependency_archive_function_type (
322+ self ,
323+ mock_docker_run_cmd ,
324+ mock_docker_build_cmd ,
325+ mock_makedirs ,
326+ mock_join ,
327+ mock_exists ,
328+ mock_temp_dir ,
329+ mock_copy ,
330+ mock_rmtree ,
331+ mock_copytree ,
332+ mock_cmd_output ,
333+ ):
334+ """Test prepare_dependency_archive with function package type."""
335+ # Mock the temporary directory context manager
336+ mock_temp_dir_instance = MagicMock ()
337+ mock_temp_dir_instance .__enter__ .return_value = "/tmp/test_dir"
338+ mock_temp_dir_instance .__exit__ .return_value = None
339+ mock_temp_dir .return_value = mock_temp_dir_instance
340+
341+ # Mock cmd_output to return image ID (indicating image exists)
342+ mock_cmd_output .return_value = "abc123"
343+
344+ # Mock os.path.join for py-files paths
345+ def join_side_effect (* args ):
346+ if args == ("/tmp/test_dir" , "py-files" ):
347+ return "/tmp/test_dir/py-files"
348+ return "/" .join (args )
349+
350+ mock_join .side_effect = join_side_effect
351+
352+ # Mock os.path.exists
353+ def exists_side_effect (path ):
354+ if path == "/tmp/test_dir/py-files" :
355+ return True
356+ if path == "payload/py-files" :
357+ return False
358+ return False
359+
360+ mock_exists .side_effect = exists_side_effect
361+
362+ # Mock the docker command functions
363+ mock_docker_build_cmd .return_value = "mock build command"
364+ mock_docker_run_cmd .return_value = "mock run command"
365+
366+ prepare_dependency_archive ("/test/dir" , "default" , "function" )
367+
368+ # Verify docker images command was called
369+ mock_cmd_output .assert_any_call (self .EXPECTED_DOCKER_IMAGES_CMD )
370+
371+ # Verify docker build command was not called (since image already exists)
372+ mock_docker_build_cmd .assert_not_called ()
373+
374+ # Verify files were copied to temp directory
375+ mock_copy .assert_any_call ("requirements.txt" , "/tmp/test_dir" )
376+ mock_copy .assert_any_call ("build_native_dependencies.sh" , "/tmp/test_dir" )
377+
378+ # Verify docker run command was called
379+ mock_docker_run_cmd .assert_called_once_with ("default" , "/tmp/test_dir" )
380+ mock_cmd_output .assert_any_call ("mock run command" , env = ANY )
381+
382+ # Verify payload directory was created
383+ mock_makedirs .assert_called_once_with ("payload" , exist_ok = True )
384+
385+ # Verify py-files was NOT removed (doesn't exist yet)
386+ mock_rmtree .assert_not_called ()
387+
388+ # Verify py-files directory was copied
389+ mock_copytree .assert_called_once_with (
390+ "/tmp/test_dir/py-files" , "payload/py-files"
391+ )
392+
393+ @patch ("datacustomcode.deploy.cmd_output" )
394+ @patch ("datacustomcode.deploy.shutil.copy" )
395+ @patch ("datacustomcode.deploy.tempfile.TemporaryDirectory" )
396+ @patch ("datacustomcode.deploy.os.path.exists" )
397+ @patch ("datacustomcode.deploy.os.path.join" )
398+ @patch ("datacustomcode.deploy.os.makedirs" )
399+ @patch ("datacustomcode.deploy.docker_build_cmd" )
400+ @patch ("datacustomcode.deploy.docker_run_cmd" )
401+ def test_prepare_dependency_archive_function_type_missing_pyfiles (
402+ self ,
403+ mock_docker_run_cmd ,
404+ mock_docker_build_cmd ,
405+ mock_makedirs ,
406+ mock_join ,
407+ mock_exists ,
408+ mock_temp_dir ,
409+ mock_copy ,
410+ mock_cmd_output ,
411+ ):
412+ """
413+ Test prepare_dependency_archive with function type when py-files is missing.
414+ Should log and continue without error.
415+ """
416+ # Mock the temporary directory context manager
417+ mock_temp_dir_instance = MagicMock ()
418+ mock_temp_dir_instance .__enter__ .return_value = "/tmp/test_dir"
419+ mock_temp_dir_instance .__exit__ .return_value = None
420+ mock_temp_dir .return_value = mock_temp_dir_instance
421+
422+ # Mock cmd_output to return image ID (indicating image exists)
423+ mock_cmd_output .return_value = "abc123"
424+
425+ # Mock os.path.join for py-files path
426+ def join_side_effect (* args ):
427+ if args == ("/tmp/test_dir" , "py-files" ):
428+ return "/tmp/test_dir/py-files"
429+ return "/" .join (args )
430+
431+ mock_join .side_effect = join_side_effect
432+
433+ # Mock os.path.exists to return False for py-files (doesn't exist)
434+ mock_exists .return_value = False
435+
436+ # Mock the docker command functions
437+ mock_docker_build_cmd .return_value = "mock build command"
438+ mock_docker_run_cmd .return_value = "mock run command"
439+
440+ # Should complete successfully without raising an error
441+ prepare_dependency_archive ("/test/dir" , "default" , "function" )
442+
443+ # Verify docker commands were called
444+ mock_cmd_output .assert_any_call (self .EXPECTED_DOCKER_IMAGES_CMD )
445+ mock_docker_run_cmd .assert_called_once_with ("default" , "/tmp/test_dir" )
446+
311447
312448class TestHasNonemptyRequirementsFile :
313449 @patch ("datacustomcode.deploy.os.path.dirname" )
@@ -609,10 +745,10 @@ def test_zip_with_requirements(
609745 ("/test/dir/subdir" , [], ["file3.py" ]),
610746 ]
611747
612- zip ("/test/dir" , "default" )
748+ zip ("/test/dir" , "default" , "script" )
613749
614750 mock_has_requirements .assert_called_once_with ("/test/dir" )
615- mock_prepare .assert_called_once_with ("/test/dir" , "default" )
751+ mock_prepare .assert_called_once_with ("/test/dir" , "default" , "script" )
616752 mock_zipfile .assert_called_once_with (
617753 "deployment.zip" , "w" , zipfile .ZIP_DEFLATED
618754 )
@@ -637,7 +773,7 @@ def test_zip_without_requirements(
637773 ("/test/dir/subdir" , [], ["file3.py" ]),
638774 ]
639775
640- zip ("/test/dir" , "default" )
776+ zip ("/test/dir" , "default" , "script" )
641777
642778 mock_has_requirements .assert_called_once_with ("/test/dir" )
643779 mock_prepare .assert_not_called ()
@@ -646,6 +782,38 @@ def test_zip_without_requirements(
646782 )
647783 assert mock_zipfile_instance .write .call_count == 3 # One call per file
648784
785+ @patch ("datacustomcode.deploy.has_nonempty_requirements_file" )
786+ @patch ("datacustomcode.deploy.prepare_dependency_archive" )
787+ @patch ("zipfile.ZipFile" )
788+ @patch ("os.walk" )
789+ def test_zip_with_function_package_type (
790+ self ,
791+ mock_walk ,
792+ mock_zipfile ,
793+ mock_prepare ,
794+ mock_has_requirements ,
795+ ):
796+ """Test zipping a directory with function package type."""
797+ mock_has_requirements .return_value = True
798+ mock_zipfile_instance = MagicMock ()
799+ mock_zipfile .return_value .__enter__ .return_value = mock_zipfile_instance
800+ mock_zipfile_instance .write = MagicMock ()
801+
802+ # Mock os.walk to return some test files
803+ mock_walk .return_value = [
804+ ("/test/dir" , ["subdir" ], ["file1.py" , "file2.py" ]),
805+ ("/test/dir/subdir" , [], ["file3.py" ]),
806+ ]
807+
808+ zip ("/test/dir" , "default" , "function" )
809+
810+ mock_has_requirements .assert_called_once_with ("/test/dir" )
811+ mock_prepare .assert_called_once_with ("/test/dir" , "default" , "function" )
812+ mock_zipfile .assert_called_once_with (
813+ "deployment.zip" , "w" , zipfile .ZIP_DEFLATED
814+ )
815+ assert mock_zipfile_instance .write .call_count == 3 # One call per file
816+
649817
650818class TestUploadZip :
651819 @patch ("datacustomcode.deploy.requests.put" )
@@ -934,7 +1102,7 @@ def test_deploy_full(
9341102 mock_retrieve_token .assert_called_once_with (credentials )
9351103 mock_get_config .assert_called_once_with ("/test/dir" )
9361104 mock_create_deployment .assert_called_once_with (access_token , metadata )
937- mock_zip .assert_called_once_with ("/test/dir" , "default" )
1105+ mock_zip .assert_called_once_with ("/test/dir" , "default" , "script" )
9381106 mock_upload_zip .assert_called_once_with ("https://upload.example.com" )
9391107 mock_wait .assert_called_once_with (access_token , metadata , callback )
9401108 mock_create_transform .assert_called_once_with (
@@ -998,7 +1166,7 @@ def test_deploy_full_client_credentials(
9981166 mock_retrieve_token .assert_called_once_with (credentials )
9991167 mock_get_config .assert_called_once_with ("/test/dir" )
10001168 mock_create_deployment .assert_called_once_with (access_token , metadata )
1001- mock_zip .assert_called_once_with ("/test/dir" , "default" )
1169+ mock_zip .assert_called_once_with ("/test/dir" , "default" , "script" )
10021170 mock_upload_zip .assert_called_once_with ("https://upload.example.com" )
10031171 mock_wait .assert_called_once_with (access_token , metadata , callback )
10041172 mock_create_transform .assert_called_once_with (
@@ -1104,7 +1272,7 @@ def test_deploy_full_happy_path(
11041272 mock_retrieve_token .assert_called_once_with (credentials )
11051273 mock_get_config .assert_called_once_with ("/test/dir" )
11061274 mock_create_deployment .assert_called_once_with (access_token , metadata )
1107- mock_zip .assert_called_once_with ("/test/dir" , "default" )
1275+ mock_zip .assert_called_once_with ("/test/dir" , "default" , "script" )
11081276 mock_upload_zip .assert_called_once_with ("https://upload.example.com" )
11091277 mock_wait .assert_called_once_with (access_token , metadata , callback )
11101278 mock_create_transform .assert_called_once_with (
0 commit comments