1+ /**
2+ * (C) Copyright IBM Corporation 2026.
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+ package io .openliberty .tools .common .plugins .util ;
17+
18+ import org .junit .Rule ;
19+ import org .junit .Test ;
20+ import org .junit .rules .TemporaryFolder ;
21+
22+ import java .io .File ;
23+ import java .io .FileOutputStream ;
24+ import java .util .jar .Attributes ;
25+ import java .util .jar .JarEntry ;
26+ import java .util .jar .JarOutputStream ;
27+ import java .util .jar .Manifest ;
28+
29+ import static org .junit .Assert .*;
30+
31+ /**
32+ * Unit tests for SpringBootUtil
33+ * Tests both Tier 1 (manifest) and Tier 2 (regex) detection for Spring Boot applications
34+ */
35+ public class SpringBootUtilTest {
36+
37+ @ Rule
38+ public TemporaryFolder tempFolder = new TemporaryFolder ();
39+
40+ // ========== Manifest Attributes based Detection Tests ==========
41+
42+ @ Test
43+ public void testIsSpringBootUberJar_SpringBoot2_WithManifest () throws Exception {
44+ File testJar = createSpringBootJarWithManifest ("2.7.18" , "BOOT-INF/lib/spring-boot-2.7.18.RELEASE.jar" );
45+ assertTrue ("Should detect Spring Boot 2.7.18 via manifest" ,
46+ SpringBootUtil .isSpringBootUberJar (testJar ));
47+ }
48+
49+ @ Test
50+ public void testIsSpringBootUberJar_SpringBoot3_WithManifest () throws Exception {
51+ File testJar = createSpringBootJarWithManifest ("3.1.3" , "BOOT-INF/lib/spring-boot-3.1.3.jar" );
52+ assertTrue ("Should detect Spring Boot 3.1.3 via manifest" ,
53+ SpringBootUtil .isSpringBootUberJar (testJar ));
54+ }
55+
56+ @ Test
57+ public void testIsSpringBootUberJar_SpringBoot4_WithManifest () throws Exception {
58+ File testJar = createSpringBootJarWithManifest ("4.0.0" , "BOOT-INF/lib/spring-boot-4.0.0.jar" );
59+ assertTrue ("Should detect Spring Boot 4.0.0 via manifest" ,
60+ SpringBootUtil .isSpringBootUberJar (testJar ));
61+ }
62+
63+ @ Test
64+ public void testIsSpringBootUberJar_SpringBoot4_War_WithManifest () throws Exception {
65+ File testWar = createSpringBootWarWithManifest ("4.0.0" , "WEB-INF/lib/spring-boot-4.0.0.jar" );
66+ assertTrue ("Should detect Spring Boot 4.0.0 WAR via manifest" ,
67+ SpringBootUtil .isSpringBootUberJar (testWar ));
68+ }
69+
70+ // ========== Regex based Detection Tests ( Fallback) ==========
71+
72+ @ Test
73+ public void testIsSpringBootUberJar_SpringBoot2_WithoutManifest () throws Exception {
74+ File testJar = createSpringBootJarWithoutManifest ("BOOT-INF/lib/spring-boot-2.7.18.RELEASE.jar" );
75+ assertTrue ("Should detect Spring Boot 2.7.18.RELEASE via regex" ,
76+ SpringBootUtil .isSpringBootUberJar (testJar ));
77+ }
78+
79+ @ Test
80+ public void testIsSpringBootUberJar_SpringBoot3_WithoutManifest () throws Exception {
81+ File testJar = createSpringBootJarWithoutManifest ("BOOT-INF/lib/spring-boot-3.1.3.jar" );
82+ assertTrue ("Should detect Spring Boot 3.1.3 via regex" ,
83+ SpringBootUtil .isSpringBootUberJar (testJar ));
84+ }
85+
86+ @ Test
87+ public void testIsSpringBootUberJar_SpringBoot4_WithoutManifest () throws Exception {
88+ File testJar = createSpringBootJarWithoutManifest ("BOOT-INF/lib/spring-boot-4.0.0.jar" );
89+ assertTrue ("Should detect Spring Boot 4.0.0 via regex - CRITICAL TEST" ,
90+ SpringBootUtil .isSpringBootUberJar (testJar ));
91+ }
92+
93+ @ Test
94+ public void testIsSpringBootUberJar_SpringBoot3_4_WithoutManifest () throws Exception {
95+ File testJar = createSpringBootJarWithoutManifest ("BOOT-INF/lib/spring-boot-3.4.13.jar" );
96+ assertTrue ("Should detect Spring Boot 3.4.13 via regex" ,
97+ SpringBootUtil .isSpringBootUberJar (testJar ));
98+ }
99+
100+ @ Test
101+ public void testIsSpringBootUberJar_SpringBoot4_War_WithoutManifest () throws Exception {
102+ File testWar = createSpringBootJarWithoutManifest ("WEB-INF/lib/spring-boot-4.0.0.jar" );
103+ assertTrue ("Should detect Spring Boot 4.0.0 WAR via regex" ,
104+ SpringBootUtil .isSpringBootUberJar (testWar ));
105+ }
106+
107+ @ Test
108+ public void testIsSpringBootUberJar_MultiDigitVersion () throws Exception {
109+ File testJar = createSpringBootJarWithoutManifest ("BOOT-INF/lib/spring-boot-10.5.3.jar" );
110+ assertTrue ("Should detect Spring Boot 10.5.3 via regex - future-proof test" ,
111+ SpringBootUtil .isSpringBootUberJar (testJar ));
112+ }
113+
114+ // ========== Negative Tests ==========
115+
116+ @ Test
117+ public void testIsSpringBootUberJar_RegularJar () throws Exception {
118+ File testJar = tempFolder .newFile ("regular.jar" );
119+ try (JarOutputStream jos = new JarOutputStream (new FileOutputStream (testJar ))) {
120+ Manifest manifest = new Manifest ();
121+ manifest .getMainAttributes ().put (Attributes .Name .MANIFEST_VERSION , "1.0" );
122+ jos .putNextEntry (new JarEntry ("com/example/Main.class" ));
123+ jos .write (new byte [0 ]);
124+ jos .closeEntry ();
125+ }
126+ assertFalse ("Should NOT detect regular JAR as Spring Boot" ,
127+ SpringBootUtil .isSpringBootUberJar (testJar ));
128+ }
129+
130+ @ Test
131+ public void testIsSpringBootUberJar_NullFile () {
132+ assertFalse ("Should return false for null file" ,
133+ SpringBootUtil .isSpringBootUberJar (null ));
134+ }
135+
136+ @ Test
137+ public void testIsSpringBootUberJar_NonExistentFile () {
138+ File nonExistent = new File ("non-existent-file.jar" );
139+ assertFalse ("Should return false for non-existent file" ,
140+ SpringBootUtil .isSpringBootUberJar (nonExistent ));
141+ }
142+
143+ /**
144+ * Creates a Spring Boot JAR with manifest attributes
145+ */
146+ private File createSpringBootJarWithManifest (String version , String springBootJarPath ) throws Exception {
147+ File testJar = tempFolder .newFile ("spring-boot-" + version + "-test.jar" );
148+
149+ try (JarOutputStream jos = new JarOutputStream (new FileOutputStream (testJar ))) {
150+ // Create manifest with Spring Boot attributes
151+ Manifest manifest = new Manifest ();
152+ manifest .getMainAttributes ().put (Attributes .Name .MANIFEST_VERSION , "1.0" );
153+ manifest .getMainAttributes ().putValue (SpringBootUtil .BOOT_VERSION_ATTRIBUTE , version );
154+ manifest .getMainAttributes ().putValue (SpringBootUtil .BOOT_START_CLASS_ATTRIBUTE , "com.example.Application" );
155+
156+ // Write manifest
157+ JarEntry manifestEntry = new JarEntry ("META-INF/MANIFEST.MF" );
158+ jos .putNextEntry (manifestEntry );
159+ manifest .write (jos );
160+ jos .closeEntry ();
161+
162+ // Add spring-boot jar entry
163+ JarEntry springBootEntry = new JarEntry (springBootJarPath );
164+ jos .putNextEntry (springBootEntry );
165+ jos .write (new byte [0 ]);
166+ jos .closeEntry ();
167+ }
168+
169+ return testJar ;
170+ }
171+
172+ /**
173+ * Creates a Spring Boot WAR with manifest attributes
174+ */
175+ private File createSpringBootWarWithManifest (String version , String springBootJarPath ) throws Exception {
176+ File testWar = tempFolder .newFile ("spring-boot-" + version + "-test.war" );
177+
178+ try (JarOutputStream jos = new JarOutputStream (new FileOutputStream (testWar ))) {
179+ // Create manifest with Spring Boot attributes
180+ Manifest manifest = new Manifest ();
181+ manifest .getMainAttributes ().put (Attributes .Name .MANIFEST_VERSION , "1.0" );
182+ manifest .getMainAttributes ().putValue (SpringBootUtil .BOOT_VERSION_ATTRIBUTE , version );
183+ manifest .getMainAttributes ().putValue (SpringBootUtil .BOOT_START_CLASS_ATTRIBUTE , "com.example.Application" );
184+
185+ // Write manifest
186+ JarEntry manifestEntry = new JarEntry ("META-INF/MANIFEST.MF" );
187+ jos .putNextEntry (manifestEntry );
188+ manifest .write (jos );
189+ jos .closeEntry ();
190+
191+ // Add spring-boot jar entry
192+ JarEntry springBootEntry = new JarEntry (springBootJarPath );
193+ jos .putNextEntry (springBootEntry );
194+ jos .write (new byte [0 ]);
195+ jos .closeEntry ();
196+ }
197+
198+ return testWar ;
199+ }
200+
201+ /**
202+ * Creates a Spring Boot JAR WITHOUT manifest attributes
203+ * This forces the regex-based detection to be used
204+ */
205+ private File createSpringBootJarWithoutManifest (String springBootJarPath ) throws Exception {
206+ File testJar = tempFolder .newFile ("spring-boot-no-manifest-test.jar" );
207+
208+ try (JarOutputStream jos = new JarOutputStream (new FileOutputStream (testJar ))) {
209+ // Create manifest WITHOUT Spring Boot attributes
210+ Manifest manifest = new Manifest ();
211+ manifest .getMainAttributes ().put (Attributes .Name .MANIFEST_VERSION , "1.0" );
212+ // NO Spring-Boot-Version attribute
213+ // NO Start-Class attribute
214+
215+ // Write manifest
216+ JarEntry manifestEntry = new JarEntry ("META-INF/MANIFEST.MF" );
217+ jos .putNextEntry (manifestEntry );
218+ manifest .write (jos );
219+ jos .closeEntry ();
220+
221+ // Add spring-boot jar entry - this is what regex will detect
222+ JarEntry springBootEntry = new JarEntry (springBootJarPath );
223+ jos .putNextEntry (springBootEntry );
224+ jos .write (new byte [0 ]);
225+ jos .closeEntry ();
226+ }
227+
228+ return testJar ;
229+ }
230+ }
0 commit comments