From 20661e1301a41c163f1ed585758f2742ee7ff5c2 Mon Sep 17 00:00:00 2001 From: Irfanuddin Date: Tue, 8 Nov 2022 01:55:39 +0530 Subject: [PATCH] tests: added tests --- fastapi/dependencies/utils.py | 6 +- fastapi/routing.py | 7 +- tests/test_include_duplicate_path_route.py | 121 +++++++++++++++++++++ 3 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 tests/test_include_duplicate_path_route.py diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 64a6c1276..d35aa1b36 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -12,6 +12,7 @@ from typing import ( Mapping, Optional, Sequence, + Set, Tuple, Type, Union, @@ -74,7 +75,6 @@ sequence_shape_to_type = { SHAPE_TUPLE_ELLIPSIS: list, } - multipart_not_installed_error = ( 'Form data requires "python-multipart" to be installed. \n' 'You can install "python-multipart" with: \n\n' @@ -755,3 +755,7 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]: ) check_file_field(final_field) return final_field + + +def get_path_hash_val(path: str, methods: Optional[Set[str]]) -> str: + return f"path:{path};methods:{methods}" diff --git a/fastapi/routing.py b/fastapi/routing.py index 06c0a7342..a923cd29f 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -26,6 +26,7 @@ from fastapi.dependencies.utils import ( get_body_field, get_dependant, get_parameterless_sub_dependant, + get_path_hash_val, solve_dependencies, ) from fastapi.encoders import DictIntStrAny, SetIntStr, jsonable_encoder @@ -446,7 +447,7 @@ class APIRoute(routing.Route): get_parameterless_sub_dependant(depends=depends, path=self.path_format), ) self.body_field = get_body_field(dependant=self.dependant, name=self.unique_id) - self.hash_val = f"path:{self.path};methods:{self.methods}" + self.hash_val = get_path_hash_val(self.path, self.methods) self.app = request_response(self.get_route_handler()) def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]: @@ -780,6 +781,10 @@ class APIRouter(routing.Router): ) elif isinstance(route, routing.Route): methods = list(route.methods or []) + hash_val = get_path_hash_val(route.path, route.methods) + if hash_val in self.added_routes: + raise RouteAlreadyExistsError(route.name) + self.added_routes.add(hash_val) self.add_route( prefix + route.path, route.endpoint, diff --git a/tests/test_include_duplicate_path_route.py b/tests/test_include_duplicate_path_route.py new file mode 100644 index 000000000..ca191ffdd --- /dev/null +++ b/tests/test_include_duplicate_path_route.py @@ -0,0 +1,121 @@ +import pytest +from fastapi import APIRouter, FastAPI +from fastapi.exceptions import RouteAlreadyExistsError + + +def test_app_router_with_duplicate_path(): + with pytest.raises(RouteAlreadyExistsError): + app = FastAPI() + + @app.get("/items/") + def read_items(): + return + + @app.get("/items/") + def read_items2(): + return + + +def test_sub_with_duplicate_path(): + with pytest.raises(RouteAlreadyExistsError): + app = FastAPI() + router = APIRouter() + + @router.get("/items/") + def read_items(): + return + + @router.get("/items/") + def read_items2(): + return + + app.include_router(router) + + +def test_mix_app_sub_with_duplicate_path(): + with pytest.raises(RouteAlreadyExistsError): + app = FastAPI() + router = APIRouter() + + @app.get("/items/") + def read_items(): + return + + @router.get("/items/") + def read_items2(): + return + + app.include_router(router) + + +def test_sub_route_direct_duplicate_path(): + with pytest.raises(RouteAlreadyExistsError): + app = FastAPI() + router = APIRouter() + + @router.route("/items/") + def read_items(): + return + + @router.route("/items/") + def read_items2(): + return + + app.include_router(router) + + +def test_app_router_with_duplicate_path_different_method(): + app = FastAPI() + + @app.get("/items/") + def read_items(): + return + + @app.post("/items/") + def read_items2(): + return + + +def test_sub_with_duplicate_path_different_method(): + app = FastAPI() + router = APIRouter() + + @router.get("/items/") + def read_items(): + return + + @router.post("/items/") + def read_items2(): + return + + app.include_router(router) + + +def test_mix_app_sub_with_duplicate_different_method(): + app = FastAPI() + router = APIRouter() + + @app.get("/items/") + def read_items(): + return + + @router.post("/items/") + def read_items2(): + return + + app.include_router(router) + + +def test_sub_route_direct_duplicate_path_different_method(): + app = FastAPI() + router = APIRouter() + + @router.route("/items/") + def read_items(): + return + + @router.route("/items/", methods=["POST"]) + def read_items2(): + return + + app.include_router(router)