|
23 | 23 | import com.mongodb.MongoSocketOpenException; |
24 | 24 | import com.mongodb.MongoSocketReadTimeoutException; |
25 | 25 | import com.mongodb.ServerAddress; |
| 26 | +import net.bytebuddy.ByteBuddy; |
26 | 27 | import org.junit.jupiter.api.Named; |
| 28 | +import org.junit.jupiter.api.Test; |
27 | 29 | import org.junit.jupiter.params.ParameterizedTest; |
| 30 | +import org.junit.jupiter.params.provider.Arguments; |
28 | 31 | import org.junit.jupiter.params.provider.MethodSource; |
29 | 32 | import org.junit.jupiter.params.provider.ValueSource; |
30 | 33 |
|
@@ -166,6 +169,72 @@ void nonSocketErrorShouldNotBeLabeled(final Throwable e) { |
166 | 169 | } |
167 | 170 | } |
168 | 171 |
|
| 172 | + /** |
| 173 | + * BouncyCastle isn't on the test classpath, so we use ByteBuddy to synthesise classes with the |
| 174 | + * exact FQCNs the labeler matches against. This exercises the FQCN-walk in |
| 175 | + * {@code isBouncyCastleTlsFatalType} without taking a compile- or runtime dependency on BC. |
| 176 | + */ |
| 177 | + @ParameterizedTest(name = "{0} with alert {1}") |
| 178 | + @MethodSource |
| 179 | + void bouncyCastleTlsFatalAlertShouldNotBeLabeled(final String bcFqcn, final String alertMessage) throws Exception { |
| 180 | + Throwable bcCause = newBouncyCastleStub(bcFqcn, alertMessage); |
| 181 | + MongoSocketException e = new MongoSocketException("tls", ADDRESS, bcCause); |
| 182 | + BackpressureErrorLabeler.applyLabelsIfEligible(e); |
| 183 | + assertLacksBackpressureLabels(e); |
| 184 | + } |
| 185 | + |
| 186 | + static Stream<Arguments> bouncyCastleTlsFatalAlertShouldNotBeLabeled() { |
| 187 | + return Stream.of( |
| 188 | + Arguments.of("org.bouncycastle.tls.TlsFatalAlert", "handshake_failure(40)"), |
| 189 | + Arguments.of("org.bouncycastle.tls.TlsFatalAlertReceived", "unknown_ca(48)"), |
| 190 | + Arguments.of("org.bouncycastle.tls.crypto.TlsCryptoException", "bad_certificate(42)")); |
| 191 | + } |
| 192 | + |
| 193 | + /** |
| 194 | + * Subclasses of known BC types must still match — the labeler walks the superclass chain. |
| 195 | + */ |
| 196 | + @Test |
| 197 | + void bouncyCastleSubclassWithAlertShouldNotBeLabeled() throws Exception { |
| 198 | + Class<?> bcParent = new ByteBuddy() |
| 199 | + .subclass(Exception.class) |
| 200 | + .name("org.bouncycastle.tls.TlsFatalAlert") |
| 201 | + .make() |
| 202 | + .load(BackpressureErrorLabelerTest.class.getClassLoader()) |
| 203 | + .getLoaded(); |
| 204 | + Class<?> bcSubclass = new ByteBuddy() |
| 205 | + .subclass(bcParent) |
| 206 | + .name("com.example.CustomBcSubclass") |
| 207 | + .make() |
| 208 | + .load(bcParent.getClassLoader()) |
| 209 | + .getLoaded(); |
| 210 | + Throwable cause = (Throwable) bcSubclass.getConstructor(String.class).newInstance("handshake_failure(40)"); |
| 211 | + MongoSocketException e = new MongoSocketException("tls", ADDRESS, cause); |
| 212 | + BackpressureErrorLabeler.applyLabelsIfEligible(e); |
| 213 | + assertLacksBackpressureLabels(e); |
| 214 | + } |
| 215 | + |
| 216 | + /** |
| 217 | + * BC type but the message has no recognised alert description — the alert-keyword filter |
| 218 | + * rejects it, so the labeler falls through and applies backpressure labels. |
| 219 | + */ |
| 220 | + @Test |
| 221 | + void bouncyCastleTypeWithoutAlertKeywordShouldBeLabeled() throws Exception { |
| 222 | + Throwable bcCause = newBouncyCastleStub("org.bouncycastle.tls.TlsFatalAlert", "something unrelated"); |
| 223 | + MongoSocketException e = new MongoSocketException("tls", ADDRESS, bcCause); |
| 224 | + BackpressureErrorLabeler.applyLabelsIfEligible(e); |
| 225 | + assertHasBackpressureLabels(e); |
| 226 | + } |
| 227 | + |
| 228 | + private static Throwable newBouncyCastleStub(final String fqcn, final String message) throws Exception { |
| 229 | + Class<?> cls = new ByteBuddy() |
| 230 | + .subclass(Exception.class) |
| 231 | + .name(fqcn) |
| 232 | + .make() |
| 233 | + .load(BackpressureErrorLabelerTest.class.getClassLoader()) |
| 234 | + .getLoaded(); |
| 235 | + return (Throwable) cls.getConstructor(String.class).newInstance(message); |
| 236 | + } |
| 237 | + |
169 | 238 | private static <T extends Throwable> Named<T> named(final T e) { |
170 | 239 | return Named.of(e.getClass().getSimpleName(), e); |
171 | 240 | } |
|
0 commit comments