testClientHandler.py 44 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#
# Copyright (C) 2009  Nexedi SA
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import unittest
19
from neo import logging
20
import threading
21
from mock import Mock, ReturnValues
22
from neo.tests import NeoTestBase
23
from neo import protocol
Grégory Wisniewski's avatar
Grégory Wisniewski committed
24
from neo.pt import PartitionTable
25 26
from neo.protocol import UnexpectedPacketError, INVALID_UUID
from neo.protocol import \
27
     INVALID_PTID, STORAGE_NODE_TYPE, CLIENT_NODE_TYPE, MASTER_NODE_TYPE, \
28 29
     RUNNING_STATE, BROKEN_STATE, TEMPORARILY_DOWN_STATE, \
     UP_TO_DATE_STATE, FEEDING_STATE, DISCARDED_STATE
30 31 32 33
from neo.client.handlers import BaseHandler
from neo.client.handlers.master import PrimaryBootstrapHandler
from neo.client.handlers.master import PrimaryNotificationsHandler, PrimaryAnswersHandler
from neo.client.handlers.storage import StorageBootstrapHandler, StorageAnswersHandler
34 35 36

MARKER = []

37
class ClientHandlerTests(NeoTestBase):
38 39 40

    def setUp(self):
        # Silence all log messages
41
        pass
42 43 44

    def getConnection(self, uuid=None, port=10010, next_id=None, ip='127.0.0.1'):
        if uuid is None:
Grégory Wisniewski's avatar
Grégory Wisniewski committed
45
            uuid = self.getNewUUID()
46
        return Mock({'_addPacket': None,
47 48 49 50 51 52 53 54 55
                     'getUUID': uuid,
                     'getAddress': (ip, port),
                     'getNextId': next_id,
                     'lock': None,
                     'unlock': None})

    def getDispatcher(self, queue=None):
      return Mock({'getQueue': queue, 'connectToPrimaryMasterNode': None})

56 57 58 59 60 61 62
    def buildHandler(self, handler_class, app, dispatcher):
        # some handlers do not accept the second argument
        try:
            return handler_class(app, dispatcher)
        except TypeError:
            return handler_class(app)

63 64 65 66 67 68
    def test_ping(self):
        """
        Simplest test: check that a PING packet is answered by a PONG
        packet.
        """
        dispatcher = self.getDispatcher()
69
        client_handler = BaseHandler(None, dispatcher)
70
        conn = self.getConnection()
71
        client_handler.packetReceived(conn, protocol.ping())
Grégory Wisniewski's avatar
Grégory Wisniewski committed
72
        self.checkAnswerPacket(conn, protocol.PONG)
73 74 75 76

    def _testInitialMasterWithMethod(self, method):
        class App:
            primary_master_node = None
77
            trying_master_node = 1
78
        app = App()
79
        method(self.getDispatcher(), app, PrimaryBootstrapHandler)
80
        self.assertEqual(app.primary_master_node, None)
81

82
    def _testMasterWithMethod(self, method, handler_class):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
83
        uuid = self.getNewUUID()
84 85
        app = Mock({'connectToPrimaryMasterNode': None})
        app.primary_master_node = Mock({'getUUID': uuid})
86
        app.master_conn = Mock({'close': None, 'getUUID': uuid, 'getAddress': ('127.0.0.1', 10000)})
87
        dispatcher = self.getDispatcher()
88
        method(dispatcher, app, handler_class, uuid=uuid, conn=app.master_conn)
89 90 91 92 93
        # XXX: should connection closure be tested ? It's not implemented in all cases
        #self.assertEquals(len(App.master_conn.mockGetNamedCalls('close')), 1)
        #self.assertEquals(app.master_conn, None)
        #self.assertEquals(app.primary_master_node, None)

94
    def _testStorageWithMethod(self, method, handler_class, state=TEMPORARILY_DOWN_STATE):
95 96
        storage_ip = '127.0.0.1'
        storage_port = 10011
Grégory Wisniewski's avatar
Grégory Wisniewski committed
97
        fake_storage_node_uuid = self.getNewUUID()
98
        fake_storage_node = Mock({'getUUID': fake_storage_node_uuid, 'getAddress': (storage_ip, storage_port), 'getType': STORAGE_NODE_TYPE})
99 100
        master_node_next_packet_id = 1
        class App:
Grégory Wisniewski's avatar
Grégory Wisniewski committed
101
            primary_master_node = Mock({'getUUID': self.getNewUUID()})
102
            nm = Mock({'getByAddress': fake_storage_node})
103
            cp = Mock({'removeConnection': None})
104
            master_conn = Mock({
105
                '_addPacket': None,
Grégory Wisniewski's avatar
Grégory Wisniewski committed
106
                'getUUID': self.getNewUUID(),
107 108 109 110 111
                'getAddress': ('127.0.0.1', 10010),
                'getNextId': master_node_next_packet_id,
                'lock': None,
                'unlock': None
            })
112 113 114
        app = App()
        conn = self.getConnection(port=storage_port, ip=storage_ip)
        key_1 = (id(conn), 0)
115
        queue_1 = Mock({'put': None, '__hash__': 1})
116 117 118 119 120 121 122
        # Fake another Storage connection by adding 1 to id(conn)
        key_2 = (id(conn) + 1, 0)
        queue_2 = Mock({'put': None, '__hash__': 2})
        class Dispatcher:
            message_table = {key_1: queue_1,
                             key_2: queue_2}
        dispatcher = Dispatcher()
123
        method(dispatcher, app, handler_class, conn=conn)
124
        # The master should be notified, but this is done in app.py 
125 126 127 128 129 130 131 132 133 134 135
        # Check that failed connection got removed from connection pool
        removeConnection_call_list = app.cp.mockGetNamedCalls('removeConnection')
          # Test sanity check
        self.assertEqual(len(removeConnection_call_list), 1)
        self.assertTrue(removeConnection_call_list[0].getParam(0) is fake_storage_node)
        # Check that fake packet was put into queue_1, and none in queue_2.
        queue_1_put_call_list = queue_1.mockGetNamedCalls('put')
        self.assertEqual(len(queue_1_put_call_list), 1)
        self.assertEqual(queue_1_put_call_list[0].getParam(0), (conn, None))
        self.assertEqual(len(queue_2.mockGetNamedCalls('put')), 0)

136
    def _testConnectionFailed(self, dispatcher, app, handler_class, uuid=None, conn=None):
137
        client_handler = handler_class(app)
138 139 140 141 142 143 144 145
        if conn is None:
            conn = self.getConnection(uuid=uuid)
        client_handler.connectionFailed(conn)

    def test_initialMasterConnectionFailed(self):
        self._testInitialMasterWithMethod(self._testConnectionFailed)

    def test_storageConnectionFailed(self):
146
        self._testStorageWithMethod(self._testConnectionFailed, 
147
                StorageBootstrapHandler)
148

149
    def _testConnectionClosed(self, dispatcher, app, handler_class, uuid=None, conn=None):
150
        client_handler = self.buildHandler(handler_class, app, dispatcher)
151 152 153 154 155 156 157 158
        if conn is None:
            conn = self.getConnection(uuid=uuid)
        client_handler.connectionClosed(conn)

    def test_initialMasterConnectionClosed(self):
        self._testInitialMasterWithMethod(self._testConnectionClosed)

    def test_masterConnectionClosed(self):
159
        self._testMasterWithMethod(self._testConnectionClosed,
160
                PrimaryNotificationsHandler)
161 162

    def test_storageConnectionClosed(self):
163
        self._testStorageWithMethod(self._testConnectionClosed, 
164
                StorageBootstrapHandler)
165
        self._testStorageWithMethod(self._testConnectionClosed, 
166
                StorageAnswersHandler)
167

168
    def _testTimeoutExpired(self, dispatcher, app, handler_class, uuid=None, conn=None):
169
        client_handler = self.buildHandler(handler_class, app, dispatcher)
170 171 172 173 174 175 176 177
        if conn is None:
            conn = self.getConnection(uuid=uuid)
        client_handler.timeoutExpired(conn)

    def test_initialMasterTimeoutExpired(self):
        self._testInitialMasterWithMethod(self._testTimeoutExpired)

    def test_masterTimeoutExpired(self):
178
        self._testMasterWithMethod(self._testTimeoutExpired, PrimaryNotificationsHandler)
179 180

    def test_storageTimeoutExpired(self):
181
        self._testStorageWithMethod(self._testTimeoutExpired, 
182
                StorageAnswersHandler)
183
        self._testStorageWithMethod(self._testTimeoutExpired, 
184
                StorageBootstrapHandler)
185

186
    def _testPeerBroken(self, dispatcher, app, handler_class, uuid=None, conn=None):
187
        client_handler = self.buildHandler(handler_class, app, dispatcher)
188 189 190 191 192 193 194 195
        if conn is None:
            conn = self.getConnection(uuid=uuid)
        client_handler.peerBroken(conn)

    def test_initialMasterPeerBroken(self):
        self._testInitialMasterWithMethod(self._testPeerBroken)

    def test_masterPeerBroken(self):
196
        self._testMasterWithMethod(self._testPeerBroken, PrimaryNotificationsHandler)
197 198

    def test_storagePeerBroken(self):
199
        self._testStorageWithMethod(self._testPeerBroken,
200
                StorageBootstrapHandler, state=BROKEN_STATE)
201
        self._testStorageWithMethod(self._testPeerBroken,
202
                StorageAnswersHandler, state=BROKEN_STATE)
203 204

    def test_notReady(self):
205
        app = Mock({'setNodeNotReady': None})
206 207
        dispatcher = self.getDispatcher()
        conn = self.getConnection()
208
        client_handler = StorageBootstrapHandler(app)
209
        client_handler.handleNotReady(conn, None, None)
210
        self.assertEquals(len(app.mockGetNamedCalls('setNodeNotReady')), 1)
211 212 213

    def test_clientAcceptNodeIdentification(self):
        class App:
214
            nm = Mock({'getByAddress': None})
215 216 217 218
            storage_node = None
            pt = None
        app = App()
        dispatcher = self.getDispatcher()
219
        client_handler = PrimaryBootstrapHandler(app)
220
        conn = self.getConnection()
Grégory Wisniewski's avatar
Grégory Wisniewski committed
221
        uuid = self.getNewUUID()
222
        app.uuid = 'C' * 16
223
        client_handler.handleAcceptNodeIdentification(conn, None, CLIENT_NODE_TYPE,
Grégory Wisniewski's avatar
Grégory Wisniewski committed
224
                                  uuid, ('127.0.0.1', 10010), 0, 0, INVALID_UUID)
Grégory Wisniewski's avatar
Grégory Wisniewski committed
225
        self.checkClosed(conn)
226 227
        self.assertEquals(app.storage_node, None)
        self.assertEquals(app.pt, None)
228
        self.assertEquals(app.uuid, 'C' * 16)
229 230 231

    def test_masterAcceptNodeIdentification(self):
        node = Mock({'setUUID': None})
232 233 234
        class FakeLocal:
            from Queue import Queue
            queue = Queue()
235
        class App:
236
            nm = Mock({'getByAddress': node})
237 238
            storage_node = None
            pt = None
239
            local_var = FakeLocal()
240 241
        app = App()
        dispatcher = self.getDispatcher()
242
        client_handler = PrimaryBootstrapHandler(app)
243
        conn = self.getConnection()
Grégory Wisniewski's avatar
Grégory Wisniewski committed
244
        uuid = self.getNewUUID()
245 246
        your_uuid = 'C' * 16
        app.uuid = INVALID_UUID
247
        client_handler.handleAcceptNodeIdentification(conn, None, MASTER_NODE_TYPE,
Grégory Wisniewski's avatar
Grégory Wisniewski committed
248
                                  uuid, ('127.0.0.1', 10010), 10, 2, your_uuid)
Grégory Wisniewski's avatar
Grégory Wisniewski committed
249 250
        self.checkNotClosed(conn)
        self.checkUUIDSet(conn, uuid)
251 252
        self.assertEquals(app.storage_node, None)
        self.assertTrue(app.pt is not None)
253
        self.assertEquals(app.uuid, your_uuid)
254 255 256 257

    def test_storageAcceptNodeIdentification(self):
        node = Mock({'setUUID': None})
        class App:
258
            nm = Mock({'getByAddress': node})
259 260 261 262
            storage_node = None
            pt = None
        app = App()
        dispatcher = self.getDispatcher()
263
        client_handler = StorageBootstrapHandler(app)
264
        conn = self.getConnection()
Grégory Wisniewski's avatar
Grégory Wisniewski committed
265
        uuid = self.getNewUUID()
266
        app.uuid = 'C' * 16
267
        client_handler.handleAcceptNodeIdentification(conn, None, STORAGE_NODE_TYPE,
Grégory Wisniewski's avatar
Grégory Wisniewski committed
268
                                  uuid, ('127.0.0.1', 10010), 0, 0, INVALID_UUID)
Grégory Wisniewski's avatar
Grégory Wisniewski committed
269 270
        self.checkNotClosed(conn)
        self.checkUUIDSet(conn, uuid)
271
        self.assertEquals(app.pt,  None)
272
        self.assertEquals(app.uuid, 'C' * 16)
273

274
    def _testHandleUnexpectedPacketCalledWithMedhod(self, method, args=(), kw=()):
275
        self.assertRaises(UnexpectedPacketError, method, *args, **dict(kw))
276 277 278 279

    # Master node handler
    def test_nonMasterAnswerPrimaryMaster(self):
        for node_type in (CLIENT_NODE_TYPE, STORAGE_NODE_TYPE):
280
            node = Mock({'getType': node_type})
281
            class App:
282
                nm = Mock({'getByUUID': node, 'getByAddress': None, 'add': None})
283
                trying_master_node = None
284
            app = App()
285
            client_handler = PrimaryBootstrapHandler(app)
286 287 288
            conn = self.getConnection()
            client_handler.handleAnswerPrimaryMaster(conn, None, 0, [])
            # Check that nothing happened
289
            self.assertEqual(len(app.nm.mockGetNamedCalls('getByAddress')), 0)
290 291 292
            self.assertEqual(len(app.nm.mockGetNamedCalls('add')), 0)

    def test_unknownNodeAnswerPrimaryMaster(self):
293
        node = Mock({'getType': MASTER_NODE_TYPE})
294
        class App:
295
            nm = Mock({'getByAddress': None, 'add': None})
296 297
            primary_master_node = None
        app = App()
298
        client_handler = PrimaryBootstrapHandler(app)
299
        conn = self.getConnection()
Grégory Wisniewski's avatar
Grégory Wisniewski committed
300
        test_master_list = [(('127.0.0.1', 10010), self.getNewUUID())]
301 302
        client_handler.handleAnswerPrimaryMaster(conn, None, INVALID_UUID, test_master_list)
        # Check that yet-unknown master node got added
303
        getByAddress_call_list = app.nm.mockGetNamedCalls('getNodeByServer')
304
        add_call_list = app.nm.mockGetNamedCalls('add')
305
        self.assertEqual(len(getByAddress_call_list), 1)
306
        self.assertEqual(len(add_call_list), 1)
Grégory Wisniewski's avatar
Grégory Wisniewski committed
307
        (address, port), test_uuid = test_master_list[0]
308
        getByAddress_call = getNodeByServer_call_list[0]
309
        add_call = add_call_list[0]
310
        self.assertEquals((address, port), getByAddress_call.getParam(0))
311 312 313 314 315 316 317
        node_instance = add_call.getParam(0)
        self.assertEquals(test_uuid, node_instance.getUUID())
        # Check that primary master was not updated (it is not known yet,
        # hence INVALID_UUID in call).
        self.assertEquals(app.primary_master_node, None)

    def test_knownNodeUnknownUUIDNodeAnswerPrimaryMaster(self):
318
        node = Mock({'getType': MASTER_NODE_TYPE, 'getUUID': None, 'setUUID': None})
319
        class App:
320
            nm = Mock({'getByAddress': node, 'add': None})
321 322
            primary_master_node = None
        app = App()
323
        client_handler = PrimaryBootstrapHandler(app)
324
        conn = self.getConnection()
Grégory Wisniewski's avatar
Grégory Wisniewski committed
325
        test_node_uuid = self.getNewUUID()
Grégory Wisniewski's avatar
Grégory Wisniewski committed
326
        test_master_list = [(('127.0.0.1', 10010), test_node_uuid)]
327
        client_handler.handleAnswerPrimaryMaster(conn, None, INVALID_UUID, test_master_list)
328
        # Test sanity checks
329 330 331
        getByAddress_call_list = app.nm.mockGetNamedCalls('getNodeByServer')
        self.assertEqual(len(getByAddress_call_list), 1)
        self.assertEqual(getByAddress_call_list[0].getParam(0), test_master_list[0][0])
332
        # Check that known master node did not get added
333
        getByAddress_call_list = app.nm.mockGetNamedCalls('getNodeByServer')
334
        add_call_list = app.nm.mockGetNamedCalls('add')
335
        self.assertEqual(len(getByAddress_call_list), 1)
336 337
        self.assertEqual(len(add_call_list), 0)
        # Check that node UUID got updated
Grégory Wisniewski's avatar
Grégory Wisniewski committed
338
        self.checkUUIDSet(node, test_node_uuid)
339 340 341 342 343
        # Check that primary master was not updated (it is not known yet,
        # hence INVALID_UUID in call).
        self.assertEquals(app.primary_master_node, None)

    def test_knownNodeKnownUUIDNodeAnswerPrimaryMaster(self):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
344
        test_node_uuid = self.getNewUUID()
345
        node = Mock({'getType': MASTER_NODE_TYPE, 'getUUID': test_node_uuid, 'setUUID': None})
346
        class App:
347
            nm = Mock({'getByAddress': node, 'add': None})
348 349
            primary_master_node = None
        app = App()
350
        client_handler = PrimaryBootstrapHandler(app)
351
        conn = self.getConnection()
Grégory Wisniewski's avatar
Grégory Wisniewski committed
352
        test_master_list = [(('127.0.0.1', 10010), test_node_uuid)]
353
        client_handler.handleAnswerPrimaryMaster(conn, None, INVALID_UUID, test_master_list)
354
        # Test sanity checks
355 356 357
        getByAddress_call_list = app.nm.mockGetNamedCalls('getNodeByServer')
        self.assertEqual(len(getByAddress_call_list), 1)
        self.assertEqual(getByAddress_call_list[0].getParam(0), test_master_list[0][0])
358 359
        # Check that known master node did not get added
        add_call_list = app.nm.mockGetNamedCalls('add')
360 361 362 363 364
        self.assertEqual(len(add_call_list), 0)
        # Check that node UUID was untouched
        # XXX: should we just check that there was either no call or a call
        # with same uuid, or enforce no call ? Here we enforce no call just
        # because it's what implementation does.
Grégory Wisniewski's avatar
Grégory Wisniewski committed
365
        self.checkNoUUIDSet(node)
366 367 368 369 370 371 372 373
        # Check that primary master was not updated (it is not known yet,
        # hence INVALID_UUID in call).
        self.assertEquals(app.primary_master_node, None)

    # TODO: test known node, known but different uuid (not detected in code,
    # desired behaviour unknown)

    def test_alreadyDifferentPrimaryAnswerPrimaryMaster(self):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
374
        test_node_uuid = self.getNewUUID()
375 376
        test_primary_node_uuid = test_node_uuid
        while test_primary_node_uuid == test_node_uuid:
Grégory Wisniewski's avatar
Grégory Wisniewski committed
377
            test_primary_node_uuid = self.getNewUUID()
378
        test_primary_master_node = Mock({'getUUID': test_primary_node_uuid})
379
        node = Mock({'getType': MASTER_NODE_TYPE, 'getUUID': test_node_uuid, 'setUUID': None})
380
        class App:
381
            nm = Mock({'getByUUID': node, 'getByAddress': node, 'add': None})
382
            primary_master_node = test_primary_master_node
383
            trying_master_node = None
384
        app = App()
385
        client_handler = PrimaryBootstrapHandler(app)
386 387 388
        conn = self.getConnection()
        # If primary master is already set *and* is not given primary master
        # handle call raises.
389 390 391 392
        # Check that the call doesn't raise
        client_handler.handleAnswerPrimaryMaster(conn, None, test_node_uuid, [])
        # Check that the primary master changed
        self.assertTrue(app.primary_master_node is node)
393
        # Test sanity checks
394 395 396 397 398
        getByUUID_call_list = app.nm.mockGetNamedCalls('getNodeByUUID')
        self.assertEqual(len(getByUUID_call_list), 1)
        self.assertEqual(getByUUID_call_list[0].getParam(0), test_node_uuid)
        getByAddress_call_list = app.nm.mockGetNamedCalls('getNodeByServer')
        self.assertEqual(len(getByAddress_call_list), 0)
399 400

    def test_alreadySamePrimaryAnswerPrimaryMaster(self):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
401
        test_node_uuid = self.getNewUUID()
402
        node = Mock({'getType': MASTER_NODE_TYPE, 'getUUID': test_node_uuid, 'setUUID': None})
403
        class App:
404
            nm = Mock({'getByUUID': node, 'getByAddress': node, 'add': None})
405
            primary_master_node = node
406
            trying_master_node = node
407
        app = App()
408
        client_handler = PrimaryBootstrapHandler(app)
409 410 411 412 413 414
        conn = self.getConnection()
        client_handler.handleAnswerPrimaryMaster(conn, None, test_node_uuid, [])
        # Check that primary node is (still) node.
        self.assertTrue(app.primary_master_node is node)

    def test_unknownNewPrimaryAnswerPrimaryMaster(self):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
415
        test_node_uuid = self.getNewUUID()
416 417
        test_primary_node_uuid = test_node_uuid
        while test_primary_node_uuid == test_node_uuid:
Grégory Wisniewski's avatar
Grégory Wisniewski committed
418
            test_primary_node_uuid = self.getNewUUID()
419
        node = Mock({'getType': MASTER_NODE_TYPE, 'getUUID': test_node_uuid, 'setUUID': None})
420
        class App:
421
            nm = Mock({'getByUUID': None, 'getByAddress': node, 'add': None})
422
            primary_master_node = None
423
            trying_master_node = None
424
        app = App()
425
        client_handler = PrimaryBootstrapHandler(app)
426 427
        conn = self.getConnection()
        client_handler.handleAnswerPrimaryMaster(conn, None, test_primary_node_uuid, [])
428
        # Test sanity checks
429 430 431
        getByUUID_call_list = app.nm.mockGetNamedCalls('getNodeByUUID')
        self.assertEqual(len(getByUUID_call_list), 1)
        self.assertEqual(getByUUID_call_list[0].getParam(0), test_primary_node_uuid)
432 433 434 435
        # Check that primary node was not updated.
        self.assertTrue(app.primary_master_node is None)

    def test_AnswerPrimaryMaster(self):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
436
        test_node_uuid = self.getNewUUID()
437
        node = Mock({'getType': MASTER_NODE_TYPE, 'getUUID': test_node_uuid, 'setUUID': None})
438
        class App:
439
            nm = Mock({'getByUUID': node, 'getByAddress': node, 'add': None})
440
            primary_master_node = None
441
            trying_master_node = None
442
        app = App()
443
        client_handler = PrimaryBootstrapHandler(app)
444
        conn = self.getConnection()
Grégory Wisniewski's avatar
Grégory Wisniewski committed
445
        test_master_list = [(('127.0.0.1', 10010), test_node_uuid)]
446
        client_handler.handleAnswerPrimaryMaster(conn, None, test_node_uuid, test_master_list)
447
        # Test sanity checks
448 449 450 451 452 453
        getByUUID_call_list = app.nm.mockGetNamedCalls('getNodeByUUID')
        self.assertEqual(len(getByUUID_call_list), 1)
        self.assertEqual(getByUUID_call_list[0].getParam(0), test_node_uuid)
        getByAddress_call_list = app.nm.mockGetNamedCalls('getNodeByServer')
        self.assertEqual(len(getByAddress_call_list), 1)
        self.assertEqual(getByAddress_call_list[0].getParam(0), test_master_list[0][0])
454 455 456 457
        # Check that primary master was updated to known node
        self.assertTrue(app.primary_master_node is node)

    def test_newSendPartitionTable(self):
458
        node = Mock({'getType': MASTER_NODE_TYPE})
459 460
        test_ptid = 0
        class App:
461
            nm = Mock({'getByUUID': node})
Grégory Wisniewski's avatar
Grégory Wisniewski committed
462
            pt = PartitionTable(1, 1)
463
        app = App()
Grégory Wisniewski's avatar
Grégory Wisniewski committed
464
        client_handler = PrimaryNotificationsHandler(app, Mock())
465 466 467
        conn = self.getConnection()
        client_handler.handleSendPartitionTable(conn, None, test_ptid + 1, [])
        # Check that partition table got cleared and ptid got updated
Grégory Wisniewski's avatar
Grégory Wisniewski committed
468
        self.assertEquals(app.pt.getID(), 1)
469 470

    def test_unknownNodeSendPartitionTable(self):
471
        test_node = Mock({'getType': MASTER_NODE_TYPE})
472 473
        test_ptid = 0
        class App:
474
            nm = Mock({'getByUUID': ReturnValues(test_node, None), 'add': None})
475 476
            pt = Mock({'setCell': None})
            ptid = test_ptid
Grégory Wisniewski's avatar
Grégory Wisniewski committed
477
        test_storage_uuid = self.getNewUUID()
478
        app = App()
479
        client_handler = PrimaryBootstrapHandler(app)
480 481 482 483 484 485 486 487 488 489 490 491
        conn = self.getConnection()
        # TODO: use realistic values
        test_row_list = [(0, [(test_storage_uuid, 0)])]
        client_handler.handleSendPartitionTable(conn, None, test_ptid, test_row_list)
        # Check that node got created
        add_call_list = app.nm.mockGetNamedCalls('add')
        self.assertEquals(len(add_call_list), 1)
        created_node = add_call_list[0].getParam(0)
        self.assertEqual(created_node.getUUID(), test_storage_uuid)
        # Check that partition table cell got added
        setCell_call_list = app.pt.mockGetNamedCalls('setCell')
        self.assertEquals(len(setCell_call_list), 1)
492 493
        setCell_call_list[0].checkArgs(test_row_list[0][0], created_node,
                test_row_list[0][1][0][1])
494 495

    def test_knownNodeSendPartitionTable(self):
496
        test_node = Mock({'getType': MASTER_NODE_TYPE})
497 498
        test_ptid = 0
        class App:
499
            nm = Mock({'getByUUID': test_node, 'add': None})
500 501
            pt = Mock({'setCell': None})
            ptid = test_ptid
Grégory Wisniewski's avatar
Grégory Wisniewski committed
502
        test_storage_uuid = self.getNewUUID()
503
        app = App()
504
        client_handler = PrimaryBootstrapHandler(app)
505 506 507 508 509 510 511 512 513
        conn = self.getConnection()
        # TODO: use realistic values
        test_row_list = [(0, [(test_storage_uuid, 0)])]
        client_handler.handleSendPartitionTable(conn, None, test_ptid, test_row_list)
        # Check that node did not get created
        self.assertEquals(len(app.nm.mockGetNamedCalls('add')), 0)
        # Check that partition table cell got added
        setCell_call_list = app.pt.mockGetNamedCalls('setCell')
        self.assertEquals(len(setCell_call_list), 1)
514 515
        setCell_call_list[0].checkArgs(test_row_list[0][0], test_node,
                test_row_list[0][1][0][1])
516 517 518

    def test_nonMasterNotifyNodeInformation(self):
        for node_type in (CLIENT_NODE_TYPE, STORAGE_NODE_TYPE):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
519
            test_master_uuid = self.getNewUUID()
520
            node = Mock({'getType': node_type})
521
            class App:
522
                nm = Mock({'getByUUID': node})
523
            app = App()
524
            client_handler = PrimaryNotificationsHandler(app, self.getDispatcher())
525
            conn = self.getConnection(uuid=test_master_uuid)
526
            client_handler.handleNotifyNodeInformation(conn, None, ())
527 528 529 530 531 532

    def test_nonIterableParameterRaisesNotifyNodeInformation(self):
        # XXX: this test is here for sanity self-check: it verifies the
        # assumption described in test_nonMasterNotifyNodeInformation
        # by making a valid call with a non-iterable parameter given as
        # node_list value.
Grégory Wisniewski's avatar
Grégory Wisniewski committed
533
        test_master_uuid = self.getNewUUID()
534
        node = Mock({'getType': MASTER_NODE_TYPE})
535
        class App:
536
            nm = Mock({'getByUUID': node})
537
        app = App()
538
        client_handler = PrimaryNotificationsHandler(app, self.getDispatcher())
539 540 541 542
        conn = self.getConnection(uuid=test_master_uuid)
        self.assertRaises(TypeError, client_handler.handleNotifyNodeInformation,
            conn, None, None)

543
    def _testNotifyNodeInformation(self, test_node, getByAddress=None, getByUUID=MARKER):
544 545 546
        invalid_uid_test_node = (test_node[0], test_node[1], test_node[2] + 1,
                                 INVALID_UUID, test_node[4])
        test_node_list = [test_node, invalid_uid_test_node]
Grégory Wisniewski's avatar
Grégory Wisniewski committed
547
        test_master_uuid = self.getNewUUID()
548
        node = Mock({'getType': MASTER_NODE_TYPE})
549 550
        if getByUUID is not MARKER:
            getByUUID = ReturnValues(node, getNodeByUUID)
551
        class App:
552 553
            nm = Mock({'getByUUID': getNodeByUUID,
                       'getByAddress': getNodeByServer,
554 555 556
                       'add': None,
                       'remove': None})
        app = App()
557
        client_handler = PrimaryBootstrapHandler(app)
558 559 560 561 562 563
        conn = self.getConnection(uuid=test_master_uuid)
        client_handler.handleNotifyNodeInformation(conn, None, test_node_list)
        # Return nm so caller can check handler actions.
        return app.nm

    def test_unknownMasterNotifyNodeInformation(self):
564
        # first notify unknown master nodes
Grégory Wisniewski's avatar
Grégory Wisniewski committed
565
        uuid = self.getNewUUID()
566 567
        test_node = (MASTER_NODE_TYPE, '127.0.0.1', 10010, uuid,
                     RUNNING_STATE)
568
        nm = self._testNotifyNodeInformation(test_node, getByUUID=None)
569 570 571 572 573 574 575
        # Check that two nodes got added (second is with INVALID_UUID)
        add_call_list = nm.mockGetNamedCalls('add')
        self.assertEqual(len(add_call_list), 2)
        added_node = add_call_list[0].getParam(0)
        self.assertEquals(added_node.getUUID(), uuid)
        added_node = add_call_list[1].getParam(0)
        self.assertEquals(added_node.getUUID(), None)
576 577

    def test_knownMasterNotifyNodeInformation(self):
578
        node = Mock({})
Grégory Wisniewski's avatar
Grégory Wisniewski committed
579
        uuid = self.getNewUUID()
580 581
        test_node = (MASTER_NODE_TYPE, '127.0.0.1', 10010, uuid,
                     RUNNING_STATE)
582 583
        nm = self._testNotifyNodeInformation(test_node, getByAddress=node,
                getByUUID=node)
584 585 586 587 588 589 590 591 592
        # Check that node got replaced
        add_call_list = nm.mockGetNamedCalls('add')
        self.assertEquals(len(add_call_list), 1)
        remove_call_list = nm.mockGetNamedCalls('remove')
        self.assertEquals(len(remove_call_list), 1)
        # Check node state has been updated
        setState_call_list = node.mockGetNamedCalls('setState')
        self.assertEqual(len(setState_call_list), 1)
        self.assertEqual(setState_call_list[0].getParam(0), test_node[4])
593 594

    def test_unknownStorageNotifyNodeInformation(self):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
595
        test_node = (STORAGE_NODE_TYPE, '127.0.0.1', 10010, self.getNewUUID(),
596
                     RUNNING_STATE)
597
        nm = self._testNotifyNodeInformation(test_node, getByUUID=None)
598 599 600 601 602 603 604 605 606 607 608
        # Check that node got added
        add_call_list = nm.mockGetNamedCalls('add')
        self.assertEqual(len(add_call_list), 1)
        added_node = add_call_list[0].getParam(0)
        # XXX: this test does not check that node state got updated.
        # This is because there would be no way to tell the difference between
        # an updated state and default state if they are the same value (we
        # don't control node class/instance here)
        # Likewise for server address and node uuid.

    def test_knownStorageNotifyNodeInformation(self):
609
        node = Mock({'setState': None, 'setAddress': None})
Grégory Wisniewski's avatar
Grégory Wisniewski committed
610
        test_node = (STORAGE_NODE_TYPE, '127.0.0.1', 10010, self.getNewUUID(),
611
                     RUNNING_STATE)
612
        nm = self._testNotifyNodeInformation(test_node, getByUUID=node)
613 614 615 616 617
        # Check that node got replaced
        add_call_list = nm.mockGetNamedCalls('add')
        self.assertEquals(len(add_call_list), 1)
        remove_call_list = nm.mockGetNamedCalls('remove')
        self.assertEquals(len(remove_call_list), 1)
618
        # Check node state has been updated
619 620
        node = add_call_list[0].getParam(0)
        self.assertEquals(node.getState(), test_node[4])
621 622 623 624 625

    def test_initialNotifyPartitionChanges(self):
        class App:
            nm = None
            pt = None
626
            ptid = INVALID_PTID
627
        app = App()
628
        client_handler = PrimaryBootstrapHandler(app)
629
        conn = Mock({'getUUID': None})
630
        self._testHandleUnexpectedPacketCalledWithMedhod(
631
            client_handler.handleNotifyPartitionChanges,
632 633 634 635
            args=(conn, None, None, None))

    def test_nonMasterNotifyPartitionChanges(self):
        for node_type in (CLIENT_NODE_TYPE, STORAGE_NODE_TYPE):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
636
            test_master_uuid = self.getNewUUID()
637
            node = Mock({'getType': node_type, 'getUUID': test_master_uuid})
638
            class App:
639
                nm = Mock({'getByUUID': node})
640
                pt = Mock()
641
                ptid = INVALID_PTID
642 643
                primary_master_node = node
            app = App()
644
            client_handler = PrimaryNotificationsHandler(app, self.getDispatcher())
645 646 647
            conn = self.getConnection(uuid=test_master_uuid)
            client_handler.handleNotifyPartitionChanges(conn, None, 0, [])
            # Check that nothing happened
648 649
            self.assertEquals(len(app.pt.mockGetNamedCalls('setCell')), 0)
            self.assertEquals(len(app.pt.mockGetNamedCalls('removeCell')), 0)
650 651

    def test_noPrimaryMasterNotifyPartitionChanges(self):
652
        node = Mock({'getType': MASTER_NODE_TYPE})
653
        class App:
654
            nm = Mock({'getByUUID': node})
655
            pt = Mock()
656
            ptid = INVALID_PTID
657 658
            primary_master_node = None
        app = App()
659
        client_handler = PrimaryNotificationsHandler(app, self.getDispatcher())
660 661 662
        conn = self.getConnection()
        client_handler.handleNotifyPartitionChanges(conn, None, 0, [])
        # Check that nothing happened
663 664
        self.assertEquals(len(app.pt.mockGetNamedCalls('setCell')), 0)
        self.assertEquals(len(app.pt.mockGetNamedCalls('removeCell')), 0)
665 666

    def test_nonPrimaryMasterNotifyPartitionChanges(self):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
667
        test_master_uuid = self.getNewUUID()
668 669
        test_sender_uuid = test_master_uuid
        while test_sender_uuid == test_master_uuid:
Grégory Wisniewski's avatar
Grégory Wisniewski committed
670
            test_sender_uuid = self.getNewUUID()
671
        node = Mock({'getType': MASTER_NODE_TYPE})
672 673
        test_master_node = Mock({'getUUID': test_master_uuid})
        class App:
674
            nm = Mock({'getByUUID': node})
675
            pt = Mock()
676
            ptid = INVALID_PTID
677 678
            primary_master_node = test_master_node
        app = App()
679
        client_handler = PrimaryNotificationsHandler(app, self.getDispatcher())
680 681 682
        conn = self.getConnection(uuid=test_sender_uuid)
        client_handler.handleNotifyPartitionChanges(conn, None, 0, [])
        # Check that nothing happened
683 684
        self.assertEquals(len(app.pt.mockGetNamedCalls('setCell')), 0)
        self.assertEquals(len(app.pt.mockGetNamedCalls('removeCell')), 0)
685 686

    def test_ignoreOutdatedPTIDNotifyPartitionChanges(self):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
687
        test_master_uuid = self.getNewUUID()
688
        node = Mock({'getType': MASTER_NODE_TYPE, 'getUUID': test_master_uuid})
689 690
        test_ptid = 1
        class App:
691
            nm = Mock({'getByUUID': node})
692
            pt = Mock()
693 694 695
            primary_master_node = node
            ptid = test_ptid
        app = App()
696
        client_handler = PrimaryNotificationsHandler(app, self.getDispatcher())
697 698 699
        conn = self.getConnection(uuid=test_master_uuid)
        client_handler.handleNotifyPartitionChanges(conn, None, test_ptid, [])
        # Check that nothing happened
700 701
        self.assertEquals(len(app.pt.mockGetNamedCalls('setCell')), 0)
        self.assertEquals(len(app.pt.mockGetNamedCalls('removeCell')), 0)
702 703 704
        self.assertEquals(app.ptid, test_ptid)

    def test_unknownNodeNotifyPartitionChanges(self):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
705
        test_master_uuid = self.getNewUUID()
706
        test_node = Mock({'getType': MASTER_NODE_TYPE, 'getUUID': test_master_uuid})
707 708
        test_ptid = 1
        class App:
709
            nm = Mock({'getByUUID': ReturnValues(None)})
710 711 712 713 714
            pt = Mock({'setCell': None})
            primary_master_node = test_node
            ptid = test_ptid
            uuid = None # XXX: Is it really needed ?
        app = App()
715
        client_handler = PrimaryNotificationsHandler(app, self.getDispatcher())
716
        conn = self.getConnection(uuid=test_master_uuid)
Grégory Wisniewski's avatar
Grégory Wisniewski committed
717
        test_storage_uuid = self.getNewUUID()
718
        # TODO: use realistic values
719
        test_cell_list = [(0, test_storage_uuid, UP_TO_DATE_STATE)]
720 721 722 723 724 725 726 727
        client_handler.handleNotifyPartitionChanges(conn, None, test_ptid + 1, test_cell_list)
        # Check that a new node got added
        add_call_list = app.nm.mockGetNamedCalls('add')
        self.assertEqual(len(add_call_list), 1)
        added_node = add_call_list[0].getParam(0)
        # Check that partition got updated
        self.assertEqual(app.ptid, test_ptid + 1)
        setCell_call_list = app.pt.mockGetNamedCalls('setCell')
728 729
        setCell_call_list[0].checkArgs(test_cell_list[0][0], added_node,
                test_cell_list[0][2])
730 731 732 733 734

    # TODO: confirm condition under which an unknown node should be added with a TEMPORARILY_DOWN_STATE (implementation is unclear)

    def test_knownNodeNotifyPartitionChanges(self):
        test_ptid = 1
Grégory Wisniewski's avatar
Grégory Wisniewski committed
735 736
        uuid1, uuid2 = self.getNewUUID(), self.getNewUUID()
        uuid3, uuid4 = self.getNewUUID(), self.getNewUUID()
737
        test_node = Mock({'getType': MASTER_NODE_TYPE, 'getUUID': uuid1})
738
        class App:
739
            nm = Mock({'getByUUID': ReturnValues(test_node, None, None, None), 'add': None})
740 741 742
            pt = Mock({'setCell': None})
            primary_master_node = test_node
            ptid = test_ptid
743
            uuid = uuid4
744
        app = App()
745
        client_handler = PrimaryNotificationsHandler(app, self.getDispatcher())
746 747 748 749 750 751 752
        conn = self.getConnection(uuid=uuid1)
        test_cell_list = [
            (0, uuid1, UP_TO_DATE_STATE),
            (0, uuid2, DISCARDED_STATE),
            (0, uuid3, FEEDING_STATE),
            (0, uuid4, UP_TO_DATE_STATE),
        ]
753
        client_handler.handleNotifyPartitionChanges(conn, None, test_ptid + 1, test_cell_list)
754 755 756 757 758 759 760 761 762
        # Check that the three last node got added
        calls = app.nm.mockGetNamedCalls('add')
        self.assertEquals(len(calls), 3)
        self.assertEquals(calls[0].getParam(0).getUUID(), uuid2)
        self.assertEquals(calls[1].getParam(0).getUUID(), uuid3)
        self.assertEquals(calls[2].getParam(0).getUUID(), uuid4)
        self.assertEquals(calls[0].getParam(0).getState(), TEMPORARILY_DOWN_STATE)
        self.assertEquals(calls[1].getParam(0).getState(), TEMPORARILY_DOWN_STATE)
        # and the others are updated
763
        self.assertEqual(app.ptid, test_ptid + 1)
764
        calls = app.pt.mockGetNamedCalls('setCell')
765
        self.assertEqual(len(calls), 4)
766
        self.assertEquals(calls[0].getParam(1).getUUID(), uuid1)
767 768 769
        self.assertEquals(calls[1].getParam(1).getUUID(), uuid2)
        self.assertEquals(calls[2].getParam(1).getUUID(), uuid3)
        self.assertEquals(calls[3].getParam(1).getUUID(), uuid4)
770

771
    def test_AnswerBeginTransaction(self):
772
        app = Mock({'setTID': None})
773
        dispatcher = self.getDispatcher()
774
        client_handler = PrimaryAnswersHandler(app)
775 776
        conn = self.getConnection()
        test_tid = 1
777
        client_handler.handleAnswerBeginTransaction(conn, None, test_tid)
778 779 780
        setTID_call_list = app.mockGetNamedCalls('setTID')
        self.assertEquals(len(setTID_call_list), 1)
        self.assertEquals(setTID_call_list[0].getParam(0), test_tid)
781 782

    def test_NotifyTransactionFinished(self):
783 784
        test_tid = 1
        app = Mock({'getTID': test_tid, 'setTransactionFinished': None})
785
        dispatcher = self.getDispatcher()
786
        client_handler = PrimaryAnswersHandler(app)
787
        conn = self.getConnection()
788 789
        client_handler.handleNotifyTransactionFinished(conn, None, test_tid)
        self.assertEquals(len(app.mockGetNamedCalls('setTransactionFinished')), 1)
790 791 792 793 794 795 796 797 798 799
        # TODO: decide what to do when non-current transaction is notified as finished, and test that behaviour

    def test_InvalidateObjects(self):
        class App:
            def _cache_lock_acquire(self):
                pass

            def _cache_lock_release(self):
                pass

800 801 802 803 804 805
            def registerDB(self, db, limit):
                self.db = db

            def getDB(self):
                return self.db

806 807 808
            mq_cache = Mock({'__delitem__': None})
        app = App()
        dispatcher = self.getDispatcher()
809
        client_handler = PrimaryNotificationsHandler(app, self.getDispatcher())
810 811 812
        conn = self.getConnection()
        test_tid = 1
        test_oid_list = ['\x00\x00\x00\x00\x00\x00\x00\x01', '\x00\x00\x00\x00\x00\x00\x00\x02']
813 814
        test_db = Mock({'invalidate': None})
        app.registerDB(test_db, None)
815 816
        client_handler.handleInvalidateObjects(conn, None, test_oid_list[:], test_tid)
        # 'invalidate' is called just once
817 818 819
        db = app.getDB()
        self.assertTrue(db is test_db)
        invalidate_call_list = db.mockGetNamedCalls('invalidate')
820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
        self.assertEquals(len(invalidate_call_list), 1)
        invalidate_call = invalidate_call_list[0]
        invalidate_tid = invalidate_call.getParam(0)
        self.assertEquals(invalidate_tid, test_tid)
        invalidate_oid_dict = invalidate_call.getParam(1)
        self.assertEquals(len(invalidate_oid_dict), len(test_oid_list))
        self.assertEquals(set(invalidate_oid_dict), set(test_oid_list))
        self.assertEquals(set(invalidate_oid_dict.itervalues()), set([test_tid]))
        # '__delitem__' is called once per invalidated object
        delitem_call_list = app.mq_cache.mockGetNamedCalls('__delitem__')
        self.assertEquals(len(delitem_call_list), len(test_oid_list))
        oid_list = [x.getParam(0) for x in delitem_call_list]
        self.assertEquals(set(oid_list), set(test_oid_list))

    def test_AnswerNewOIDs(self):
        class App:
            new_oid_list = []
        app = App()
        dispatcher = self.getDispatcher()
839
        client_handler = PrimaryAnswersHandler(app)
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
        conn = self.getConnection()
        test_oid_list = ['\x00\x00\x00\x00\x00\x00\x00\x01', '\x00\x00\x00\x00\x00\x00\x00\x02']
        client_handler.handleAnswerNewOIDs(conn, None, test_oid_list[:])
        self.assertEquals(set(app.new_oid_list), set(test_oid_list))

    def test_StopOperation(self):
        raise NotImplementedError

    # Storage node handler

    def test_AnswerObject(self):
        class FakeLocal:
            asked_object = ()
        class App:
            local_var = FakeLocal()
        app = App()
        dispatcher = self.getDispatcher()
857
        client_handler = StorageAnswersHandler(app)
858 859 860 861 862 863 864 865
        conn = self.getConnection()
        # TODO: use realistic values
        test_object_data = ('\x00\x00\x00\x00\x00\x00\x00\x01', 0, 0, 0, 0, 'test')
        client_handler.handleAnswerObject(conn, None, *test_object_data)
        self.assertEquals(app.local_var.asked_object, test_object_data)

    def _testAnswerStoreObject(self, app, conflicting, oid, serial):
        dispatcher = self.getDispatcher()
866
        client_handler = StorageAnswersHandler(app)
867 868 869 870 871
        conn = self.getConnection()
        client_handler.handleAnswerStoreObject(conn, None, conflicting, oid, serial)

    def test_conflictingAnswerStoreObject(self):
        class App:
872
            local_var = threading.local()
873
        app = App()
874
        app.local_var.object_stored = (0, 0)
875 876 877
        test_oid = '\x00\x00\x00\x00\x00\x00\x00\x01'
        test_serial = 1
        self._testAnswerStoreObject(app, 1, test_oid, test_serial)
878
        self.assertEqual(app.local_var.object_stored, (-1, test_serial))
879 880 881

    def test_AnswerStoreObject(self):
        class App:
882
            local_var = threading.local()
883
        app = App()
884
        app.local_var.object_stored = (0, 0)
885 886 887
        test_oid = '\x00\x00\x00\x00\x00\x00\x00\x01'
        test_serial = 1
        self._testAnswerStoreObject(app, 0, test_oid, test_serial)
888
        self.assertEqual(app.local_var.object_stored, (test_oid, test_serial))
889 890

    def test_AnswerStoreTransaction(self):
891 892
        test_tid = 10
        app = Mock({'getTID': test_tid, 'setTransactionVoted': None})
893
        dispatcher = self.getDispatcher()
894
        client_handler = StorageAnswersHandler(app)
895 896
        conn = self.getConnection()
        client_handler.handleAnswerStoreTransaction(conn, None, test_tid)
897
        self.assertEquals(len(app.mockGetNamedCalls('setTransactionVoted')), 1)
898 899 900 901 902 903 904 905 906
        # TODO: test handleAnswerObject with test_tid not matching app.tid (not handled in program)

    def test_AnswerTransactionInformation(self):
        class FakeLocal:
            txn_info = {}
        class App:
            local_var = FakeLocal()
        app = App()
        dispatcher = self.getDispatcher()
907
        client_handler = StorageAnswersHandler(app)
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
        conn = self.getConnection()
        tid = '\x00\x00\x00\x00\x00\x00\x00\x01' # TODO: use a more realistic tid
        user = 'bar'
        desc = 'foo'
        ext = 0 # XXX: unused in implementation
        oid_list = ['\x00\x00\x00\x00\x00\x00\x00\x01', '\x00\x00\x00\x00\x00\x00\x00\x02']
        client_handler.handleAnswerTransactionInformation(conn, None, tid, user, desc, ext, oid_list[:])
        stored_dict = app.local_var.txn_info
        # TODO: test 'time' value ?
        self.assertEquals(stored_dict['user_name'], user)
        self.assertEquals(stored_dict['description'], desc)
        self.assertEquals(stored_dict['id'], tid)
        self.assertEquals(stored_dict['oids'], oid_list)

    def test_AnswerObjectHistory(self):
        class FakeLocal:
            history = (0, [])
        class App:
            local_var = FakeLocal()
        app = App()
        dispatcher = self.getDispatcher()
929
        client_handler = StorageAnswersHandler(app)
930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
        conn = self.getConnection()
        test_oid = '\x00\x00\x00\x00\x00\x00\x00\x01'
        # TODO: use realistic values
        test_history_list = [(1, 2), (3, 4)]
        client_handler.handleAnswerObjectHistory(conn, None, test_oid, test_history_list[:])
        oid, history = app.local_var.history
        self.assertEquals(oid, test_oid)
        self.assertEquals(len(history), len(test_history_list))
        self.assertEquals(set(history), set(test_history_list))

    def test_OidNotFound(self):
        class FakeLocal:
            asked_object = 0
            history = 0
        class App:
            local_var = FakeLocal()
        app = App()
        dispatcher = self.getDispatcher()
948
        client_handler = StorageAnswersHandler(app)
949 950 951 952 953 954 955 956 957 958 959 960
        conn = self.getConnection()
        client_handler.handleOidNotFound(conn, None, None)
        self.assertEquals(app.local_var.asked_object, -1)
        self.assertEquals(app.local_var.history, -1)

    def test_TidNotFound(self):
        class FakeLocal:
            txn_info = 0
        class App:
            local_var = FakeLocal()
        app = App()
        dispatcher = self.getDispatcher()
961
        client_handler = StorageAnswersHandler(app)
962 963 964 965 966 967 968 969 970 971 972
        conn = self.getConnection()
        client_handler.handleTidNotFound(conn, None, None)
        self.assertEquals(app.local_var.txn_info, -1)

    def test_AnswerTIDs(self):
        class FakeLocal:
            node_tids = {}
        class App:
            local_var = FakeLocal()
        app = App()
        dispatcher = self.getDispatcher()
973
        client_handler = StorageAnswersHandler(app)
974 975 976 977 978 979 980 981 982 983 984 985
        conn = self.getConnection()
        test_tid_list = ['\x00\x00\x00\x00\x00\x00\x00\x01', '\x00\x00\x00\x00\x00\x00\x00\x02']
        client_handler.handleAnswerTIDs(conn, None, test_tid_list[:])
        stored_tid_list = []
        for tid_list in app.local_var.node_tids.itervalues():
            stored_tid_list.extend(tid_list)
        self.assertEquals(len(stored_tid_list), len(test_tid_list))
        self.assertEquals(set(stored_tid_list), set(test_tid_list))

if __name__ == '__main__':
    unittest.main()