2323import com .mongodb .MongoSocketOpenException ;
2424import com .mongodb .MongoSocketReadTimeoutException ;
2525import com .mongodb .ServerAddress ;
26- import org .bson .BsonDocument ;
27- import org .junit .jupiter .api .Test ;
26+ import org .junit .jupiter .api .Named ;
27+ import org .junit .jupiter .params .ParameterizedTest ;
28+ import org .junit .jupiter .params .provider .MethodSource ;
29+ import org .junit .jupiter .params .provider .ValueSource ;
2830
2931import javax .net .ssl .SSLHandshakeException ;
3032import javax .net .ssl .SSLPeerUnverifiedException ;
33+ import javax .net .ssl .SSLProtocolException ;
34+ import java .io .EOFException ;
3135import java .io .IOException ;
3236import java .net .UnknownHostException ;
37+ import java .security .cert .CertPathBuilderException ;
38+ import java .security .cert .CertPathValidatorException ;
3339import java .security .cert .CertificateException ;
40+ import java .util .stream .Stream ;
3441
3542import static org .junit .jupiter .api .Assertions .assertFalse ;
3643import static org .junit .jupiter .api .Assertions .assertTrue ;
@@ -39,105 +46,125 @@ class BackpressureErrorLabelerTest {
3946
4047 private static final ServerAddress ADDRESS = new ServerAddress ();
4148
42- @ Test
43- void mongoSocketExceptionIsLabeled () {
44- MongoSocketException e = new MongoSocketException ("boom" , ADDRESS );
45- BackpressureErrorLabeler .applyLabelsIfEligible (e );
46- assertHasBackpressureLabels (e );
49+ static Stream <Named <MongoSocketException >> networkErrorShouldBeLabeled () {
50+ return Stream .of (
51+ named (new MongoSocketException ("boom" , ADDRESS )),
52+ named (new MongoSocketReadTimeoutException ("slow" , ADDRESS , new IOException ("read timed out" ))),
53+ named (new MongoSocketOpenException ("open failed" , ADDRESS , new IOException ("connection refused" ))),
54+ // FIN-during-handshake: server closed the TCP connection while the client was mid-handshake
55+ // (no protocol-level alert). I/O failure → must be labeled per CMAP "I/O error during TLS handshake".
56+ named (new MongoSocketException ("tls" , ADDRESS , initCause (
57+ new SSLHandshakeException ("Remote host terminated the handshake" ),
58+ new EOFException ("SSL peer shut down incorrectly" ))))
59+ );
4760 }
4861
49- @ Test
50- void mongoSocketReadTimeoutIsLabeled () {
51- MongoSocketReadTimeoutException e = new MongoSocketReadTimeoutException ( "slow" , ADDRESS , new IOException ( "read timed out" ));
62+ @ ParameterizedTest
63+ @ MethodSource
64+ void networkErrorShouldBeLabeled ( final MongoSocketException e ) {
5265 BackpressureErrorLabeler .applyLabelsIfEligible (e );
5366 assertHasBackpressureLabels (e );
5467 }
5568
56- @ Test
57- void mongoSocketOpenExceptionIsLabeled () {
58- MongoSocketOpenException e = new MongoSocketOpenException ( "open failed" , ADDRESS , new IOException ( "connection refused " ));
59- BackpressureErrorLabeler . applyLabelsIfEligible ( e );
60- assertHasBackpressureLabels ( e );
69+ static Stream < Named < MongoSocketException >> dnsFailureShouldNotBeLabeled () {
70+ return Stream . of (
71+ named ( new MongoSocketException ( "lookup failed" , ADDRESS , new UnknownHostException ( "nope " ))),
72+ named ( new MongoSocketException ( "wrap" , ADDRESS , new IOException ( "wrap" , new UnknownHostException ( "nope" ))))
73+ );
6174 }
6275
63- @ Test
64- void dnsFailureIsNotLabeled () {
65- MongoSocketException e = new MongoSocketException ( "lookup failed" , ADDRESS , new UnknownHostException ( "nope" ));
76+ @ ParameterizedTest
77+ @ MethodSource
78+ void dnsFailureShouldNotBeLabeled ( final MongoSocketException e ) {
6679 BackpressureErrorLabeler .applyLabelsIfEligible (e );
6780 assertLacksBackpressureLabels (e );
6881 }
6982
70- @ Test
71- void dnsFailureDeepInCauseChainIsNotLabeled () {
72- Throwable dns = new UnknownHostException ("nope" );
73- IOException wrap1 = new IOException ("wrap1" , dns );
74- MongoSocketException e = new MongoSocketException ("wrap2" , ADDRESS , wrap1 );
75- BackpressureErrorLabeler .applyLabelsIfEligible (e );
76- assertLacksBackpressureLabels (e );
83+ static Stream <Named <Throwable >> localTlsConfigErrorShouldNotBeLabeled () {
84+ return Stream .of (
85+ named (new CertificateException ("bad cert" )),
86+ named (new CertPathBuilderException ("path build failed" )),
87+ named (new CertPathValidatorException ("validation failed" )),
88+ named (new SSLPeerUnverifiedException ("peer not verified" )),
89+ named (new SSLProtocolException ("protocol error" )),
90+ named (new SSLHandshakeException ("PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: "
91+ + "unable to find valid certification path to requested target" ))
92+ );
7793 }
7894
79- @ Test
80- void tlsCertificateErrorIsNotLabeled () {
81- CertificateException cert = new CertificateException ("bad cert" );
82- MongoSocketException e = new MongoSocketException ("tls" , ADDRESS , cert );
83- BackpressureErrorLabeler .applyLabelsIfEligible (e );
84- assertLacksBackpressureLabels (e );
85- }
86-
87- @ Test
88- void tlsPeerUnverifiedIsNotLabeled () {
89- SSLPeerUnverifiedException tls = new SSLPeerUnverifiedException ("peer not verified" );
90- MongoSocketException e = new MongoSocketException ("tls" , ADDRESS , tls );
95+ @ ParameterizedTest
96+ @ MethodSource
97+ void localTlsConfigErrorShouldNotBeLabeled (final Throwable cause ) {
98+ MongoSocketException e = new MongoSocketException ("tls" , ADDRESS , cause );
9199 BackpressureErrorLabeler .applyLabelsIfEligible (e );
92100 assertLacksBackpressureLabels (e );
93101 }
94102
95- @ Test
96- void sslHandshakeWithCertificateMessageIsNotLabeled () {
97- SSLHandshakeException tls = new SSLHandshakeException ("PKIX path building failed: certificate chain invalid" );
103+ /**
104+ * "Received fatal alert: <description>" means the peer actively answered with a TLS protocol
105+ * error — definitively a config/protocol issue, not an overload signal. Covers all 25
106+ * handshake-only RFC alert descriptions emitted by OpenJDK's JSSE provider.
107+ */
108+ @ ParameterizedTest (name = "Received fatal alert: {0}" )
109+ @ ValueSource (strings = {
110+ "handshake_failure" ,
111+ "no_certificate" ,
112+ "bad_certificate" ,
113+ "unsupported_certificate" ,
114+ "certificate_revoked" ,
115+ "certificate_expired" ,
116+ "certificate_unknown" ,
117+ "illegal_parameter" ,
118+ "unknown_ca" ,
119+ "access_denied" ,
120+ "decode_error" ,
121+ "decrypt_error" ,
122+ "export_restriction" ,
123+ "protocol_version" ,
124+ "insufficient_security" ,
125+ "no_renegotiation" ,
126+ "missing_extension" ,
127+ "unsupported_extension" ,
128+ "certificate_unobtainable" ,
129+ "unrecognized_name" ,
130+ "bad_certificate_status_response" ,
131+ "bad_certificate_hash_value" ,
132+ "unknown_psk_identity" ,
133+ "certificate_required" ,
134+ "no_application_protocol"
135+ })
136+ void receivedTlsAlertShouldNotBeLabeled (final String alertDescription ) {
137+ SSLHandshakeException tls = new SSLHandshakeException ("Received fatal alert: " + alertDescription );
98138 MongoSocketException e = new MongoSocketException ("tls" , ADDRESS , tls );
99139 BackpressureErrorLabeler .applyLabelsIfEligible (e );
100140 assertLacksBackpressureLabels (e );
101141 }
102142
103- @ Test
104- void sslHandshakeWithoutConfigKeywordIsLabeled () {
105- SSLHandshakeException tls = new SSLHandshakeException ("remote host closed connection during handshake" );
106- MongoSocketException e = new MongoSocketException ("tls" , ADDRESS , tls );
107- BackpressureErrorLabeler .applyLabelsIfEligible (e );
108- assertHasBackpressureLabels (e );
143+ static Stream <Named <Throwable >> nonSocketErrorShouldNotBeLabeled () {
144+ return Stream .of (
145+ named (new MongoSecurityException (
146+ MongoCredential .createCredential ("user" , "db" , "pwd" .toCharArray ()), "auth failed" )),
147+ named (new MongoException (42 , "some command error" )),
148+ named (new IOException ("raw" ))
149+ );
109150 }
110151
111- @ Test
112- void authErrorIsNotLabeled () {
113- MongoCredential credential = MongoCredential .createCredential ("user" , "db" , "pwd" .toCharArray ());
114- MongoSecurityException e = new MongoSecurityException (credential , "auth failed" );
152+ @ ParameterizedTest
153+ @ MethodSource
154+ void nonSocketErrorShouldNotBeLabeled (final Throwable e ) {
115155 BackpressureErrorLabeler .applyLabelsIfEligible (e );
116- assertLacksBackpressureLabels (e );
156+ if (e instanceof MongoException ) {
157+ assertLacksBackpressureLabels ((MongoException ) e );
158+ }
117159 }
118160
119- @ Test
120- void commandExceptionIsNotLabeled () {
121- MongoException e = new MongoException (42 , "some command error" );
122- BackpressureErrorLabeler .applyLabelsIfEligible (e );
123- assertLacksBackpressureLabels (e );
161+ private static <T extends Throwable > Named <T > named (final T e ) {
162+ return Named .of (e .getClass ().getSimpleName (), e );
124163 }
125164
126- @ Test
127- void nonMongoThrowableIsNotLabeled () {
128- IOException e = new IOException ("raw" );
129- BackpressureErrorLabeler .applyLabelsIfEligible (e );
130- }
131-
132- @ Test
133- void sdamIssueTlsCheckDelegatesToHelper () {
134- MongoSocketException tlsException = new MongoSocketException ("tls" , ADDRESS , new CertificateException ("bad cert" ));
135- assertTrue (BackpressureErrorLabeler .isTlsConfigurationError (tlsException ));
136-
137- MongoSocketException plainException = new MongoSocketException ("plain" , ADDRESS );
138- assertFalse (BackpressureErrorLabeler .isTlsConfigurationError (plainException ));
139-
140- assertFalse (BackpressureErrorLabeler .isTlsConfigurationError (new MongoException (0 , "not socket" , new BsonDocument ())));
165+ private static <T extends Throwable > T initCause (final T exception , final Throwable cause ) {
166+ exception .initCause (cause );
167+ return exception ;
141168 }
142169
143170 private static void assertHasBackpressureLabels (final MongoException e ) {
0 commit comments