Пример #1
0
class TorrentAPITestCase(asynctest.TestCase):
    async def setUp(self):
        self.daemon = rsrc.FakeTransmissionDaemon(loop=self.loop)
        await self.daemon.start()
        self.rpc = TransmissionRPC(self.daemon.host, self.daemon.port, loop=self.loop)
        self.api = TorrentAPI(self.rpc)
        await self.rpc.connect()
        assert self.rpc.connected == True

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

    def assert_torrentkeys_equal(self, key, tlist, *exp):
        self.assertEqual(tuple(t[key] for t in tlist), exp)
Пример #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
        await self.daemon.start()
        self.client = TransmissionRPC(self.daemon.host, self.daemon.port, 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):
        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_url_property(self):
        self.client.host = 'foo'
        self.client.port = 123
        self.client.tls = True
        self.assertEqual(self.client.url, 'https://foo:123/transmission/rpc')
        self.client.tls = False
        self.assertEqual(self.client.url, 'http://foo:123/transmission/rpc')
        self.client.host = 'fuu'
        self.assertEqual(self.client.url, 'http://fuu:123/transmission/rpc')
        self.client.port = '1724'
        self.assertEqual(self.client.url, 'http://fuu:1724/transmission/rpc')
        self.client.path = 'user/transmission/rpc'
        self.assertEqual(self.client.url, 'http://fuu:1724/user/transmission/rpc')

    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)

        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.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.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(),
                             loop=self.loop)
        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, loop=self.loop)
            return web.Response(json=rsrc.SESSION_GET_RESPONSE)
        self.daemon.response = delayed_response

        with self.assertRaises(ConnectionError) as cm:
            await asyncio.gather(self.advance(delay),
                                 self.client.connect(),
                                 loop=self.loop)
        self.assertEqual(str(cm.exception), 'Failed to connect: 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}])