Skip to content

Commit 8fc6dc3

Browse files
authored
PYTHON-1489 Merge ajdavis/pymongo-mockup-tests into pymongo (#787)
1 parent 99aab1b commit 8fc6dc3

24 files changed

+2577
-9
lines changed

.evergreen/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ functions:
346346
script: |
347347
set -o xtrace
348348
${PREPARE_SHELL}
349-
PYTHON_BINARY=${PYTHON_BINARY} PROJECT_DIRECTORY=${PROJECT_DIRECTORY} bash ${PROJECT_DIRECTORY}/.evergreen/run-mockupdb-tests.sh
349+
PYTHON_BINARY=${PYTHON_BINARY} bash ${PROJECT_DIRECTORY}/.evergreen/run-mockupdb-tests.sh
350350
351351
"run doctests":
352352
- command: shell.exec

.evergreen/run-mockupdb-tests.sh

100644100755
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
#!/bin/bash
2-
2+
# Must be run from pymongo repo root
33
set -o xtrace
44
set -o errexit
55

66
. .evergreen/utils.sh
77

88
${PYTHON_BINARY} setup.py clean
9-
cd ..
109

1110
createvirtualenv ${PYTHON_BINARY} mockuptests
12-
trap "deactivatei, rm -rf mockuptests" EXIT HUP
11+
trap "deactivate, rm -rf mockuptests" EXIT HUP
1312

1413
# Install PyMongo from git clone so mockup-tests don't
1514
# download it from pypi.
16-
python -m pip install ${PROJECT_DIRECTORY}
17-
18-
git clone https://github.com/ajdavis/pymongo-mockup-tests.git
19-
cd pymongo-mockup-tests
20-
python setup.py test
15+
python -m pip install .
16+
python -m pip install --upgrade 'https://github.com/ajdavis/mongo-mockup-db/archive/master.zip'
17+
cd ./test/mockupdb
18+
python -m unittest discover -v

test/mockupdb/operations.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Copyright 2015 MongoDB, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"),;
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from collections import namedtuple
16+
17+
from mockupdb import *
18+
from pymongo import ReadPreference
19+
20+
__all__ = ['operations', 'upgrades']
21+
22+
23+
Operation = namedtuple(
24+
'operation',
25+
['name', 'function', 'reply', 'op_type', 'not_master'])
26+
"""Client operations on MongoDB.
27+
28+
Each has a human-readable name, a function that actually executes a test, and
29+
a type that maps to one of the types in the Server Selection Spec:
30+
'may-use-secondary', 'must-use-primary', etc.
31+
32+
The special type 'always-use-secondary' applies to an operation with an explicit
33+
read mode, like the operation "command('c', read_preference=SECONDARY)".
34+
35+
The not-master response is how a secondary responds to a must-use-primary op,
36+
or how a recovering member responds to a may-use-secondary op.
37+
38+
Example uses:
39+
40+
We can use "find_one" to validate that the SlaveOk bit is set when querying a
41+
standalone, even with mode PRIMARY, but that it isn't set when sent to a mongos
42+
with mode PRIMARY. Or it can validate that "$readPreference" is included in
43+
mongos queries except with mode PRIMARY or SECONDARY_PREFERRED (PYTHON-865).
44+
45+
We can use "options_old" and "options_new" to test that the driver queries an
46+
old server's system.namespaces collection, but uses the listCollections command
47+
on a new server (PYTHON-857).
48+
49+
"secondary command" is good to test that the client can direct reads to
50+
secondaries in a replica set, or select a mongos for secondary reads in a
51+
sharded cluster (PYTHON-868).
52+
"""
53+
54+
not_master_reply_to_query = OpReply(
55+
{'$err': 'not master'},
56+
flags=REPLY_FLAGS['QueryFailure'])
57+
58+
not_master_reply_to_command = OpReply(ok=0, errmsg='not master')
59+
60+
operations = [
61+
Operation(
62+
'find_one',
63+
lambda client: client.db.collection.find_one(),
64+
reply={'cursor': {'id': 0, 'firstBatch': []}},
65+
op_type='may-use-secondary',
66+
not_master=not_master_reply_to_query),
67+
Operation(
68+
'count',
69+
lambda client: client.db.collection.count_documents({}),
70+
reply={'n': 1},
71+
op_type='may-use-secondary',
72+
not_master=not_master_reply_to_command),
73+
Operation(
74+
'aggregate',
75+
lambda client: client.db.collection.aggregate([]),
76+
reply={'cursor': {'id': 0, 'firstBatch': []}},
77+
op_type='may-use-secondary',
78+
not_master=not_master_reply_to_command),
79+
Operation(
80+
'mapreduce',
81+
lambda client: client.db.collection.map_reduce(
82+
'function() {}', 'function() {}'),
83+
reply={'result': {'db': 'db', 'collection': 'out_collection'}},
84+
op_type='must-use-primary',
85+
not_master=not_master_reply_to_command),
86+
Operation(
87+
'inline_mapreduce',
88+
lambda client: client.db.collection.inline_map_reduce(
89+
'function() {}', 'function() {}', {'out': {'inline': 1}}),
90+
reply={'results': []},
91+
op_type='may-use-secondary',
92+
not_master=not_master_reply_to_command),
93+
Operation(
94+
'options',
95+
lambda client: client.db.collection.options(),
96+
reply={'cursor': {'id': 0, 'firstBatch': []}},
97+
op_type='must-use-primary',
98+
not_master=not_master_reply_to_command),
99+
Operation(
100+
'command',
101+
lambda client: client.db.command('foo'),
102+
reply={'ok': 1},
103+
op_type='must-use-primary', # Ignores client's read preference.
104+
not_master=not_master_reply_to_command),
105+
Operation(
106+
'secondary command',
107+
lambda client:
108+
client.db.command('foo', read_preference=ReadPreference.SECONDARY),
109+
reply={'ok': 1},
110+
op_type='always-use-secondary',
111+
not_master=OpReply(ok=0, errmsg='node is recovering')),
112+
Operation(
113+
'listCollections',
114+
lambda client: client.db.collection_names(),
115+
reply={'cursor': {'id': 0, 'firstBatch': []}},
116+
op_type='must-use-primary',
117+
not_master=not_master_reply_to_command),
118+
Operation(
119+
'listIndexes',
120+
lambda client: client.db.collection.index_information(),
121+
reply={'cursor': {'id': 0, 'firstBatch': []}},
122+
op_type='must-use-primary',
123+
not_master=not_master_reply_to_command),
124+
]
125+
126+
127+
_ops_by_name = dict([(op.name, op) for op in operations])
128+
129+
Upgrade = namedtuple('Upgrade',
130+
['name', 'function', 'old', 'new', 'wire_version'])
131+
132+
upgrades = [
133+
Upgrade('index_information',
134+
lambda client: client.db.collection.index_information(),
135+
old=OpQuery(namespace='db.system.indexes'),
136+
new=Command('listIndexes', 'collection', namespace='db'),
137+
wire_version=3),
138+
Upgrade('collection_names',
139+
lambda client: client.db.collection_names(),
140+
old=Command('aggregate', 'system.namespaces', namespace='db'),
141+
new=Command('listCollections', namespace='db'),
142+
wire_version=3),
143+
Upgrade('options',
144+
lambda client: client.db.collection.options(),
145+
old=Command('aggregate', 'system.namespaces', namespace='db'),
146+
new=Command('listCollections', namespace='db'),
147+
wire_version=3),
148+
]
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Copyright 2015 MongoDB, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from mockupdb import MockupDB
16+
from pymongo import MongoClient
17+
from pymongo.errors import ServerSelectionTimeoutError
18+
19+
import unittest
20+
21+
22+
class TestAuthRecoveringMember(unittest.TestCase):
23+
def test_auth_recovering_member(self):
24+
# Test that we don't attempt auth against a recovering RS member.
25+
server = MockupDB()
26+
server.autoresponds('ismaster', {
27+
'minWireVersion': 2,
28+
'maxWireVersion': 6,
29+
'ismaster': False,
30+
'secondary': False,
31+
'setName': 'rs'})
32+
33+
server.run()
34+
self.addCleanup(server.stop)
35+
36+
client = MongoClient(server.uri,
37+
replicaSet='rs',
38+
serverSelectionTimeoutMS=100,
39+
socketTimeoutMS=100)
40+
41+
self.addCleanup(client.close)
42+
43+
# Should see there's no primary or secondary and raise selection timeout
44+
# error. If it raises AutoReconnect we know it actually tried the
45+
# server, and that's wrong.
46+
with self.assertRaises(ServerSelectionTimeoutError):
47+
client.db.authenticate('user', 'password')
48+
49+
if __name__ == '__main__':
50+
unittest.main()

0 commit comments

Comments
 (0)