OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python2.4 |
| 2 # |
| 3 # Copyright 2016 Google Inc. All Rights Reserved. |
| 4 # |
| 5 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 # you may not use this file except in compliance with the License. |
| 7 # You may obtain a copy of the License at |
| 8 # |
| 9 # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 # |
| 11 # Unless required by applicable law or agreed to in writing, software |
| 12 # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 # See the License for the specific language governing permissions and |
| 15 # limitations under the License. |
| 16 |
| 17 """Tests for Apiserving library. |
| 18 |
| 19 Tests: |
| 20 ModuleInterfaceTest: Tests interface of module. |
| 21 ApiServerTest: Tests core functionality: registry, requests, errors. |
| 22 """ |
| 23 |
| 24 import httplib |
| 25 import json |
| 26 import logging |
| 27 import unittest |
| 28 import urllib2 |
| 29 |
| 30 import endpoints.api_backend_service as api_backend_service |
| 31 import endpoints.api_config as api_config |
| 32 import endpoints.api_exceptions as api_exceptions |
| 33 import endpoints.apiserving as apiserving |
| 34 import mox |
| 35 from protorpc import message_types |
| 36 from protorpc import messages |
| 37 from protorpc import protojson |
| 38 from protorpc import registry |
| 39 from protorpc import remote |
| 40 from protorpc import transport |
| 41 |
| 42 import endpoints.resource_container as resource_container |
| 43 import test_util |
| 44 |
| 45 import webtest |
| 46 |
| 47 package = 'endpoints.test' |
| 48 |
| 49 |
| 50 class ExceptionRequest(messages.Message): |
| 51 exception_type = messages.StringField(1) |
| 52 message = messages.StringField(2) |
| 53 |
| 54 |
| 55 class Custom405Exception(api_exceptions.ServiceException): |
| 56 http_status = httplib.METHOD_NOT_ALLOWED |
| 57 |
| 58 |
| 59 class Custom408Exception(api_exceptions.ServiceException): |
| 60 http_status = httplib.REQUEST_TIMEOUT |
| 61 |
| 62 |
| 63 @api_config.api('aservice', 'v2', hostname='aservice.appspot.com', |
| 64 description='A Service API') |
| 65 class AService(remote.Service): |
| 66 |
| 67 @api_config.method(name='noopname', path='nooppath') |
| 68 def Noop(self, unused_request): |
| 69 return message_types.VoidMessage() |
| 70 |
| 71 @api_config.method(path='type_error') |
| 72 def RaiseTypeError(self, unused_request): |
| 73 raise TypeError('a type error') |
| 74 |
| 75 @api_config.method(path='app_error') |
| 76 def RaiseNormalApplicationError(self, unused_request): |
| 77 raise remote.ApplicationError('whatever') |
| 78 |
| 79 # Silence warning about not deriving from Exception |
| 80 # pylint: disable=nonstandard-exception |
| 81 @api_config.method(ExceptionRequest, path='exception') |
| 82 def RaiseException(self, request): |
| 83 exception_class = (getattr(api_exceptions, request.exception_type, None) or |
| 84 globals().get(request.exception_type)) |
| 85 raise exception_class(request.message) |
| 86 |
| 87 |
| 88 @api_config.api('bservice', 'v3', hostname='bservice.appspot.com', |
| 89 description='Another Service API') |
| 90 class BService(remote.Service): |
| 91 |
| 92 @api_config.method(path='noop') |
| 93 def Noop(self, unused_request): |
| 94 return message_types.VoidMessage() |
| 95 |
| 96 |
| 97 test_request = resource_container.ResourceContainer( |
| 98 message_types.VoidMessage, |
| 99 id=messages.IntegerField(1, required=True)) |
| 100 |
| 101 |
| 102 @api_config.api(name='testapi', version='v3', description='A wonderful API.') |
| 103 class TestService(remote.Service): |
| 104 |
| 105 @api_config.method(test_request, |
| 106 message_types.VoidMessage, |
| 107 http_method='DELETE', path='items/{id}') |
| 108 # Silence lint warning about method naming conventions |
| 109 # pylint: disable=g-bad-name |
| 110 def delete(self, unused_request): |
| 111 return message_types.VoidMessage() |
| 112 |
| 113 |
| 114 @api_config.api(name='testapicustomurl', version='v3', |
| 115 description='A wonderful API.', base_path='/my/base/path/') |
| 116 class TestServiceCustomUrl(remote.Service): |
| 117 |
| 118 @api_config.method(test_request, |
| 119 message_types.VoidMessage, |
| 120 http_method='DELETE', path='items/{id}') |
| 121 # Silence lint warning about method naming conventions |
| 122 # pylint: disable=g-bad-name |
| 123 def delete(self, unused_request): |
| 124 return message_types.VoidMessage() |
| 125 |
| 126 |
| 127 my_api = api_config.api(name='My Service', version='v1') |
| 128 |
| 129 |
| 130 @my_api.api_class() |
| 131 class MultiClassService1(remote.Service): |
| 132 |
| 133 @api_config.method(path='s1') |
| 134 def S1method(self, unused_request): |
| 135 return message_types.VoidMessage() |
| 136 |
| 137 |
| 138 @my_api.api_class() |
| 139 class MultiClassService2(remote.Service): |
| 140 |
| 141 @api_config.method(path='s2') |
| 142 def S2method(self, unused_request): |
| 143 return message_types.VoidMessage() |
| 144 |
| 145 |
| 146 TEST_SERVICE_API_CONFIG = {'items': [{ |
| 147 'abstract': False, |
| 148 'adapter': { |
| 149 'bns': 'https://None/_ah/api', |
| 150 'type': 'lily', |
| 151 'deadline': 10.0 |
| 152 }, |
| 153 'defaultVersion': True, |
| 154 'description': 'A wonderful API.', |
| 155 'descriptor': { |
| 156 'methods': { |
| 157 'TestService.delete': {} |
| 158 }, |
| 159 'schemas': { |
| 160 'ProtorpcMessageTypesVoidMessage': { |
| 161 'description': 'Empty message.', |
| 162 'id': 'ProtorpcMessageTypesVoidMessage', |
| 163 'properties': {}, |
| 164 'type': 'object' |
| 165 } |
| 166 } |
| 167 }, |
| 168 'extends': 'thirdParty.api', |
| 169 'methods': { |
| 170 'testapi.delete': { |
| 171 'httpMethod': 'DELETE', |
| 172 'path': 'items/{id}', |
| 173 'request': { |
| 174 'body': 'empty', |
| 175 'parameterOrder': ['id'], |
| 176 'parameters': { |
| 177 'id': { |
| 178 'required': True, |
| 179 'type': 'int64', |
| 180 } |
| 181 } |
| 182 }, |
| 183 'response': { |
| 184 'body': 'empty' |
| 185 }, |
| 186 'rosyMethod': 'TestService.delete', |
| 187 'scopes': ['https://www.googleapis.com/auth/userinfo.email'], |
| 188 'clientIds': ['292824132082.apps.googleusercontent.com'], |
| 189 'authLevel': 'NONE' |
| 190 } |
| 191 }, |
| 192 'name': 'testapi', |
| 193 'root': 'https://None/_ah/api', |
| 194 'version': 'v3' |
| 195 }]} |
| 196 |
| 197 |
| 198 TEST_SERVICE_CUSTOM_URL_API_CONFIG = {'items': [{ |
| 199 'abstract': False, |
| 200 'adapter': { |
| 201 'bns': 'https://None/my/base/path', |
| 202 'type': 'lily', |
| 203 'deadline': 10.0 |
| 204 }, |
| 205 'defaultVersion': True, |
| 206 'description': 'A wonderful API.', |
| 207 'descriptor': { |
| 208 'methods': { |
| 209 'TestServiceCustomUrl.delete': {} |
| 210 }, |
| 211 'schemas': { |
| 212 'ProtorpcMessageTypesVoidMessage': { |
| 213 'description': 'Empty message.', |
| 214 'id': 'ProtorpcMessageTypesVoidMessage', |
| 215 'properties': {}, |
| 216 'type': 'object' |
| 217 } |
| 218 } |
| 219 }, |
| 220 'extends': 'thirdParty.api', |
| 221 'methods': { |
| 222 'testapicustomurl.delete': { |
| 223 'httpMethod': 'DELETE', |
| 224 'path': 'items/{id}', |
| 225 'request': { |
| 226 'body': 'empty', |
| 227 'parameterOrder': ['id'], |
| 228 'parameters': { |
| 229 'id': { |
| 230 'required': True, |
| 231 'type': 'int64', |
| 232 } |
| 233 } |
| 234 }, |
| 235 'response': { |
| 236 'body': 'empty' |
| 237 }, |
| 238 'rosyMethod': 'TestServiceCustomUrl.delete', |
| 239 'scopes': ['https://www.googleapis.com/auth/userinfo.email'], |
| 240 'clientIds': ['292824132082.apps.googleusercontent.com'], |
| 241 'authLevel': 'NONE' |
| 242 } |
| 243 }, |
| 244 'name': 'testapicustomurl', |
| 245 'root': 'https://None/my/base/path', |
| 246 'version': 'v3' |
| 247 }]} |
| 248 |
| 249 |
| 250 class ModuleInterfaceTest(test_util.ModuleInterfaceTest, |
| 251 unittest.TestCase): |
| 252 |
| 253 MODULE = apiserving |
| 254 |
| 255 |
| 256 class ApiServerBaseTest(unittest.TestCase): |
| 257 |
| 258 def setUp(self): |
| 259 super(ApiServerBaseTest, self).setUp() |
| 260 |
| 261 |
| 262 class ApiServerTestApiConfigRegistryEndToEnd(unittest.TestCase): |
| 263 # Show diff with expected API config |
| 264 maxDiff = None |
| 265 |
| 266 def setUp(self): |
| 267 super(ApiServerTestApiConfigRegistryEndToEnd, self).setUp() |
| 268 |
| 269 def testGetApiConfigs(self): |
| 270 my_app = apiserving.api_server([TestService]) |
| 271 |
| 272 # 200 with X-Appengine-Peer: apiserving header |
| 273 configs = my_app.get_api_configs() |
| 274 self.assertEqual(TEST_SERVICE_API_CONFIG, configs) |
| 275 |
| 276 |
| 277 class GetAppRevisionTest(unittest.TestCase): |
| 278 def testGetAppRevision(self): |
| 279 environ = {'CURRENT_VERSION_ID': '1.1'} |
| 280 self.assertEqual('1', apiserving._get_app_revision(environ=environ)) |
| 281 |
| 282 def testGetAppRevisionWithNoEntry(self): |
| 283 environ = {} |
| 284 self.assertEqual(None, apiserving._get_app_revision(environ=environ)) |
| 285 |
| 286 |
| 287 class ApiServerTestApiConfigRegistryEndToEndCustomUrl(unittest.TestCase): |
| 288 # Show diff with expected API config |
| 289 maxDiff = None |
| 290 |
| 291 def setUp(self): |
| 292 super(ApiServerTestApiConfigRegistryEndToEndCustomUrl, self).setUp() |
| 293 |
| 294 def testGetApiConfigs(self): |
| 295 my_app = apiserving.api_server([TestServiceCustomUrl]) |
| 296 |
| 297 # 200 with X-Appengine-Peer: apiserving header |
| 298 configs = my_app.get_api_configs() |
| 299 self.assertEqual(TEST_SERVICE_CUSTOM_URL_API_CONFIG, configs) |
| 300 |
| 301 |
| 302 if __name__ == '__main__': |
| 303 unittest.main() |
OLD | NEW |