15
15
"""Test clients and replica set configuration changes, using mocks."""
20
19
sys.path[0:0] = [""]
22
from pymongo.errors import ConfigurationError, ConnectionFailure
21
from pymongo.errors import ConnectionFailure, AutoReconnect
23
22
from pymongo import ReadPreference
24
from test.pymongo_mocks import MockClient, MockReplicaSetClient
27
class TestSecondaryBecomesStandalone(unittest.TestCase):
23
from test import unittest, client_context, client_knobs, MockClientTest
24
from test.pymongo_mocks import MockClient
25
from test.utils import wait_until
28
@client_context.require_connection
33
class TestSecondaryBecomesStandalone(MockClientTest):
28
34
# An administrator removes a secondary from a 3-node set and
29
35
# brings it back up as standalone, without updating the other
30
36
# members' config. Verify we don't continue using it.
34
40
members=['a:1', 'b:2', 'c:3'],
36
42
host='a:1,b:2,c:3',
44
serverSelectionTimeoutMS=100)
39
46
# MongoClient connects to primary by default.
40
self.assertEqual('a', c.host)
41
self.assertEqual(1, c.port)
47
wait_until(lambda: c.address is not None, 'connect to primary')
48
self.assertEqual(c.address, ('a', 1))
43
50
# C is brought up as a standalone.
44
51
c.mock_members.remove('c:3')
55
c.db.collection.find_one()
56
except ConfigurationError, e:
57
self.assertTrue('not a member of replica set' in str(e))
59
self.fail("MongoClient didn't raise AutoReconnect")
61
self.assertEqual(None, c.host)
62
self.assertEqual(None, c.port)
61
with self.assertRaises(AutoReconnect):
62
c.db.command('ismaster')
64
self.assertEqual(c.address, None)
64
66
def test_replica_set_client(self):
65
c = MockReplicaSetClient(
67
69
members=['a:1', 'b:2', 'c:3'],
69
71
host='a:1,b:2,c:3',
72
self.assertTrue(('b', 2) in c.secondaries)
73
self.assertTrue(('c', 3) in c.secondaries)
74
wait_until(lambda: ('b', 2) in c.secondaries,
77
wait_until(lambda: ('c', 3) in c.secondaries,
75
80
# C is brought up as a standalone.
76
81
c.mock_members.remove('c:3')
77
82
c.mock_standalones.append('c:3')
84
wait_until(lambda: set([('b', 2)]) == c.secondaries,
85
'update the list of secondaries')
80
87
self.assertEqual(('a', 1), c.primary)
81
self.assertEqual(set([('b', 2)]), c.secondaries)
84
class TestSecondaryRemoved(unittest.TestCase):
90
class TestSecondaryRemoved(MockClientTest):
85
91
# An administrator removes a secondary from a 3-node set *without*
86
92
# restarting it as standalone.
87
93
def test_replica_set_client(self):
88
c = MockReplicaSetClient(
90
96
members=['a:1', 'b:2', 'c:3'],
92
98
host='a:1,b:2,c:3',
95
self.assertTrue(('b', 2) in c.secondaries)
96
self.assertTrue(('c', 3) in c.secondaries)
101
wait_until(lambda: ('b', 2) in c.secondaries, 'discover host "b"')
102
wait_until(lambda: ('c', 3) in c.secondaries, 'discover host "c"')
99
105
c.mock_ismaster_hosts.remove('c:3')
106
wait_until(lambda: set([('b', 2)]) == c.secondaries,
107
'update list of secondaries')
102
109
self.assertEqual(('a', 1), c.primary)
103
self.assertEqual(set([('b', 2)]), c.secondaries)
106
class TestSocketError(unittest.TestCase):
112
class TestSocketError(MockClientTest):
107
113
def test_socket_error_marks_member_down(self):
108
c = MockReplicaSetClient(
110
members=['a:1', 'b:2'],
115
self.assertEqual(2, len(c._MongoReplicaSetClient__rs_state.members))
117
# b now raises socket.error.
118
c.mock_down_hosts.append('b:2')
121
c.db.collection.find_one, read_preference=ReadPreference.SECONDARY)
123
self.assertEqual(1, len(c._MongoReplicaSetClient__rs_state.members))
126
class TestSecondaryAdded(unittest.TestCase):
114
# Disable background refresh.
115
with client_knobs(heartbeat_frequency=999999):
118
members=['a:1', 'b:2'],
123
wait_until(lambda: len(c.nodes) == 2, 'discover both nodes')
125
# b now raises socket.error.
126
c.mock_down_hosts.append('b:2')
129
c.db.collection.with_options(
130
read_preference=ReadPreference.SECONDARY).find_one)
132
self.assertEqual(1, len(c.nodes))
135
class TestSecondaryAdded(MockClientTest):
127
136
def test_client(self):
144
wait_until(lambda: len(c.nodes) == 2, 'discover both nodes')
135
146
# MongoClient connects to primary by default.
136
self.assertEqual('a', c.host)
137
self.assertEqual(1, c.port)
147
self.assertEqual(c.address, ('a', 1))
138
148
self.assertEqual(set([('a', 1), ('b', 2)]), c.nodes)
141
151
c.mock_members.append('c:3')
142
152
c.mock_ismaster_hosts.append('c:3')
145
c.db.collection.find_one()
147
self.assertEqual('a', c.host)
148
self.assertEqual(1, c.port)
149
self.assertEqual(set([('a', 1), ('b', 2), ('c', 3)]), c.nodes)
155
c.db.command('ismaster')
157
self.assertEqual(c.address, ('a', 1))
159
wait_until(lambda: set([('a', 1), ('b', 2), ('c', 3)]) == c.nodes,
160
'reconnect to both secondaries')
151
162
def test_replica_set_client(self):
152
c = MockReplicaSetClient(
154
165
members=['a:1', 'b:2'],
159
self.assertEqual(('a', 1), c.primary)
160
self.assertEqual(set([('b', 2)]), c.secondaries)
170
wait_until(lambda: ('a', 1) == c.primary, 'discover the primary')
171
wait_until(lambda: set([('b', 2)]) == c.secondaries,
172
'discover the secondary')
163
175
c.mock_members.append('c:3')
164
176
c.mock_ismaster_hosts.append('c:3')
178
wait_until(lambda: set([('b', 2), ('c', 3)]) == c.secondaries,
179
'discover the new secondary')
167
181
self.assertEqual(('a', 1), c.primary)
168
self.assertEqual(set([('b', 2), ('c', 3)]), c.secondaries)
171
184
if __name__ == "__main__":