1#
2# Copyright 2015 Google Inc.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""Tests for util.py."""
17import unittest2
18
19from apitools.base.protorpclite import messages
20from apitools.base.py import encoding
21from apitools.base.py import exceptions
22from apitools.base.py import util
23
24
25class MockedMethodConfig(object):
26
27    def __init__(self, relative_path, path_params):
28        self.relative_path = relative_path
29        self.path_params = path_params
30
31
32class MessageWithRemappings(messages.Message):
33
34    class AnEnum(messages.Enum):
35        value_one = 1
36        value_two = 2
37
38    str_field = messages.StringField(1)
39    enum_field = messages.EnumField('AnEnum', 2)
40
41
42encoding.AddCustomJsonFieldMapping(
43    MessageWithRemappings, 'str_field', 'path_field')
44encoding.AddCustomJsonEnumMapping(
45    MessageWithRemappings.AnEnum, 'value_one', 'ONE')
46
47
48class UtilTest(unittest2.TestCase):
49
50    def testExpand(self):
51        method_config_xy = MockedMethodConfig(relative_path='{x}/y/{z}',
52                                              path_params=['x', 'z'])
53        self.assertEquals(
54            util.ExpandRelativePath(method_config_xy, {'x': '1', 'z': '2'}),
55            '1/y/2')
56        self.assertEquals(
57            util.ExpandRelativePath(
58                method_config_xy,
59                {'x': '1', 'z': '2'},
60                relative_path='{x}/y/{z}/q'),
61            '1/y/2/q')
62
63    def testReservedExpansion(self):
64        method_config_reserved = MockedMethodConfig(relative_path='{+x}/baz',
65                                                    path_params=['x'])
66        self.assertEquals('foo/:bar:/baz', util.ExpandRelativePath(
67            method_config_reserved, {'x': 'foo/:bar:'}))
68        method_config_no_reserved = MockedMethodConfig(relative_path='{x}/baz',
69                                                       path_params=['x'])
70        self.assertEquals('foo%2F%3Abar%3A/baz', util.ExpandRelativePath(
71            method_config_no_reserved, {'x': 'foo/:bar:'}))
72
73    def testCalculateWaitForRetry(self):
74        try0 = util.CalculateWaitForRetry(0)
75        self.assertTrue(try0 >= 1.0)
76        self.assertTrue(try0 <= 1.5)
77        try1 = util.CalculateWaitForRetry(1)
78        self.assertTrue(try1 >= 1.0)
79        self.assertTrue(try1 <= 3.0)
80        try2 = util.CalculateWaitForRetry(2)
81        self.assertTrue(try2 >= 2.0)
82        self.assertTrue(try2 <= 6.0)
83        try3 = util.CalculateWaitForRetry(3)
84        self.assertTrue(try3 >= 4.0)
85        self.assertTrue(try3 <= 12.0)
86        try4 = util.CalculateWaitForRetry(4)
87        self.assertTrue(try4 >= 8.0)
88        self.assertTrue(try4 <= 24.0)
89
90        self.assertAlmostEqual(10, util.CalculateWaitForRetry(5, max_wait=10))
91
92    def testTypecheck(self):
93
94        class Class1(object):
95            pass
96
97        class Class2(object):
98            pass
99
100        class Class3(object):
101            pass
102
103        instance_of_class1 = Class1()
104
105        self.assertEquals(
106            instance_of_class1, util.Typecheck(instance_of_class1, Class1))
107
108        self.assertEquals(
109            instance_of_class1,
110            util.Typecheck(instance_of_class1, ((Class1, Class2), Class3)))
111
112        self.assertEquals(
113            instance_of_class1,
114            util.Typecheck(instance_of_class1, (Class1, (Class2, Class3))))
115
116        self.assertEquals(
117            instance_of_class1,
118            util.Typecheck(instance_of_class1, Class1, 'message'))
119
120        self.assertEquals(
121            instance_of_class1,
122            util.Typecheck(
123                instance_of_class1, ((Class1, Class2), Class3), 'message'))
124
125        self.assertEquals(
126            instance_of_class1,
127            util.Typecheck(
128                instance_of_class1, (Class1, (Class2, Class3)), 'message'))
129
130        with self.assertRaises(exceptions.TypecheckError):
131            util.Typecheck(instance_of_class1, Class2)
132
133        with self.assertRaises(exceptions.TypecheckError):
134            util.Typecheck(instance_of_class1, (Class2, Class3))
135
136        with self.assertRaises(exceptions.TypecheckError):
137            util.Typecheck(instance_of_class1, Class2, 'message')
138
139        with self.assertRaises(exceptions.TypecheckError):
140            util.Typecheck(instance_of_class1, (Class2, Class3), 'message')
141
142    def testAcceptableMimeType(self):
143        valid_pairs = (
144            ('*', 'text/plain'),
145            ('*/*', 'text/plain'),
146            ('text/*', 'text/plain'),
147            ('*/plain', 'text/plain'),
148            ('text/plain', 'text/plain'),
149        )
150
151        for accept, mime_type in valid_pairs:
152            self.assertTrue(util.AcceptableMimeType([accept], mime_type))
153
154        invalid_pairs = (
155            ('text/*', 'application/json'),
156            ('text/plain', 'application/json'),
157        )
158
159        for accept, mime_type in invalid_pairs:
160            self.assertFalse(util.AcceptableMimeType([accept], mime_type))
161
162        self.assertTrue(util.AcceptableMimeType(['application/json', '*/*'],
163                                                'text/plain'))
164        self.assertFalse(util.AcceptableMimeType(['application/json', 'img/*'],
165                                                 'text/plain'))
166
167    def testMalformedMimeType(self):
168        self.assertRaises(
169            exceptions.InvalidUserInputError,
170            util.AcceptableMimeType, ['*/*'], 'abcd')
171
172    def testUnsupportedMimeType(self):
173        self.assertRaises(
174            exceptions.GeneratedClientError,
175            util.AcceptableMimeType, ['text/html;q=0.9'], 'text/html')
176
177    def testMapRequestParams(self):
178        params = {
179            'str_field': 'foo',
180            'enum_field': MessageWithRemappings.AnEnum.value_one,
181        }
182        remapped_params = {
183            'path_field': 'foo',
184            'enum_field': 'ONE',
185        }
186        self.assertEqual(remapped_params,
187                         util.MapRequestParams(params, MessageWithRemappings))
188
189        params['enum_field'] = MessageWithRemappings.AnEnum.value_two
190        remapped_params['enum_field'] = 'value_two'
191        self.assertEqual(remapped_params,
192                         util.MapRequestParams(params, MessageWithRemappings))
193
194    def testMapParamNames(self):
195        params = ['path_field', 'enum_field']
196        remapped_params = ['str_field', 'enum_field']
197        self.assertEqual(remapped_params,
198                         util.MapParamNames(params, MessageWithRemappings))
199