コード例 #1
0
ファイル: test_utils.py プロジェクト: tbille/theblues
 def test_make_request_with_macaroons(self):
     def handler(url, request):
         self.assertEqual(request.headers['Macaroons'], 'my-macaroons')
         return {'status_code': 200}
     # Test with a JSON decoded object.
     with HTTMock(handler):
         make_request(URL, macaroons='my-macaroons')
コード例 #2
0
ファイル: test_utils.py プロジェクト: johnsca/theblues
 def test_make_request_server_error(self):
     with HTTMock(self.failing_response_server):
         with patch_log_error() as mock_log_error:
             with self.assertRaises(ServerError) as ctx:
                 make_request(URL, query={'uuid': 'foo'})
     expected_error = ('Error during request: http://example.com/?uuid=foo '
                       'message: server-failed')
     self.assertEqual((500, expected_error), ctx.exception.args)
     mock_log_error.assert_called_once_with(expected_error)
コード例 #3
0
ファイル: test_utils.py プロジェクト: johnsca/theblues
 def test_make_request_unexpected_error(self):
     with mock.patch('theblues.utils.requests.get') as mock_get:
         mock_get.side_effect = ValueError('bad wolf')
         with patch_log_error() as mock_log_error:
             with self.assertRaises(ServerError) as ctx:
                 make_request(URL, query={'uuid': 'foo'})
     expected_error = ('Error during request: http://example.com/?uuid=foo '
                       'message: bad wolf')
     self.assertEqual(expected_error, ctx.exception.args[0])
     mock_log_error.assert_called_once_with(expected_error)
コード例 #4
0
ファイル: test_utils.py プロジェクト: frankban/theblues
 def test_make_request_server_error(self):
     with HTTMock(self.failing_response_server):
         with patch_log_error() as mock_log_error:
             with self.assertRaises(ServerError) as ctx:
                 make_request(URL, query={'uuid': 'foo'})
     expected_error = (
         'Error during request: http://example.com/?uuid=foo '
         'message: server-failed')
     self.assertEqual((500, expected_error), ctx.exception.args)
     mock_log_error.assert_called_once_with(expected_error)
コード例 #5
0
    def set_extra_info(self, username, extra_info):
        """Set extra info for the given user.

        Raise a ServerError if an error occurs in the request process.

        @param username The username for the user to update.
        @param info The extra info as a JSON encoded string, or as a Python
            dictionary like object.
        """
        url = self._get_extra_info_url(username)
        make_request(url, method='PUT', body=extra_info, timeout=self.timeout)
コード例 #6
0
ファイル: test_utils.py プロジェクト: frankban/theblues
 def test_make_request_unexpected_error(self):
     with mock.patch('theblues.utils.requests.get') as mock_get:
         mock_get.side_effect = ValueError('bad wolf')
         with patch_log_error() as mock_log_error:
             with self.assertRaises(ServerError) as ctx:
                 make_request(URL, query={'uuid': 'foo'})
     expected_error = (
         'Error during request: http://example.com/?uuid=foo '
         'message: bad wolf')
     self.assertEqual(expected_error, ctx.exception.args[0])
     mock_log_error.assert_called_once_with(expected_error)
コード例 #7
0
    def set_extra_info(self, username, extra_info):
        """Set extra info for the given user.

        Raise a ServerError if an error occurs in the request process.

        @param username The username for the user to update.
        @param info The extra info as a JSON encoded string, or as a Python
            dictionary like object.
        """
        url = self._get_extra_info_url(username)
        make_request(
            url, method='PUT', body=extra_info, auth=self.auth,
            timeout=self.timeout)
コード例 #8
0
    def login(self, username, json_document):
        """Send user identity information to the identity manager.

        Raise a ServerError if an error occurs in the request process.

        @param username The logged in user.
        @param json_document The JSON payload for login.
        """
        url = '{}u/{}'.format(self.url, username)
        make_request(url,
                     method='PUT',
                     body=json_document,
                     timeout=self.timeout)
コード例 #9
0
    def login(self, username, json_document):
        """Send user identity information to the identity manager.

        Raise a ServerError if an error occurs in the request process.

        @param username The logged in user.
        @param json_document The JSON payload for login.
        """
        url = '{}u/{}'.format(self.url, username)
        make_request(
            url,
            method='PUT',
            body=json_document,
            auth=self.auth,
            timeout=self.timeout)
コード例 #10
0
ファイル: jem.py プロジェクト: pombredanne/theblues
    def get_users_models(self, macaroons):
        """ Get the logged in user's models from the JEM service.

        @param macaroons The discharged JEM macaroons.
        @return The json decoded list of environments.
        """
        return make_request("{}model".format(self.url), macaroons=macaroons)
コード例 #11
0
    def discharge(self, username, macaroon):
        """Discharge the macarooon for the identity.

        Raise a ServerError if an error occurs in the request process.

        @param username The logged in user.
        @param macaroon The macaroon returned from the charm store.
        @return The resulting base64 encoded macaroon.
        @raises ServerError when making request to the discharge endpoint
        InvalidMacaroon when the macaroon passedin or discharged is invalid
        """
        caveats = macaroon.third_party_caveats()
        if len(caveats) != 1:
            raise InvalidMacaroon(
                'Invalid number of third party caveats (1 != {})'
                ''.format(len(caveats)))
        url = '{}discharger/discharge?discharge-for-user={}&id={}'.format(
            self.url, quote(username), caveats[0][1])
        logging.debug('Sending identity info to {}'.format(url))
        logging.debug('data is {}'.format(caveats[0][1]))
        response = make_request(
            url, method='POST', auth=self.auth, timeout=self.timeout)
        try:
            macaroon = response['Macaroon']
            json_macaroon = json.dumps(macaroon)
        except (KeyError, UnicodeDecodeError) as err:
            raise InvalidMacaroon(
                'Invalid macaroon from discharger: {}'.format(err.message))

        return base64.urlsafe_b64encode(json_macaroon.encode('utf-8'))
コード例 #12
0
 def debug(self):
     """Retrieve the debug information from the identity manager."""
     url = '{}debug/status'.format(self.url)
     try:
         return make_request(url, timeout=self.timeout)
     except ServerError as err:
         return {"error": str(err)}
コード例 #13
0
ファイル: plans.py プロジェクト: pombredanne/theblues
    def get_plans(self, reference):
        """Get the plans for a given charm.

        @param the Reference to a charm.
        @return a tuple of plans or an empty tuple if no plans.
        @raise ServerError
        """
        json = make_request(
            '{}charm?charm-url={}'.format(self.url,
                                          'cs:' + reference.path()))
        try:
            return tuple(map(lambda plan: Plan(
                url=plan['url'], plan=plan['plan'],
                created_on=datetime.datetime.strptime(
                    plan['created-on'],
                    "%Y-%m-%dT%H:%M:%SZ"
                ),
                description=plan.get('description'),
                price=plan.get('price')), json))
        except (KeyError, TypeError, ValueError) as err:
            log.info(
                'cannot process terms: invalid JSON response: {!r}'.format(
                    json))
            raise ServerError(
                'unable to get list of plans for {}: {}'.format(
                    reference.path(), err))
コード例 #14
0
ファイル: terms.py プロジェクト: pombredanne/theblues
    def get_terms(self, name, revision=None):
        """ Retrieve a specific term and condition.

        @param name of the terms.
        @param revision of the terms,
               if none provided it will return the latest.
        @return The list of terms.
        @raise ServerError
        """
        url = '{}terms/{}'.format(self.url, name)
        if revision:
            url = '{}?revision={}'.format(url, revision)
        json = make_request(url)
        try:
            # This is always a list of one element.
            data = json[0]
            return Term(name=data['name'],
                        title=data.get('title'),
                        revision=data['revision'],
                        created_on=datetime.datetime.strptime(
                            data['created-on'],
                            "%Y-%m-%dT%H:%M:%SZ"
                            ),
                        content=data['content'])
        except (KeyError, TypeError, ValueError) as err:
            log.info(
                'cannot process terms: invalid JSON response: {!r}'.format(
                    json))
            raise ServerError(
                'unable to get terms for {}: {}'.format(name, err))
コード例 #15
0
    def discharge(self, username, macaroon):
        """Discharge the macarooon for the identity.

        Raise a ServerError if an error occurs in the request process.

        @param username The logged in user.
        @param macaroon The macaroon returned from the charm store.
        @return The resulting base64 encoded macaroon.
        @raises ServerError when making request to the discharge endpoint
        InvalidMacaroon when the macaroon passedin or discharged is invalid
        """
        caveats = macaroon.third_party_caveats()
        if len(caveats) != 1:
            raise InvalidMacaroon(
                'Invalid number of third party caveats (1 != {})'
                ''.format(len(caveats)))
        url = '{}discharger/discharge?discharge-for-user={}&id={}'.format(
            self.url, quote(username), caveats[0][1])
        logging.debug('Sending identity info to {}'.format(url))
        logging.debug('data is {}'.format(caveats[0][1]))
        response = make_request(url,
                                method='POST',
                                auth=self.auth,
                                timeout=self.timeout)
        try:
            macaroon = response['Macaroon']
            json_macaroon = json.dumps(macaroon)
        except (KeyError, UnicodeDecodeError) as err:
            raise InvalidMacaroon(
                'Invalid macaroon from discharger: {}'.format(err.message))

        return base64.urlsafe_b64encode(json_macaroon.encode('utf-8'))
コード例 #16
0
 def debug(self):
     """Retrieve the debug information from the identity manager."""
     url = '{}debug/status'.format(self.url)
     try:
         return make_request(url, timeout=self.timeout)
     except ServerError as err:
         return {"error": str(err)}
コード例 #17
0
    def get_terms(self, name, revision=None):
        """ Retrieve a specific term and condition.

        @param name of the terms.
        @param revision of the terms,
               if none provided it will return the latest.
        @return The list of terms.
        @raise ServerError
        """
        url = '{}terms/{}'.format(self.url, name)
        if revision:
            url = '{}?revision={}'.format(url, revision)
        json = make_request(url, timeout=self.timeout, client=self._client)
        try:
            # This is always a list of one element.
            data = json[0]
            return Term(name=data['name'],
                        title=data.get('title'),
                        revision=data['revision'],
                        created_on=datetime.datetime.strptime(
                            data['created-on'], "%Y-%m-%dT%H:%M:%SZ"),
                        content=data['content'])
        except (KeyError, TypeError, ValueError, IndexError) as err:
            log.info(
                'cannot process terms: invalid JSON response: {!r}'.format(
                    json))
            raise ServerError('unable to get terms for {}: {}'.format(
                name, err))
コード例 #18
0
ファイル: plans.py プロジェクト: chaitujaya720/JujuPractice
    def get_wallet(self, wallet_name):
        """Get a single wallet.

        @param the name of the wallet.
        @return the wallet's total.
        @raise ServerError
        """
        response = make_request(
            '{}wallet/{}'.format(self.url, wallet_name),
            timeout=self.timeout,
            client=self._client)
        try:
            total = response['total']
            return {
                'credit': response['credit'],
                'limit': response['limit'],
                'total': WalletTotal(
                    limit=total['limit'],
                    budgeted=total['budgeted'],
                    available=total['available'],
                    unallocated=total['unallocated'],
                    usage=total['usage'],
                    consumed=total['consumed'])
            }
        except Exception as exc:
            log.error(
                'cannot get wallet from server: {!r}'.format(exc))
            raise ServerError(
                'unable to get list of wallets: {!r}'.format(exc))
コード例 #19
0
ファイル: plans.py プロジェクト: chaitujaya720/JujuPractice
    def get_plans(self, reference):
        """Get the plans for a given charm.

        @param the Reference to a charm.
        @return a tuple of plans or an empty tuple if no plans.
        @raise ServerError
        """
        response = make_request(
            '{}charm?charm-url={}'.format(self.url,
                                          'cs:' + reference.path()),
            timeout=self.timeout, client=self._client)
        try:
            return tuple(map(lambda plan: Plan(
                url=plan['url'], plan=plan['plan'],
                created_on=datetime.datetime.strptime(
                    plan['created-on'],
                    "%Y-%m-%dT%H:%M:%SZ"
                ),
                description=plan.get('description'),
                price=plan.get('price')), response))
        except Exception as err:
            log.error(
                'cannot process plans: invalid JSON response: {!r}'.format(
                    response))
            raise ServerError(
                'unable to get list of plans for {}: {}'.format(
                    reference.path(), err))
コード例 #20
0
    def list_models(self, macaroons):
        """ Get the logged in user's models from the JIMM controller.

        @param macaroons The discharged JIMM macaroons.
        @return The json decoded list of environments.
        """
        return make_request("{}model".format(self.url), macaroons=macaroons,
                            timeout=self.timeout)
コード例 #21
0
    def get_extra_info(self, username):
        """Get extra info for the given user.

        Raise a ServerError if an error occurs in the request process.

        @param username The username for the user who's info is being accessed.
        """
        url = self._get_extra_info_url(username)
        return make_request(url, auth=self.auth, timeout=self.timeout)
コード例 #22
0
    def get_extra_info(self, username):
        """Get extra info for the given user.

        Raise a ServerError if an error occurs in the request process.

        @param username The username for the user who's info is being accessed.
        """
        url = self._get_extra_info_url(username)
        return make_request(url, auth=self.auth, timeout=self.timeout)
コード例 #23
0
    def get_user(self, username):
        """Fetch user data.

        Raise a ServerError if an error occurs in the request process.

        @param username the user's name.
        """
        url = '{}u/{}'.format(self.url, username)
        return make_request(url, auth=self.auth, timeout=self.timeout)
コード例 #24
0
    def get_user(self, username):
        """Fetch user data.

        Raise a ServerError if an error occurs in the request process.

        @param username the user's name.
        """
        url = '{}u/{}'.format(self.url, username)
        return make_request(url, auth=self.auth, timeout=self.timeout)
コード例 #25
0
ファイル: jem.py プロジェクト: pombredanne/theblues
    def get_model(self, macaroons, user, name):
        """ Get a specified model.

        @param macaroons The discharged JEM macaroons.
        @param user The username of the model's owner.
        @param name The name of the model.
        @return The json decoded model.
        """
        return make_request('{}model/{}/{}'.format(self.url, user, name),
                            macaroons=macaroons)
コード例 #26
0
ファイル: test_utils.py プロジェクト: frankban/theblues
 def check_write_request(self, method):
     def handler(url, request):
         self.assertEqual('http://example.com/', url.geturl())
         self.assertEqual(method, request.method)
         self.assertEqual('{"uuid": "foo"}', request.body)
         self.assertEqual(
             'application/json', request.headers['Content-Type'])
         return {
             'status_code': 200,
             'content': b'{"foo":"bar","baz":"bax"}',
         }
     # Test with a JSON decoded object.
     with HTTMock(handler):
         response = make_request(URL, method=method, body={'uuid': 'foo'})
     self.assertEqual({u'foo': u'bar', u'baz': u'bax'}, response)
     # Test with a JSON encoded string.
     with HTTMock(handler):
         response = make_request(URL, method=method, body='{"uuid": "foo"}')
     self.assertEqual({u'foo': u'bar', u'baz': u'bax'}, response)
コード例 #27
0
ファイル: test_utils.py プロジェクト: tbille/theblues
 def check_write_request(self, method):
     def handler(url, request):
         self.assertEqual('http://example.com/', url.geturl())
         self.assertEqual(method, request.method)
         self.assertEqual('{"uuid": "foo"}', request.body)
         self.assertEqual(
             'application/json', request.headers['Content-Type'])
         return {
             'status_code': 200,
             'content': b'{"foo":"bar","baz":"bax"}',
         }
     # Test with a JSON decoded object.
     with HTTMock(handler):
         response = make_request(URL, method=method, body={'uuid': 'foo'})
     self.assertEqual({u'foo': u'bar', u'baz': u'bax'}, response)
     # Test with a JSON encoded string.
     with HTTMock(handler):
         response = make_request(URL, method=method, body='{"uuid": "foo"}')
     self.assertEqual({u'foo': u'bar', u'baz': u'bax'}, response)
コード例 #28
0
    def get_user(self, username, macaroons):
        """Fetch user data.

        Raise a ServerError if an error occurs in the request process.

        @param username the user's name.
        @param macaroons the encoded macaroons string.
        """
        url = '{}u/{}'.format(self.url, username)
        return make_request(url, timeout=self.timeout, macaroons=macaroons)
コード例 #29
0
ファイル: plans.py プロジェクト: chaitujaya720/JujuPractice
    def delete_wallet(self, wallet_name):
        """Delete a wallet.

        @param the name of the wallet.
        @return a success string from the plans server.
        @raise ServerError via make_request.
        """
        return make_request(
            '{}wallet/{}'.format(self.url, wallet_name),
            method='DELETE',
            timeout=self.timeout,
            client=self._client)
コード例 #30
0
ファイル: plans.py プロジェクト: chaitujaya720/JujuPractice
    def delete_budget(self, model_uuid):
        """Delete a budget.

        @param the name of the wallet.
        @param the model UUID.
        @return a success string from the plans server.
        @raise ServerError via make_request.
        """
        return make_request(
            '{}model/{}/budget'.format(self.url, model_uuid),
            method='DELETE',
            timeout=self.timeout,
            client=self._client)
コード例 #31
0
ファイル: plans.py プロジェクト: chaitujaya720/JujuPractice
    def create_wallet(self, wallet_name, limit):
        """Create a new wallet.

        @param the name of the wallet.
        @param the value of the limit.
        @return a success string from the plans server.
        @raise ServerError via make_request.
        """
        request = {
            'wallet': wallet_name,
            'limit': str(limit),
        }
        return make_request(
            '{}wallet'.format(self.url),
            method='POST',
            body=request,
            timeout=self.timeout,
            client=self._client)
コード例 #32
0
ファイル: plans.py プロジェクト: chaitujaya720/JujuPractice
    def create_budget(self, wallet_name, model_uuid, limit):
        """Create a new budget for a model and wallet.

        @param the name of the wallet.
        @param the model UUID.
        @param the new value of the limit.
        @return a success string from the plans server.
        @raise ServerError via make_request.
        """
        request = {
            'model': model_uuid,
            'limit': limit,
        }
        return make_request(
            '{}wallet/{}/budget'.format(self.url, wallet_name),
            method='POST',
            body=request,
            timeout=self.timeout,
            client=self._client)
コード例 #33
0
ファイル: plans.py プロジェクト: chaitujaya720/JujuPractice
    def update_wallet(self, wallet_name, limit):
        """Update a wallet with a new limit.

        @param the name of the wallet.
        @param the new value of the limit.
        @return a success string from the plans server.
        @raise ServerError via make_request.
        """
        request = {
            'update': {
                'limit': str(limit),
            }
        }
        return make_request(
            '{}wallet/{}'.format(self.url, wallet_name),
            method='PATCH',
            body=request,
            timeout=self.timeout,
            client=self._client)
コード例 #34
0
    def discharge_token(self, username):
        """Discharge token for a user.

        Raise a ServerError if an error occurs in the request process.

        @param username The logged in user.
        @return The resulting base64 encoded discharged token.
        """
        url = '{}discharge-token-for-user?username={}'.format(
            self.url, quote(username))
        logging.debug('Sending identity info to {}'.format(url))
        response = make_request(url, method='GET', timeout=self.timeout)
        try:
            macaroon = response['DischargeToken']
            json_macaroon = json.dumps(macaroon)
        except (KeyError, UnicodeDecodeError) as err:
            raise InvalidMacaroon(
                'Invalid macaroon from discharger: {}'.format(err.message))
        return base64.urlsafe_b64encode(
            "[{}]".format(json_macaroon).encode('utf-8'))
コード例 #35
0
ファイル: plans.py プロジェクト: chaitujaya720/JujuPractice
    def update_budget(self, wallet_name, model_uuid, limit):
        """Update a budget limit.

        @param the name of the wallet.
        @param the model UUID.
        @param the new value of the limit.
        @return a success string from the plans server.
        @raise ServerError via make_request.
        """
        request = {
            'update': {
                'wallet': wallet_name,
                'limit': limit,
            }
        }
        return make_request(
            '{}model/{}/budget'.format(self.url, model_uuid),
            method='PATCH',
            body=request,
            timeout=self.timeout,
            client=self._client)
コード例 #36
0
    def discharge_token(self, username):
        """Discharge token for a user.

        Raise a ServerError if an error occurs in the request process.

        @param username The logged in user.
        @return The resulting base64 encoded discharged token.
        """
        url = '{}discharge-token-for-user?username={}'.format(
            self.url, quote(username))
        logging.debug('Sending identity info to {}'.format(url))
        response = make_request(
            url, method='GET', auth=self.auth, timeout=self.timeout)
        try:
            macaroon = response['DischargeToken']
            json_macaroon = json.dumps(macaroon)
        except (KeyError, UnicodeDecodeError) as err:
            raise InvalidMacaroon(
                'Invalid macaroon from discharger: {}'.format(err.message))
        return base64.urlsafe_b64encode("[{}]".format(
            json_macaroon).encode('utf-8'))
コード例 #37
0
ファイル: plans.py プロジェクト: chaitujaya720/JujuPractice
    def list_wallets(self):
        """Get the list of wallets.

        @return an dict containing a list of wallets, a total, and available
            credit.
        @raise ServerError
        """
        response = make_request(
            '{}wallet'.format(self.url),
            timeout=self.timeout,
            client=self._client)
        try:
            total = response['total']
            return {
                'credit': response['credit'],
                'total': WalletTotal(
                    limit=total['limit'],
                    budgeted=total['budgeted'],
                    available=total['available'],
                    unallocated=total['unallocated'],
                    usage=total['usage'],
                    consumed=total['consumed']),
                'wallets': tuple(Wallet(
                    owner=wallet['owner'],
                    wallet=wallet['wallet'],
                    limit=wallet['limit'],
                    budgeted=wallet['budgeted'],
                    unallocated=wallet['unallocated'],
                    available=wallet['available'],
                    consumed=wallet['consumed'],
                    default='default' in wallet)
                    for wallet in response['wallets']),
            }
        except Exception as err:
            log.error(
                'cannot process wallets: invalid JSON response: {!r}'.format(
                    response))
            raise ServerError(
                'unable to get list of wallets: {!r}'.format(err))
コード例 #38
0
ファイル: test_utils.py プロジェクト: tbille/theblues
 def test_make_request_invalid_method(self):
     with self.assertRaises(ValueError) as ctx:
         make_request('http://1.2.3.4', method='bad')
     self.assertEqual('invalid method bad', ctx.exception.args[0])
コード例 #39
0
ファイル: test_utils.py プロジェクト: frankban/theblues
 def test_make_request_invalid_method(self):
     with self.assertRaises(ValueError) as ctx:
         make_request('http://1.2.3.4', method='bad')
     self.assertEqual('invalid method bad', ctx.exception.args[0])
コード例 #40
0
ファイル: test_utils.py プロジェクト: frankban/theblues
 def test_make_request(self):
     with HTTMock(self.subscription_response):
         response = make_request(URL, query={'uuid': 'foo'})
     self.assertEqual({u'foo': u'bar', u'baz': u'bax'}, response)
コード例 #41
0
ファイル: test_utils.py プロジェクト: tbille/theblues
 def test_make_request_empty_response(self):
     with HTTMock(self.empty_response):
         response = make_request(URL, method='POST', body={'uuid': 'foo'})
     self.assertEqual({}, response)
コード例 #42
0
ファイル: test_utils.py プロジェクト: frankban/theblues
 def test_make_request_timeout(self):
     with self.assert_timeout('http://example.com/?uuid=foo', 42):
         make_request(URL, query={'uuid': 'foo'}, timeout=42)
コード例 #43
0
ファイル: test_utils.py プロジェクト: tbille/theblues
 def test_make_request_timeout(self):
     with self.assert_timeout('http://example.com/?uuid=foo', 42):
         make_request(URL, query={'uuid': 'foo'}, timeout=42)
コード例 #44
0
ファイル: test_utils.py プロジェクト: tbille/theblues
 def test_make_request_invalid_json(self):
     with HTTMock(self.invalid_response):
         with self.assertRaises(ServerError) as ctx:
             make_request(URL, query={'uuid': 'foo'})
     self.assertIn('Error decoding JSON response', ctx.exception.args[0])
コード例 #45
0
ファイル: test_utils.py プロジェクト: frankban/theblues
 def test_make_request_empty_response(self):
     with HTTMock(self.empty_response):
         response = make_request(URL, method='POST', body={'uuid': 'foo'})
     self.assertEqual({}, response)
コード例 #46
0
ファイル: test_utils.py プロジェクト: tbille/theblues
 def test_make_request(self):
     with HTTMock(self.subscription_response):
         response = make_request(URL, query={'uuid': 'foo'})
     self.assertEqual({u'foo': u'bar', u'baz': u'bax'}, response)
コード例 #47
0
ファイル: test_utils.py プロジェクト: frankban/theblues
 def test_make_request_invalid_json(self):
     with HTTMock(self.invalid_response):
         with self.assertRaises(ServerError) as ctx:
             make_request(URL, query={'uuid': 'foo'})
     self.assertIn('Error decoding JSON response', ctx.exception.args[0])