1# -*- coding: utf-8 -*-
2import random
3
4import webapp2
5from webapp2 import BaseRoute, RedirectHandler, Request, Route, Router
6
7import test_base
8
9
10class TestRoute(test_base.BaseTestCase):
11    def test_no_variable(self):
12        route = Route(r'/hello', None)
13        route, args, kwargs = route.match(Request.blank('/hello'))
14        self.assertEqual(kwargs, {})
15        url = route.build(Request.blank('/'), (), {})
16        self.assertEqual(url, '/hello')
17
18        route = Route(r'/hello/world/', None)
19        route, args, kwargs = route.match(Request.blank('/hello/world/'))
20        self.assertEqual(kwargs, {})
21        url = route.build(Request.blank('/'), (), {})
22        self.assertEqual(url, '/hello/world/')
23
24    def test_repetition_operator(self):
25        route = Route(r'/<:\d>', None)
26        self.assertEqual(route.match(Request.blank('/1')), (route, ('1',), {}))
27        self.assertEqual(route.match(Request.blank('/2')), (route, ('2',), {}))
28
29        route = Route(r'/<:\d{2,3}>', None)
30        self.assertEqual(route.match(Request.blank('/11')), (route, ('11',), {}))
31        self.assertEqual(route.match(Request.blank('/111')), (route, ('111',), {}))
32        self.assertEqual(route.match(Request.blank('/1111')), None)
33
34    def test_unnamed_variable(self):
35        route = Route(r'/<:\d{4}>', None)
36        self.assertEqual(route.match(Request.blank('/2010')), (route, ('2010',), {}))
37        self.assertEqual(route.match(Request.blank('/aaaa')), None)
38
39        route = Route(r'/<:\d{2}>.<:\d{2}>', None)
40        self.assertEqual(route.match(Request.blank('/98.99')), (route, ('98', '99'), {}))
41        self.assertEqual(route.match(Request.blank('/aa.aa')), None)
42
43        route = Route(r'/<:\d{2}>.<:\d{2}>/<foo>', None)
44        self.assertEqual(route.match(Request.blank('/98.99/test')), (route, ('98', '99'), {'foo': 'test'}))
45        self.assertEqual(route.match(Request.blank('/aa.aa/test')), None)
46
47    def test_simple_variable(self):
48        route = Route(r'/<foo>', None)
49        self.assertEqual(route.match(Request.blank('/bar')), (route, (), {'foo': 'bar'}))
50        url = route.build(Request.blank('/'), (), dict(foo='baz'))
51        self.assertEqual(url, '/baz')
52
53    def test_expr_variable(self):
54        route = Route(r'/<year:\d{4}>', None)
55        self.assertEqual(route.match(Request.blank('/bar')), None)
56        self.assertEqual(route.match(Request.blank('/2010')), (route, (), {'year': '2010'}))
57        self.assertEqual(route.match(Request.blank('/1900')), (route, (), {'year': '1900'}))
58        url = route.build(Request.blank('/'), (), dict(year='2010'))
59        self.assertEqual(url, '/2010')
60
61    def test_expr_variable2(self):
62        route = Route(r'/<year:\d{4}>/foo/', None)
63        url = route.build(Request.blank('/'), (), dict(year='2010'))
64        self.assertEqual(url, '/2010/foo/')
65
66    def test_build_missing_argument(self):
67        route = Route(r'/<:\d{4}>', None)
68        self.assertRaises(KeyError, route.build, Request.blank('/'), (), {})
69
70        route = Route(r'/<:\d{4}>/<:\d{2}>', None)
71        self.assertRaises(KeyError, route.build, Request.blank('/'), (2010,), {})
72
73    def test_build_invalid_argument(self):
74        route = Route(r'/<:\d{4}>', None)
75        self.assertRaises(ValueError, route.build, Request.blank('/'), ('20100',), {})
76
77    def test_build_invalid_argument2(self):
78        route = Route(r'/<:\d{4}>', None)
79        self.assertRaises(ValueError, route.build, Request.blank('/'), ('201a',), {})
80
81    def test_build_missing_keyword(self):
82        route = Route(r'/<year:\d{4}>', None)
83        self.assertRaises(KeyError, route.build, Request.blank('/'), (), {})
84
85    def test_build_missing_keyword2(self):
86        route = Route(r'/<year:\d{4}>/<month:\d{2}>', None)
87        self.assertRaises(KeyError, route.build, Request.blank('/'), (), dict(year='2010'))
88
89    def test_build_invalid_keyword(self):
90        route = Route(r'/<year:\d{4}>', None)
91        self.assertRaises(ValueError, route.build, Request.blank('/'), (), dict(year='20100'))
92
93    def test_build_invalid_keyword2(self):
94        route = Route(r'/<year:\d{4}>', None)
95        self.assertRaises(ValueError, route.build, Request.blank('/'), (), dict(year='201a'))
96
97    def test_build_with_unnamed_variable(self):
98        route = Route(r'/<:\d{4}>/<month:\d{2}>', None)
99
100        url = route.build(Request.blank('/'), (2010,), dict(month=10))
101        self.assertEqual(url, '/2010/10')
102
103        url = route.build(Request.blank('/'), ('1999',), dict(month='07'))
104        self.assertEqual(url, '/1999/07')
105
106    def test_build_default_keyword(self):
107        route = Route(r'/<year:\d{4}>/<month:\d{2}>', None,
108            defaults={'month': 10})
109        url = route.build(Request.blank('/'), (), dict(year='2010'))
110        self.assertEqual(url, '/2010/10')
111
112        route = Route(r'/<year:\d{4}>/<month:\d{2}>', None,
113            defaults={'year': 1900})
114        url = route.build(Request.blank('/'), (), dict(month='07'))
115        self.assertEqual(url, '/1900/07')
116
117    def test_build_extra_keyword(self):
118        route = Route(r'/<year:\d{4}>', None)
119        url = route.build(Request.blank('/'), (), dict(year='2010', foo='bar'))
120        self.assertEqual(url, '/2010?foo=bar')
121        # Arguments are sorted.
122        url = route.build(Request.blank('/'), (), dict(year='2010', foo='bar', baz='ding'))
123        self.assertEqual(url, '/2010?baz=ding&foo=bar')
124
125    def test_build_extra_positional_keyword(self):
126        route = Route(r'/<year:\d{4}>/<:\d{2}>', None)
127
128        url = route.build(Request.blank('/'), ('08', 'i-should-be-ignored', 'me-too'), dict(year='2010', foo='bar'))
129        self.assertEqual(url, '/2010/08?foo=bar')
130
131        url = route.build(Request.blank('/'), ('08', 'i-should-be-ignored', 'me-too'), dict(year='2010', foo='bar', baz='ding'))
132        self.assertEqual(url, '/2010/08?baz=ding&foo=bar')
133
134    def test_build_int_keyword(self):
135        route = Route(r'/<year:\d{4}>', None)
136        url = route.build(Request.blank('/'), (), dict(year=2010))
137        self.assertEqual(url, '/2010')
138
139    def test_build_int_variable(self):
140        route = Route(r'/<:\d{4}>', None)
141        url = route.build(Request.blank('/'), (2010,), {})
142        self.assertEqual(url, '/2010')
143
144    def test_router_build_error(self):
145        router = Router(None)
146        router.add(Route('/<year:\d{4}>', None, name='year-page'))
147
148        url = router.build(Request.blank('/'), 'year-page', (), dict(year='2010'))
149        self.assertEqual(url, '/2010')
150
151        self.assertRaises(KeyError, router.build, Request.blank('/'), 'i-dont-exist', (), dict(year='2010'))
152
153    def test_reverse_template(self):
154        route = Route('/foo', None)
155        # Access route.regex just to set the lazy properties.
156        regex = route.regex
157        self.assertEqual(route.reverse_template, '/foo')
158
159        route = Route('/foo/<bar>', None)
160        # Access route.regex just to set the lazy properties.
161        regex = route.regex
162        self.assertEqual(route.reverse_template, '/foo/%(bar)s')
163
164        route = Route('/foo/<bar>/<baz:\d>', None)
165        # Access route.regex just to set the lazy properties.
166        regex = route.regex
167        self.assertEqual(route.reverse_template, '/foo/%(bar)s/%(baz)s')
168
169    def test_invalid_template(self):
170        # To break it:
171        # <>foo:><bar<:baz>
172        route = Route('/<foo/<:bar', None)
173        # Access route.regex just to set the lazy properties.
174        regex = route.regex
175        self.assertEqual(route.reverse_template, '/<foo/<:bar')
176
177    def test_build_full_without_request(self):
178        router = Router(None)
179        router.add(Route(r'/hello', None, name='hello'))
180        self.assertRaises(AttributeError, router.build, None, 'hello', (), dict(_full=True))
181        self.assertRaises(AttributeError, router.build, None, 'hello', (), dict(_scheme='https'))
182
183    def test_positions(self):
184        template = '/<:\d+>' * 98
185        args = tuple(str(i) for i in range(98))
186        url_res = '/' + '/'.join(args)
187
188        route = Route(template, None)
189        self.assertEqual(route.match(Request.blank(url_res)), (route, args, {}))
190        url = route.build(Request.blank('/'), args, {})
191        self.assertEqual(url_res, url)
192
193        args = [str(i) for i in range(1000)]
194        random.shuffle(args)
195        args = tuple(args[:98])
196        url_res = '/' + '/'.join(args)
197        self.assertEqual(route.match(Request.blank(url_res)), (route, args, {}))
198        url = route.build(Request.blank('/'), args, {})
199        self.assertEqual(url_res, url)
200
201    def test_build_only_without_name(self):
202        self.assertRaises(ValueError, Route, r'/<foo>', None, build_only=True)
203
204    def test_route_repr(self):
205        self.assertEqual(Route(r'/<foo>', None).__repr__(),
206            "<Route('/<foo>', None, name=None, defaults={}, build_only=False)>")
207        self.assertEqual(Route(r'/<foo>', None, name='bar', defaults={'baz': 'ding'}, build_only=True).__repr__(),
208            "<Route('/<foo>', None, name='bar', defaults={'baz': 'ding'}, build_only=True)>")
209
210        self.assertEqual(str(Route(r'/<foo>', None)),
211            "<Route('/<foo>', None, name=None, defaults={}, build_only=False)>")
212        self.assertEqual(str(Route(r'/<foo>', None, name='bar', defaults={'baz': 'ding'}, build_only=True)),
213            "<Route('/<foo>', None, name='bar', defaults={'baz': 'ding'}, build_only=True)>")
214
215    def test_router_repr(self):
216        router = Router(None)
217        router.add(Route(r'/hello', None, name='hello', build_only=True))
218        router.add(Route(r'/world', None))
219
220        self.assertEqual(router.__repr__(), "<Router([<Route('/world', None, name=None, defaults={}, build_only=False)>, <Route('/hello', None, name='hello', defaults={}, build_only=True)>])>")
221
222    def test_base_route(self):
223        route = BaseRoute('foo', 'bar')
224        self.assertRaises(NotImplementedError, route.match, None)
225
226    def test_set_matcher(self):
227        req = Request.blank('/')
228        def custom_matcher(router, request):
229            self.assertEqual(request, req)
230
231        router = Router(None)
232        router.set_matcher(custom_matcher)
233        router.match(req)
234
235    def test_set_builder(self):
236        req = Request.blank('/')
237        def custom_builder(router, request, name, args, kwargs):
238            self.assertEqual(request, req)
239            return 'http://www.google.com'
240
241        router = Router(None)
242        router.set_builder(custom_builder)
243        res = router.build(req, '', (), {})
244        self.assertEqual(res, 'http://www.google.com')
245
246    def test_set_adapter(self):
247        def custom_adapter(router, handler):
248            class MyAdapter(webapp2.BaseHandlerAdapter):
249                def __call__(self, request, response):
250                    response.write('hello my adapter')
251
252            return MyAdapter(handler)
253
254        def myhandler(request, *args, **kwargs):
255            return webapp2.Response('hello')
256
257        app = webapp2.WSGIApplication([('/', myhandler)])
258        app.router.set_adapter(custom_adapter)
259
260        rsp = app.get_response('/')
261        self.assertEqual(rsp.status_int, 200)
262        self.assertEqual(rsp.body, 'hello my adapter')
263
264    def test_methods(self):
265        route = Route(r'/', methods=['GET', 'POST'])
266        router = Router([route])
267        req = Request.blank('/')
268        req.method = 'GET'
269        self.assertTrue(router.match(req) is not None)
270        req.method = 'POST'
271        self.assertTrue(router.match(req) is not None)
272        req.method = 'PUT'
273        self.assertRaises(webapp2.exc.HTTPMethodNotAllowed, router.match, req)
274
275    def test_schemes(self):
276        route = Route(r'/', schemes=['http'])
277        req = Request.blank('http://mydomain.com/')
278        self.assertTrue(route.match(req) is not None)
279        req = Request.blank('https://mydomain.com/')
280        self.assertTrue(route.match(req) is None)
281
282        route = Route(r'/', schemes=['https'])
283        req = Request.blank('https://mydomain.com/')
284        self.assertTrue(route.match(req) is not None)
285        req = Request.blank('http://mydomain.com/')
286        self.assertTrue(route.match(req) is None)
287
288
289class TestSimpleRoute(test_base.BaseTestCase):
290    def test_no_variable(self):
291        router = webapp2.Router([(r'/', 'my_handler')])
292
293        matched_route, args, kwargs = router.match(webapp2.Request.blank('/'))
294        self.assertEqual(args, ())
295        self.assertEqual(kwargs, {})
296
297    def test_simple_variables(self):
298        router = webapp2.Router([(r'/(\d{4})/(\d{2})', 'my_handler')])
299
300        matched_route, args, kwargs = router.match(webapp2.Request.blank('/2007/10'))
301        self.assertEqual(args, ('2007', '10'))
302        self.assertEqual(kwargs, {})
303
304    def test_build(self):
305        route = webapp2.SimpleRoute('/', None)
306        self.assertRaises(NotImplementedError, route.build, None, None, None)
307
308    def test_route_repr(self):
309        self.assertEqual(webapp2.SimpleRoute(r'/<foo>', None).__repr__(), "<SimpleRoute('/<foo>', None)>")
310        self.assertEqual(str(webapp2.SimpleRoute(r'/<foo>', None)), "<SimpleRoute('/<foo>', None)>")
311
312
313if __name__ == '__main__':
314    test_base.main()
315