Skip to content

Commit b1875c6

Browse files
Improve JwtHelper test coverage above 0.90
Add tests covering createSignedJwt, the unrecognized PEM header branch, isSkdEnabled fall-through paths, the default constructor, and both Deserializer overloads. JwtHelper instruction coverage goes from 90% to 100% and branch coverage from 71% to 92%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d95159c commit b1875c6

1 file changed

Lines changed: 92 additions & 0 deletions

File tree

symphony-bdk-core/src/test/java/com/symphony/bdk/core/auth/JwtHelperTest.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import com.migcomponents.migbase64.Base64;
1616
import io.jsonwebtoken.Jwts;
1717
import io.jsonwebtoken.SignatureAlgorithm;
18+
import io.jsonwebtoken.io.DeserializationException;
19+
import io.jsonwebtoken.io.Deserializer;
1820
import lombok.SneakyThrows;
1921
import lombok.extern.slf4j.Slf4j;
2022
import org.bouncycastle.asn1.ASN1Encodable;
@@ -24,8 +26,13 @@
2426
import org.bouncycastle.util.io.pem.PemWriter;
2527
import org.junit.jupiter.api.Test;
2628

29+
import java.io.ByteArrayInputStream;
2730
import java.io.FileInputStream;
31+
import java.io.IOException;
32+
import java.io.InputStreamReader;
33+
import java.io.Reader;
2834
import java.io.StringWriter;
35+
import java.lang.reflect.Method;
2936
import java.nio.charset.StandardCharsets;
3037
import java.security.GeneralSecurityException;
3138
import java.security.Key;
@@ -36,6 +43,7 @@
3643
import java.security.Signature;
3744
import java.security.cert.Certificate;
3845
import java.util.Date;
46+
import java.util.Map;
3947

4048
/**
4149
* Test class for the {@link JwtHelper}.
@@ -206,6 +214,90 @@ public void testValidateJwtWithNumericSubject() {
206214
assertEquals("../avatars/static/50/default.png", userClaim.getAvatarSmallUrl());
207215
}
208216

217+
@Test
218+
@SneakyThrows
219+
public void testCreateSignedJwt() {
220+
final KeyStore keyStore = getKeyStoreFromFile();
221+
final PrivateKey privateKey = (PrivateKey) keyStore.getKey(CERT_ALIAS, CERT_PASSWORD.toCharArray());
222+
223+
String jwt = JwtHelper.createSignedJwt("alice", JwtHelper.JWT_EXPIRATION_MILLIS, privateKey);
224+
225+
assertNotNull(jwt);
226+
// jjwt compact form must have exactly three dot-separated segments
227+
assertEquals(3, jwt.split("\\.").length);
228+
}
229+
230+
@Test
231+
void loadUnknownFormatPrivateKey() {
232+
String unknownFormat = "-----BEGIN SOMETHING ELSE-----\nabcdef\n-----END SOMETHING ELSE-----";
233+
GeneralSecurityException ex = assertThrows(
234+
GeneralSecurityException.class, () -> JwtHelper.parseRsaPrivateKey(unknownFormat));
235+
assertTrue(ex.getMessage().contains("Header not recognized"));
236+
}
237+
238+
@Test
239+
public void testIsSkdEnabledMissingClaim() {
240+
// JWT parses successfully but has no canUseSimplifiedKeyDelivery claim → final return false
241+
assertFalse(JwtHelper.isSkdEnabled(JWT));
242+
}
243+
244+
@Test
245+
public void testIsSkdEnabledNonBooleanClaim() {
246+
// canUseSimplifiedKeyDelivery present but as a string → falls through to final return false
247+
// payload: {"sub":"123","canUseSimplifiedKeyDelivery":"yes"}
248+
String jwtWithStringSkd =
249+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
250+
+ "eyJzdWIiOiIxMjMiLCJjYW5Vc2VTaW1wbGlmaWVkS2V5RGVsaXZlcnkiOiJ5ZXMifQ."
251+
+ "signature";
252+
assertFalse(JwtHelper.isSkdEnabled(jwtWithStringSkd));
253+
}
254+
255+
@Test
256+
public void testIsSkdEnabledTriggersCatch() {
257+
// Too few segments → extractDecodedClaims throws → catch returns false
258+
assertFalse(JwtHelper.isSkdEnabled("only.two"));
259+
}
260+
261+
@Test
262+
public void testJwtHelperConstructor() {
263+
// Cover the implicit default constructor of the utility class
264+
assertNotNull(new JwtHelper());
265+
}
266+
267+
@Test
268+
@SneakyThrows
269+
public void testNormalizeSubjectDeserializerByteArray() {
270+
// The byte[] overload is unreachable through jjwt's public API (Reader is used),
271+
// so exercise it directly via reflection to cover both the success and the
272+
// IOException catch path.
273+
Method method = JwtHelper.class.getDeclaredMethod("normalizeSubjectDeserializer");
274+
method.setAccessible(true);
275+
@SuppressWarnings("unchecked")
276+
Deserializer<Map<String, ?>> deserializer = (Deserializer<Map<String, ?>>) method.invoke(null);
277+
278+
Map<String, ?> result = deserializer.deserialize(
279+
"{\"sub\":12345,\"user\":{\"id\":42}}".getBytes(StandardCharsets.UTF_8));
280+
assertEquals("12345", result.get("sub"));
281+
282+
assertThrows(DeserializationException.class,
283+
() -> deserializer.deserialize("not-json".getBytes(StandardCharsets.UTF_8)));
284+
}
285+
286+
@Test
287+
@SneakyThrows
288+
public void testNormalizeSubjectDeserializerReaderIOException() {
289+
// Exercise the IOException catch in the Reader overload by feeding malformed JSON
290+
Method method = JwtHelper.class.getDeclaredMethod("normalizeSubjectDeserializer");
291+
method.setAccessible(true);
292+
@SuppressWarnings("unchecked")
293+
Deserializer<Map<String, ?>> deserializer = (Deserializer<Map<String, ?>>) method.invoke(null);
294+
295+
try (Reader reader = new InputStreamReader(
296+
new ByteArrayInputStream("not-json".getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8)) {
297+
assertThrows(DeserializationException.class, () -> deserializer.deserialize(reader));
298+
}
299+
}
300+
209301
@SneakyThrows
210302
private static String generatePkcs8RsaPrivateKey() {
211303
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");

0 commit comments

Comments
 (0)