示例#1
0
class TestClient(unittest.TestCase):

    def setUp(self):
        self.server_url = 'https://test.url/'
        self.api_path = '/api/downstream/v1'
        self.size = 100
        self.address = base58.b58encode_check(b'\x00' + os.urandom(20))
        self.token = binascii.hexlify(os.urandom(16)).decode('ascii')
        self.msg = ''
        self.sig = ''
        self.thread_manager = ShellApplication()
        self.contract_thread = ManagedThread()
        self.chunk_dir = os.path.join('data', 'chunks')
        self.client = DownstreamClient(self.server_url,
                                       self.token,
                                       self.address,
                                       self.size,
                                       self.msg,
                                       self.sig,
                                       self.thread_manager,
                                       self.chunk_dir)
        self.test_contract = \
            DownstreamContract(self.client,
                               MockValues.get_chunks_response[
                                   'chunks'][0]['file_hash'],
                               MockValues.get_chunks_response[
                                   'chunks'][0]['seed'],
                               MockValues.get_chunks_response[
                                   'chunks'][0]['size'],
                               Heartbeat.challenge_type().fromdict(
                                   MockValues
                                   .get_chunks_response
                                   ['chunks'][0]['challenge']),
                               datetime.utcnow() + timedelta(
                                   seconds=int(
                                       MockValues
                                       .get_chunks_response
                                       ['chunks'][0]['due'])),
                               Heartbeat.tag_type().fromdict(
                                   MockValues
                                   .get_chunks_response
                                   ['chunks'][0]['tag']),
                               self.thread_manager,
                               self.chunk_dir)
        self.test_heartbeat = Heartbeat.fromdict(
            MockValues.connect_response['heartbeat'])

    def tearDown(self):
        pass

    def test_initialization(self):
        self.assertEqual(self.client.server, self.server_url.strip('/'))
        self.assertEqual(self.client.address, self.address)
        self.assertEqual(self.client.token, self.token)
        self.assertEqual(self.client.desired_size, self.size)
        self.assertIsNone(self.client.heartbeat)

    def test_connect_no_token_no_address(self):
        self.client.address = None
        self.client.token = None
        with self.assertRaises(DownstreamError) as ex:
            self.client.connect()
        self.assertEqual(
            str(ex.exception), 'If no token is specified, address must be.')

    def test_connect_failed(self):
        with mock.patch('downstream_farmer.client.requests.get'),\
                mock.patch('downstream_farmer.client.handle_json_response')\
                as hp:
            hp.side_effect = DownstreamError('test error')
            with self.assertRaises(DownstreamError) as ex:
                self.client.connect()
            self.assertEqual(
                str(ex.exception), 'Unable to connect: test error')

    def test_connect_malformed(self):
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = {"invalid": "dict"}
            with self.assertRaises(DownstreamError) as ex:
                self.client.connect()
            self.assertEqual(
                str(ex.exception), 'Malformed response from server.')

    def test_connect_invalid_heartbeat(self):
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = {"heartbeat": "test heartbeat",
                                      "token": "test token",
                                      "type": "invalid type"}
            with self.assertRaises(DownstreamError) as ex:
                self.client.connect()
            self.assertEqual(str(ex.exception), 'Unknown Heartbeat Type')

    def test_connect_working_new(self):
        self.client.token = None
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            patch.return_value.json.return_value = MockValues.connect_response
            self.client.connect()
        patch.assert_called_with(
            '{0}/new/{1}'.format(self.server_url.strip('/') + self.api_path,
                                 self.address), verify=None)
        self.assertEqual(
            self.client.token, MockValues.connect_response['token'])
        self.assertEqual(self.client.heartbeat,
                         Heartbeat
                         .fromdict(MockValues.connect_response['heartbeat']))

    def test_connect_working(self):
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            patch.return_value.json.return_value = MockValues.connect_response
            self.client.connect()
        patch.assert_called_with('{0}/heartbeat/{1}'.format(
            self.server_url.strip('/') + self.api_path, self.token),
            verify=None)
        self.assertEqual(
            self.client.token, MockValues.connect_response['token'])
        self.assertEqual(self.client.heartbeat,
                         Heartbeat
                         .fromdict(MockValues.connect_response['heartbeat']))

    def test_connect_sign(self):
        self.client.msg = 'test message'
        self.client.sig = 'HyzVUenXXo4pa+kgm1vS8PNJM83eIXFC5r0q86FGbqFcdla6rcw'
        '72/ciXiEPfjli3ENfwWuESHhv6K9esI0dl5I='
        self.client.address = '19qVgG8C6eXwKMMyvVegsi3xCsKyk3Z3jV'
        self.client.token = None
        with mock.patch('downstream_farmer.client.requests.post') as patch:
            patch.return_value.json.return_value = MockValues.connect_response
            self.client.connect()
        patch.assert_called_with('{0}/new/{1}'.format(self.server_url
                                                      .strip('/') + self
                                                      .api_path, self
                                                      .client.address),
                                 data=json.dumps({
                                     "message": self.client.msg,
                                     "signature": self.client.sig
                                 }),
                                 headers={
            'Content-Type': 'application/json'
        },
            verify=None)
        self.assertEqual(
            self.client.token, MockValues.connect_response['token'])
        self.assertEqual(self.client.heartbeat,
                         Heartbeat.fromdict(MockValues
                                            .connect_response['heartbeat']))

    def test_get_contract_no_token(self):
        with mock.patch('downstream_farmer.client.requests.get'),\
                mock.patch('downstream_farmer.client.handle_json_response')\
                as hp:
            hp.side_effect = DownstreamError('test error')
            with self.assertRaises(DownstreamError) as ex:
                self.client._get_contracts()
            self.assertEqual(
                str(ex.exception), 'Unable to get token: test error')

    def test_get_contract_malformed(self):
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            patch.return_value.json.return_value = {"invalid": "dict"}
            with self.assertRaises(DownstreamError) as ex:
                self.client._get_contracts()
            self.assertEqual(
                str(ex.exception), 'Malformed response from server.')

    def test_get_contracts_working(self):
        self.client.heartbeat = self.test_heartbeat
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = MockValues.get_chunks_response.copy()
            contracts = self.client._get_contracts(100)
        self.assertEqual(
            contracts[0].hash, self.test_contract.hash)
        self.assertEqual(
            contracts[0].seed, self.test_contract.seed)
        self.assertEqual(
            contracts[0].size, self.test_contract.size)
        self.assertEqual(
            contracts[0].challenge, self.test_contract.challenge)
        self.assertAlmostEqual((contracts[0].expiration -
                                self.test_contract.expiration)
                               .total_seconds(), 0, delta=1)
        self.assertEqual(contracts[0].tag, self.test_contract.tag)

    def test_get_contract_no_chunks_available(self):
        self.client.heartbeat = self.test_heartbeat
        with mock.patch('downstream_farmer.client.requests.get'),\
                mock.patch(
                'downstream_farmer.client.handle_json_response'
        ) as hpatch:
            hpatch.return_value = dict(chunks=[])
            contracts = self.client._get_contracts()
            self.assertEqual(len(contracts), 0)

    def setup_run_mocks(self):
        self.client.thread_manager = mock.MagicMock()
        self.client.thread_manager.running = True
        self.client.worker_pool = mock.MagicMock()
        self.client._get_contracts = mock.MagicMock()
        self.client._get_contracts.return_value = [self.test_contract]
        self.client._add_contract = mock.MagicMock()
        self.client.contract_thread = mock.MagicMock()
        self.client.get_total_size = mock.MagicMock()
        self.client.get_total_size.return_value = 0

    def test_run_contract_manager(self):
        self.setup_run_mocks()

        self.client.contract_thread.wait.side_effect = \
            MockContractShutdown(self.client.thread_manager)

        self.client._add_contract.side_effect = AddContractMock(self.client)

        self.client._run_contract_manager()

        self.assertFalse(self.client.thread_manager.signal_shutdown.called)
        self.client._add_contract.assert_called_with(self.test_contract)

    def test_run_contract_manager_obtain_fail_no_retry(self):
        self.setup_run_mocks()

        self.client.contract_count = mock.MagicMock()
        self.client.contract_count.return_value = 0
        self.client._get_contracts.side_effect = DownstreamError('test error')

        self.client.thread_manager.signal_shutdown.side_effect = \
            MockContractShutdown(self.client.thread_manager)

        with self.assertRaises(DownstreamError) as ex:
            self.client._run_contract_manager()
        self.assertEqual(str(ex.exception), 'test error')

    def test_run_contract_manager_obtain_fail_retry(self):
        self.setup_run_mocks()

        self.client.contract_count = mock.MagicMock()
        self.client.contract_count.return_value = 0
        self.client._get_contracts.side_effect = \
            [DownstreamError('test error'), [self.test_contract]]

        self.client._add_contract.side_effect = AddContractMock(self.client)

        self.client.contract_thread.wait.side_effect = \
            MockContractShutdown(self.client.thread_manager)

        self.client._run_contract_manager(True)

        self.assertFalse(self.client.thread_manager.signal_shutdown.called)

    def test_run_contract_manager_shutdown_during_acquisition(self):
        self.setup_run_mocks()

        self.client._add_contract.side_effect = \
            AddContractMock(self.client, self.client.thread_manager)

        self.client._run_contract_manager()

        self.assertFalse(self.client.thread_manager.signal_shutdown.called)

    def test_run_contract_manager_number_requirement(self):
        self.setup_run_mocks()

        self.client._add_contract.side_effect = \
            AddContractMock(self.client)

        self.client.heartbeat_count = 1

        self.client.thread_manager.signal_shutdown.side_effect = \
            MockContractShutdown(self.client.thread_manager)

        self.client.desired_heartbeats = 1

        self.client._run_contract_manager()

        self.assertTrue(self.client.thread_manager.signal_shutdown.called)

    def test_run_async(self):
        self.client.thread_manager = mock.MagicMock()
        self.contract_thread = mock.MagicMock()
        self.client.thread_manager.create_thread.return_value = \
            self.contract_thread

        self.client.run_async()

        self.assertTrue(self.contract_thread.start.called)

    def test_set_cert_path(self):
        test_path = 'testpath'
        self.client._set_requests_verify_arg = mock.MagicMock()
        self.client.set_cert_path(test_path)
        self.assertEqual(self.client.cert_path, test_path)
        self.assertTrue(self.client._set_requests_verify_arg.called)

    def test_set_verify_cert(self):
        val = not self.client.verify_cert
        self.client._set_requests_verify_arg = mock.MagicMock()
        self.client.set_verify_cert(val)
        self.assertEqual(self.client.verify_cert, val)
        self.assertTrue(self.client._set_requests_verify_arg.called)

    def test_set_requests_verify_arg_false(self):
        self.client.verify_cert = False
        self.client.requests_verify_arg = True
        self.client._set_requests_verify_arg()
        self.assertFalse(self.client.requests_verify_arg)
示例#2
0
class TestClient(unittest.TestCase):

    def setUp(self):
        self.server_url = 'https://test.url/'
        self.api_path = '/api/downstream/v1'
        self.size = 100
        self.address = base58.b58encode_check(b'\x00' + os.urandom(20))
        self.token = binascii.hexlify(os.urandom(16)).decode('ascii')
        self.msg = ''
        self.sig = ''
        self.api = API()
        self.client = DownstreamClient(self.server_url,
                                       self.token,
                                       self.address,
                                       self.size,
                                       self.msg,
                                       self.sig,
                                       self.api)
        self.test_contract = DownstreamContract(self.client,
                                                MockValues.get_chunk_response[
                                                    'file_hash'],
                                                MockValues.get_chunk_response[
                                                    'seed'],
                                                MockValues.get_chunk_response[
                                                    'size'],
                                                Heartbeat.challenge_type()
                                                .fromdict(
                                                    MockValues.
                                                    get_chunk_response['challe'
                                                                       'nge']),
                                                datetime.utcnow() +
                                                timedelta(
                                                    seconds=int(
                                                        MockValues.
                                                        get_chunk_response['du'
                                                                           'e']
                                                    )),
                                                Heartbeat.tag_type().fromdict(
                                                    MockValues
                                                    .get_chunk_response['ta'
                                                                        'g'],
                                                ),
                                                self.api)
        self.test_heartbeat = Heartbeat.fromdict(
            MockValues.connect_response['heartbeat'])

    def tearDown(self):
        pass

    def test_initialization(self):
        self.assertEqual(self.client.server, self.server_url.strip('/'))
        self.assertEqual(self.client.address, self.address)
        self.assertEqual(self.client.token, self.token)
        self.assertEqual(self.client.desired_size, self.size)
        self.assertIsNone(self.client.heartbeat)
        self.assertEqual(len(self.client.contracts), 0)

    def test_connect_no_token_no_address(self):
        self.client.address = None
        self.client.token = None
        with self.assertRaises(DownstreamError) as ex:
            self.client.connect()
        self.assertEqual(
            str(ex.exception), 'If no token is specified, address must be.')

    def test_connect_failed(self):
        with mock.patch('downstream_farmer.client.requests.get'),\
                mock.patch('downstream_farmer.client.handle_json_response')\
                as hp:
            hp.side_effect = DownstreamError('test error')
            with self.assertRaises(DownstreamError) as ex:
                self.client.connect()
            self.assertEqual(
                str(ex.exception), 'Unable to connect: test error')

    def test_connect_malformed(self):
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = {"invalid": "dict"}
            with self.assertRaises(DownstreamError) as ex:
                self.client.connect()
            self.assertEqual(
                str(ex.exception), 'Malformed response from server.')

    def test_connect_invalid_heartbeat(self):
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = {"heartbeat": "test heartbeat",
                                      "token": "test token",
                                      "type": "invalid type"}
            with self.assertRaises(DownstreamError) as ex:
                self.client.connect()
            self.assertEqual(str(ex.exception), 'Unknown Heartbeat Type')

    def test_connect_working_new(self):
        self.client.token = None
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            patch.return_value.json.return_value = MockValues.connect_response
            self.client.connect()
        patch.assert_called_with(
            '{0}/new/{1}'.format(self.server_url.strip('/') + self.api_path,
                                 self.address), verify=None)
        self.assertEqual(
            self.client.token, MockValues.connect_response['token'])
        self.assertEqual(self.client.heartbeat,
                         Heartbeat
                         .fromdict(MockValues.connect_response['heartbeat']))

    def test_connect_working(self):
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            patch.return_value.json.return_value = MockValues.connect_response
            self.client.connect()
        patch.assert_called_with('{0}/heartbeat/{1}'.format(
            self.server_url.strip('/') + self.api_path, self.token),
            verify=None)
        self.assertEqual(
            self.client.token, MockValues.connect_response['token'])
        self.assertEqual(self.client.heartbeat,
                         Heartbeat
                         .fromdict(MockValues.connect_response['heartbeat']))

    def test_connect_sign(self):
        self.client.msg = 'test message'
        self.client.sig = 'HyzVUenXXo4pa+kgm1vS8PNJM83eIXFC5r0q86FGbqFcdla6rcw'
        '72/ciXiEPfjli3ENfwWuESHhv6K9esI0dl5I='
        self.client.address = '19qVgG8C6eXwKMMyvVegsi3xCsKyk3Z3jV'
        self.client.token = None
        with mock.patch('downstream_farmer.client.requests.post') as patch:
            patch.return_value.json.return_value = MockValues.connect_response
            self.client.connect()
        patch.assert_called_with('{0}/new/{1}'.format(self.server_url
                                                      .strip('/') + self
                                                      .api_path, self
                                                      .client.address),
                                 data=json.dumps({
                                     "message": self.client.msg,
                                     "signature": self.client.sig
                                 }),
                                 headers={
                                     'Content-Type': 'application/json'
        },
            verify=None)
        self.assertEqual(
            self.client.token, MockValues.connect_response['token'])
        self.assertEqual(self.client.heartbeat,
                         Heartbeat.fromdict(MockValues
                                            .connect_response['heartbeat']))

    def test_get_chunk_no_token(self):
        with mock.patch('downstream_farmer.client.requests.get'),\
                mock.patch('downstream_farmer.client.handle_json_response')\
                as hp:
            hp.side_effect = DownstreamError('test error')
            with self.assertRaises(DownstreamError) as ex:
                self.client.get_chunk()
            self.assertEqual(
                str(ex.exception), 'Unable to get token: test error')

    def test_get_chunk_malformed(self):
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            patch.return_value.json.return_value = {"invalid": "dict"}
            with self.assertRaises(DownstreamError) as ex:
                self.client.get_chunk()
            self.assertEqual(
                str(ex.exception), 'Malformed response from server.')

    def test_get_chunk_working(self):
        self.client.heartbeat = self.test_heartbeat
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = MockValues.get_chunk_response
            self.client.get_chunk()
        self.assertEqual(
            self.client.contracts[0].hash, self.test_contract.hash)
        self.assertEqual(
            self.client.contracts[0].seed, self.test_contract.seed)
        self.assertEqual(
            self.client.contracts[0].size, self.test_contract.size)
        self.assertEqual(
            self.client.contracts[0].challenge, self.test_contract.challenge)
        self.assertAlmostEqual((self.client.contracts[
                               0].expiration - self.test_contract.expiration)
                               .total_seconds(), 0, delta=1)
        self.assertEqual(self.client.contracts[0].tag, self.test_contract.tag)

    def test_get_total_size(self):
        client = mock.MagicMock(spec=DownstreamClient)
        contract1 = mock.MagicMock(spec=DownstreamContract)
        contract2 = mock.MagicMock(spec=DownstreamContract)
        contract1.size = 10
        contract2.size = 100
        client.contracts = [contract1, contract2]
        self.assertEqual(DownstreamClient.get_total_size(client), 110)
        client.contracts = list()
        self.assertEqual(DownstreamClient.get_total_size(client), 0)

    def test_get_next_contract(self):
        client = mock.MagicMock(spec=DownstreamClient)
        contract1 = mock.MagicMock(spec=DownstreamContract)
        contract2 = mock.MagicMock(spec=DownstreamContract)
        contract1.time_remaining.return_value = 10
        contract2.time_remaining.return_value = 100
        client.contracts = [contract1, contract2]
        self.assertEqual(contract1, DownstreamClient.get_next_contract(client))

    def test_run_obtain_contract_fail(self):
        client = mock.MagicMock(spec=DownstreamClient)
        client.get_total_size.return_value = 0
        client.desired_size = 100
        client.api = API()

        client.get_chunk.side_effect = DownstreamError('test error')
        client.contracts = []
        with self.assertRaises(DownstreamError) as ex:
            DownstreamClient.run(client, 1)
        self.assertEqual(
            str(ex.exception), 'Unable to obtain a contract: test error')

    def test_run_working(self):
        client = mock.MagicMock(spec=DownstreamClient)
        client.get_total_size.return_value = 0
        client.desired_size = 100
        client.api = API()

        contract = mock.MagicMock(spec=DownstreamContract)
        contract.time_remaining.return_value = 0
        contract.hash = '1'
        contract.api = client.api

        def patch_get_chunk(size):
            client.get_total_size.return_value = \
                client.get_total_size.return_value + \
                size
            client.contracts = [contract]

        client.get_chunk.side_effect = patch_get_chunk
        client.get_next_contract.return_value = contract
        DownstreamClient.run(client, 1)
        self.assertEqual(client.get_chunk.call_count, 1)
        self.assertEqual(contract.update_challenge.call_count, 1)
        self.assertEqual(contract.answer_challenge.call_count, 1)

    def test_run_working_block(self):
        client = mock.MagicMock(spec=DownstreamClient)
        client.get_total_size.return_value = 0
        client.desired_size = 100
        client.api = API()

        contract = mock.MagicMock(spec=DownstreamContract)
        contract.time_remaining.return_value = 1
        contract.hash = '1'
        contract.api = client.api

        def patch_get_chunk(size):
            client.get_total_size.return_value = \
                client.get_total_size.return_value + \
                size
            client.contracts = [contract]

        client.get_chunk.side_effect = patch_get_chunk
        client.get_next_contract.return_value = contract
        with mock.patch('time.sleep') as a:
            DownstreamClient.run(client, 1)
        self.assertEqual(client.get_chunk.call_count, 1)
        self.assertEqual(contract.update_challenge.call_count, 1)
        self.assertEqual(contract.answer_challenge.call_count, 1)
        self.assertEqual(a.call_count, 1)

    def test_run_update_failed(self):
        client = mock.MagicMock(spec=DownstreamClient)
        client.get_total_size.return_value = 100
        client.desired_size = 100
        client.api = API()

        contract = mock.MagicMock(spec=DownstreamContract)
        contract.time_remaining.return_value = 0
        contract.hash = '1'
        contract.api = client.api

        client.contracts = mock.MagicMock()
        client.contracts.remove = mock.MagicMock()

        contract.update_challenge.side_effect = DownstreamError('test error')
        client.get_next_contract.return_value = contract
        DownstreamClient.run(client, 1)
        self.assertTrue(client.contracts.remove.called)

    def test_run_answer_failed(self):
        client = mock.MagicMock(spec=DownstreamClient)
        client.get_total_size.return_value = 100
        client.desired_size = 100
        client.api = API()

        contract = mock.MagicMock(spec=DownstreamContract)
        contract.time_remaining.return_value = 0
        contract.hash = '1'
        contract.api = client.api

        client.contracts = mock.MagicMock()
        client.contracts.remove = mock.MagicMock()

        contract.answer_challenge.side_effect = DownstreamError('test error')
        client.get_next_contract.return_value = contract
        DownstreamClient.run(client, 1)
        self.assertTrue(client.contracts.remove.called)
示例#3
0
class TestClient(unittest.TestCase):
    def setUp(self):
        self.server_url = 'https://test.url/'
        self.address = base58.b58encode_check(b'\x00'+os.urandom(20))
        self.client = DownstreamClient(self.address)
        self.test_contract = Contract(MockValues.get_chunk_response['file_hash'],
                                      MockValues.get_chunk_response['seed'],
                                      MockValues.get_chunk_response['size'],
                                      Heartbeat.challenge_type().fromdict(
                                        MockValues.get_chunk_response['challenge']),
                                      datetime.strptime(
                                        MockValues.get_chunk_response['expiration'],
                                        '%Y-%m-%dT%H:%M:%S'),
                                      Heartbeat.tag_type().fromdict(
                                        MockValues.get_chunk_response['tag']))
        self.test_heartbeat = Heartbeat.fromdict(MockValues.connect_response['heartbeat'])

    def tearDown(self):
        pass

    def test_initialization(self):
        self.assertEqual(self.client.address, self.address)
        self.assertEqual(len(self.client.token),0)
        self.assertEqual(len(self.client.server),0)
        self.assertIsNone(self.client.heartbeat)
        self.assertIsNone(self.client.contract)

    def test_connect_malformed(self):
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = {"invalid":"dict"}
            with self.assertRaises(DownstreamError) as ex:
                self.client.connect(self.server_url)
            self.assertEqual(str(ex.exception),'Malformed response from server.')
    
    def test_connect_invalid_heartbeat(self):
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = {"heartbeat":"test heartbeat",
                                      "token":"test token",
                                      "type":"invalid type"}
            with self.assertRaises(DownstreamError) as ex:
                self.client.connect(self.server_url)
            self.assertEqual(str(ex.exception),'Unknown Heartbeat Type')

    def test_connect_working(self):
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = MockValues.connect_response
            self.client.connect(self.server_url)
        self.assertEqual(self.client.token,MockValues.connect_response['token'])
        self.assertEqual(self.client.heartbeat,
                         Heartbeat.fromdict(MockValues.connect_response['heartbeat']))

    def test_get_chunk_malformed(self):
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = {"invalid":"dict"}
            with self.assertRaises(DownstreamError) as ex:
                self.client.get_chunk()
            self.assertEqual(str(ex.exception),'Malformed response from server.')

    def test_get_chunk_working(self):
        self.client.heartbeat = self.test_heartbeat
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = MockValues.get_chunk_response
            self.client.get_chunk()
        self.assertEqual(self.client.contract.hash, self.test_contract.hash)
        self.assertEqual(self.client.contract.seed, self.test_contract.seed)
        self.assertEqual(self.client.contract.size, self.test_contract.size)
        self.assertEqual(self.client.contract.challenge, self.test_contract.challenge)
        self.assertEqual(self.client.contract.expiration, self.test_contract.expiration)
        self.assertEqual(self.client.contract.tag, self.test_contract.tag)

    def test_challenge_no_contract(self):
        self.client.contract = None
        with self.assertRaises(DownstreamError) as ex:
            self.client.get_challenge()
        self.assertEqual(str(ex.exception),'No contract to get a new challenge for.')

    def test_challenge_malformed(self):
        self.client.contract = self.test_contract
        self.client.heartbeat = self.test_heartbeat
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = {"invalid":"dict"}
            with self.assertRaises(DownstreamError) as ex:
                self.client.get_challenge()
            self.assertEqual(str(ex.exception),'Malformed response from server.')
    
    def test_challenge_block_til_expired(self):
        self.client.contract = self.test_contract
        self.client.heartbeat = self.test_heartbeat
        self.client.contract.expiration = datetime.utcnow()+timedelta(seconds=3)
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = MockValues.get_challenge_response
            self.assertIsNotNone(self.client.get_challenge())
            
    def test_challenge_no_block(self):
        self.client.contract = self.test_contract
        self.client.heartbeat = self.test_heartbeat
        self.client.contract.expiration = datetime.utcnow()+timedelta(seconds=3)
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = MockValues.get_challenge_response
            self.assertIsNone(self.client.get_challenge(block=False))

    def test_challenge_working(self):
        self.client.contract = self.test_contract
        self.client.heartbeat = self.test_heartbeat
        with mock.patch('downstream_farmer.client.requests.get') as patch:
            inst = patch.return_value
            inst.json.return_value = MockValues.get_challenge_response
            self.client.get_challenge()
            self.assertEqual(self.client.contract.challenge, 
                             Heartbeat.challenge_type().fromdict(
                                MockValues.get_challenge_response['challenge']))
            self.assertEqual(self.client.contract.expiration,
                             datetime.strptime(
                                MockValues.get_challenge_response['expiration'],
                                '%Y-%m-%dT%H:%M:%S'))
            
    def test_answer_no_contract(self):
        self.client.contract = None
        with self.assertRaises(DownstreamError) as ex:
            self.client.answer_challenge()
        self.assertEqual(str(ex.exception),'No contract to answer.')
        
    def test_answer_malformed(self):
        self.client.contract = self.test_contract
        self.client.heartbeat = self.test_heartbeat
        with mock.patch('downstream_farmer.client.requests.post') as patch:
            inst = patch.return_value
            inst.json.return_value = {"invalid":"dict"}
            with self.assertRaises(DownstreamError) as ex:
                self.client.answer_challenge()
            self.assertEqual(str(ex.exception),'Malformed response from server.')
    
    def test_answer_invalid(self):
        self.client.contract = self.test_contract
        self.client.heartbeat = self.test_heartbeat
        with mock.patch('downstream_farmer.client.requests.post') as patch:
            inst = patch.return_value
            inst.json.return_value = {"status":"dict"}
            with self.assertRaises(DownstreamError) as ex:
                self.client.answer_challenge()
            self.assertEqual(str(ex.exception),'Challenge response rejected.')
            
    def test_answer_working(self):
        self.client.contract = self.test_contract
        self.client.heartbeat = self.test_heartbeat
        with mock.patch('downstream_farmer.client.requests.post') as patch:
            inst = patch.return_value
            inst.json.return_value = {"status":"ok"}
            self.client.answer_challenge()
示例#4
0
class TestClient(unittest.TestCase):
    def setUp(self):
        self.server_url = 'https://test.url/'
        self.api_path = '/api/downstream/v1'
        self.size = 100
        self.address = base58.b58encode_check(b'\x00' + os.urandom(20))
        self.token = binascii.hexlify(os.urandom(16)).decode('ascii')
        self.msg = ''
        self.sig = ''
        self.thread_manager = ShellApplication()
        self.contract_thread = ManagedThread()
        self.chunk_dir = os.path.join('data', 'chunks')
        self.client = DownstreamClient(self.server_url, self.token,
                                       self.address, self.size, self.msg,
                                       self.sig, self.thread_manager,
                                       self.chunk_dir)
        self.client.session = mock.MagicMock()
        self.test_contract = \
            DownstreamContract(self.client,
                               MockValues.get_chunks_response[
                                   'chunks'][0]['file_hash'],
                               MockValues.get_chunks_response[
                                   'chunks'][0]['seed'],
                               MockValues.get_chunks_response[
                                   'chunks'][0]['size'],
                               Heartbeat.challenge_type().fromdict(
                                   MockValues
                                   .get_chunks_response
                                   ['chunks'][0]['challenge']),
                               datetime.utcnow() + timedelta(
                                   seconds=int(
                                       MockValues
                                       .get_chunks_response
                                       ['chunks'][0]['due'])),
                               Heartbeat.tag_type().fromdict(
                                   MockValues
                                   .get_chunks_response
                                   ['chunks'][0]['tag']),
                               self.thread_manager,
                               self.chunk_dir)
        self.test_heartbeat = Heartbeat.fromdict(
            MockValues.connect_response['heartbeat'])

    def tearDown(self):
        pass

    def test_initialization(self):
        self.assertEqual(self.client.server, self.server_url.strip('/'))
        self.assertEqual(self.client.address, self.address)
        self.assertEqual(self.client.token, self.token)
        self.assertEqual(self.client.desired_size, self.size)
        self.assertIsNone(self.client.heartbeat)

    def test_connect_no_token_no_address(self):
        self.client.address = None
        self.client.token = None
        with self.assertRaises(DownstreamError) as ex:
            self.client.connect()
        self.assertEqual(str(ex.exception),
                         'If no token is specified, address must be.')

    def test_connect_failed(self):
        with mock.patch('downstream_farmer.client.handle_json_response')\
                as hp:
            hp.side_effect = DownstreamError('test error')
            with self.assertRaises(DownstreamError) as ex:
                self.client.connect()
            self.assertEqual(str(ex.exception),
                             'Unable to connect: test error')

    def test_connect_malformed(self):
        inst = self.client.session.get.return_value
        inst.json.return_value = {"invalid": "dict"}
        with self.assertRaises(DownstreamError) as ex:
            self.client.connect()
        self.assertEqual(str(ex.exception), 'Malformed response from server.')

    def test_connect_invalid_heartbeat(self):
        inst = self.client.session.get.return_value
        inst.json.return_value = {
            "heartbeat": "test heartbeat",
            "token": "test token",
            "type": "invalid type"
        }
        with self.assertRaises(DownstreamError) as ex:
            self.client.connect()
        self.assertEqual(str(ex.exception), 'Unknown Heartbeat Type')

    def test_connect_working_new(self):
        self.client.token = None
        self.client.session.get.return_value.json.return_value \
            = MockValues.connect_response
        self.client.connect()
        self.client.session.get.assert_called_with('{0}/new/{1}'.format(
            self.server_url.strip('/') + self.api_path, self.address),
                                                   verify=None)
        self.assertEqual(self.client.token,
                         MockValues.connect_response['token'])
        self.assertEqual(
            self.client.heartbeat,
            Heartbeat.fromdict(MockValues.connect_response['heartbeat']))

    def test_connect_working(self):
        self.client.session.get.return_value.json.return_value \
            = MockValues.connect_response
        self.client.connect()
        self.client.session.get.assert_called_with('{0}/heartbeat/{1}'.format(
            self.server_url.strip('/') + self.api_path, self.token),
                                                   verify=None)
        self.assertEqual(self.client.token,
                         MockValues.connect_response['token'])
        self.assertEqual(
            self.client.heartbeat,
            Heartbeat.fromdict(MockValues.connect_response['heartbeat']))

    def test_connect_sign(self):
        self.client.msg = 'test message'
        self.client.sig = 'HyzVUenXXo4pa+kgm1vS8PNJM83eIXFC5r0q86FGbqFcdla6rcw'
        '72/ciXiEPfjli3ENfwWuESHhv6K9esI0dl5I='
        self.client.address = '19qVgG8C6eXwKMMyvVegsi3xCsKyk3Z3jV'
        self.client.token = None
        self.client.session.post.return_value.json.return_value \
            = MockValues.connect_response
        self.client.connect()
        self.client.session.post.\
            assert_called_with(
                '{0}/new/{1}'
                .format(self.server_url.strip('/') + self.api_path,
                        self.client.address),
                data=json.dumps({
                    "message": self.client.msg,
                    "signature": self.client.sig
                }),
                headers={
                    'Content-Type': 'application/json'
                },
                verify=None)
        self.assertEqual(self.client.token,
                         MockValues.connect_response['token'])
        self.assertEqual(
            self.client.heartbeat,
            Heartbeat.fromdict(MockValues.connect_response['heartbeat']))

    def test_get_contract_no_token(self):
        with mock.patch('downstream_farmer.client.handle_json_response')\
                as hp:
            hp.side_effect = DownstreamError('test error')
            with self.assertRaises(DownstreamError) as ex:
                self.client._get_contracts()
            self.assertEqual(str(ex.exception),
                             'Unable to get contracts: test error')

    def test_get_contract_malformed(self):
        patch = self.client.session.get
        patch.return_value.json.return_value = {"invalid": "dict"}
        with self.assertRaises(DownstreamError) as ex:
            self.client._get_contracts()
        self.assertEqual(str(ex.exception), 'Malformed response from server.')

    def test_get_contracts_working(self):
        self.client.heartbeat = self.test_heartbeat
        patch = self.client.session.get
        inst = patch.return_value
        inst.json.return_value = MockValues.get_chunks_response.copy()
        contracts = self.client._get_contracts(100)
        self.assertEqual(contracts[0].hash, self.test_contract.hash)
        self.assertEqual(contracts[0].seed, self.test_contract.seed)
        self.assertEqual(contracts[0].size, self.test_contract.size)
        self.assertEqual(contracts[0].challenge, self.test_contract.challenge)
        self.assertAlmostEqual((contracts[0].expiration -
                                self.test_contract.expiration).total_seconds(),
                               0,
                               delta=1)
        self.assertEqual(contracts[0].tag, self.test_contract.tag)

    def test_get_contract_no_chunks_available(self):
        self.client.heartbeat = self.test_heartbeat
        with mock.patch(
                'downstream_farmer.client.handle_json_response') as hpatch:
            hpatch.return_value = dict(chunks=[])
            contracts = self.client._get_contracts()
            self.assertEqual(len(contracts), 0)

    def setup_run_mocks(self):
        self.client.thread_manager = mock.MagicMock()
        self.client.thread_manager.running = True
        self.client.worker_pool = mock.MagicMock()
        self.client._get_contracts = mock.MagicMock()
        self.client._get_contracts.return_value = [self.test_contract]
        self.client._add_contract = mock.MagicMock()
        self.client.contract_thread = mock.MagicMock()
        self.client.get_total_size = mock.MagicMock()
        self.client.get_total_size.return_value = 0

    def test_run_contract_manager(self):
        self.setup_run_mocks()

        self.client.contract_thread.wait.side_effect = \
            MockContractShutdown(self.client.thread_manager)

        self.client._add_contract.side_effect = AddContractMock(self.client)

        self.client._run_contract_manager()

        self.assertFalse(self.client.thread_manager.signal_shutdown.called)
        self.client._add_contract.assert_called_with(self.test_contract)

    def test_run_contract_manager_obtain_fail_no_retry(self):
        self.setup_run_mocks()

        self.client.contract_count = mock.MagicMock()
        self.client.contract_count.return_value = 0
        self.client._get_contracts.side_effect = DownstreamError('test error')

        self.client.thread_manager.signal_shutdown.side_effect = \
            MockContractShutdown(self.client.thread_manager)

        with self.assertRaises(DownstreamError) as ex:
            self.client._run_contract_manager()
        self.assertEqual(str(ex.exception), 'test error')

    def test_run_contract_manager_obtain_fail_retry(self):
        self.setup_run_mocks()

        self.client.contract_count = mock.MagicMock()
        self.client.contract_count.return_value = 0
        self.client._get_contracts.side_effect = \
            [DownstreamError('test error'), [self.test_contract]]

        self.client._add_contract.side_effect = AddContractMock(self.client)

        self.client.contract_thread.wait.side_effect = \
            MockContractShutdown(self.client.thread_manager)

        self.client._run_contract_manager(True)

        self.assertFalse(self.client.thread_manager.signal_shutdown.called)

    def test_run_contract_manager_shutdown_during_acquisition(self):
        self.setup_run_mocks()

        self.client._add_contract.side_effect = \
            AddContractMock(self.client, self.client.thread_manager)

        self.client._run_contract_manager()

        self.assertFalse(self.client.thread_manager.signal_shutdown.called)

    def test_run_contract_manager_number_requirement(self):
        self.setup_run_mocks()

        self.client._add_contract.side_effect = \
            AddContractMock(self.client)

        self.client.heartbeat_count = 1

        self.client.thread_manager.signal_shutdown.side_effect = \
            MockContractShutdown(self.client.thread_manager)

        self.client.desired_heartbeats = 1

        self.client._run_contract_manager()

        self.assertTrue(self.client.thread_manager.signal_shutdown.called)

    def test_run_async(self):
        self.client.thread_manager = mock.MagicMock()
        self.contract_thread = mock.MagicMock()
        self.client.thread_manager.create_thread.return_value = \
            self.contract_thread

        self.client.run_async()

        self.assertTrue(self.contract_thread.start.called)

    def test_set_cert_path(self):
        test_path = 'testpath'
        self.client._set_requests_verify_arg = mock.MagicMock()
        self.client.set_cert_path(test_path)
        self.assertEqual(self.client.cert_path, test_path)
        self.assertTrue(self.client._set_requests_verify_arg.called)

    def test_set_verify_cert(self):
        val = not self.client.verify_cert
        self.client._set_requests_verify_arg = mock.MagicMock()
        self.client.set_verify_cert(val)
        self.assertEqual(self.client.verify_cert, val)
        self.assertTrue(self.client._set_requests_verify_arg.called)

    def test_set_requests_verify_arg_false(self):
        self.client.verify_cert = False
        self.client.requests_verify_arg = True
        self.client._set_requests_verify_arg()
        self.assertFalse(self.client.requests_verify_arg)