3030import java .util .Arrays ;
3131import java .util .Optional ;
3232import java .util .stream .Stream ;
33+ import org .junit .jupiter .api .Test ;
3334import org .junit .jupiter .api .extension .ExtendWith ;
3435import org .junit .jupiter .params .ParameterizedTest ;
3536import org .junit .jupiter .params .provider .MethodSource ;
@@ -56,7 +57,8 @@ static Stream<String> testFolders() {
5657 "deps_with_ignore_on_version" ,
5758 "deps_with_ignore_on_wrong" ,
5859 "deps_with_no_ignore" ,
59- "pom_deps_with_no_ignore_common_paths" );
60+ "pom_deps_with_no_ignore_common_paths" ,
61+ "deps_with_version_range" );
6062 }
6163
6264 @ ParameterizedTest
@@ -143,12 +145,29 @@ void test_the_provideComponent(String testFolder) throws IOException {
143145 getClass (), String .format ("tst_manifests/maven/%s/effectivePom.xml" , testFolder ))) {
144146 effectivePom = new String (is .readAllBytes ());
145147 }
148+
149+ String depTree ;
150+ try (var is =
151+ getResourceAsStreamDecision (
152+ getClass (), String .format ("tst_manifests/maven/%s/depTree.txt" , testFolder ))) {
153+ depTree = new String (is .readAllBytes ());
154+ }
155+
146156 try (MockedStatic <Operations > mockedOperations = mockStatic (Operations .class )) {
147157 mockedOperations
148158 .when (() -> Operations .runProcess (any (), any (), any ()))
149159 .thenAnswer (
150- invocationOnMock ->
151- getOutputFileAndOverwriteItWithMock (effectivePom , invocationOnMock , "-Doutput" ));
160+ invocationOnMock -> {
161+ String result =
162+ getOutputFileAndOverwriteItWithMock (
163+ effectivePom , invocationOnMock , "-Doutput=" );
164+ if (result == null ) {
165+ result =
166+ getOutputFileAndOverwriteItWithMock (
167+ depTree , invocationOnMock , "-DoutputFile" );
168+ }
169+ return result ;
170+ });
152171 // Mock Operations.getCustomPathOrElse to return "mvn"
153172 mockedOperations .when (() -> Operations .getCustomPathOrElse (anyString ())).thenReturn ("mvn" );
154173 mockedOperations
@@ -189,12 +208,29 @@ void test_the_provideComponent_With_Path(String testFolder) throws IOException {
189208 getClass (), String .format ("tst_manifests/maven/%s/effectivePom.xml" , testFolder ))) {
190209 effectivePom = new String (is .readAllBytes ());
191210 }
211+
212+ String depTree ;
213+ try (var is =
214+ getResourceAsStreamDecision (
215+ getClass (), String .format ("tst_manifests/maven/%s/depTree.txt" , testFolder ))) {
216+ depTree = new String (is .readAllBytes ());
217+ }
218+
192219 try (MockedStatic <Operations > mockedOperations = mockStatic (Operations .class )) {
193220 mockedOperations
194221 .when (() -> Operations .runProcess (any (), any (), any ()))
195222 .thenAnswer (
196- invocationOnMock ->
197- getOutputFileAndOverwriteItWithMock (effectivePom , invocationOnMock , "-Doutput" ));
223+ invocationOnMock -> {
224+ String result =
225+ getOutputFileAndOverwriteItWithMock (
226+ effectivePom , invocationOnMock , "-Doutput=" );
227+ if (result == null ) {
228+ result =
229+ getOutputFileAndOverwriteItWithMock (
230+ depTree , invocationOnMock , "-DoutputFile" );
231+ }
232+ return result ;
233+ });
198234 // Mock Operations.getCustomPathOrElse to return "mvn"
199235 mockedOperations .when (() -> Operations .getCustomPathOrElse (anyString ())).thenReturn ("mvn" );
200236 mockedOperations
@@ -209,6 +245,48 @@ void test_the_provideComponent_With_Path(String testFolder) throws IOException {
209245 }
210246 }
211247
248+ @ Test
249+ void test_provideComponent_fallsBack_when_depTree_fails () throws IOException {
250+ String testFolder = "deps_with_version_range" ;
251+
252+ var targetPom = resolveFile (String .format ("tst_manifests/maven/%s/pom.xml" , testFolder ));
253+
254+ String effectivePom ;
255+ try (var is =
256+ getResourceAsStreamDecision (
257+ getClass (), String .format ("tst_manifests/maven/%s/effectivePom.xml" , testFolder ))) {
258+ effectivePom = new String (is .readAllBytes ());
259+ }
260+
261+ try (MockedStatic <Operations > mockedOperations = mockStatic (Operations .class )) {
262+ mockedOperations
263+ .when (() -> Operations .runProcess (any (), any (), any ()))
264+ .thenAnswer (
265+ invocationOnMock -> {
266+ String result =
267+ getOutputFileAndOverwriteItWithMock (
268+ effectivePom , invocationOnMock , "-Doutput=" );
269+ if (result == null ) {
270+ throw new IOException ("Simulated dependency:tree failure" );
271+ }
272+ return result ;
273+ });
274+ mockedOperations .when (() -> Operations .getCustomPathOrElse (anyString ())).thenReturn ("mvn" );
275+ mockedOperations
276+ .when (() -> Operations .getExecutable (anyString (), anyString ()))
277+ .thenReturn ("mvn" );
278+
279+ var content = new JavaMavenProvider (targetPom ).provideComponent ();
280+
281+ assertThat (content .type ).isEqualTo (Api .CYCLONEDX_MEDIA_TYPE );
282+ String sbom = new String (content .buffer );
283+ assertThat (sbom ).contains ("log4j" );
284+ assertThat (sbom ).contains ("snappy-java" );
285+ // Version range should remain unresolved since dep tree failed
286+ assertThat (sbom ).contains ("[1.2.17,1.3.0)" );
287+ }
288+ }
289+
212290 private String dropIgnored (String s ) {
213291 return s .replaceAll ("\\ s+" , "" ).replaceAll ("\" timestamp\" :\" [a-zA-Z0-9\\ -\\ :]+\" ," , "" );
214292 }
0 commit comments