changeset 568:6b6c5442c790

bug: Correctly handle root URLs with special characters. The `site/root` setting is now pre-escaped to get a correct URL, and routing excludes it from escaping. Add unit tests.
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 13 Oct 2015 22:50:38 -0700
parents a65f04ddbea2
children 34e57d4b97e2
files piecrust/app.py piecrust/routing.py tests/bakes/test_special_root.yaml tests/test_baking_baker.py tests/test_routing.py
diffstat 5 files changed, 57 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/piecrust/app.py	Tue Oct 13 22:46:05 2015 -0700
+++ b/piecrust/app.py	Tue Oct 13 22:50:38 2015 -0700
@@ -2,6 +2,7 @@
 import json
 import time
 import os.path
+import urllib.parse
 import codecs
 import hashlib
 import logging
@@ -145,7 +146,7 @@
         if not sitec['root'].startswith('/'):
             raise ConfigurationError("The `site/root` setting must start "
                                      "with a slash.")
-        sitec['root'] = sitec['root'].rstrip('/') + '/'
+        sitec['root'] = urllib.parse.quote(sitec['root'].rstrip('/') + '/')
 
         # Cache auto-format regexes.
         if not isinstance(sitec['auto_formats'], dict):
--- a/piecrust/routing.py	Tue Oct 13 22:46:05 2015 -0700
+++ b/piecrust/routing.py	Tue Oct 13 22:50:38 2015 -0700
@@ -227,7 +227,7 @@
                 else:
                     uri = base_uri + ext
 
-        uri = urllib.parse.quote(self.uri_root + uri)
+        uri = self.uri_root + urllib.parse.quote(uri)
 
         if self.show_debug_info:
             uri += '?!debug'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/bakes/test_special_root.yaml	Tue Oct 13 22:50:38 2015 -0700
@@ -0,0 +1,10 @@
+---
+config:
+    site:
+        root: /~john/public/
+in:
+    pages/about.md: 'URL: {{page.url}}, LINK: {{pcurl("missing")}}'
+    pages/_index.md: 'URL: {{page.url}}'
+out:
+    about.html: 'URL: /%7Ejohn/public/about.html, LINK: /%7Ejohn/public/missing.html'
+    index.html: 'URL: /%7Ejohn/public/'
--- a/tests/test_baking_baker.py	Tue Oct 13 22:46:05 2015 -0700
+++ b/tests/test_baking_baker.py	Tue Oct 13 22:50:38 2015 -0700
@@ -1,5 +1,6 @@
 import time
 import os.path
+import urllib.parse
 import pytest
 from piecrust.baking.baker import Baker
 from piecrust.baking.single import PageBaker
@@ -41,8 +42,8 @@
         app.config.set('site/pretty_urls', True)
     assert app.config.get('site/pretty_urls') == pretty
 
-    for site_root in ['/', '/whatever/']:
-        app.config.set('site/root', site_root)
+    for site_root in ['/', '/whatever/', '/~johndoe/']:
+        app.config.set('site/root', urllib.parse.quote(site_root))
         baker = PageBaker(app, '/destination')
         path = baker.getOutputPath(site_root + uri)
         expected = os.path.normpath(
--- a/tests/test_routing.py	Tue Oct 13 22:46:05 2015 -0700
+++ b/tests/test_routing.py	Tue Oct 13 22:50:38 2015 -0700
@@ -1,3 +1,4 @@
+import urllib.parse
 import pytest
 from piecrust.routing import Route
 from .mockutil import get_mock_app
@@ -34,7 +35,11 @@
             ('/something', '/%foo%', set(['foo'])),
             ('/something', '/%path:foo%', set(['foo'])),
             ('/something', '/%foo%/%bar%', set(['foo', 'bar'])),
-            ('/something', '/%foo%/%path:bar%', set(['foo', 'bar']))
+            ('/something', '/%foo%/%path:bar%', set(['foo', 'bar'])),
+            ('/~johndoe', '/%foo%', set(['foo'])),
+            ('/~johndoe', '/%path:foo%', set(['foo'])),
+            ('/~johndoe', '/%foo%/%bar%', set(['foo', 'bar'])),
+            ('/~johndoe', '/%foo%/%path:bar%', set(['foo', 'bar']))
             ])
 def test_required_metadata(site_root, route_pattern,
                            expected_required_metadata):
@@ -91,22 +96,44 @@
             ('/blah', {'url': '/prefix/%path:foo%'},
                 'prefix',
                 {'foo': ''}),
+
+            ('/~johndoe', {'url': '/%foo%'},
+                'something',
+                {'foo': 'something'}),
+            ('/~johndoe', {'url': '/%foo%'},
+                'something/other',
+                None),
+            ('/~johndoe', {'url': '/%path:foo%'},
+                'something/other',
+                {'foo': 'something/other'}),
+            ('/~johndoe', {'url': '/%path:foo%'},
+                '',
+                {'foo': ''}),
+            ('/~johndoe', {'url': '/prefix/%path:foo%'},
+                'prefix/something/other',
+                {'foo': 'something/other'}),
+            ('/~johndoe', {'url': '/prefix/%path:foo%'},
+                'prefix/',
+                {'foo': ''}),
+            ('/~johndoe', {'url': '/prefix/%path:foo%'},
+                'prefix',
+                {'foo': ''}),
             ])
 def test_match_uri(site_root, config, uri, expected_match):
     site_root = site_root.rstrip('/') + '/'
     app = get_mock_app()
-    app.config.set('site/root', site_root)
+    app.config.set('site/root', urllib.parse.quote(site_root))
     config.setdefault('source', 'blah')
     route = Route(app, config)
     assert route.uri_pattern == config['url'].lstrip('/')
-    m = route.matchUri(site_root + uri)
+    m = route.matchUri(urllib.parse.quote(site_root) + uri)
     assert m == expected_match
 
 
 @pytest.mark.parametrize(
         'site_root',
         [
-            ('/'), ('/whatever')
+            ('/'), ('/whatever'), ('/~johndoe')
             ])
 def test_match_uri_requires_absolute_uri(site_root):
     with pytest.raises(Exception):
@@ -148,14 +175,15 @@
             ('foo.bar.ext', 2, False, 'foo.bar/2.ext')
             ])
 def test_get_uri(slug, page_num, pretty, expected):
-    app = get_mock_app()
-    app.config.set('site/root', '/blah/')
-    app.config.set('site/pretty_urls', pretty)
-    app.config.set('site/trailing_slash', False)
-    app.config.set('__cache/pagination_suffix_format', '/%(num)d')
+    for root in ['/', '/blah/', '/~johndoe/']:
+        app = get_mock_app()
+        app.config.set('site/root', urllib.parse.quote(root))
+        app.config.set('site/pretty_urls', pretty)
+        app.config.set('site/trailing_slash', False)
+        app.config.set('__cache/pagination_suffix_format', '/%(num)d')
 
-    config = {'url': '/%path:slug%', 'source': 'blah'}
-    route = Route(app, config)
-    uri = route.getUri({'slug': slug}, sub_num=page_num)
-    assert uri == ('/blah/' + expected)
+        config = {'url': '/%path:slug%', 'source': 'blah'}
+        route = Route(app, config)
+        uri = route.getUri({'slug': slug}, sub_num=page_num)
+        assert uri == (urllib.parse.quote(root) + expected)