@@ -924,7 +924,7 @@ fn string_to_protocol(s: &str) -> Result<native_tls::Protocol, String> {
924924 "TLSV1.1" | "TLS1.1" => Ok ( native_tls:: Protocol :: Tlsv11 ) ,
925925 "TLSV1.2" | "TLS1.2" => Ok ( native_tls:: Protocol :: Tlsv12 ) ,
926926 "TLSV1.3" | "TLS1.3" => {
927- Err ( "TLS 1.3 is not supported by this connector build" . to_string ( ) )
927+ Err ( "TLS 1.3 is not yet supported in this build (native-tls version constraint) " . to_string ( ) )
928928 }
929929 _ => Err ( format ! (
930930 "Unsupported TLS version: '{}'. Supported versions are: TLSv1.0, TLSv1.1, TLSv1.2" ,
@@ -1090,7 +1090,126 @@ mod tests {
10901090 #[ test]
10911091 fn rejects_tls13_with_neutral_error_message ( ) {
10921092 let err = string_to_protocol ( "TLS1.3" ) . unwrap_err ( ) ;
1093- assert_eq ! ( err, "TLS 1.3 is not supported by this connector build" ) ;
1093+ assert_eq ! ( err, "TLS 1.3 is not yet supported in this build (native-tls version constraint)" ) ;
1094+ }
1095+
1096+ // --- TLS version bound validation (unit tests via get_connector) ---
1097+
1098+ /// Build a PostgresNativeUrl with the given tls_min_version / tls_max_version query params.
1099+ fn make_tls_with_bounds ( min : Option < & str > , max : Option < & str > ) -> PostgresNativeUrl {
1100+ let mut url = Url :: parse ( "postgresql://localhost:5432/test" ) . unwrap ( ) ;
1101+ {
1102+ let mut pairs = url. query_pairs_mut ( ) ;
1103+ if let Some ( v) = min {
1104+ pairs. append_pair ( "tls_min_version" , v) ;
1105+ }
1106+ if let Some ( v) = max {
1107+ pairs. append_pair ( "tls_max_version" , v) ;
1108+ }
1109+ }
1110+ PostgresNativeUrl :: new ( url) . unwrap ( )
1111+ }
1112+
1113+ /// Helper: call get_connector and return the error message string if it fails.
1114+ async fn get_connector_err ( url : PostgresNativeUrl ) -> String {
1115+ let mgr = MakeTlsConnectorManager :: new ( url) ;
1116+ match mgr. get_connector ( ) . await {
1117+ Ok ( _) => panic ! ( "expected get_connector to fail" ) ,
1118+ Err ( e) => e. to_string ( ) ,
1119+ }
1120+ }
1121+
1122+ #[ tokio:: test]
1123+ async fn tls_min_version_greater_than_max_is_rejected ( ) {
1124+ let url = make_tls_with_bounds ( Some ( "TLSv1.2" ) , Some ( "TLSv1.1" ) ) ;
1125+ let err = get_connector_err ( url) . await ;
1126+ assert ! (
1127+ err. contains( "tls_min_version cannot be greater than tls_max_version" ) ,
1128+ "unexpected error: {err:?}"
1129+ ) ;
1130+ }
1131+
1132+ #[ tokio:: test]
1133+ async fn tls_invalid_min_version_is_rejected ( ) {
1134+ let url = make_tls_with_bounds ( Some ( "TLSv9.9" ) , None ) ;
1135+ let err = get_connector_err ( url) . await ;
1136+ assert ! (
1137+ err. contains( "Invalid tls_min_version" ) ,
1138+ "unexpected error: {err:?}"
1139+ ) ;
1140+ }
1141+
1142+ #[ tokio:: test]
1143+ async fn tls_invalid_max_version_is_rejected ( ) {
1144+ let url = make_tls_with_bounds ( None , Some ( "TLSv9.9" ) ) ;
1145+ let err = get_connector_err ( url) . await ;
1146+ assert ! (
1147+ err. contains( "Invalid tls_max_version" ) ,
1148+ "unexpected error: {err:?}"
1149+ ) ;
1150+ }
1151+
1152+ #[ tokio:: test]
1153+ async fn tls13_in_min_version_is_rejected ( ) {
1154+ let url = make_tls_with_bounds ( Some ( "TLSv1.3" ) , None ) ;
1155+ let err = get_connector_err ( url) . await ;
1156+ assert ! (
1157+ err. contains( "Invalid tls_min_version" ) &&
1158+ err. contains( "TLS 1.3 is not yet supported in this build" ) ,
1159+ "unexpected error: {err:?}"
1160+ ) ;
1161+ }
1162+
1163+ #[ tokio:: test]
1164+ async fn tls13_in_max_version_is_rejected ( ) {
1165+ let url = make_tls_with_bounds ( None , Some ( "TLSv1.3" ) ) ;
1166+ let err = get_connector_err ( url) . await ;
1167+ assert ! (
1168+ err. contains( "Invalid tls_max_version" ) &&
1169+ err. contains( "TLS 1.3 is not yet supported in this build" ) ,
1170+ "unexpected error: {err:?}"
1171+ ) ;
1172+ }
1173+
1174+ #[ tokio:: test]
1175+ async fn tls_valid_bounds_pass_validation ( ) {
1176+ let url = make_tls_with_bounds ( Some ( "TLSv1.0" ) , Some ( "TLSv1.2" ) ) ;
1177+ let err = get_connector_err ( url) . await ;
1178+ // Valid bounds pass the validation layer; the call fails later at
1179+ // tls_builder.build() because there is no real TLS backend in unit tests.
1180+ assert ! (
1181+ !err. contains( "tls_min_version" )
1182+ && !err. contains( "tls_max_version" )
1183+ && !err. contains( "Invalid TLS configuration" ) ,
1184+ "unexpected validation error: {err}"
1185+ ) ;
1186+ }
1187+
1188+ #[ tokio:: test]
1189+ #[ cfg( target_os = "macos" ) ]
1190+ async fn macos_tls12_floor_enforced ( ) {
1191+ // On macOS, setting tls_min_version=TLSv1.0 should still result in
1192+ // TLSv1.2 as the effective minimum. Setting tls_max_version=TLSv1.0
1193+ // should then fail because the floor (1.2) > max (1.0).
1194+ let url = make_tls_with_bounds ( Some ( "TLSv1.0" ) , Some ( "TLSv1.0" ) ) ;
1195+ let err = get_connector_err ( url) . await ;
1196+ assert ! (
1197+ err. contains( "tls_max_version" ) && err. contains( "effective minimum" ) ,
1198+ "unexpected error: {err:?}"
1199+ ) ;
1200+ }
1201+
1202+ #[ tokio:: test]
1203+ #[ cfg( not( target_os = "macos" ) ) ]
1204+ async fn non_macos_default_min_is_tls12 ( ) {
1205+ // On non-macOS, the default minimum is TLSv1.2. Setting
1206+ // tls_max_version=TLSv1.0 should fail because effective min (1.2) > max (1.0).
1207+ let url = make_tls_with_bounds ( None , Some ( "TLSv1.0" ) ) ;
1208+ let err = get_connector_err ( url) . await ;
1209+ assert ! (
1210+ err. contains( "tls_min_version cannot be greater than tls_max_version" ) ,
1211+ "unexpected error: {err:?}"
1212+ ) ;
10941213 }
10951214
10961215 #[ tokio:: test]
@@ -1396,4 +1515,4 @@ mod tests {
13961515 assert ! ( !is_safe_identifier( ident) ) ;
13971516 }
13981517 }
1399- }
1518+ }
0 commit comments