Index: third_party/google-endpoints/endpoints/test/message_parser_test.py |
diff --git a/third_party/google-endpoints/endpoints/test/message_parser_test.py b/third_party/google-endpoints/endpoints/test/message_parser_test.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1f0270b1606442889c8487d2f59302f01384d80d |
--- /dev/null |
+++ b/third_party/google-endpoints/endpoints/test/message_parser_test.py |
@@ -0,0 +1,349 @@ |
+# 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. |
+ |
+"""Tests for message_parser.""" |
+ |
+import difflib |
+import json |
+import unittest |
+ |
+import endpoints.message_parser as message_parser |
+from protorpc import message_types |
+from protorpc import messages |
+ |
+import test_util |
+ |
+ |
+package = 'TestPackage' |
+ |
+ |
+class ModuleInterfaceTest(test_util.ModuleInterfaceTest, |
+ unittest.TestCase): |
+ |
+ MODULE = message_parser |
+ |
+ |
+def _assertSchemaEqual(expected, actual, testcase): |
+ """Utility method to dump diffs if the schema aren't equal. |
+ |
+ Args: |
+ expected: object, the expected results. |
+ actual: object, the actual results. |
+ testcase: unittest.TestCase, the test case this assertion is used within. |
+ """ |
+ if expected != actual: |
+ expected_text = json.dumps(expected, indent=2, sort_keys=True) |
+ actual_text = json.dumps(actual, indent=2, sort_keys=True) |
+ diff = difflib.unified_diff(expected_text.splitlines(True), |
+ actual_text.splitlines(True), |
+ fromfile='expected.schema', |
+ tofile='actual.schema') |
+ diff_text = ''.join(list(diff)) |
+ testcase.fail('Schema differs from expected:\n%s' % diff_text) |
+ |
+ |
+class SelfReference(messages.Message): |
+ """This must be at top level to be found by MessageField.""" |
+ self = messages.MessageField('SelfReference', 1) |
+ |
+ |
+class MessageTypeToJsonSchemaTest(unittest.TestCase): |
+ |
+ def testSelfReferenceMessageField(self): |
+ """MessageFields should be recursively parsed.""" |
+ |
+ parser = message_parser.MessageTypeToJsonSchema() |
+ parser.add_message(SelfReference) |
+ schemas = parser.schemas() |
+ self.assertEquals(1, len(schemas)) |
+ self.assertTrue(package + 'SelfReference' in schemas) |
+ |
+ def testRecursiveDescent(self): |
+ """MessageFields should be recursively parsed.""" |
+ |
+ class C(messages.Message): |
+ text = messages.StringField(1, required=True) |
+ |
+ class B(messages.Message): |
+ c = messages.MessageField(C, 1) |
+ |
+ class A(messages.Message): |
+ b = messages.MessageField(B, 1, repeated=True) |
+ |
+ parser = message_parser.MessageTypeToJsonSchema() |
+ parser.add_message(A) |
+ schemas = parser.schemas() |
+ self.assertEquals(3, len(schemas)) |
+ self.assertTrue(package + 'A' in schemas) |
+ self.assertTrue(package + 'B' in schemas) |
+ self.assertTrue(package + 'C' in schemas) |
+ |
+ def testRepeatedAndRequired(self): |
+ """Repeated and required fields should show up as such in the schema.""" |
+ |
+ class AllFields(messages.Message): |
+ """Documentation for AllFields.""" |
+ string = messages.StringField(1) |
+ string_required = messages.StringField(2, required=True) |
+ string_default_required = messages.StringField(3, required=True, |
+ default='Foo') |
+ string_repeated = messages.StringField(4, repeated=True) |
+ |
+ class SimpleEnum(messages.Enum): |
+ """Simple enumeration type.""" |
+ VAL1 = 1 |
+ VAL2 = 2 |
+ |
+ enum_value = messages.EnumField(SimpleEnum, 5, default=SimpleEnum.VAL2) |
+ |
+ parser = message_parser.MessageTypeToJsonSchema() |
+ parser.add_message(AllFields) |
+ schemas = parser.schemas() |
+ |
+ expected = { |
+ package + 'AllFields': { |
+ 'type': 'object', |
+ 'id': package + 'AllFields', |
+ 'description': 'Documentation for AllFields.', |
+ 'properties': { |
+ 'string': { |
+ 'type': 'string' |
+ }, |
+ 'string_required': { |
+ 'type': 'string', |
+ 'required': True |
+ }, |
+ 'string_default_required': { |
+ 'type': 'string', |
+ 'required': True, |
+ 'default': 'Foo' |
+ }, |
+ 'string_repeated': { |
+ 'items': { |
+ 'type': 'string', |
+ }, |
+ 'type': 'array' |
+ }, |
+ 'enum_value': { |
+ 'default': 'VAL2', |
+ 'type': 'string', |
+ 'enum': ['VAL1', 'VAL2'] |
+ }, |
+ } |
+ } |
+ } |
+ _assertSchemaEqual(expected, schemas, self) |
+ |
+ def testAllFieldTypes(self): |
+ """Test all the Field types that ProtoRPC supports.""" |
+ |
+ class AllTypes(messages.Message): |
+ """Contains all field types.""" |
+ |
+ class SimpleEnum(messages.Enum): |
+ """Simple enumeration type.""" |
+ VAL1 = 1 |
+ VAL2 = 2 |
+ |
+ bool_value = messages.BooleanField(1, variant=messages.Variant.BOOL) |
+ bytes_value = messages.BytesField(2, variant=messages.Variant.BYTES) |
+ double_value = messages.FloatField(3, variant=messages.Variant.DOUBLE) |
+ enum_value = messages.EnumField(SimpleEnum, 4) |
+ float_value = messages.FloatField(5, variant=messages.Variant.FLOAT) |
+ int32_value = messages.IntegerField(6, variant=messages.Variant.INT32) |
+ int64_value = messages.IntegerField(7, variant=messages.Variant.INT64) |
+ string_value = messages.StringField(8, variant=messages.Variant.STRING) |
+ uint32_value = messages.IntegerField(9, variant=messages.Variant.UINT32) |
+ uint64_value = messages.IntegerField(10, variant=messages.Variant.UINT64) |
+ int_value = messages.IntegerField(11) # Default variant is INT64. |
+ datetime_value = message_types.DateTimeField(12) |
+ repeated_datetime_value = message_types.DateTimeField(13, repeated=True) |
+ |
+ parser = message_parser.MessageTypeToJsonSchema() |
+ parser.add_message(AllTypes) |
+ schemas = parser.schemas() |
+ |
+ expected = { |
+ package + 'AllTypes': { |
+ 'type': 'object', |
+ 'id': package + 'AllTypes', |
+ 'description': 'Contains all field types.', |
+ 'properties': { |
+ 'bool_value': {'type': 'boolean'}, |
+ 'bytes_value': {'type': 'string', 'format': 'byte'}, |
+ 'double_value': {'type': 'number', 'format': 'double'}, |
+ 'enum_value': {'type': 'string', 'enum': ['VAL1', 'VAL2']}, |
+ 'float_value': {'type': 'number', 'format': 'float'}, |
+ 'int32_value': {'type': 'integer', 'format': 'int32'}, |
+ 'int64_value': {'type': 'string', 'format': 'int64'}, |
+ 'string_value': {'type': 'string'}, |
+ 'uint32_value': {'type': 'integer', 'format': 'uint32'}, |
+ 'uint64_value': {'type': 'string', 'format': 'uint64'}, |
+ 'int_value': {'type': 'string', 'format': 'int64'}, |
+ 'datetime_value': {'type': 'string', 'format': 'date-time'}, |
+ 'repeated_datetime_value': |
+ {'items': {'type': 'string', 'format': 'date-time'}, |
+ 'type': 'array'} |
+ } |
+ } |
+ } |
+ |
+ _assertSchemaEqual(expected, schemas, self) |
+ |
+ def testLargeEnum(self): |
+ """Test that an enum with lots of values works.""" |
+ |
+ class MyMessage(messages.Message): |
+ """Documentation for MyMessage.""" |
+ |
+ class LargeEnum(messages.Enum): |
+ """Large enumeration type, in a strange order.""" |
+ ALL = 1000 |
+ AND = 1050 |
+ BAR = 4 |
+ BIND = 3141 |
+ DARKNESS = 2123 |
+ FOO = 3 |
+ IN = 1200 |
+ ONE = 5 |
+ RING = 6 |
+ RULE = 8 |
+ THE = 1500 |
+ THEM1 = 9 |
+ THEM2 = 10000 |
+ TO = 7 |
+ VAL1 = 1 |
+ VAL2 = 2 |
+ |
+ enum_value = messages.EnumField(LargeEnum, 1) |
+ |
+ parser = message_parser.MessageTypeToJsonSchema() |
+ parser.add_message(MyMessage) |
+ schemas = parser.schemas() |
+ |
+ expected = { |
+ package + 'MyMessage': { |
+ 'type': 'object', |
+ 'id': package + 'MyMessage', |
+ 'description': 'Documentation for MyMessage.', |
+ 'properties': { |
+ 'enum_value': { |
+ 'type': 'string', |
+ 'enum': ['VAL1', 'VAL2', 'FOO', 'BAR', |
+ 'ONE', 'RING', 'TO', 'RULE', 'THEM1', 'ALL', |
+ 'AND', 'IN', 'THE', 'DARKNESS', 'BIND', 'THEM2'] |
+ }, |
+ } |
+ } |
+ } |
+ _assertSchemaEqual(expected, schemas, self) |
+ |
+ def testEmptyMessage(self): |
+ """Test the empty edge case.""" |
+ |
+ class NoFields(messages.Message): |
+ pass |
+ |
+ parser = message_parser.MessageTypeToJsonSchema() |
+ parser.add_message(NoFields) |
+ schemas = parser.schemas() |
+ |
+ expected = { |
+ package + 'NoFields': { |
+ 'type': 'object', |
+ 'id': package + 'NoFields', |
+ 'properties': { |
+ } |
+ } |
+ } |
+ |
+ _assertSchemaEqual(expected, schemas, self) |
+ |
+ def testRefForMessage(self): |
+ |
+ class NoFields(messages.Message): |
+ pass |
+ |
+ parser = message_parser.MessageTypeToJsonSchema() |
+ |
+ self.assertRaises(KeyError, parser.ref_for_message_type, NoFields) |
+ |
+ parser.add_message(NoFields) |
+ self.assertEqual(package + 'NoFields', |
+ parser.ref_for_message_type(NoFields)) |
+ |
+ def testMessageFieldDocsAndArrayRef(self): |
+ """Descriptions for MessageFields and a reference in an array.""" |
+ |
+ class B(messages.Message): |
+ """A description of B.""" |
+ pass |
+ |
+ class A(messages.Message): |
+ b = messages.MessageField(B, 1, repeated=True) |
+ |
+ parser = message_parser.MessageTypeToJsonSchema() |
+ parser.add_message(A) |
+ schemas = parser.schemas() |
+ |
+ expected = { |
+ package + 'A': { |
+ 'type': 'object', |
+ 'id': package + 'A', |
+ 'properties': { |
+ 'b': { |
+ 'type': 'array', |
+ 'description': 'A description of B.', |
+ 'items': { |
+ '$ref': package + 'B' |
+ } |
+ } |
+ } |
+ }, |
+ package + 'B': { |
+ 'type': 'object', |
+ 'id': package + 'B', |
+ 'description': 'A description of B.', |
+ 'properties': {} |
+ } |
+ } |
+ |
+ _assertSchemaEqual(expected, schemas, self) |
+ |
+ def testNormalizeSchemaName(self): |
+ |
+ class _1_lower_case_name_(messages.Message): |
+ pass |
+ |
+ parser = message_parser.MessageTypeToJsonSchema() |
+ # Test _, numbers, and case fixing. |
+ self.assertEqual( |
+ package + '1LowerCaseName', |
+ parser.add_message(_1_lower_case_name_)) |
+ |
+ def testNormalizeSchemaNameCollision(self): |
+ |
+ class A(messages.Message): |
+ pass |
+ |
+ class A_(messages.Message): |
+ pass |
+ |
+ parser = message_parser.MessageTypeToJsonSchema() |
+ parser.add_message(A) |
+ self.assertRaises(KeyError, parser.add_message, A_) |
+ |
+ |
+if __name__ == '__main__': |
+ unittest.main() |