1313# limitations under the License.
1414from __future__ import annotations
1515
16+ import re
1617import unittest
1718
1819import pytest
1920
2021try :
21- from mockupdb import Command , MockupDB , OpMsg , OpMsgReply , OpQuery , OpReply , absent , go
22+ from mockupdb import Command , MockupDB , OpMsg , OpMsgReply , OpReply , absent , go
2223
2324 _HAVE_MOCKUPDB = True
2425except ImportError :
2829from bson .objectid import ObjectId
2930from pymongo import MongoClient , has_c
3031from pymongo import version as pymongo_version
31- from pymongo .common import MIN_SUPPORTED_WIRE_VERSION
32- from pymongo .errors import OperationFailure
32+ from pymongo .common import MIN_SUPPORTED_SERVER_VERSION , MIN_SUPPORTED_WIRE_VERSION
33+ from pymongo .errors import ConfigurationError , OperationFailure , ServerSelectionTimeoutError
3334from pymongo .server_api import ServerApi , ServerApiVersion
3435
3536pytestmark = pytest .mark .mockupdb
@@ -53,7 +54,7 @@ def _check_handshake_data(request):
5354
5455class TestHandshake (unittest .TestCase ):
5556 def hello_with_option_helper (self , protocol , ** kwargs ):
56- hello = "ismaster " if isinstance ( protocol (), OpQuery ) else "hello "
57+ hello = "hello " if ( "apiVersion" in kwargs or "loadBalanced" in kwargs ) else "ismaster "
5758 # `db.command("hello"|"ismaster")` commands are the same for primaries and
5859 # secondaries, so we only need one server.
5960 primary = MockupDB ()
@@ -165,7 +166,7 @@ def test_client_handshake_data(self):
165166 future = go (client .db .command , "whatever" )
166167
167168 for request in primary :
168- if request .matches (Command ( "ismaster" ) ):
169+ if request .matches ("ismaster" ):
169170 if request .client_port == heartbeat .client_port :
170171 # This is the monitor again, keep going.
171172 request .ok (primary_response )
@@ -242,11 +243,10 @@ def test_handshake_versioned_api(self):
242243 self .hello_with_option_helper (Command , apiVersion = "1" )
243244
244245 def test_handshake_not_either (self ):
245- # If we don't specify either option then it should be using
246- # OP_QUERY for the initial step of the handshake.
247- self .hello_with_option_helper (Command )
246+ # As of PYTHON-5713, always use OP_MSG for the initial handshake.
247+ self .hello_with_option_helper (OpMsg )
248248 with self .assertRaisesRegex (AssertionError , "does not match" ):
249- self .hello_with_option_helper (OpMsg )
249+ self .hello_with_option_helper (Command )
250250
251251 def test_handshake_max_wire (self ):
252252 server = MockupDB ()
@@ -292,6 +292,26 @@ def responder(request):
292292 self .found_auth_msg , "Could not find authentication command with correct protocol"
293293 )
294294
295+ def test_handshake_op_msg_not_supported (self ):
296+ # If a server responds with maxWireVersion < 6 (no OP_MSG support),
297+ # the wire version error must surface to the user.
298+ server = MockupDB ()
299+ server .autoresponds ("ismaster" , ok = 1 , ismaster = True , minWireVersion = 0 , maxWireVersion = 5 )
300+ server .run ()
301+ self .addCleanup (server .stop )
302+
303+ client = MongoClient (server .uri , serverSelectionTimeoutMS = 500 )
304+ self .addCleanup (client .close )
305+
306+ # The ConfigurationError from _hello() is stored as the server's error
307+ # and surfaces inside ServerSelectionTimeoutError.
308+ expected = re .escape (
309+ "reports wire version 5, but this version of PyMongo requires at least "
310+ "%d (MongoDB %s)." % (MIN_SUPPORTED_WIRE_VERSION , MIN_SUPPORTED_SERVER_VERSION )
311+ )
312+ with self .assertRaisesRegex (ServerSelectionTimeoutError , expected ):
313+ client .db .command ("ping" )
314+
295315
296316if __name__ == "__main__" :
297317 unittest .main ()
0 commit comments