@@ -243,6 +243,108 @@ def test_map_bom_item_purl_release_conflict(self) -> None:
243243 else :
244244 assert False , "Unexpected release id"
245245
246+ @responses .activate
247+ def test_map_bom_item_purl_release_w_qualifiers (self ) -> None :
248+ """test bom mapping: search for releases by PURL with qualifiers
249+ """
250+ if not self .app .client :
251+ return
252+
253+ self .app .purl_service = PurlService (self .app .client , cache = {'maven' : {
254+ 'com.fasterxml.jackson.core' : {'jackson-core' : {
255+ None : [{
256+ "purl" : PackageURL ("maven" , "com.fasterxml.jackson.core" , "jackson-core" ),
257+ "href" : SW360_BASE_URL + "components/a035" }],
258+ "2.18.0" : [
259+ {"purl" : PackageURL ("maven" , "com.fasterxml.jackson.core" , "jackson-core" , version = "2.18.0" ,
260+ qualifiers = {"classifier" : "sources" }),
261+ "href" : SW360_BASE_URL + "releases/1234" },
262+ {"purl" : PackageURL ("maven" , "com.fasterxml.jackson.core" , "jackson-core" , version = "2.18.0" ,
263+ qualifiers = {"classifier" : "javadoc" }),
264+ "href" : SW360_BASE_URL + "releases/1235" },
265+ {"purl" : PackageURL ("maven" , "com.fasterxml.jackson.core" , "jackson-core" , version = "2.18.0" ,
266+ qualifiers = {"classifier" : "sources" , "packaging" : "jar" }),
267+ "href" : SW360_BASE_URL + "releases/1236" }]}}}})
268+
269+ self .app .releases = [{"Id" : "1234" , "ComponentId" : "a035" ,
270+ "Name" : "Jackson Core" , "Version" : "2.18.0" ,
271+ "ExternalIds" : {
272+ "package-url" :
"pkg:maven/com.fasterxml.jackson.core/[email protected] " 273+ "?classifier=sources" }},
274+ {"Id" : "1235" , "ComponentId" : "a034" ,
275+ "Name" : "com.fasterxml.jackson.core:jackson-core" , "Version" : "2.18.0_javadoc" ,
276+ "ExternalIds" : {
277+ "package-url" :
"pkg:maven/com.fasterxml.jackson.core/[email protected] " 278+ "?classifier=javadoc" }},
279+ {"Id" : "1236" , "ComponentId" : "a034" ,
280+ "Name" : "com.fasterxml.jackson.core:jackson-core" , "Version" : "2.18.0_jar" ,
281+ "ExternalIds" : {
282+ "package-url" :
"pkg:maven/com.fasterxml.jackson.core/[email protected] " 283+ "?classifier=sources&packaging=jar" }}]
284+
285+ bomitem = Component (
286+ name = "jackson-core" ,
287+ version = "2.18.0" ,
288+ purl = PackageURL .
from_string (
"pkg:maven/com.fasterxml.jackson.core/[email protected] " 289+ "?classifier=sources" ))
290+
291+ self .app .full_search = True
292+
293+ # 3 matches when ignoring qualifiers
294+ res = self .app .map_bom_item (bomitem , check_similar = False , result_required = False )
295+ assert res .result == MapResult .FULL_MATCH_BY_ID
296+ assert len (res .releases ) == 3
297+
298+ self .app .qualifier_match = True
299+
300+ # 2 matches for classifier=sources
301+ bomitem .properties .clear () # resert properties to remove results from previous mapping
302+ res = self .app .map_bom_item (bomitem , check_similar = False , result_required = False )
303+ assert res .result == MapResult .FULL_MATCH_BY_ID
304+ assert len (res .releases ) == 2
305+
306+ if res .releases [0 ]["Sw360Id" ] == "1234" :
307+ assert res .releases [0 ]["ComponentId" ] == "a035"
308+ assert res .releases [1 ]["Sw360Id" ] == "1236"
309+ assert res .releases [1 ]["ComponentId" ] == "a034"
310+ elif res .releases [0 ]["Sw360Id" ] == "1236" :
311+ assert res .releases [0 ]["ComponentId" ] == "a034"
312+ assert res .releases [1 ]["Sw360Id" ] == "1234"
313+ assert res .releases [1 ]["ComponentId" ] == "a035"
314+ else :
315+ assert False , "Unexpected release id"
316+ assert res .input_component is not None
317+ assert (
318+ CycloneDxSupport .get_property (res .input_component , CycloneDxSupport .CDX_PROP_MAPRESULT_BY_ID ).value
319+ == "qualifiers-full-match" )
320+
321+ self .app .qualifier_match = False
322+
323+ # bomitem has unknown qualifier -> all PURL version matches returned
324+ assert bomitem .purl is not None
325+ assert type (bomitem .purl .qualifiers ) is dict
326+ bomitem .purl .qualifiers ["themorequalifiers" ] = "thebetter"
327+ bomitem .properties .clear () # resert properties to remove results from previous mapping
328+ res = self .app .map_bom_item (bomitem , check_similar = False , result_required = False )
329+ assert res .result == MapResult .FULL_MATCH_BY_ID
330+ assert len (res .releases ) == 3
331+ all_results = [r ["Sw360Id" ] for r in res .releases ]
332+ assert all_results == ["1234" , "1235" , "1236" ]
333+ assert res .input_component is not None
334+ assert (
335+ CycloneDxSupport .get_property (res .input_component , CycloneDxSupport .CDX_PROP_MAPRESULT_BY_ID )
336+ is None )
337+
338+ self .app .qualifier_match = True
339+
340+ bomitem .properties .clear () # resert properties to remove results from previous mapping
341+ res = self .app .map_bom_item (bomitem , check_similar = False , result_required = False )
342+ assert len (res .releases ) == 3
343+ assert res .input_component is not None
344+ assert (
345+ CycloneDxSupport .get_property (res .input_component , CycloneDxSupport .CDX_PROP_MAPRESULT_BY_ID ).value
346+ == "qualifiers-ignored" )
347+
246348 @responses .activate
247349 def test_map_bom_item_mixed_match (self ) -> None :
248350 bomitem = Component (
0 commit comments