@@ -32,7 +32,7 @@ def setUp(self):
3232            profile = None ,
3333        )
3434
35-     @patch ("samcli.commands .package.package_context .get_validated_container_client" ) 
35+     @patch ("samcli.lib .package.ecr_uploader .get_validated_container_client" ) 
3636    @patch .object (SamLocalStackProvider , "get_stacks" ) 
3737    @patch .object (Template , "export" , MagicMock (sideeffect = OSError )) 
3838    @patch ("boto3.client" ) 
@@ -46,7 +46,7 @@ def test_template_permissions_error(self, patched_boto, patched_get_stacks, mock
4646            with  patch .object (self .package_command_context , "_warn_preview_runtime" ) as  patched_warn_preview_runtime :
4747                self .package_command_context .run ()
4848
49-     @patch ("samcli.commands .package.package_context .get_validated_container_client" ) 
49+     @patch ("samcli.lib .package.ecr_uploader .get_validated_container_client" ) 
5050    @patch .object (ResourceMetadataNormalizer , "normalize" , MagicMock ()) 
5151    @patch .object (Template , "export" , MagicMock (return_value = {})) 
5252    @patch ("boto3.client" ) 
@@ -74,7 +74,7 @@ def test_template_path_valid_with_output_template(self, patched_boto, mock_get_v
7474                )
7575                package_command_context .run ()
7676
77-     @patch ("samcli.commands .package.package_context .get_validated_container_client" ) 
77+     @patch ("samcli.lib .package.ecr_uploader .get_validated_container_client" ) 
7878    @patch .object (ResourceMetadataNormalizer , "normalize" , MagicMock ()) 
7979    @patch .object (Template , "export" , MagicMock (return_value = {})) 
8080    @patch ("boto3.client" ) 
@@ -101,7 +101,7 @@ def test_template_path_valid(self, patched_boto, mock_get_validated_client):
101101            )
102102            package_command_context .run ()
103103
104-     @patch ("samcli.commands .package.package_context .get_validated_container_client" ) 
104+     @patch ("samcli.lib .package.ecr_uploader .get_validated_container_client" ) 
105105    @patch .object (ResourceMetadataNormalizer , "normalize" , MagicMock ()) 
106106    @patch .object (Template , "export" , MagicMock (return_value = {})) 
107107    @patch ("boto3.client" ) 
@@ -128,7 +128,7 @@ def test_template_path_valid_no_json(self, patched_boto, mock_get_validated_clie
128128            )
129129            package_command_context .run ()
130130
131-     @patch ("samcli.commands .package.package_context .get_validated_container_client" ) 
131+     @patch ("samcli.lib .package.ecr_uploader .get_validated_container_client" ) 
132132    @patch ("samcli.commands.package.package_context.PackageContext._warn_preview_runtime" ) 
133133    @patch ("samcli.commands.package.package_context.get_resource_full_path_by_id" ) 
134134    @patch .object (SamLocalStackProvider , "get_stacks" ) 
@@ -214,3 +214,176 @@ def test_warn_preview_runtime(self, runtime, should_warn, function_type, patched
214214            patched_click .secho .assert_called_once ()
215215        else :
216216            patched_click .secho .assert_not_called ()
217+ 
218+ 
219+ class  TestPackageContextDockerLazyInitialization (TestCase ):
220+     """Test cases for lazy Docker client initialization in PackageContext""" 
221+ 
222+     def  setUp (self ):
223+         self .package_command_context  =  PackageContext (
224+             template_file = "template-file" ,
225+             s3_bucket = "s3-bucket" ,
226+             s3_prefix = "s3-prefix" ,
227+             image_repository = "image-repo" ,
228+             image_repositories = None ,
229+             kms_key_id = "kms-key-id" ,
230+             output_template_file = None ,
231+             use_json = True ,
232+             force_upload = True ,
233+             no_progressbar = False ,
234+             metadata = {},
235+             region = None ,
236+             profile = None ,
237+         )
238+ 
239+     @patch ("samcli.lib.package.ecr_uploader.get_validated_container_client" ) 
240+     @patch .object (ResourceMetadataNormalizer , "normalize" , MagicMock ()) 
241+     @patch .object (Template , "export" , MagicMock (return_value = {})) 
242+     @patch ("boto3.client" ) 
243+     def  test_docker_client_not_validated_during_package_context_run (self , patched_boto , mock_get_validated_client ):
244+         """Test that Docker client is not validated during PackageContext.run()""" 
245+         mock_docker_client  =  Mock ()
246+         mock_get_validated_client .return_value  =  mock_docker_client 
247+ 
248+         with  tempfile .NamedTemporaryFile (mode = "w" , delete = False ) as  temp_template_file :
249+             package_command_context  =  PackageContext (
250+                 template_file = temp_template_file .name ,
251+                 s3_bucket = "s3-bucket" ,
252+                 s3_prefix = "s3-prefix" ,
253+                 image_repository = "image-repo" ,
254+                 image_repositories = None ,
255+                 kms_key_id = "kms-key-id" ,
256+                 output_template_file = None ,
257+                 use_json = True ,
258+                 force_upload = True ,
259+                 no_progressbar = False ,
260+                 metadata = {},
261+                 region = "us-east-2" ,
262+                 profile = None ,
263+             )
264+ 
265+             # Run the package context 
266+             package_command_context .run ()
267+ 
268+             # Docker client validation should not have been called during run() 
269+             mock_get_validated_client .assert_not_called ()
270+ 
271+             # ECR uploader should be created but Docker client not validated yet 
272+             ecr_uploader  =  package_command_context .uploaders .ecr 
273+             self .assertIsNone (ecr_uploader ._validated_docker_client )
274+ 
275+     @patch ("samcli.lib.package.ecr_uploader.get_validated_container_client" ) 
276+     @patch .object (ResourceMetadataNormalizer , "normalize" , MagicMock ()) 
277+     @patch .object (Template , "export" , MagicMock (return_value = {})) 
278+     @patch ("boto3.client" ) 
279+     def  test_docker_client_validated_only_when_ecr_operations_performed (self , patched_boto , mock_get_validated_client ):
280+         """Test that Docker client is validated only when ECR operations are performed""" 
281+         mock_docker_client  =  Mock ()
282+         mock_get_validated_client .return_value  =  mock_docker_client 
283+ 
284+         with  tempfile .NamedTemporaryFile (mode = "w" , delete = False ) as  temp_template_file :
285+             package_command_context  =  PackageContext (
286+                 template_file = temp_template_file .name ,
287+                 s3_bucket = "s3-bucket" ,
288+                 s3_prefix = "s3-prefix" ,
289+                 image_repository = "image-repo" ,
290+                 image_repositories = None ,
291+                 kms_key_id = "kms-key-id" ,
292+                 output_template_file = None ,
293+                 use_json = True ,
294+                 force_upload = True ,
295+                 no_progressbar = False ,
296+                 metadata = {},
297+                 region = "us-east-2" ,
298+                 profile = None ,
299+             )
300+ 
301+             # Run the package context 
302+             package_command_context .run ()
303+ 
304+             # Docker client validation should not have been called yet 
305+             mock_get_validated_client .assert_not_called ()
306+ 
307+             # Access the docker_client property to trigger validation 
308+             ecr_uploader  =  package_command_context .uploaders .ecr 
309+             docker_client  =  ecr_uploader .docker_client 
310+ 
311+             # Now Docker client validation should have been called 
312+             mock_get_validated_client .assert_called_once ()
313+             self .assertEqual (docker_client , mock_docker_client )
314+ 
315+     @patch ("samcli.lib.package.ecr_uploader.get_validated_container_client" ) 
316+     @patch .object (ResourceMetadataNormalizer , "normalize" , MagicMock ()) 
317+     @patch .object (Template , "export" , MagicMock (return_value = {})) 
318+     @patch ("boto3.client" ) 
319+     def  test_ecr_uploader_created_with_none_docker_client (self , patched_boto , mock_get_validated_client ):
320+         """Test that ECRUploader is created with None docker_client parameter""" 
321+         mock_docker_client  =  Mock ()
322+         mock_get_validated_client .return_value  =  mock_docker_client 
323+ 
324+         with  tempfile .NamedTemporaryFile (mode = "w" , delete = False ) as  temp_template_file :
325+             package_command_context  =  PackageContext (
326+                 template_file = temp_template_file .name ,
327+                 s3_bucket = "s3-bucket" ,
328+                 s3_prefix = "s3-prefix" ,
329+                 image_repository = "image-repo" ,
330+                 image_repositories = None ,
331+                 kms_key_id = "kms-key-id" ,
332+                 output_template_file = None ,
333+                 use_json = True ,
334+                 force_upload = True ,
335+                 no_progressbar = False ,
336+                 metadata = {},
337+                 region = "us-east-2" ,
338+                 profile = None ,
339+             )
340+ 
341+             # Run the package context 
342+             package_command_context .run ()
343+ 
344+             # Verify ECRUploader was created with None docker_client parameter 
345+             ecr_uploader  =  package_command_context .uploaders .ecr 
346+             self .assertIsNone (ecr_uploader ._docker_client_param )
347+             self .assertIsNone (ecr_uploader ._validated_docker_client )
348+ 
349+     @patch ("samcli.lib.package.ecr_uploader.get_validated_container_client" ) 
350+     @patch .object (ResourceMetadataNormalizer , "normalize" , MagicMock ()) 
351+     @patch .object (Template , "export" , MagicMock (return_value = {})) 
352+     @patch ("boto3.client" ) 
353+     def  test_multiple_docker_client_accesses_only_validate_once (self , patched_boto , mock_get_validated_client ):
354+         """Test that multiple accesses to docker_client only validate once""" 
355+         mock_docker_client  =  Mock ()
356+         mock_get_validated_client .return_value  =  mock_docker_client 
357+ 
358+         with  tempfile .NamedTemporaryFile (mode = "w" , delete = False ) as  temp_template_file :
359+             package_command_context  =  PackageContext (
360+                 template_file = temp_template_file .name ,
361+                 s3_bucket = "s3-bucket" ,
362+                 s3_prefix = "s3-prefix" ,
363+                 image_repository = "image-repo" ,
364+                 image_repositories = None ,
365+                 kms_key_id = "kms-key-id" ,
366+                 output_template_file = None ,
367+                 use_json = True ,
368+                 force_upload = True ,
369+                 no_progressbar = False ,
370+                 metadata = {},
371+                 region = "us-east-2" ,
372+                 profile = None ,
373+             )
374+ 
375+             # Run the package context 
376+             package_command_context .run ()
377+ 
378+             ecr_uploader  =  package_command_context .uploaders .ecr 
379+ 
380+             # Access docker_client multiple times 
381+             docker_client1  =  ecr_uploader .docker_client 
382+             docker_client2  =  ecr_uploader .docker_client 
383+             docker_client3  =  ecr_uploader .docker_client 
384+ 
385+             # Docker client validation should only be called once 
386+             mock_get_validated_client .assert_called_once ()
387+             self .assertEqual (docker_client1 , docker_client2 )
388+             self .assertEqual (docker_client2 , docker_client3 )
389+             self .assertEqual (docker_client1 , mock_docker_client )
0 commit comments