def test_verify_response(self): response = dispatcher.ResponseTuple('200', [('Content-Type', 'a')], '') # Expected response self.assertEqual(True, self.server.verify_response(response, 200, 'a')) # Any content type accepted self.assertEqual(True, self.server.verify_response(response, 200, None)) # Status code mismatch self.assertEqual(False, self.server.verify_response(response, 400, 'a')) # Content type mismatch self.assertEqual(False, self.server.verify_response(response, 200, 'b')) response = dispatcher.ResponseTuple('200', [('Content-Length', '10')], '') # Any content type accepted self.assertEqual(True, self.server.verify_response(response, 200, None)) # Specified content type not matched self.assertEqual(False, self.server.verify_response(response, 200, 'a'))
def test_chat(self): self.mox.StubOutWithMock(xmpp_request_handler.XmppRequestHandler, '_send') request = webapp2.Request.blank('/xmpp', POST={'message_type': 'chat', 'to': '*****@*****.**', 'from': '*****@*****.**', 'chat': 'Chat content'}) response = webapp2.Response() handler = xmpp_request_handler.XmppRequestHandler(request, response) data = xmpp_request_handler._FormData() data.add_text('from', '*****@*****.**', 'plain') data.add_text('to', '*****@*****.**', 'plain') data.add_text('body', 'Chat content', 'plain') data.add_text( 'stanza', CompareXml( '<ns0:message from="*****@*****.**" to="*****@*****.**" ' 'type="chat" xmlns:ns0="jabber:client">' '<ns0:body>Chat content</ns0:body>' '</ns0:message>'), 'xml') handler._send('/_ah/xmpp/message/chat/', data).AndReturn( dispatcher.ResponseTuple('404 Not Found', [], 'Response')) self.mox.ReplayAll() handler.post() self.mox.VerifyAll() self.assertEqual('404 Not Found', response.status)
def test_handle_spi_response_batch_json_rpc(self): """Verify that batch requests have an appropriate batch response.""" orig_request = test_utils.build_request( '/_ah/api/rpc', '[{"method": "foo.bar", "apiVersion": "X"}]') self.assertTrue(orig_request.is_batch()) self.assertTrue(orig_request.is_rpc()) orig_request.request_id = 'Z' spi_request = orig_request.copy() spi_response = dispatcher.ResponseTuple('200 OK', [('a', 'b')], '{"some": "response"}') response = self.server.handle_spi_response(orig_request, spi_request, spi_response, {}, self.start_response) response = ''.join( response) # Merge response iterator into single body. self.assertEqual(self.response_status, '200 OK') self.assertIn(('a', 'b'), self.response_headers) self.assertEqual([{ 'id': 'Z', 'result': { 'some': 'response' } }], json.loads(response))
def test_subscribe(self): self.mox.StubOutWithMock(xmpp_request_handler.XmppRequestHandler, '_send') request = webapp2.Request.blank('/xmpp', POST={'message_type': 'subscribe', 'to': '*****@*****.**', 'from': '*****@*****.**', 'subscription_type': 'subscribe'}) response = webapp2.Response() handler = xmpp_request_handler.XmppRequestHandler(request, response) data = xmpp_request_handler._FormData() data.add_text('from', '*****@*****.**', 'plain') data.add_text('to', '*****@*****.**', 'plain') data.add_text( 'stanza', CompareXml( '<ns0:presence from="*****@*****.**" to="*****@*****.**" ' 'type="subscribe" xmlns:ns0="jabber:client" />'), 'xml') handler._send('/_ah/xmpp/subscription/subscribe/', data).AndReturn( dispatcher.ResponseTuple('404 Not Found', [], 'Response')) self.mox.ReplayAll() handler.post() self.mox.VerifyAll() self.assertEqual('404 Not Found', response.status)
def test_handle_non_json_spi_response_cors(self): """Test that an error response still handles CORS headers.""" server_response = dispatcher.ResponseTuple( '200 OK', [('Content-type', 'text/plain')], 'This is an invalid response.') response = self.check_cors([('origin', 'test.com')], True, 'test.com', server_response=server_response) self.assertEqual( {'error': {'message': 'Non-JSON reply: This is an invalid response.'}}, json.loads(response))
def test_handle_spi_response_rest(self): orig_request = test_utils.build_request('/_ah/api/test', '{}') spi_request = orig_request.copy() body = json.dumps({'some': 'response'}, indent=1) spi_response = dispatcher.ResponseTuple('200 OK', [('a', 'b')], body) response = self.server.handle_spi_response(orig_request, spi_request, spi_response, {}, self.start_response) self.assert_http_match(response, '200 OK', [('a', 'b'), ('Content-Length', '%d' % len(body))], body)
def test_send_email(self): response = webapp2.Response() handler = mail_request_handler.MailRequestHandler(None, response) message = object() self.mox.StubOutWithMock(handler, '_send') self.mox.StubOutWithMock(handler, '_generate_email') handler._generate_email('to', 'from', 'cc', 'subject', 'body').AndReturn( message) handler._send('/_ah/mail/to', message).AndReturn( dispatcher.ResponseTuple('500 Internal Server Error', [], 'Response')) self.mox.ReplayAll() handler._send_email('to', 'from', 'cc', 'subject', 'body') self.mox.VerifyAll() self.assertEqual(500, response.status_int)
def prepare_dispatch(self, config): # The dispatch call will make a call to get_api_configs, making a # dispatcher request. Set up that request. request_method = 'POST' request_path = '/_ah/spi/BackendService.getApiConfigs' request_headers = [('Content-Type', 'application/json')] request_body = '{}' response_body = json.dumps({'items': [config]}) self.mock_dispatcher.add_request( request_method, request_path, request_headers, request_body, endpoints_server._SERVER_SOURCE_IP).AndReturn( dispatcher.ResponseTuple( '200 OK', [('Content-Type', 'application/json'), ('Content-Length', str(len(response_body)))], response_body))
def test_handle_non_json_spi_response(self): orig_request = test_utils.build_request('/_ah/api/fake/path') spi_request = orig_request.copy() spi_response = dispatcher.ResponseTuple( 200, [('Content-type', 'text/plain')], 'This is an invalid response.') response = self.server.handle_spi_response(orig_request, spi_request, spi_response, {}, self.start_response) error_json = {'error': {'message': 'Non-JSON reply: This is an invalid response.'}} body = json.dumps(error_json) self.assert_http_match(response, '500', [('Content-Type', 'application/json'), ('Content-Length', '%d' % len(body))], body)
def test_post(self): self.mox.StubOutWithMock(cron_handler.CronHandler, 'dispatcher') request = webapp2.Request.blank('/cron', POST={'url': '/url'}) response = webapp2.Response() handler = cron_handler.CronHandler(request, response) handler.dispatcher = self.mox.CreateMock(dispatcher.Dispatcher) handler.dispatcher.add_request( method='GET', relative_url='/url', headers=[('X-AppEngine-Cron', 'true')], body='', source_ip='0.1.0.1').AndReturn( dispatcher.ResponseTuple('500 Internal Server Error', [], '')) self.mox.ReplayAll() handler.post() self.mox.VerifyAll() self.assertEqual(500, response.status_int)
def test_dispatch_spi_error(self): """Check the error response if the SPI returns an error.""" config = json.dumps({ 'name': 'guestbook_api', 'version': 'v1', 'methods': { 'guestbook.get': { 'httpMethod': 'GET', 'path': 'greetings/{gid}', 'rosyMethod': 'MyApi.greetings_get' } } }) request = test_utils.build_request('/_ah/api/foo') self.prepare_dispatch(config) self.mox.StubOutWithMock(self.server, 'call_spi') # The application chose to throw a 404 error. response = dispatcher.ResponseTuple('404 Not Found', [], ('{"state": "APPLICATION_ERROR",' ' "error_message": "Test error"}')) self.server.call_spi(request, mox.IgnoreArg()).AndRaise( errors.BackendError(response)) self.mox.ReplayAll() response = self.server.dispatch(request, self.start_response) self.mox.VerifyAll() expected_response = ( '{\n' ' "error": {\n' ' "code": 404, \n' ' "errors": [\n' ' {\n' ' "domain": "global", \n' ' "message": "Test error", \n' ' "reason": "notFound"\n' ' }\n' ' ], \n' ' "message": "Test error"\n' ' }\n' '}') response = ''.join(response) self.assert_http_match(response, '404 Not Found', [('Content-Length', '%d' % len(expected_response)), ('Content-Type', 'application/json')], expected_response)
def test_dispatch_rpc_error(self): """Test than an RPC call that returns an error is handled properly.""" config = json.dumps({ 'name': 'guestbook_api', 'version': 'v1', 'methods': { 'guestbook.get': { 'httpMethod': 'GET', 'path': 'greetings/{gid}', 'rosyMethod': 'MyApi.greetings_get' } } }) request = test_utils.build_request( '/_ah/api/rpc', '{"method": "foo.bar", "apiVersion": "X", "id": "gapiRpc"}') self.prepare_dispatch(config) self.mox.StubOutWithMock(self.server, 'call_spi') # The application chose to throw a 404 error. response = dispatcher.ResponseTuple( '404 Not Found', [], ('{"state": "APPLICATION_ERROR",' ' "error_message": "Test error"}')) self.server.call_spi(request, mox.IgnoreArg()).AndRaise( errors.BackendError(response)) self.mox.ReplayAll() response = self.server.dispatch(request, self.start_response) self.mox.VerifyAll() expected_response = { 'error': { 'code': 404, 'message': 'Test error', 'data': [{ 'domain': 'global', 'reason': 'notFound', 'message': 'Test error', }] }, 'id': 'gapiRpc' } response = ''.join(response) self.assertEqual('200 OK', self.response_status) self.assertEqual(expected_response, json.loads(response))
def test_handle_spi_response_json_rpc(self): """Verify headers transformed, JsonRpc response transformed, written.""" orig_request = test_utils.build_request( '/_ah/api/rpc', '{"method": "foo.bar", "apiVersion": "X"}') self.assertTrue(orig_request.is_rpc()) orig_request.request_id = 'Z' spi_request = orig_request.copy() spi_response = dispatcher.ResponseTuple('200 OK', [('a', 'b')], '{"some": "response"}') response = self.server.handle_spi_response(orig_request, spi_request, spi_response, self.start_response) response = ''.join(response) # Merge response iterator into single body. self.assertEqual(self.response_status, '200 OK') self.assertIn(('a', 'b'), self.response_headers) self.assertEqual({'id': 'Z', 'result': {'some': 'response'}}, json.loads(response))
def assert_dispatch_to_spi(self, request, config, spi_path, expected_spi_body_json=None): """Assert that dispatching a request to the SPI works. Mock out the dispatcher.add_request and handle_spi_response, and use these to ensure that the correct request is being sent to the back end when Dispatch is called. Args: request: An ApiRequest, the request to dispatch. config: A dict containing the API configuration. spi_path: A string containing the relative path to the SPI. expected_spi_body_json: If not None, this is a JSON object containing the mock response sent by the back end. If None, this will create an empty response. """ self.prepare_dispatch(config) spi_headers = [('Content-Type', 'application/json')] spi_body_json = expected_spi_body_json or {} spi_response = dispatcher.ResponseTuple('200 OK', [], 'Test') self.mock_dispatcher.add_request( 'POST', spi_path, spi_headers, JsonMatches(spi_body_json), request.source_ip).AndReturn(spi_response) self.mox.StubOutWithMock(self.server, 'handle_spi_response') self.server.handle_spi_response(mox.IsA(api_request.ApiRequest), mox.IsA(api_request.ApiRequest), spi_response, mox.IsA(dict), self.start_response).AndReturn('Test') # Run the test. self.mox.ReplayAll() response = self.server.dispatch(request, self.start_response) self.mox.VerifyAll() self.assertEqual('Test', response)
def check_cors(self, request_headers, expect_response, expected_origin=None, expected_allow_headers=None, server_response=None): """Check that CORS headers are handled correctly. Args: request_headers: A list of (header, value), to be used as headers in the request. expect_response: A boolean, whether or not CORS headers are expected in the response. expected_origin: A string or None. If this is a string, this is the value that's expected in the response's allow origin header. This can be None if expect_response is False. expected_allow_headers: A string or None. If this is a string, this is the value that's expected in the response's allow headers header. If this is None, then the response shouldn't have any allow headers headers. server_response: A dispatcher.ResponseTuple or None. The backend's response, to be wrapped and returned as the server's response. If this is None, a generic response will be generated. Returns: A string containing the body of the response that would be sent. """ orig_request = test_utils.build_request('/_ah/api/fake/path', http_headers=request_headers) spi_request = orig_request.copy() if server_response is None: server_response = dispatcher.ResponseTuple( '200 OK', [('Content-type', 'application/json')], '{}') response = self.server.handle_spi_response(orig_request, spi_request, server_response, {}, self.start_response) headers = dict(self.response_headers) if expect_response: self.assertIn(endpoints_server._CORS_HEADER_ALLOW_ORIGIN, headers) self.assertEqual( headers[endpoints_server._CORS_HEADER_ALLOW_ORIGIN], expected_origin) self.assertIn(endpoints_server._CORS_HEADER_ALLOW_METHODS, headers) self.assertEqual(set(headers[ endpoints_server._CORS_HEADER_ALLOW_METHODS].split(',')), endpoints_server._CORS_ALLOWED_METHODS) if expected_allow_headers is not None: self.assertIn(endpoints_server._CORS_HEADER_ALLOW_HEADERS, headers) self.assertEqual( headers[endpoints_server._CORS_HEADER_ALLOW_HEADERS], expected_allow_headers) else: self.assertNotIn(endpoints_server._CORS_HEADER_ALLOW_HEADERS, headers) else: self.assertNotIn(endpoints_server._CORS_HEADER_ALLOW_ORIGIN, headers) self.assertNotIn(endpoints_server._CORS_HEADER_ALLOW_METHODS, headers) self.assertNotIn(endpoints_server._CORS_HEADER_ALLOW_HEADERS, headers) return ''.join(response)