@@ -5775,6 +5775,141 @@ def test_active_image_proof_binds_full_sha_tag_to_exact_digest(self) -> None:
57755775 with self .assertRaisesMessage (ReleaseContractError , "source SHA tag" ):
57765776 gateway .verify_image_digest_exists (identity )
57775777
5778+ def test_active_image_proof_uses_exact_sha_fallback_for_manifest_lookup (self ) -> None :
5779+ gateway = self .gateway (FakeMigrationEcs ({}, {}))
5780+ gateway .ecr = Mock ()
5781+ source_sha = "a" * 40
5782+ image_digest = f"sha256:{ 'a' * 64 } "
5783+ identity = ReleaseIdentity (
5784+ source_sha ,
5785+ image_digest ,
5786+ ECR_REPOSITORY_URI ,
5787+ f"20260809-143205-{ source_sha [:7 ]} " ,
5788+ )
5789+ gateway .ecr .describe_images .side_effect = [
5790+ {"imageDetails" : [{"imageDigest" : image_digest }]},
5791+ {"imageDetails" : [{"imageDigest" : image_digest }]},
5792+ {"imageDetails" : [{"imageDigest" : image_digest }]},
5793+ ]
5794+ gateway .ecr .batch_get_image .side_effect = [
5795+ {
5796+ "failures" : [{"failureCode" : "ImageNotFound" }],
5797+ "images" : [],
5798+ },
5799+ {
5800+ "failures" : [],
5801+ "images" : [
5802+ {
5803+ "imageId" : {
5804+ "imageDigest" : image_digest ,
5805+ "imageTag" : source_sha ,
5806+ },
5807+ "imageManifest" : "{}" ,
5808+ }
5809+ ],
5810+ },
5811+ ]
5812+
5813+ gateway .verify_image_digest_exists (identity )
5814+
5815+ self .assertEqual (
5816+ [call .kwargs ["imageIds" ] for call in gateway .ecr .batch_get_image .call_args_list ],
5817+ [
5818+ [{"imageDigest" : image_digest }],
5819+ [{"imageTag" : source_sha }],
5820+ ],
5821+ )
5822+
5823+ def test_active_image_manifest_fallback_remains_fail_closed (self ) -> None :
5824+ source_sha = "a" * 40
5825+ image_digest = f"sha256:{ 'a' * 64 } "
5826+ identity = ReleaseIdentity (
5827+ source_sha ,
5828+ image_digest ,
5829+ ECR_REPOSITORY_URI ,
5830+ f"20260809-143205-{ source_sha [:7 ]} " ,
5831+ )
5832+ malformed_failure_values : tuple [object , ...] = ({}, None , "" , 0 , False )
5833+ invalid_fallbacks = (
5834+ * (
5835+ {
5836+ "failures" : malformed_failures ,
5837+ "images" : [
5838+ {
5839+ "imageId" : {"imageDigest" : image_digest },
5840+ "imageManifest" : "{}" ,
5841+ }
5842+ ],
5843+ }
5844+ for malformed_failures in malformed_failure_values
5845+ ),
5846+ {
5847+ "failures" : [],
5848+ "images" : [
5849+ {
5850+ "imageId" : {"imageDigest" : f"sha256:{ 'b' * 64 } " },
5851+ "imageManifest" : "{}" ,
5852+ }
5853+ ],
5854+ },
5855+ {
5856+ "failures" : [{"failureCode" : "ImageNotFound" }],
5857+ "images" : [],
5858+ },
5859+ {
5860+ "failures" : [],
5861+ "images" : [{"imageId" : {"imageDigest" : image_digest }}],
5862+ },
5863+ {
5864+ "failures" : [],
5865+ "images" : [
5866+ {
5867+ "imageId" : {"imageDigest" : image_digest },
5868+ "imageManifest" : " " ,
5869+ }
5870+ ],
5871+ },
5872+ {
5873+ "failures" : [],
5874+ "images" : [
5875+ {
5876+ "imageId" : {"imageDigest" : image_digest },
5877+ "imageManifest" : "{}" ,
5878+ },
5879+ {
5880+ "imageId" : {"imageDigest" : image_digest },
5881+ "imageManifest" : "{}" ,
5882+ },
5883+ ],
5884+ },
5885+ )
5886+ unusable_digest_lookup = {
5887+ "failures" : [{"failureCode" : "ImageNotFound" }],
5888+ "images" : [],
5889+ }
5890+
5891+ for fallback in invalid_fallbacks :
5892+ gateway = self .gateway (FakeMigrationEcs ({}, {}))
5893+ gateway .ecr = Mock ()
5894+ gateway .ecr .describe_images .side_effect = [
5895+ {"imageDetails" : [{"imageDigest" : image_digest }]},
5896+ {"imageDetails" : [{"imageDigest" : image_digest }]},
5897+ {"imageDetails" : [{"imageDigest" : image_digest }]},
5898+ ]
5899+ gateway .ecr .batch_get_image .side_effect = [
5900+ unusable_digest_lookup ,
5901+ fallback ,
5902+ ]
5903+
5904+ with (
5905+ self .subTest (fallback = fallback ),
5906+ self .assertRaisesMessage (
5907+ ReleaseContractError ,
5908+ "active image manifest is missing" ,
5909+ ),
5910+ ):
5911+ gateway .verify_image_digest_exists (identity )
5912+
57785913 def test_public_health_polls_until_exact_readiness_or_timeout (self ) -> None :
57795914 gateway = self .gateway (FakeMigrationEcs ({}, {}))
57805915 source_sha = "a" * 40
0 commit comments