| Index: third_party/google-endpoints/endpoints/test/api_config_manager_test.py
|
| diff --git a/third_party/google-endpoints/endpoints/test/api_config_manager_test.py b/third_party/google-endpoints/endpoints/test/api_config_manager_test.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6c9efd9e89acfd87735ea74c93fe542b354dee47
|
| --- /dev/null
|
| +++ b/third_party/google-endpoints/endpoints/test/api_config_manager_test.py
|
| @@ -0,0 +1,338 @@
|
| +# Copyright 2016 Google Inc. All Rights Reserved.
|
| +#
|
| +# Licensed under the Apache License, Version 2.0 (the "License");
|
| +# you may not use this file except in compliance with the License.
|
| +# You may obtain a copy of the License at
|
| +#
|
| +# http://www.apache.org/licenses/LICENSE-2.0
|
| +#
|
| +# Unless required by applicable law or agreed to in writing, software
|
| +# distributed under the License is distributed on an "AS IS" BASIS,
|
| +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +# See the License for the specific language governing permissions and
|
| +# limitations under the License.
|
| +
|
| +"""Unit tests for the api_config_manager module."""
|
| +
|
| +import re
|
| +import unittest
|
| +
|
| +import endpoints.api_config_manager as api_config_manager
|
| +
|
| +
|
| +class ApiConfigManagerTest(unittest.TestCase):
|
| +
|
| + def setUp(self):
|
| + """Make ApiConfigManager with a few helpful fakes."""
|
| + self.config_manager = api_config_manager.ApiConfigManager()
|
| +
|
| + def test_process_api_config_empty_response(self):
|
| + self.config_manager.process_api_config_response({})
|
| + actual_method = self.config_manager.lookup_rpc_method('guestbook_api.get',
|
| + 'v1')
|
| + self.assertEqual(None, actual_method)
|
| +
|
| + def test_process_api_config_invalid_response(self):
|
| + self.config_manager.process_api_config_response({'name': 'foo'})
|
| + actual_method = self.config_manager.lookup_rpc_method('guestbook_api.get',
|
| + 'v1')
|
| + self.assertEqual(None, actual_method)
|
| +
|
| + def test_process_api_config(self):
|
| + fake_method = {'httpMethod': 'GET',
|
| + 'path': 'greetings/{gid}',
|
| + 'rosyMethod': 'baz.bim'}
|
| + config = {'name': 'guestbook_api',
|
| + 'version': 'X',
|
| + 'methods': {'guestbook_api.foo.bar': fake_method}}
|
| + self.config_manager.process_api_config_response({'items': [config]})
|
| + actual_method = self.config_manager.lookup_rpc_method(
|
| + 'guestbook_api.foo.bar', 'X')
|
| + self.assertEqual(fake_method, actual_method)
|
| +
|
| + def test_process_api_config_order_length(self):
|
| + test_method_info = (
|
| + ('guestbook_api.foo.bar', 'greetings/{gid}', 'baz.bim'),
|
| + ('guestbook_api.list', 'greetings', 'greetings.list'),
|
| + ('guestbook_api.f3', 'greetings/{gid}/sender/property/blah',
|
| + 'greetings.f3'),
|
| + ('guestbook_api.shortgreet', 'greet', 'greetings.short_greeting'))
|
| + methods = {}
|
| + for method_name, path, rosy_method in test_method_info:
|
| + method = {'httpMethod': 'GET',
|
| + 'path': path,
|
| + 'rosyMethod': rosy_method}
|
| + methods[method_name] = method
|
| + config = {'name': 'guestbook_api',
|
| + 'version': 'X',
|
| + 'methods': methods}
|
| + self.config_manager.process_api_config_response(
|
| + {'items': [config]})
|
| + # Make sure all methods appear in the result.
|
| + for method_name, _, _ in test_method_info:
|
| + self.assertIsNotNone(
|
| + self.config_manager.lookup_rpc_method(method_name, 'X'))
|
| + # Make sure paths and partial paths return the right methods.
|
| + self.assertEqual(
|
| + self.config_manager.lookup_rest_method(
|
| + 'guestbook_api/X/greetings', 'GET')[0],
|
| + 'guestbook_api.list')
|
| + self.assertEqual(
|
| + self.config_manager.lookup_rest_method(
|
| + 'guestbook_api/X/greetings/1', 'GET')[0],
|
| + 'guestbook_api.foo.bar')
|
| + self.assertEqual(
|
| + self.config_manager.lookup_rest_method(
|
| + 'guestbook_api/X/greetings/2/sender/property/blah', 'GET')[0],
|
| + 'guestbook_api.f3')
|
| + self.assertEqual(
|
| + self.config_manager.lookup_rest_method(
|
| + 'guestbook_api/X/greet', 'GET')[0],
|
| + 'guestbook_api.shortgreet')
|
| +
|
| + def test_get_sorted_methods1(self):
|
| + test_method_info = (
|
| + ('name1', 'greetings', 'POST'),
|
| + ('name2', 'greetings', 'GET'),
|
| + ('name3', 'short/but/many/constants', 'GET'),
|
| + ('name4', 'greetings', ''),
|
| + ('name5', 'greetings/{gid}', 'GET'),
|
| + ('name6', 'greetings/{gid}', 'PUT'),
|
| + ('name7', 'a/b/{var}/{var2}', 'GET'))
|
| + methods = {}
|
| + for method_name, path, http_method in test_method_info:
|
| + method = {'httpMethod': http_method,
|
| + 'path': path}
|
| + methods[method_name] = method
|
| + sorted_methods = self.config_manager._get_sorted_methods(methods)
|
| +
|
| + expected_data = [
|
| + ('name3', 'short/but/many/constants', 'GET'),
|
| + ('name7', 'a/b/{var}/{var2}', 'GET'),
|
| + ('name4', 'greetings', ''),
|
| + ('name2', 'greetings', 'GET'),
|
| + ('name1', 'greetings', 'POST'),
|
| + ('name5', 'greetings/{gid}', 'GET'),
|
| + ('name6', 'greetings/{gid}', 'PUT')]
|
| + expected_methods = [(name, {'httpMethod': http_method, 'path': path})
|
| + for name, path, http_method in expected_data]
|
| + self.assertEqual(expected_methods, sorted_methods)
|
| +
|
| + def test_get_sorted_methods2(self):
|
| + test_method_info = (
|
| + ('name1', 'abcdefghi', 'GET'),
|
| + ('name2', 'foo', 'GET'),
|
| + ('name3', 'greetings', 'GET'),
|
| + ('name4', 'bar', 'POST'),
|
| + ('name5', 'baz', 'GET'),
|
| + ('name6', 'baz', 'PUT'),
|
| + ('name7', 'baz', 'DELETE'))
|
| + methods = {}
|
| + for method_name, path, http_method in test_method_info:
|
| + method = {'httpMethod': http_method,
|
| + 'path': path}
|
| + methods[method_name] = method
|
| + sorted_methods = self.config_manager._get_sorted_methods(methods)
|
| +
|
| + # Single-part paths should be sorted by path name, http_method.
|
| + expected_data = [
|
| + ('name1', 'abcdefghi', 'GET'),
|
| + ('name4', 'bar', 'POST'),
|
| + ('name7', 'baz', 'DELETE'),
|
| + ('name5', 'baz', 'GET'),
|
| + ('name6', 'baz', 'PUT'),
|
| + ('name2', 'foo', 'GET'),
|
| + ('name3', 'greetings', 'GET')]
|
| + expected_methods = [(name, {'httpMethod': http_method, 'path': path})
|
| + for name, path, http_method in expected_data]
|
| + self.assertEqual(expected_methods, sorted_methods)
|
| +
|
| + def test_process_api_config_convert_https(self):
|
| + """Test that the parsed API config has switched HTTPS to HTTP."""
|
| + config = {'name': 'guestbook_api',
|
| + 'version': 'X',
|
| + 'adapter': {'bns': 'https://localhost/_ah/spi',
|
| + 'type': 'lily'},
|
| + 'root': 'https://localhost/_ah/api',
|
| + 'methods': {}}
|
| + self.config_manager.process_api_config_response({'items': [config]})
|
| +
|
| + self.assertEqual(
|
| + 'https://localhost/_ah/spi',
|
| + self.config_manager.configs[('guestbook_api', 'X')]['adapter']['bns'])
|
| + self.assertEqual(
|
| + 'https://localhost/_ah/api',
|
| + self.config_manager.configs[('guestbook_api', 'X')]['root'])
|
| +
|
| + def test_save_lookup_rpc_method(self):
|
| + # First attempt, guestbook.get does not exist
|
| + actual_method = self.config_manager.lookup_rpc_method('guestbook_api.get',
|
| + 'v1')
|
| + self.assertEqual(None, actual_method)
|
| +
|
| + # Now we manually save it, and should find it
|
| + fake_method = {'some': 'object'}
|
| + self.config_manager._save_rpc_method('guestbook_api.get', 'v1', fake_method)
|
| + actual_method = self.config_manager.lookup_rpc_method('guestbook_api.get',
|
| + 'v1')
|
| + self.assertEqual(fake_method, actual_method)
|
| +
|
| + def test_save_lookup_rest_method(self):
|
| + # First attempt, guestbook.get does not exist
|
| + method_spec = self.config_manager.lookup_rest_method(
|
| + 'guestbook_api/v1/greetings/i', 'GET')
|
| + self.assertEqual((None, None, None), method_spec)
|
| +
|
| + # Now we manually save it, and should find it
|
| + fake_method = {'httpMethod': 'GET',
|
| + 'path': 'greetings/{id}'}
|
| + self.config_manager._save_rest_method('guestbook_api.get', 'guestbook_api',
|
| + 'v1', fake_method)
|
| + method_name, method_spec, params = self.config_manager.lookup_rest_method(
|
| + 'guestbook_api/v1/greetings/i', 'GET')
|
| + self.assertEqual('guestbook_api.get', method_name)
|
| + self.assertEqual(fake_method, method_spec)
|
| + self.assertEqual({'id': 'i'}, params)
|
| +
|
| + def test_lookup_rest_method_with_colon(self):
|
| + fake_method = {'httpMethod': 'GET',
|
| + 'path': 'greetings:testcolon'}
|
| + self.config_manager._save_rest_method('guestbook_api.get', 'guestbook_api',
|
| + 'v1', fake_method)
|
| + method_name, method_spec, params = self.config_manager.lookup_rest_method(
|
| + 'guestbook_api/v1/greetings%3Atestcolon', 'GET')
|
| + self.assertEqual('guestbook_api.get', method_name)
|
| + self.assertEqual(fake_method, method_spec)
|
| +
|
| + def test_trailing_slash_optional(self):
|
| + # Create a typical get resource URL.
|
| + fake_method = {'httpMethod': 'GET', 'path': 'trailingslash'}
|
| + self.config_manager._save_rest_method('guestbook_api.trailingslash',
|
| + 'guestbook_api', 'v1', fake_method)
|
| +
|
| + # Make sure we get this method when we query without a slash.
|
| + method_name, method_spec, params = self.config_manager.lookup_rest_method(
|
| + 'guestbook_api/v1/trailingslash', 'GET')
|
| + self.assertEqual('guestbook_api.trailingslash', method_name)
|
| + self.assertEqual(fake_method, method_spec)
|
| + self.assertEqual({}, params)
|
| +
|
| + # Make sure we get this method when we query with a slash.
|
| + method_name, method_spec, params = self.config_manager.lookup_rest_method(
|
| + 'guestbook_api/v1/trailingslash/', 'GET')
|
| + self.assertEqual('guestbook_api.trailingslash', method_name)
|
| + self.assertEqual(fake_method, method_spec)
|
| + self.assertEqual({}, params)
|
| +
|
| +
|
| +class ParameterizedPathTest(unittest.TestCase):
|
| +
|
| + # <scrub>
|
| + # See http://cs/spi/tools/devserver/ParameterizedPathTest.java
|
| + # </scrub>
|
| + def test_invalid_variable_name_leading_digit(self):
|
| + self.assertEqual(
|
| + None, re.match(api_config_manager._PATH_VARIABLE_PATTERN, '1abc'))
|
| +
|
| + # Ensure users can not add variables starting with !
|
| + # This is used for reserved variables (e.g. !name and !version)
|
| + def test_invalid_var_name_leading_exclamation(self):
|
| + self.assertEqual(
|
| + None, re.match(api_config_manager._PATH_VARIABLE_PATTERN, '!abc'))
|
| +
|
| + def test_valid_variable_name(self):
|
| + self.assertEqual(
|
| + 'AbC1', re.match(api_config_manager._PATH_VARIABLE_PATTERN,
|
| + 'AbC1').group(0))
|
| +
|
| + def assert_no_match(self, path, param_path):
|
| + """Assert that the given path does not match param_path pattern.
|
| +
|
| + For example, /xyz/123 does not match /abc/{x}.
|
| +
|
| + Args:
|
| + path: A string, the inbound request path.
|
| + param_path: A string, the parameterized path pattern to match against
|
| + this path.
|
| + """
|
| + config_manager = api_config_manager.ApiConfigManager
|
| + params = config_manager._compile_path_pattern(param_path).match(path)
|
| + self.assertEqual(None, params)
|
| +
|
| + def test_prefix_no_match(self):
|
| + self.assert_no_match('/xyz/123', '/abc/{x}')
|
| +
|
| + def test_suffix_no_match(self):
|
| + self.assert_no_match('/abc/123', '/abc/{x}/456')
|
| +
|
| + def test_suffix_no_match_with_more_variables(self):
|
| + self.assert_no_match('/abc/456/123/789', '/abc/{x}/123/{y}/xyz')
|
| +
|
| + def test_no_match_collection_with_item(self):
|
| + self.assert_no_match('/api/v1/resources/123', '/{name}/{version}/resources')
|
| +
|
| + def assert_match(self, path, param_path, param_count):
|
| + """Assert that the given path does match param_path pattern.
|
| +
|
| + For example, /abc/123 does not match /abc/{x}.
|
| +
|
| + Args:
|
| + path: A string, the inbound request path.
|
| + param_path: A string, the parameterized path pattern to match against
|
| + this path.
|
| + param_count: An int, the expected number of parameters to match in
|
| + pattern.
|
| +
|
| + Returns:
|
| + Dict mapping path variable name to path variable value.
|
| + """
|
| + config_manager = api_config_manager.ApiConfigManager
|
| + match = config_manager._compile_path_pattern(param_path).match(path)
|
| + self.assertTrue(match is not None) # Will be None if path was not matched
|
| + params = config_manager._get_path_params(match)
|
| + self.assertEquals(param_count, len(params))
|
| + return params
|
| +
|
| + def test_one_variable_match(self):
|
| + params = self.assert_match('/abc/123', '/abc/{x}', 1)
|
| + self.assertEquals('123', params.get('x'))
|
| +
|
| + def test_two_variable_match(self):
|
| + params = self.assert_match('/abc/456/123/789', '/abc/{x}/123/{y}', 2)
|
| + self.assertEquals('456', params.get('x'))
|
| + self.assertEquals('789', params.get('y'))
|
| +
|
| + def test_message_variable_match(self):
|
| + params = self.assert_match('/abc/123', '/abc/{x.y}', 1)
|
| + self.assertEquals('123', params.get('x.y'))
|
| +
|
| + def test_message_and_simple_variable_match(self):
|
| + params = self.assert_match('/abc/123/456', '/abc/{x.y.z}/{t}', 2)
|
| + self.assertEquals('123', params.get('x.y.z'))
|
| + self.assertEquals('456', params.get('t'))
|
| +
|
| + def test_space_in_path(self):
|
| + params = self.assert_match('/abc/foo+bar', '/abc/{x}', 1)
|
| + self.assertEquals('foo bar', params.get('x'))
|
| +
|
| + def assert_invalid_value(self, value):
|
| + """Assert that the path parameter value is not valid.
|
| +
|
| + For example, /abc/3!:2 is invalid for /abc/{x}.
|
| +
|
| + Args:
|
| + value: A string containing a variable value to check for validity.
|
| + """
|
| + param_path = '/abc/{x}'
|
| + path = '/abc/%s' % value
|
| + config_manager = api_config_manager.ApiConfigManager
|
| + params = config_manager._compile_path_pattern(param_path).match(path)
|
| + self.assertEqual(None, params)
|
| +
|
| + def test_invalid_values(self):
|
| + for reserved in [':', '?', '#', '[', ']', '{', '}']:
|
| + self.assert_invalid_value('123%s' % reserved)
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + unittest.main()
|
|
|