3 # Copyright 2007 Google Inc.
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 """Tests for google.appengine.tools.devappserver2.static_files_handler."""
27 from google
.appengine
.api
import appinfo
28 from google
.appengine
.tools
.devappserver2
import errors
29 from google
.appengine
.tools
.devappserver2
import static_files_handler
30 from google
.appengine
.tools
.devappserver2
import wsgi_test_utils
33 class TestStaticContentHandlerHandlePath(wsgi_test_utils
.WSGITestCase
):
34 """Tests for static_files_handler.StaticContentHandler._handle_path."""
38 self
.mox
.StubOutWithMock(os
.path
, 'getmtime')
39 self
.mox
.StubOutWithMock(static_files_handler
.StaticContentHandler
,
43 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
= {}
46 def test_load_file(self
):
47 url_map
= appinfo
.URLMap(url
='/',
48 static_files
='index.html')
50 h
= static_files_handler
.StaticContentHandler(
55 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
56 static_files_handler
.StaticContentHandler
._read
_file
(
57 '/home/appdir/index.html').AndReturn('Hello World!')
60 self
.assertResponse('200 OK',
61 {'Content-type': 'text/html',
62 'Content-length': '12',
63 'Expires': 'Fri, 01 Jan 1990 00:00:00 GMT',
64 'Cache-Control': 'no-cache',
65 'ETag': '"NDcyNDU2MzU1"'},
68 '/home/appdir/index.html',
69 {'REQUEST_METHOD': 'GET'})
72 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
,
73 {'/home/appdir/index.html': (12345.6, 'NDcyNDU2MzU1')})
75 def test_load_cached_file(self
):
76 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
= {
77 '/home/appdir/index.html': (12345.6, 'NDcyNDU2MzU1')}
79 url_map
= appinfo
.URLMap(url
='/',
80 static_files
='index.html')
82 h
= static_files_handler
.StaticContentHandler(
87 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
88 static_files_handler
.StaticContentHandler
._read
_file
(
89 '/home/appdir/index.html').AndReturn('Hello World!')
92 self
.assertResponse('200 OK',
93 {'Content-type': 'text/html',
94 'Content-length': '12',
95 'Expires': 'Fri, 01 Jan 1990 00:00:00 GMT',
96 'Cache-Control': 'no-cache',
97 'ETag': '"NDcyNDU2MzU1"'},
100 '/home/appdir/index.html',
101 {'REQUEST_METHOD': 'GET'})
104 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
,
105 {'/home/appdir/index.html': (12345.6, 'NDcyNDU2MzU1')})
107 def test_load_head(self
):
108 url_map
= appinfo
.URLMap(url
='/',
109 static_files
='index.html')
111 h
= static_files_handler
.StaticContentHandler(
116 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
117 static_files_handler
.StaticContentHandler
._read
_file
(
118 '/home/appdir/index.html').AndReturn('Hello World!')
121 self
.assertResponse('200 OK',
122 {'Content-type': 'text/html',
123 'Content-length': '12',
124 'Expires': 'Fri, 01 Jan 1990 00:00:00 GMT',
125 'Cache-Control': 'no-cache',
126 'ETag': '"NDcyNDU2MzU1"'},
129 '/home/appdir/index.html',
130 {'REQUEST_METHOD': 'HEAD'})
133 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
,
134 {'/home/appdir/index.html': (12345.6, 'NDcyNDU2MzU1')})
136 def test_no_permission_read(self
):
137 url_map
= appinfo
.URLMap(url
='/',
138 static_files
='index.html')
140 h
= static_files_handler
.StaticContentHandler(
145 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
147 error
.errno
= errno
.EPERM
148 static_files_handler
.StaticContentHandler
._read
_file
(
149 '/home/appdir/index.html').AndRaise(error
)
152 self
.assertResponse('403 Forbidden',
156 '/home/appdir/index.html',
157 {'REQUEST_METHOD': 'GET',
161 def test_cached_no_permission_read(self
):
162 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
= {
163 '/home/appdir/index.html': (12345.6, 'NDcyNDU2MzU1')}
165 url_map
= appinfo
.URLMap(url
='/',
166 static_files
='index.html')
168 h
= static_files_handler
.StaticContentHandler(
173 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
175 error
.errno
= errno
.EPERM
176 static_files_handler
.StaticContentHandler
._read
_file
(
177 '/home/appdir/index.html').AndRaise(error
)
180 self
.assertResponse('403 Forbidden',
184 '/home/appdir/index.html',
185 {'REQUEST_METHOD': 'GET',
189 def test_file_does_not_exist_read(self
):
190 url_map
= appinfo
.URLMap(url
='/',
191 static_files
='index.html')
193 h
= static_files_handler
.StaticContentHandler(
198 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
200 error
.errno
= errno
.ENOENT
201 static_files_handler
.StaticContentHandler
._read
_file
(
202 '/home/appdir/index.html').AndRaise(error
)
205 self
.assertResponse('404 Not Found',
209 '/home/appdir/index.html',
210 {'REQUEST_METHOD': 'GET',
214 def test_file_does_not_exist_stat(self
):
215 url_map
= appinfo
.URLMap(url
='/',
216 static_files
='index.html')
218 h
= static_files_handler
.StaticContentHandler(
224 error
.errno
= errno
.ENOENT
225 os
.path
.getmtime('/home/appdir/index.html').AndRaise(error
)
228 self
.assertResponse('404 Not Found',
232 '/home/appdir/index.html',
233 {'REQUEST_METHOD': 'GET',
237 def test_if_match_without_match(self
):
238 url_map
= appinfo
.URLMap(url
='/',
239 static_files
='index.html')
241 h
= static_files_handler
.StaticContentHandler(
246 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
247 static_files_handler
.StaticContentHandler
._read
_file
(
248 '/home/appdir/index.html').AndReturn('Hello World!')
251 self
.assertResponse('412 Precondition Failed',
252 {'ETag': '"NDcyNDU2MzU1"'},
255 '/home/appdir/index.html',
256 {'REQUEST_METHOD': 'GET',
257 'HTTP_IF_MATCH': '"nomatch"'})
260 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
,
261 {'/home/appdir/index.html': (12345.6, 'NDcyNDU2MzU1')})
263 def test_if_match_no_file(self
):
264 url_map
= appinfo
.URLMap(url
='/',
265 static_files
='index.html')
267 h
= static_files_handler
.StaticContentHandler(
273 error
.errno
= errno
.ENOENT
274 os
.path
.getmtime('/home/appdir/index.html').AndRaise(error
)
277 self
.assertResponse('412 Precondition Failed',
281 '/home/appdir/index.html',
282 {'REQUEST_METHOD': 'GET',
283 'HTTP_IF_MATCH': '"nomatch"'})
286 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
,
289 def test_cached_if_match_without_match(self
):
290 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
= {
291 '/home/appdir/index.html': (12345.6, 'abc')}
293 url_map
= appinfo
.URLMap(url
='/',
294 static_files
='index.html')
296 h
= static_files_handler
.StaticContentHandler(
301 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
304 self
.assertResponse('412 Precondition Failed',
308 '/home/appdir/index.html',
309 {'REQUEST_METHOD': 'GET',
310 'HTTP_IF_MATCH': '"nomatch"'})
313 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
,
314 {'/home/appdir/index.html': (12345.6, 'abc')})
316 def test_if_none_match_with_match(self
):
317 url_map
= appinfo
.URLMap(url
='/',
318 static_files
='index.html')
320 h
= static_files_handler
.StaticContentHandler(
325 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
326 static_files_handler
.StaticContentHandler
._read
_file
(
327 '/home/appdir/index.html').AndReturn('Hello World!')
330 self
.assertResponse('304 Not Modified',
331 {'ETag': '"NDcyNDU2MzU1"'},
334 '/home/appdir/index.html',
335 {'REQUEST_METHOD': 'GET',
336 'HTTP_IF_NONE_MATCH': '"NDcyNDU2MzU1"'})
339 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
,
340 {'/home/appdir/index.html': (12345.6, 'NDcyNDU2MzU1')})
342 def test_cached_if_none_match_with_match(self
):
343 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
= {
344 '/home/appdir/index.html': (12345.6, 'match')}
346 url_map
= appinfo
.URLMap(url
='/',
347 static_files
='index.html')
349 h
= static_files_handler
.StaticContentHandler(
354 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
357 self
.assertResponse('304 Not Modified',
361 '/home/appdir/index.html',
362 {'REQUEST_METHOD': 'GET',
363 'HTTP_IF_NONE_MATCH': '"match"'})
366 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
,
367 {'/home/appdir/index.html': (12345.6, 'match')})
369 def test_custom_headers(self
):
370 http_headers
= appinfo
.HttpHeadersDict()
371 http_headers
['Content-type'] = 'text/xml'
372 http_headers
['ETag'] = 'abc123'
373 http_headers
['Expires'] = 'tomorrow'
374 http_headers
['Cache-Control'] = 'private'
375 http_headers
['Custom-Header'] = 'custom,value'
377 url_map
= appinfo
.URLMap(url
='/',
378 static_files
='index.html',
379 http_headers
=http_headers
)
381 h
= static_files_handler
.StaticContentHandler(
386 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
387 static_files_handler
.StaticContentHandler
._read
_file
(
388 '/home/appdir/index.html').AndReturn('Hello World!')
391 self
.assertResponse('200 OK',
392 {'Content-length': '12',
393 'Content-type': 'text/xml',
395 'Expires': 'tomorrow',
396 'Cache-Control': 'private',
397 'Custom-Header': 'custom,value'},
400 '/home/appdir/index.html',
401 {'REQUEST_METHOD': 'GET'})
404 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
,
405 {'/home/appdir/index.html': (12345.6, 'NDcyNDU2MzU1')})
407 def test_custom_mimetype(self
):
408 url_map
= appinfo
.URLMap(url
='/',
409 mime_type
='text/xml',
410 static_files
='index.html')
412 h
= static_files_handler
.StaticContentHandler(
417 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
418 static_files_handler
.StaticContentHandler
._read
_file
(
419 '/home/appdir/index.html').AndReturn('Hello World!')
422 self
.assertResponse('200 OK',
423 {'Content-type': 'text/xml',
424 'Content-length': '12',
425 'Expires': 'Fri, 01 Jan 1990 00:00:00 GMT',
426 'Cache-Control': 'no-cache',
427 'ETag': '"NDcyNDU2MzU1"'},
430 '/home/appdir/index.html',
431 {'REQUEST_METHOD': 'GET'})
434 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
,
435 {'/home/appdir/index.html': (12345.6, 'NDcyNDU2MzU1')})
437 def test_custom_expiration_ignored(self
):
438 url_map
= appinfo
.URLMap(url
='/',
439 expiration
='1d 2h 3m 4s',
440 static_files
='index.html')
442 h
= static_files_handler
.StaticContentHandler(
447 os
.path
.getmtime('/home/appdir/index.html').AndReturn(12345.6)
448 static_files_handler
.StaticContentHandler
._read
_file
(
449 '/home/appdir/index.html').AndReturn('Hello World!')
452 self
.assertResponse('200 OK',
453 {'Content-type': 'text/html',
454 'Content-length': '12',
455 'ETag': '"NDcyNDU2MzU1"',
456 'Expires': 'Fri, 01 Jan 1990 00:00:00 GMT',
457 'Cache-Control': 'no-cache'},
460 '/home/appdir/index.html',
461 {'REQUEST_METHOD': 'GET'})
464 static_files_handler
.StaticContentHandler
._filename
_to
_mtime
_and
_etag
,
465 {'/home/appdir/index.html': (12345.6, 'NDcyNDU2MzU1')})
467 def test_nonstandard_mimetype(self
):
468 url_map
= appinfo
.URLMap(url
='/',
469 static_files
='simple.dart')
471 h
= static_files_handler
.StaticContentHandler(
476 os
.path
.getmtime('/home/appdir/simple.dart').AndReturn(12345.6)
477 static_files_handler
.StaticContentHandler
._read
_file
(
478 '/home/appdir/simple.dart').AndReturn('void main() {}')
481 self
.assertResponse('200 OK',
482 {'Content-type': 'application/dart',
483 'Content-length': '14',
484 'Expires': 'Fri, 01 Jan 1990 00:00:00 GMT',
485 'Cache-Control': 'no-cache',
486 'ETag': '"LTE2OTA2MzYyMTM="'},
489 '/home/appdir/simple.dart',
490 {'REQUEST_METHOD': 'GET'})
494 class TestStaticContentHandlerCheckEtagMatch(unittest
.TestCase
):
495 """Tests for static_files_handler.StaticContentHandler._check_etag_match."""
497 def test_strong_match_required(self
):
499 static_files_handler
.StaticContentHandler
._check
_etag
_match
(
500 '"abc"', 'abc', allow_weak_match
=False))
502 def test_strong_match_no_match(self
):
504 static_files_handler
.StaticContentHandler
._check
_etag
_match
(
505 '"nomatch"', 'abc', allow_weak_match
=False))
507 def test_strong_match_with_weak_tag(self
):
509 static_files_handler
.StaticContentHandler
._check
_etag
_match
(
510 'W/"abc"', 'abc', allow_weak_match
=False))
512 def test_strong_match_star(self
):
514 static_files_handler
.StaticContentHandler
._check
_etag
_match
(
515 '*', 'abc', allow_weak_match
=False))
517 def test_weak_match_required(self
):
519 static_files_handler
.StaticContentHandler
._check
_etag
_match
(
520 '"abc"', 'abc', allow_weak_match
=True))
522 def test_weak_match_no_match(self
):
524 static_files_handler
.StaticContentHandler
._check
_etag
_match
(
525 '"nomatch"', 'abc', allow_weak_match
=True))
527 def test_weak_match_with_weak_tag(self
):
529 static_files_handler
.StaticContentHandler
._check
_etag
_match
(
530 'W/"abc"', 'abc', allow_weak_match
=True))
532 def test_weak_match_star(self
):
534 static_files_handler
.StaticContentHandler
._check
_etag
_match
(
535 '*', 'abc', allow_weak_match
=True))
537 def test_many_etags_match(self
):
539 static_files_handler
.StaticContentHandler
._check
_etag
_match
(
540 '"abc", "def", "ghi"', 'def', allow_weak_match
=False))
542 def test_many_etags_no_match(self
):
544 static_files_handler
.StaticContentHandler
._check
_etag
_match
(
545 '"abc", "def", "ghi"', 'jkl', allow_weak_match
=False))
548 class TestStaticFilesHandler(wsgi_test_utils
.WSGITestCase
):
549 """Tests for static_files_handler.StaticFilesHandler."""
553 self
.mox
.StubOutWithMock(static_files_handler
.StaticContentHandler
,
557 self
.mox
.UnsetStubs()
559 def test_simple_path(self
):
560 url_map
= appinfo
.URLMap(url
='/',
561 static_files
='index.html')
562 h
= static_files_handler
.StaticFilesHandler(root_path
='/appdir',
565 self
.assertTrue(match
)
566 self
.assertFalse(h
.match('/other'))
568 static_files_handler
.StaticContentHandler
._handle
_path
(
569 os
.path
.join('/appdir', 'index.html'),
571 mox
.IgnoreArg()).AndReturn('<output>')
573 self
.assertEqual('<output>',
574 h
.handle(match
, {}, None))
577 def test_patterned_path(self
):
578 url_map
= appinfo
.URLMap(url
=r
'/(.*)/(.*)',
579 static_files
=r
'static/\1/subdir/\2')
580 h
= static_files_handler
.StaticFilesHandler(root_path
='/appdir',
582 match
= h
.match('/hello/foo.jpg')
583 self
.assertTrue(match
)
584 self
.assertFalse(h
.match('/'))
586 static_files_handler
.StaticContentHandler
._handle
_path
(
587 os
.path
.join('/appdir', 'static/hello/subdir/foo.jpg'),
589 mox
.IgnoreArg()).AndReturn('<output>')
591 self
.assertEqual('<output>',
592 h
.handle(match
, {}, None))
595 def test_invalid_regex(self
):
596 url_map
= appinfo
.URLMap(url
='((.*))(',
597 static_files
='index.html')
598 self
.assertRaises(errors
.InvalidAppConfigError
,
599 static_files_handler
.StaticFilesHandler
,
604 class TestStaticDirHandler(wsgi_test_utils
.WSGITestCase
):
605 """Tests for static_files_handler.StaticDirHandler."""
609 self
.mox
.StubOutWithMock(static_files_handler
.StaticContentHandler
,
613 self
.mox
.UnsetStubs()
615 def test_simple_path(self
):
616 url_map
= appinfo
.URLMap(url
='/foo',
618 h
= static_files_handler
.StaticDirHandler(root_path
='/appdir',
620 match
= h
.match('/foo/bar.jpg')
621 self
.assertTrue(match
)
622 self
.assertFalse(h
.match('/baz'))
624 static_files_handler
.StaticContentHandler
._handle
_path
(
625 os
.path
.join('/appdir', 'subdir', 'bar.jpg'),
627 mox
.IgnoreArg()).AndReturn('<output>')
629 self
.assertEqual('<output>',
630 h
.handle(match
, {}, None))
633 def test_invalid_regex(self
):
634 url_map
= appinfo
.URLMap(url
='((.*))(',
635 static_files
='index.html')
636 self
.assertRaises(errors
.InvalidAppConfigError
,
637 static_files_handler
.StaticDirHandler
,
641 if __name__
== '__main__':