Exemplo n.º 1
0
class TestTransmissionRPC(asynctest.ClockedTestCase):
    async def setUp(self):
        self.daemon = rsrc.FakeTransmissionDaemon()
        self.daemon.response = rsrc.SESSION_GET_RESPONSE  # Default response
        await self.daemon.start()
        self.client = TransmissionRPC(self.daemon.host, self.daemon.port)

        self.cb_connected = rsrc.FakeCallback('cb_connected')
        self.cb_disconnected = rsrc.FakeCallback('cb_disconnected')
        self.cb_error = rsrc.FakeCallback('cb_error')
        self.client.on('connected', self.cb_connected)
        self.client.on('disconnected', self.cb_disconnected)
        self.client.on('error', self.cb_error)

    async def tearDown(self):
        await self.client.disconnect()
        await self.daemon.stop()

    def assert_not_connected_to(self, host, port):
        self.assertEqual(self.client.host, host)
        self.assertEqual(self.client.port, port)
        self.assertEqual(self.client.connected, False)
        self.assertEqual(self.client.version, None)
        self.assertEqual(self.client.rpcversion, None)
        self.assertEqual(self.client.rpcversionmin, None)

    def assert_connected_to(self, host, port):
        self.assertEqual(self.client.host, host)
        self.assertEqual(self.client.port, port)
        self.assertEqual(self.client.connected, True)
        self.assertNotEqual(self.client.version, None)
        self.assertNotEqual(self.client.rpcversion, None)
        self.assertNotEqual(self.client.rpcversionmin, None)

    def assert_cb_connected_called(self, calls=None, args=None, kwargs=None):
        if calls is not None: self.assertEqual(self.cb_connected.calls, calls)
        if args is not None:
            self.assertEqual(self.cb_connected.args, list(args))
        if kwargs is not None:
            self.assertEqual(self.cb_connected.kwargs, list(kwargs))

    def assert_cb_disconnected_called(self, calls, args=None, kwargs=None):
        if calls is not None:
            self.assertEqual(self.cb_disconnected.calls, calls)
        if args is not None:
            self.assertEqual(self.cb_disconnected.args, list(args))
        if kwargs is not None:
            self.assertEqual(self.cb_disconnected.kwargs, list(kwargs))

    def assert_cb_error_called(self, calls, args=None, kwargs=None):
        if calls is not None: self.assertEqual(self.cb_error.calls, calls)
        if args is not None: self.assertEqual(self.cb_error.args, list(args))
        if kwargs is not None:
            for kw_cb, kw_exp in zip(self.cb_error.kwargs, kwargs):
                self.assertEqual(set(kw_cb), set(kw_exp))
                for k, v in kw_cb.items():
                    self.assertEqual(v, kw_exp[k])

    def test_setting_connection_credentials_to_None(self):
        self.client.url = 'https://*****:*****@foo:123/path'
        self.client.host = None
        self.assertEqual(self.client.url, 'https://*****:*****@some.host'
        self.assertEqual(self.client.url,
                         'http://some.host:9091/transmission/rpc')
        self.assertEqual(self.client.user, 'foo')
        self.assertEqual(self.client.password, 'bar')
        self.client.url = '[email protected]:555'
        self.assertEqual(self.client.url,
                         'http://some.host:555/transmission/rpc')
        self.assertEqual(self.client.user, 'foo')
        self.assertEqual(self.client.password, '')
        self.client.url = 'https://:[email protected]'
        self.assertEqual(self.client.url,
                         'https://some.host:9091/transmission/rpc')
        self.assertEqual(self.client.user, '')
        self.assertEqual(self.client.password, 'bar')
        self.client.url = None
        self.assertEqual(self.client.url,
                         'http://*****:*****@some.host:9091/transmission/rpc')
        self.client.password = '******'
        self.assertEqual(self.client.url_unsafe,
                         'http://*****:*****@some.host:9091/transmission/rpc')
        self.client.user = None
        self.assertEqual(self.client.url_unsafe,
                         'http://:[email protected]:9091/transmission/rpc')
        with self.assertRaises(AttributeError):
            self.client.url_unsafe = 'something'

    async def test_connect_to_good_url(self):
        # TransmissionRPC requests 'session-get' to test the connection and
        # set version properties.
        self.daemon.response = rsrc.SESSION_GET_RESPONSE

        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        await self.client.connect()
        self.assert_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=0)

        await self.client.disconnect()
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=1, args=[(self.client, )])
        self.assert_cb_error_called(calls=0)

    async def test_authentication_with_good_url(self):
        self.client.user = '******'
        self.client.password = '******'

        # TransmissionRPC requests 'session-get' to test the connection and
        # set version properties.
        self.daemon.response = rsrc.SESSION_GET_RESPONSE
        self.daemon.auth = {'user': '******', 'password': '******'}

        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        await self.client.connect()
        self.assert_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=0)

        await self.client.disconnect()
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=1, args=[(self.client, )])
        self.assert_cb_error_called(calls=0)

    async def test_authentication_with_good_url_empty_username(self):
        self.client.user = ''
        self.client.password = '******'

        # TransmissionRPC requests 'session-get' to test the connection and
        # set version properties.
        self.daemon.response = rsrc.SESSION_GET_RESPONSE
        self.daemon.auth = {'user': '', 'password': '******'}

        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        await self.client.connect()
        self.assert_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=0)

        await self.client.disconnect()
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=1, args=[(self.client, )])
        self.assert_cb_error_called(calls=0)

    async def test_authentication_with_good_url_empty_password(self):
        self.client.user = '******'
        self.client.password = ''

        # TransmissionRPC requests 'session-get' to test the connection and
        # set version properties.
        self.daemon.response = rsrc.SESSION_GET_RESPONSE
        self.daemon.auth = {'user': '******', 'password': ''}

        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        await self.client.connect()
        self.assert_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=0)

        await self.client.disconnect()
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=1, args=[(self.client, )])
        self.assert_cb_error_called(calls=0)

    async def test_connect_to_bad_url(self):
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)

        self.client.port = rsrc.unused_port()
        with self.assertRaises(ConnectionError) as cm:
            await self.client.connect()
        self.assertEqual(str(cm.exception),
                         'Failed to connect: %s' % self.client.url)
        self.assert_cb_connected_called(calls=0)
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=1,
                                    args=[(self.client, )],
                                    kwargs=[{
                                        'error': cm.exception
                                    }])

    async def test_reconnect_to_same_url(self):
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)

        await self.client.connect()
        session1 = self.client._session
        self.assert_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=0)

        await self.client.connect()
        session2 = self.client._session
        self.assertIsNot(session1, session2)
        self.assert_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=2,
                                        args=[(self.client, ),
                                              (self.client, )])
        self.assert_cb_disconnected_called(calls=1, args=[(self.client, )])
        self.assert_cb_error_called(calls=0)

    async def test_reconnect_to_bad_url(self):
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)

        await self.client.connect()
        self.assert_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=0)

        self.client.host = 'badhostname'
        with self.assertRaises(ConnectionError) as cm:
            await self.client.connect()
        self.assertEqual(str(cm.exception),
                         'Failed to connect: %s' % self.client.url)
        self.assert_not_connected_to('badhostname', self.daemon.port)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=1, args=[(self.client, )])
        self.assert_cb_error_called(calls=1,
                                    args=[(self.client, )],
                                    kwargs=[{
                                        'error': cm.exception
                                    }])

    async def test_RpcError_during_connect(self):
        self.daemon.response = {'result': 'Error from daemon'}
        with self.assertRaises(RPCError) as cm:
            await self.client.connect()

        self.assertEqual(str(cm.exception),
                         'Invalid RPC response: Error from daemon')
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=0)
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=1,
                                    args=[(self.client, )],
                                    kwargs=[{
                                        'error': cm.exception
                                    }])

    async def test_RpcError_when_connected(self):
        await self.client.connect()
        self.assertEqual(self.client.connected, True)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])

        msg = "I don't like the jib of your request"
        self.daemon.response = {'result': msg}
        with self.assertRaises(RPCError) as cm:
            await self.client.invalid_rpc_method()
        self.assertEqual(str(cm.exception), 'Invalid RPC response: ' + msg)
        self.assertEqual(self.client.connected, True)

        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=1,
                                    args=[(self.client, )],
                                    kwargs=[{
                                        'error': cm.exception
                                    }])

    async def test_ConnectionError_during_connect(self):
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)

        await self.daemon.stop()
        with self.assertRaises(ConnectionError) as cm:
            await self.client.connect()
        self.assertEqual(str(cm.exception),
                         'Failed to connect: ' + self.client.url)
        self.assertEqual(self.client.connected, False)
        self.assert_cb_connected_called(calls=0)
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=1,
                                    args=[(self.client, )],
                                    kwargs=[{
                                        'error': cm.exception
                                    }])

    async def test_ConnectionError_when_connected(self):
        await self.client.connect()
        self.assert_connected_to(self.daemon.host, self.daemon.port)

        await self.daemon.stop()
        with self.assertRaises(ConnectionError) as cm:
            await self.client.torrent_get()
        self.assertEqual(str(cm.exception),
                         'Failed to connect: ' + self.client.url)
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_disconnected_called(calls=1, args=[(self.client, )])
        self.assert_cb_error_called(calls=1,
                                    args=[(self.client, )],
                                    kwargs=[{
                                        'error': cm.exception
                                    }])

    async def test_AuthError_during_connect(self):
        self.daemon.response = web.Response(status=rsrc.AUTH_ERROR_CODE)
        with self.assertRaises(AuthError) as cm:
            await self.client.connect()
        self.assertEqual(str(cm.exception),
                         'Authentication failed: ' + self.client.url)
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=0)
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=1,
                                    args=[(self.client, )],
                                    kwargs=[{
                                        'error': cm.exception
                                    }])

    async def test_AuthError_when_connected(self):
        await self.client.connect()
        self.assert_connected_to(self.daemon.host, self.daemon.port)

        self.daemon.response = web.Response(status=rsrc.AUTH_ERROR_CODE)
        with self.assertRaises(AuthError) as cm:
            await self.client.any_method()
        self.assertEqual(str(cm.exception),
                         'Authentication failed: ' + self.client.url)
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=1, args=[(self.client, )])
        self.assert_cb_error_called(calls=1,
                                    args=[(self.client, )],
                                    kwargs=[{
                                        'error': cm.exception
                                    }])

    async def test_malformed_json(self):
        # The daemon redirects some requests to the web interface in some
        # error cases like 404.
        html = '<html><body>Fake Web Interface</body></html>'
        self.daemon.response = web.Response(text=html)
        self.client._RPC_PATH = '/wrong/path'

        with self.assertRaises(RPCError) as cm:
            await self.client.connect()
        self.assertEqual(
            str(cm.exception),
            'Invalid RPC response: Server sent malformed JSON: %s' % html)
        self.assert_not_connected_to(self.daemon.host, self.daemon.port)
        self.assert_cb_connected_called(calls=0)
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=1,
                                    args=[(self.client, )],
                                    kwargs=[{
                                        'error': cm.exception
                                    }])

    async def test_timeout_minus_one(self):
        delay = self.client.timeout - 1
        await asyncio.gather(self.advance(delay), self.client.connect())
        await self.client.disconnect()
        self.assert_cb_connected_called(calls=1, args=[(self.client, )])
        self.assert_cb_disconnected_called(calls=1, args=[(self.client, )])
        self.assert_cb_error_called(calls=0)

    async def test_timeout_plus_one(self):
        delay = self.client.timeout + 1

        # NOTE: This function is only called sometimes, probably depending on
        # which task finishes first, advance() or client.connect().
        async def delayed_response(request):
            await asyncio.sleep(delay)
            return web.Response(json=rsrc.SESSION_GET_RESPONSE)

        self.daemon.response = delayed_response

        with self.assertRaises(TimeoutError) as cm:
            await asyncio.gather(self.advance(delay), self.client.connect())
        self.assertEqual(
            str(cm.exception),
            'Timeout after %ds: %s' % (self.client.timeout, self.client.url))
        self.assert_cb_connected_called(calls=0)
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=1,
                                    args=[(self.client, )],
                                    kwargs=[{
                                        'error': cm.exception
                                    }])
Exemplo n.º 2
0
class TestTransmissionRPC(asynctest.ClockedTestCase):
    async def setUp(self):
        self.daemon = rsrc.FakeTransmissionDaemon(loop=self.loop)
        self.daemon.response = rsrc.SESSION_GET_RESPONSE   # Default response
        self.url = str(self.daemon.url)
        await self.daemon.start()
        self.client = TransmissionRPC(self.url, loop=self.loop)

        self.cb_connected = rsrc.FakeCallback('cb_connected')
        self.cb_disconnected = rsrc.FakeCallback('cb_disconnected')
        self.cb_error = rsrc.FakeCallback('cb_error')
        self.client.on('connected', self.cb_connected)
        self.client.on('disconnected', self.cb_disconnected)
        self.client.on('error', self.cb_error)

    async def tearDown(self):
        await self.client.disconnect()
        await self.daemon.stop()

    def assert_not_connected_to(self, url):
        self.assertEqual(self.client.url, url)
        self.assertEqual(self.client.connected, False)
        self.assertEqual(self.client.version, None)
        self.assertEqual(self.client.rpcversion, None)
        self.assertEqual(self.client.rpcversionmin, None)

    def assert_connected_to(self, url):
        self.assertEqual(self.client.url, url)
        self.assertEqual(self.client.connected, True)
        self.assertNotEqual(self.client.version, None)
        self.assertNotEqual(self.client.rpcversion, None)
        self.assertNotEqual(self.client.rpcversionmin, None)

    def assert_cb_connected_called(self, calls=None, args=None, kwargs=None):
        if  calls is not None: self.assertEqual(self.cb_connected.calls, calls)
        if   args is not None: self.assertEqual(self.cb_connected.args, list(args))
        if kwargs is not None: self.assertEqual(self.cb_connected.kwargs, list(kwargs))

    def assert_cb_disconnected_called(self, calls, args=None, kwargs=None):
        if  calls is not None: self.assertEqual(self.cb_disconnected.calls, calls)
        if   args is not None: self.assertEqual(self.cb_disconnected.args, list(args))
        if kwargs is not None: self.assertEqual(self.cb_disconnected.kwargs, list(kwargs))

    def assert_cb_error_called(self, calls, args=None, kwargs=None):
        if  calls is not None: self.assertEqual(self.cb_error.calls, calls)
        if   args is not None: self.assertEqual(self.cb_error.args, list(args))
        if kwargs is not None:
            for kw_cb, kw_exp in zip(self.cb_error.kwargs, kwargs):
                self.assertEqual(set(kw_cb), set(kw_exp))
                for k,v in kw_cb.items():
                    self.assertRegex(str(v), str(kw_exp[k]))

    async def test_connect_to_good_url(self):
        # TransmissionRPC requests 'session-get' to test the connection and
        # set version properties.
        self.daemon.response = rsrc.SESSION_GET_RESPONSE

        self.assert_not_connected_to(self.url)
        await self.client.connect(self.url)
        self.assert_connected_to(self.url)
        self.assert_cb_connected_called(calls=1, args=[(self.url,)])
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=0)

        await self.client.disconnect()
        self.assert_not_connected_to(self.url)
        self.assert_cb_connected_called(calls=1, args=[(self.url,)])
        self.assert_cb_disconnected_called(calls=1, args=[(self.url,)])
        self.assert_cb_error_called(calls=0)

    async def test_connect_to_bad_url(self):
        bad_url = rsrc.make_url()
        with self.assertRaises(ConnectionError) as cm:
            await self.client.connect(bad_url)
        self.assertIn(str(bad_url), str(cm.exception))
        self.assert_cb_connected_called(calls=0)
        self.assert_cb_disconnected_called(calls=1, args=[(bad_url,)])
        self.assert_cb_error_called(calls=1,
                                    args=[(bad_url,)],
                                    kwargs=[{'error': r'Connection failed.*'+str(bad_url)}])

    async def test_reconnect_to_same_url(self):
        self.assert_not_connected_to(self.url)

        await self.client.connect(self.url)
        url_id1 = id(self.client.url)
        self.assert_connected_to(self.url)
        self.assert_cb_connected_called(calls=1, args=[(self.url,)])
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=0)

        await self.client.connect()
        url_id2 = id(self.client.url)
        self.assertEqual(url_id1, url_id2)
        self.assert_connected_to(self.url)
        self.assert_cb_connected_called(calls=1, args=[(self.url,)])
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=0)

    async def test_reconnect_to_bad_url(self):
        self.assert_not_connected_to(self.url)

        await self.client.connect(self.url)
        self.assert_cb_connected_called(calls=1, args=[(self.url,)])

        url_id1 = id(self.client.url)
        self.assert_connected_to(self.url)
        self.assert_cb_connected_called(calls=1, args=[(self.url,)])
        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=0)

        other_url = rsrc.make_url()
        with self.assertRaises(ConnectionError):
            await self.client.connect(other_url)
        url_id2 = id(self.client.other_url)
        self.assertNotEqual(url_id1, url_id2)

        self.assert_cb_disconnected_called(calls=2, args=[(self.url,),
                                                          (other_url,)])
        self.assert_cb_disconnected_called(calls=2, args=[(self.url,),
                                                          (other_url,)])
        self.assert_cb_error_called(calls=1,
                                    args=[(other_url,)],
                                    kwargs=[{'error': r'Connection failed.*'+str(other_url)}])

    async def test_RpcError_during_connect(self):
        self.assertEqual(self.client.connected, False)
        self.daemon.response = {'result': 'Error from daemon'}
        with self.assertRaises(RPCError) as cm:
            await self.client.connect()
        self.assertIn('Error from daemon', str(cm.exception))
        self.assertEqual(self.client.connected, False)

        self.assert_cb_connected_called(calls=0)
        self.assert_cb_disconnected_called(calls=1, args=[(self.url,)])
        self.assert_cb_error_called(calls=1,
                                    args=[(self.url,)],
                                    kwargs=[{'error': r'Error from daemon'}])

    async def test_RpcError_when_connected(self):
        msg = "I don't like the jib of your request"
        await self.client.connect()
        self.assertEqual(self.client.connected, True)
        self.assert_cb_connected_called(calls=1, args=[(self.url,)])

        self.daemon.response = {'result': msg}
        with self.assertRaises(RPCError) as cm:
            await self.client.invalid_rpc_method()
        self.assertIn(msg, str(cm.exception))
        self.assertEqual(self.client.connected, True)

        self.assert_cb_disconnected_called(calls=0)
        self.assert_cb_error_called(calls=1,
                                    args=[(self.url,)],
                                    kwargs=[{'error': msg}])

    async def test_ConnectionError_during_connect(self):
        await self.daemon.stop()
        with self.assertRaises(ConnectionError) as cm:
            await self.client.connect()
        self.assertIn(str(self.url), str(cm.exception))
        self.assertEqual(self.client.connected, False)

        self.assert_cb_connected_called(calls=0)
        self.assert_cb_disconnected_called(calls=1, args=[(self.url,)])
        self.assert_cb_error_called(calls=1,
                                    args=[(self.url,)],
                                    kwargs=[{'error': r'Connection failed.*'+str(self.url)}])

    async def test_ConnectionError_when_connected(self):
        await self.client.connect()
        self.assertEqual(self.client.connected, True)
        self.assert_cb_connected_called(calls=1, args=[(self.url,)])

        await self.daemon.stop()
        with self.assertRaises(ConnectionError) as cm:
            await self.client.torrent_get()
        self.assertIn(str(self.url), str(cm.exception))
        self.assertEqual(self.client.connected, False)

        self.assert_cb_disconnected_called(calls=1, args=[(self.url,)])
        self.assert_cb_error_called(calls=1,
                                    args=[(self.url,)],
                                    kwargs=[{'error': r'Connection failed.*'+str(self.url)}])

    async def test_AuthError_during_connect(self):
        self.daemon.response = web.Response(status=rsrc.AUTH_ERROR_CODE)
        with self.assertRaises(AuthError) as cm:
            await self.client.connect()
        self.assertIn('Authentication failed', str(cm.exception))
        self.assertIn(str(self.url), str(cm.exception))
        self.assertEqual(self.client.connected, False)

        self.assert_cb_connected_called(calls=0)
        self.assert_cb_disconnected_called(calls=1, args=[(self.url,)])
        self.assert_cb_error_called(calls=1,
                                    args=[(self.url,)],
                                    kwargs=[{'error': r'Authentication failed.*'+str(self.url)}])

    async def test_AuthError_when_connected(self):
        await self.client.connect()
        self.assertEqual(self.client.connected, True)
        self.daemon.response = web.Response(status=rsrc.AUTH_ERROR_CODE)
        with self.assertRaises(AuthError) as cm:
            await self.client.any_method()
        self.assertIn('Authentication failed', str(cm.exception))
        self.assertIn(str(self.url), str(cm.exception))
        self.assertEqual(self.client.connected, False)

        self.assert_cb_connected_called(calls=1, args=[(self.url,)])
        self.assert_cb_disconnected_called(calls=1, args=[(self.url,)])
        self.assert_cb_error_called(calls=1,
                                    args=[(self.url,)],
                                    kwargs=[{'error': r'Authentication failed.*'+str(self.url)}])

    async def test_malformed_json(self):
        # The daemon redirects some requests to the web interface in some
        # error cases like 404.
        self.daemon.response = '<html><body>Fake Web Interface</body></html>'

        wrong_path_url = '{}/wrong_path'.format(self.url, self.client.url.path)
        with self.assertRaises(RPCError) as cm:
            await self.client.connect(wrong_path_url)
        self.assertIn('malformed JSON', str(cm.exception))
        self.assertEqual(self.client.connected, False)

        self.assert_cb_connected_called(calls=0)
        self.assert_cb_disconnected_called(calls=1, args=[(wrong_path_url,)])
        self.assert_cb_error_called(calls=1,
                                    args=[(wrong_path_url,)],
                                    kwargs=[{'error': r'malformed JSON'}])

    async def test_timeout_minus_one(self):
        delay = self.client.timeout-1
        await asyncio.gather(self.advance(delay),
                             self.client.connect(),
                             loop=self.loop)
        await self.client.disconnect()

        self.assert_cb_connected_called(calls=1, args=[(self.url,)])
        self.assert_cb_disconnected_called(calls=1, args=[(self.url,)])
        self.assert_cb_error_called(calls=0)

    async def test_timeout_plus_one(self):
        delay = self.client.timeout+1

        # NOTE: This function is only called sometimes, probably depending on
        # which task finishes first, advance() or client.connect().
        async def delay_response(request):
            await asyncio.sleep(delay, loop=self.loop)
            return web.json_response(rsrc.SESSION_GET_RESPONSE)
        self.daemon.response = delay_response

        with self.assertRaises(ConnectionError) as cm:
            await asyncio.gather(self.advance(delay),
                                 self.client.connect(),
                                 loop=self.loop)
        self.assertIn('timeout', str(cm.exception).lower())
        self.assertIn(str(self.client.timeout), str(cm.exception))
        self.assertIn(str(self.url), str(cm.exception))

        self.assert_cb_connected_called(calls=0)
        self.assert_cb_disconnected_called(calls=1, args=[(self.url,)])
        self.assert_cb_error_called(calls=1,
                                    args=[(self.url,)],
                                    kwargs=[{'error': r'{}.*{}'.format(self.client.timeout,
                                                                       self.url)}])