@@ -1184,7 +1184,6 @@ def test_streams_sign_with_es256_alg_create_intend_2(self):
11841184 output .seek (0 )
11851185 reader = Reader ("image/jpeg" , output )
11861186 json_str = reader .json ()
1187- print (json_str )
11881187 # Verify the manifest was created
11891188 self .assertIsNotNone (json_str )
11901189
@@ -2141,6 +2140,107 @@ def test_builder_sign_with_ingredient(self):
21412140
21422141 builder .close ()
21432142
2143+ def test_builder_sign_with_ingredients_edit_intend (self ):
2144+ """Test signing with EDIT intent and ingredient."""
2145+ builder = Builder .from_json ({})
2146+ assert builder ._builder is not None
2147+
2148+ # Set the intent for editing existing content
2149+ builder .set_intent (C2paBuilderIntent .EDIT )
2150+
2151+ # Test adding ingredient
2152+ ingredient_json = '{ "title": "Test Ingredient" }'
2153+ with open (self .testPath3 , 'rb' ) as f :
2154+ builder .add_ingredient (ingredient_json , "image/jpeg" , f )
2155+
2156+ with open (self .testPath2 , "rb" ) as file :
2157+ output = io .BytesIO (bytearray ())
2158+ builder .sign (self .signer , "image/jpeg" , file , output )
2159+ output .seek (0 )
2160+ reader = Reader ("image/jpeg" , output )
2161+ json_data = reader .json ()
2162+ manifest_data = json .loads (json_data )
2163+
2164+ # Verify active manifest exists
2165+ self .assertIn ("active_manifest" , manifest_data )
2166+ active_manifest_id = manifest_data ["active_manifest" ]
2167+
2168+ # Verify active manifest object exists
2169+ self .assertIn ("manifests" , manifest_data )
2170+ self .assertIn (active_manifest_id , manifest_data ["manifests" ])
2171+ active_manifest = manifest_data ["manifests" ][active_manifest_id ]
2172+
2173+ # Verify ingredients array exists with exactly 2 ingredients
2174+ self .assertIn ("ingredients" , active_manifest )
2175+ ingredients_manifest = active_manifest ["ingredients" ]
2176+ self .assertIsInstance (ingredients_manifest , list )
2177+ self .assertEqual (len (ingredients_manifest ), 2 , "Should have exactly two ingredients" )
2178+
2179+ # Verify the first ingredient is the one we added manually with componentOf relationship
2180+ first_ingredient = ingredients_manifest [0 ]
2181+ self .assertEqual (first_ingredient ["title" ], "Test Ingredient" )
2182+ self .assertEqual (first_ingredient ["format" ], "image/jpeg" )
2183+ self .assertIn ("instance_id" , first_ingredient )
2184+ self .assertIn ("thumbnail" , first_ingredient )
2185+ self .assertEqual (first_ingredient ["thumbnail" ]["format" ], "image/jpeg" )
2186+ self .assertIn ("identifier" , first_ingredient ["thumbnail" ])
2187+ self .assertEqual (first_ingredient ["relationship" ], "componentOf" )
2188+ self .assertIn ("label" , first_ingredient )
2189+
2190+ # Verify the second ingredient is the auto-created parent with parentOf relationship
2191+ second_ingredient = ingredients_manifest [1 ]
2192+ # Parent ingredient may not have a title field, or may have an empty one
2193+ self .assertEqual (second_ingredient ["format" ], "image/jpeg" )
2194+ self .assertIn ("instance_id" , second_ingredient )
2195+ self .assertIn ("thumbnail" , second_ingredient )
2196+ self .assertEqual (second_ingredient ["thumbnail" ]["format" ], "image/jpeg" )
2197+ self .assertIn ("identifier" , second_ingredient ["thumbnail" ])
2198+ self .assertEqual (second_ingredient ["relationship" ], "parentOf" )
2199+ self .assertIn ("label" , second_ingredient )
2200+
2201+ # Count ingredients with parentOf relationship - should be exactly one
2202+ parent_ingredients = [
2203+ ing for ing in ingredients_manifest
2204+ if ing .get ("relationship" ) == "parentOf"
2205+ ]
2206+ self .assertEqual (len (parent_ingredients ), 1 , "Should have exactly one parentOf ingredient" )
2207+
2208+ # Check that assertions exist
2209+ self .assertIn ("assertions" , active_manifest )
2210+ assertions = active_manifest ["assertions" ]
2211+
2212+ # Find the actions assertion
2213+ actions_assertion = None
2214+ for assertion in assertions :
2215+ if assertion ["label" ] in ["c2pa.actions" , "c2pa.actions.v2" ]:
2216+ actions_assertion = assertion
2217+ break
2218+
2219+ self .assertIsNotNone (actions_assertion , "Should have c2pa.actions assertion" )
2220+
2221+ # Verify exactly one c2pa.opened action exists for EDIT intent
2222+ actions = actions_assertion ["data" ]["actions" ]
2223+ opened_actions = [
2224+ action for action in actions
2225+ if action ["action" ] == "c2pa.opened"
2226+ ]
2227+ self .assertEqual (len (opened_actions ), 1 , "Should have exactly one c2pa.opened action" )
2228+
2229+ # Verify the c2pa.opened action has the correct structure with parameters and ingredients
2230+ opened_action = opened_actions [0 ]
2231+ self .assertIn ("parameters" , opened_action , "c2pa.opened action should have parameters" )
2232+ self .assertIn ("ingredients" , opened_action ["parameters" ], "parameters should have ingredients array" )
2233+ ingredients_params = opened_action ["parameters" ]["ingredients" ]
2234+ self .assertIsInstance (ingredients_params , list )
2235+ self .assertGreater (len (ingredients_params ), 0 , "Should have at least one ingredient reference" )
2236+
2237+ # Verify each ingredient reference has url and hash
2238+ for ingredient_ref in ingredients_params :
2239+ self .assertIn ("url" , ingredient_ref , "Ingredient reference should have url" )
2240+ self .assertIn ("hash" , ingredient_ref , "Ingredient reference should have hash" )
2241+
2242+ builder .close ()
2243+
21442244 def test_builder_sign_with_setting_no_thumbnail_and_ingredient (self ):
21452245 builder = Builder .from_json (self .manifestDefinition )
21462246 assert builder ._builder is not None
0 commit comments