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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user