@@ -153,7 +153,7 @@ describe('spdxToCdxBom', () => {
153153 assert . deepStrictEqual ( result . metadata ?. component , {
154154 'bom-ref' : '@herodevs/eol-report-card@1.0.0' ,
155155 type : 'library' ,
156- name : '@herodevs/eol-report-card ' ,
156+ name : 'test-document ' ,
157157 version : '1.0.0' ,
158158 description : '' ,
159159 purl : '' ,
@@ -168,6 +168,226 @@ describe('spdxToCdxBom', () => {
168168 // Non-root components should be in components array
169169 assert ( result . components ?. find ( ( c ) => c . name === 'some-dependency' ) ) ;
170170 } ) ;
171+
172+ test ( 'should use SPDX document name for metadata component name' , ( ) => {
173+ const result = buildSpdxAndConvert ( {
174+ name : 'My Application' ,
175+ documentDescribes : [ 'SPDXRef-Package-root' ] ,
176+ packages : [
177+ {
178+ SPDXID : 'SPDXRef-Package-root' ,
179+ name : '@herodevs/eol-report-card' ,
180+ versionInfo : '1.0.0' ,
181+ downloadLocation : 'NOASSERTION' ,
182+ } ,
183+ ] ,
184+ } ) ;
185+
186+ assert . equal ( result . metadata ?. component ?. name , 'My Application' ) ;
187+ } ) ;
188+
189+ test ( 'should strip trailing version from SPDX document name' , ( ) => {
190+ const result = buildSpdxAndConvert ( {
191+ name : 'Awesome App v1.2.3-beta.1' ,
192+ documentDescribes : [ 'SPDXRef-Package-root' ] ,
193+ packages : [
194+ {
195+ SPDXID : 'SPDXRef-Package-root' ,
196+ name : '@herodevs/eol-report-card' ,
197+ versionInfo : '1.0.0' ,
198+ downloadLocation : 'NOASSERTION' ,
199+ } ,
200+ ] ,
201+ } ) ;
202+
203+ assert . equal ( result . metadata ?. component ?. name , 'Awesome App' ) ;
204+ } ) ;
205+
206+ test ( 'should fall back to package name when document name is blank' , ( ) => {
207+ const result = buildSpdxAndConvert ( {
208+ name : ' ' ,
209+ documentDescribes : [ 'SPDXRef-Package-root' ] ,
210+ packages : [
211+ {
212+ SPDXID : 'SPDXRef-Package-root' ,
213+ name : '@herodevs/eol-report-card' ,
214+ versionInfo : '1.0.0' ,
215+ downloadLocation : 'NOASSERTION' ,
216+ } ,
217+ ] ,
218+ } ) ;
219+
220+ assert . equal (
221+ result . metadata ?. component ?. name ,
222+ '@herodevs/eol-report-card' ,
223+ ) ;
224+ } ) ;
225+
226+ test ( 'should fall back to package name when document name is only a version' , ( ) => {
227+ const result = buildSpdxAndConvert ( {
228+ name : 'v1.2.3' ,
229+ documentDescribes : [ 'SPDXRef-Package-root' ] ,
230+ packages : [
231+ {
232+ SPDXID : 'SPDXRef-Package-root' ,
233+ name : '@herodevs/eol-report-card' ,
234+ versionInfo : '1.0.0' ,
235+ downloadLocation : 'NOASSERTION' ,
236+ } ,
237+ ] ,
238+ } ) ;
239+
240+ assert . equal (
241+ result . metadata ?. component ?. name ,
242+ '@herodevs/eol-report-card' ,
243+ ) ;
244+ } ) ;
245+
246+ test ( 'should strip version from package name when falling back' , ( ) => {
247+ const cases = [
248+ { packageName : 'myapp@1.2.3' , expected : 'myapp' } ,
249+ { packageName : 'myapp v1.0.0' , expected : 'myapp' } ,
250+ { packageName : 'myapp-1.2.3' , expected : 'myapp' } ,
251+ { packageName : 'my-app (v2.0.0)' , expected : 'my-app' } ,
252+ ] ;
253+ for ( const { packageName, expected } of cases ) {
254+ const result = buildSpdxAndConvert ( {
255+ name : '' , // Empty document name forces fallback
256+ documentDescribes : [ 'SPDXRef-Package-root' ] ,
257+ packages : [
258+ {
259+ SPDXID : 'SPDXRef-Package-root' ,
260+ name : packageName ,
261+ versionInfo : '1.0.0' ,
262+ downloadLocation : 'NOASSERTION' ,
263+ } ,
264+ ] ,
265+ } ) ;
266+
267+ assert . equal (
268+ result . metadata ?. component ?. name ,
269+ expected ,
270+ `Failed for package name: ${ packageName } ` ,
271+ ) ;
272+ }
273+ } ) ;
274+
275+ test ( 'should handle Java/Maven style package names' , ( ) => {
276+ const result = buildSpdxAndConvert ( {
277+ name : 'org.springframework:spring-core-6.0.0' ,
278+ documentDescribes : [ 'SPDXRef-Package-root' ] ,
279+ packages : [
280+ {
281+ SPDXID : 'SPDXRef-Package-root' ,
282+ name : 'org.springframework:spring-core' ,
283+ versionInfo : '6.0.0' ,
284+ downloadLocation : 'NOASSERTION' ,
285+ } ,
286+ ] ,
287+ } ) ;
288+
289+ assert . equal (
290+ result . metadata ?. component ?. name ,
291+ 'org.springframework:spring-core' ,
292+ ) ;
293+ } ) ;
294+
295+ test ( 'should handle Java JAR-style names with versions' , ( ) => {
296+ const result = buildSpdxAndConvert ( {
297+ name : '' , // Empty to test fallback
298+ documentDescribes : [ 'SPDXRef-Package-root' ] ,
299+ packages : [
300+ {
301+ SPDXID : 'SPDXRef-Package-root' ,
302+ name : 'spring-core-6.0.0' ,
303+ versionInfo : '6.0.0' ,
304+ downloadLocation : 'NOASSERTION' ,
305+ } ,
306+ ] ,
307+ } ) ;
308+
309+ assert . equal ( result . metadata ?. component ?. name , 'spring-core' ) ;
310+ } ) ;
311+
312+ test ( 'synthetic component should NOT be in dependencies array' , ( ) => {
313+ const result = buildSpdxAndConvert ( {
314+ name : 'My App' ,
315+ packages : [
316+ {
317+ SPDXID : 'SPDXRef-pkg' ,
318+ name : 'lodash' ,
319+ versionInfo : '4.17.21' ,
320+ downloadLocation : 'NOASSERTION' ,
321+ } ,
322+ ] ,
323+ } ) ;
324+
325+ assert . equal ( result . metadata ?. component ?. name , 'My App' ) ;
326+ assert . equal (
327+ result . dependencies ?. find ( ( d ) => d . ref === 'My App' ) ,
328+ undefined ,
329+ ) ;
330+ } ) ;
331+
332+ test ( 'synthetic component should have type application' , ( ) => {
333+ const result = buildSpdxAndConvert ( { name : 'My App' , packages : [ ] } ) ;
334+
335+ assert . equal ( result . metadata ?. component ?. type , 'application' ) ;
336+ } ) ;
337+
338+ test ( 'should strip various version formats from document name' , ( ) => {
339+ const cases = [
340+ { input : '@scope/pkg@1.0.0' , expected : '@scope/pkg' } ,
341+ { input : 'My App v2.0.0' , expected : 'My App' } ,
342+ { input : 'Project-1.0.0-beta.1' , expected : 'Project' } ,
343+ { input : 'App version 3.0' , expected : 'App' } ,
344+ { input : 'My App (v2.0.0)' , expected : 'My App' } ,
345+ { input : 'My App [2.0.0]' , expected : 'My App' } ,
346+ { input : 'Project 2024' , expected : 'Project 2024' } , // NOT stripped - year only
347+ ] ;
348+ for ( const { input, expected } of cases ) {
349+ const result = buildSpdxAndConvert ( { name : input , packages : [ ] } ) ;
350+ assert . equal (
351+ result . metadata ?. component ?. name ,
352+ expected ,
353+ `Failed for: ${ input } ` ,
354+ ) ;
355+ }
356+ } ) ;
357+
358+ test ( 'package component names should NOT use document name' , ( ) => {
359+ const result = buildSpdxAndConvert ( {
360+ name : 'My App v1.0.0' ,
361+ packages : [
362+ {
363+ SPDXID : 'SPDXRef-pkg' ,
364+ name : 'lodash' ,
365+ versionInfo : '4.17.21' ,
366+ downloadLocation : 'NOASSERTION' ,
367+ } ,
368+ ] ,
369+ } ) ;
370+
371+ assert . equal ( result . components ?. [ 0 ] ?. name , 'lodash' ) ;
372+ } ) ;
373+
374+ test ( 'should have undefined metadata.component when no root package and no document name' , ( ) => {
375+ const result = buildSpdxAndConvert ( {
376+ name : '' , // Empty document name
377+ packages : [
378+ {
379+ SPDXID : 'SPDXRef-pkg' ,
380+ name : 'lodash' ,
381+ versionInfo : '4.17.21' ,
382+ downloadLocation : 'NOASSERTION' ,
383+ } ,
384+ ] ,
385+ // No documentDescribes
386+ } ) ;
387+
388+ assert . equal ( result . metadata ?. component , undefined ) ;
389+ assert . equal ( result . components ?. length , 1 ) ;
390+ } ) ;
171391 } ) ;
172392
173393 describe ( 'Component Mapping' , ( ) => {
@@ -606,6 +826,7 @@ describe('spdxToCdxBom', () => {
606826 describe ( 'Root Component Identification' , ( ) => {
607827 test ( 'should identify root component from documentDescribes' , ( ) => {
608828 const result = buildSpdxAndConvert ( {
829+ name : 'my-app' ,
609830 documentDescribes : [ 'SPDXRef-Package-root' ] ,
610831 packages : [
611832 {
@@ -667,7 +888,7 @@ describe('spdxToCdxBom', () => {
667888 ] ,
668889 } ) ;
669890
670- assert . equal ( result . metadata ?. component , undefined ) ;
891+ assert . equal ( result . metadata ?. component ?. name , 'test-document' ) ;
671892 assert . equal ( result . components ?. length , 1 ) ;
672893 } ) ;
673894
@@ -691,7 +912,10 @@ describe('spdxToCdxBom', () => {
691912 } ) ;
692913
693914 // Should take the last one as root (implementation overwrites rootComponent)
694- assert . equal ( result . metadata ?. component ?. name , 'second-root' ) ;
915+ assert . equal (
916+ result . metadata ?. component ?. [ 'bom-ref' ] ,
917+ 'second-root@2.0.0' ,
918+ ) ;
695919 // Both components marked as root, so neither goes to components array
696920 assert . equal ( result . components ?. length , 0 ) ;
697921 } ) ;
@@ -1155,6 +1379,7 @@ describe('spdxToCdxBom', () => {
11551379 describe ( 'Integration Tests' , ( ) => {
11561380 test ( 'should convert complete real-world SPDX BOM' , ( ) => {
11571381 const complexSpdx = {
1382+ name : '@my/app' ,
11581383 documentDescribes : [ 'SPDXRef-Package-root' ] ,
11591384 packages : [
11601385 {
@@ -1292,7 +1517,13 @@ describe('spdxToCdxBom', () => {
12921517
12931518 assert . deepStrictEqual ( result . components , [ ] ) ;
12941519 assert . deepStrictEqual ( result . dependencies , [ ] ) ;
1295- assert . equal ( result . metadata ?. component , undefined ) ;
1520+ assert . deepStrictEqual ( result . metadata ?. component , {
1521+ 'bom-ref' : 'test-document' ,
1522+ type : 'application' ,
1523+ name : 'test-document' ,
1524+ version : '' ,
1525+ description : '' ,
1526+ } ) ;
12961527 } ) ;
12971528
12981529 test ( 'should handle components with special characters in names' , ( ) => {
0 commit comments