Router tweaks (#2031)

* Add trailing slash when defined and strict_slashes

* Add partial matching, and fix some issues with url_for

* Cover additional edge cases

* cleanup tests
This commit is contained in:
Adam Hopkins
2021-03-01 15:30:52 +02:00
committed by GitHub
parent 4b968dc611
commit 27f64ddae2
14 changed files with 175 additions and 63 deletions

View File

@@ -39,7 +39,7 @@ class TestSanicRouteResolution:
iterations=1000,
rounds=1000,
)
assert await result[0](None) == 1
assert await result[1](None) == 1
@mark.asyncio
async def test_resolve_route_with_typed_args(
@@ -72,4 +72,4 @@ class TestSanicRouteResolution:
iterations=1000,
rounds=1000,
)
assert await result[0](None) == 1
assert await result[1](None) == 1

View File

@@ -4,7 +4,7 @@ import sys
from inspect import isawaitable
from os import environ
from unittest.mock import patch
from unittest.mock import Mock, patch
import pytest
@@ -123,7 +123,7 @@ def test_app_route_raise_value_error(app):
def test_app_handle_request_handler_is_none(app, monkeypatch):
def mockreturn(*args, **kwargs):
return None, {}, "", "", False
return Mock(), None, {}
# Not sure how to make app.router.get() return None, so use mock here.
monkeypatch.setattr(app.router, "get", mockreturn)

View File

@@ -752,7 +752,7 @@ def test_static_blueprint_name(static_file_directory, file_name):
app.blueprint(bp)
uri = app.url_for("static", name="static.testing")
assert uri == "/static/test.file"
assert uri == "/static/test.file/"
_, response = app.test_client.get("/static/test.file")
assert response.status == 404

View File

@@ -126,7 +126,6 @@ def test_html_traceback_output_in_debug_mode():
assert response.status == 500
soup = BeautifulSoup(response.body, "html.parser")
html = str(soup)
print(html)
assert "response = handler(request, **kwargs)" in html
assert "handler_4" in html

View File

@@ -59,7 +59,6 @@ def write_app(filename, **runargs):
def scanner(proc):
for line in proc.stdout:
line = line.decode().strip()
print(">", line)
if line.startswith("complete"):
yield line

View File

@@ -74,3 +74,12 @@ def test_custom_generator():
"/", headers={"SOME-OTHER-REQUEST-ID": f"{REQUEST_ID}"}
)
assert request.id == REQUEST_ID * 2
def test_route_assigned_to_request(app):
@app.get("/")
async def get(request):
return response.empty()
request, _ = app.test_client.get("/")
assert request.route is list(app.router.routes.values())[0]

View File

@@ -646,7 +646,6 @@ def test_streaming_echo():
data = await reader.read(4096)
assert data
buffer += data
print(res)
assert buffer[size : size + 2] == b"\r\n"
ret, buffer = buffer[:size], buffer[size + 2 :]
return ret

View File

@@ -1,15 +1,20 @@
import asyncio
import re
from unittest.mock import Mock
import pytest
from sanic_routing.exceptions import ParameterNameConflicts, RouteExists
from sanic_routing.exceptions import (
InvalidUsage,
ParameterNameConflicts,
RouteExists,
)
from sanic_testing.testing import SanicTestClient
from sanic import Blueprint, Sanic
from sanic.constants import HTTP_METHODS
from sanic.exceptions import NotFound
from sanic.exceptions import NotFound, SanicException
from sanic.request import Request
from sanic.response import json, text
@@ -189,7 +194,6 @@ def test_versioned_routes_get(app, method):
return text("OK")
else:
print(func)
raise Exception(f"Method: {method} is not callable")
client_method = getattr(app.test_client, method)
@@ -1113,3 +1117,59 @@ def test_route_invalid_host(app):
assert str(excinfo.value) == (
"Expected either string or Iterable of " "host strings, not {!r}"
).format(host)
def test_route_with_regex_group(app):
@app.route("/path/to/<ext:file\.(txt)>")
async def handler(request, ext):
return text(ext)
_, response = app.test_client.get("/path/to/file.txt")
assert response.text == "txt"
def test_route_with_regex_named_group(app):
@app.route(r"/path/to/<ext:file\.(?P<ext>txt)>")
async def handler(request, ext):
return text(ext)
_, response = app.test_client.get("/path/to/file.txt")
assert response.text == "txt"
def test_route_with_regex_named_group_invalid(app):
@app.route(r"/path/to/<ext:file\.(?P<wrong>txt)>")
async def handler(request, ext):
return text(ext)
with pytest.raises(InvalidUsage) as e:
app.router.finalize()
assert e.match(
re.escape("Named group (wrong) must match your named parameter (ext)")
)
def test_route_with_regex_group_ambiguous(app):
@app.route("/path/to/<ext:file(?:\.)(txt)>")
async def handler(request, ext):
return text(ext)
with pytest.raises(InvalidUsage) as e:
app.router.finalize()
assert e.match(
re.escape(
"Could not compile pattern file(?:\.)(txt). Try using a named "
"group instead: '(?P<ext>your_matching_group)'"
)
)
def test_route_with_bad_named_param(app):
@app.route("/foo/<__bar__>")
async def handler(request):
return text("...")
with pytest.raises(SanicException):
app.router.finalize()

View File

@@ -344,3 +344,23 @@ def test_methodview_naming(methodview_app):
assert viewone_url == "/view_one"
assert viewtwo_url == "/view_two"
@pytest.mark.parametrize(
"path,version,expected",
(
("/foo", 1, "/v1/foo"),
("/foo", 1.1, "/v1.1/foo"),
("/foo", "1", "/v1/foo"),
("/foo", "1.1", "/v1.1/foo"),
("/foo", "1.0.1", "/v1.0.1/foo"),
("/foo", "v1.0.1", "/v1.0.1/foo"),
),
)
def test_versioning(app, path, version, expected):
@app.route(path, version=version)
def handler(*_):
...
url = app.url_for("handler")
assert url == expected

View File

@@ -88,3 +88,19 @@ def test_websocket_bp_route_name(app):
# TODO: add test with a route with multiple hosts
# TODO: add test with a route with _host in url_for
@pytest.mark.parametrize(
"path,strict,expected",
(
("/foo", False, "/foo"),
("/foo/", False, "/foo"),
("/foo", True, "/foo"),
("/foo/", True, "/foo/"),
),
)
def test_trailing_slash_url_for(app, path, strict, expected):
@app.route(path, strict_slashes=strict)
def handler(*_):
...
url = app.url_for("handler")
assert url == expected