From cbc170815eb0d08e6b4eefd7a482a5b9bfc6d3ea Mon Sep 17 00:00:00 2001 From: "Walancy.S" <95140989+Walancy@users.noreply.github.com> Date: Thu, 1 May 2025 18:46:02 -0300 Subject: [PATCH 1/2] Add files via upload --- tests/test_include_more_routes.py | 113 +++++++++++++++++++ tests/test_route_with_multiple_methods.py | 130 ++++++++++++++++++++++ 2 files changed, 243 insertions(+) create mode 100644 tests/test_include_more_routes.py create mode 100644 tests/test_route_with_multiple_methods.py diff --git a/tests/test_include_more_routes.py b/tests/test_include_more_routes.py new file mode 100644 index 000000000..dc246677b --- /dev/null +++ b/tests/test_include_more_routes.py @@ -0,0 +1,113 @@ +from fastapi import APIRouter, FastAPI, Request +from fastapi.responses import JSONResponse +from fastapi.testclient import TestClient +from pydantic import BaseModel +import pytest + +# ====================== +# Configuração do app e rotas +# ====================== + +app = FastAPI() +router = APIRouter() + + +class Item(BaseModel): + nome: str + quantidade: int + + +@router.route("/items/", methods=["GET", "POST"]) +async def read_items(request: Request): + if request.method == "POST": + try: + dados = await request.json() + item = Item(**dados) + return JSONResponse({"message": "Item criado", "item": item.model_dump()}, status_code=201) + except Exception: + return JSONResponse({"detail": "Erro ao processar JSON"}, status_code=400) + return JSONResponse({"hello": "world"}) + + +app.include_router(router) +client = TestClient(app) + + +################ Testes + + +# Testa se a rota GET /items/ retorna a resposta padrão esperada +def test_get_items(): + resposta = client.get("/items/") + assert resposta.status_code == 200 + assert resposta.json() == {"hello": "world"} + + +# Testa se a rota POST /items/ aceita e retorna corretamente um JSON válido +def test_post_items(): + payload = {"nome": "Caderno", "quantidade": 10} + resposta = client.post("/items/", json=payload) + assert resposta.status_code == 201 + assert resposta.json() == { + "message": "Item criado", + "item": {"nome": "Caderno", "quantidade": 10} + } + + +# Testa se a rota POST /items/ retorna erro ao receber JSON inválido (faltando campo obrigatório) +def test_post_json_invalido(): + payload = {"quantidade": 5} # Campo 'nome' está ausente + resposta = client.post("/items/", json=payload) + assert resposta.status_code == 400 + assert resposta.json()["detail"] == "Erro ao processar JSON" + + +# Testa se a rota POST /items/ retorna erro ao receber corpo que não seja JSON +def test_post_com_corpo_nao_json(): + resposta = client.post("/items/", content="texto") # Texto cru, não é JSON + assert resposta.status_code == 400 + assert resposta.json()["detail"] == "Erro ao processar JSON" + + +# Testa se a rota POST /items/ aceita headers customizados e responde corretamente +def test_post_com_header_customizado(): + headers = {"Custom-Header": "123"} + payload = {"nome": "Caneta", "quantidade": 3} + resposta = client.post("/items/", json=payload, headers=headers) + assert resposta.status_code == 201 + assert resposta.json()["item"]["nome"] == "Caneta" + + +# Testa se métodos não permitidos como PUT são corretamente bloqueados pela API +def test_method_not_allowed(): + resposta = client.put("/items/") + assert resposta.status_code == 405 # 405 Method Not Allowed + assert "detail" in resposta.json() + + +# Testa múltiplos métodos HTTP para a mesma rota usando parametrização +# Verifica se cada método responde com o status esperado +@pytest.mark.parametrize("metodo,status_esperado", [ + ("GET", 200), + ("POST", 201), + ("PUT", 405), + ("DELETE", 405), + ("PATCH", 405), +]) +def test_varios_metodos(metodo, status_esperado): + payload = {"nome": "Caneta", "quantidade": 1} + resposta = client.request(metodo, "/items/", json=payload) + assert resposta.status_code == status_esperado + + +# Testa se a resposta da rota GET /items/ contém o header correto de Content-Type (application/json) +def test_headers_da_resposta(): + resposta = client.get("/items/") + assert resposta.headers["content-type"].startswith("application/json") + + +# Testa se a rota GET ignora parâmetros de query (ex: ?busca=algo) +def test_get_com_query_params_ignorados(): + resposta = client.get("/items/?busca=algo") + assert resposta.status_code == 200 + assert resposta.json() == {"hello": "world"} diff --git a/tests/test_route_with_multiple_methods.py b/tests/test_route_with_multiple_methods.py new file mode 100644 index 000000000..97f8fc7da --- /dev/null +++ b/tests/test_route_with_multiple_methods.py @@ -0,0 +1,130 @@ +# https://github.com/fastapi/fastapi/issues/10180 + +from fastapi import FastAPI, APIRouter +from fastapi.testclient import TestClient + +def test_mount_subapp_on_apirouter_should_not_work(): + app = FastAPI() + router = APIRouter(prefix="/api") + + @router.get("/main") + def main(): + return {"msg": "main"} + + subapp = FastAPI() + + @subapp.get("/sub") + def sub(): + return {"msg": "sub"} + + # Tentativa de montar subapp no router (não funciona) + router.mount("/subapi", subapp) + app.include_router(router) + + client = TestClient(app) + # A rota principal funciona + assert client.get("/api/main").status_code == 200 + # A rota da subaplicação NÃO funciona (deveria retornar 404) + assert client.get("/api/subapi/sub").status_code == 404 + + +def test_mount_subapp_on_app_should_work(): + app = FastAPI() + router = APIRouter(prefix="/api") + + @router.get("/main") + def main(): + return {"msg": "main"} + + subapp = FastAPI() + + @subapp.get("/sub") + def sub(): + return {"msg": "sub"} + + app.include_router(router) + # Montando corretamente no app principal + app.mount("/api/subapi", subapp) + + client = TestClient(app) + # A rota principal funciona + assert client.get("/api/main").status_code == 200 + # A rota da subaplicação funciona + assert client.get("/api/subapi/sub").status_code == 200 + + +# Testes adicionais para melhorar cobertura: + + +def test_mount_multiple_subapps(): + """Testa se é possível montar múltiplas subaplicações""" + app = FastAPI() + + # Primeira subaplicação + subapp1 = FastAPI() + + @subapp1.get("/test1") + def read_test1(): + return {"msg": "test1"} + + # Segunda subaplicação + subapp2 = FastAPI() + + @subapp2.get("/test2") + def read_test2(): + return {"msg": "test2"} + + # Montando ambas as subaplicações + app.mount("/sub1", subapp1) + app.mount("/sub2", subapp2) + + client = TestClient(app) + # Verifica se ambas as subaplicações funcionam + assert client.get("/sub1/test1").status_code == 200 + assert client.get("/sub2/test2").status_code == 200 + assert client.get("/sub1/test1").json() == {"msg": "test1"} + assert client.get("/sub2/test2").json() == {"msg": "test2"} + + +def test_nested_routes(): + """Testa rotas aninhadas em diferentes níveis""" + app = FastAPI() + + # Criando routers aninhados + router1 = APIRouter(prefix="/v1") + router2 = APIRouter(prefix="/api") + + @router2.get("/deep") + def read_deep(): + return {"msg": "deep route"} + + # Aninhando os routers + router1.include_router(router2) + app.include_router(router1) + + client = TestClient(app) + # Verifica se a rota aninhada funciona + response = client.get("/v1/api/deep") + assert response.status_code == 200 + assert response.json() == {"msg": "deep route"} + + # Verifica se uma rota inexistente retorna 404 + assert client.get("/v1/api/nonexistent").status_code == 404 + + +def test_method_not_allowed(): + """Testa se métodos HTTP não permitidos são tratados corretamente""" + app = FastAPI() + + @app.get("/only-get") + def read_only(): + return {"msg": "get only"} + + client = TestClient(app) + # Verifica se GET funciona + assert client.get("/only-get").status_code == 200 + + # Verifica se outros métodos retornam 405 (Method Not Allowed) + assert client.post("/only-get").status_code == 405 + assert client.put("/only-get").status_code == 405 + assert client.delete("/only-get").status_code == 405 From b8c46a01a5541dc75a435a3dff36162bf9a1f62a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 21:47:59 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20for?= =?UTF-8?q?mat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_include_more_routes.py | 25 ++++++++++++++--------- tests/test_route_with_multiple_methods.py | 3 ++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/test_include_more_routes.py b/tests/test_include_more_routes.py index dc246677b..ed84b4195 100644 --- a/tests/test_include_more_routes.py +++ b/tests/test_include_more_routes.py @@ -1,8 +1,8 @@ +import pytest from fastapi import APIRouter, FastAPI, Request from fastapi.responses import JSONResponse from fastapi.testclient import TestClient from pydantic import BaseModel -import pytest # ====================== # Configuração do app e rotas @@ -23,7 +23,9 @@ async def read_items(request: Request): try: dados = await request.json() item = Item(**dados) - return JSONResponse({"message": "Item criado", "item": item.model_dump()}, status_code=201) + return JSONResponse( + {"message": "Item criado", "item": item.model_dump()}, status_code=201 + ) except Exception: return JSONResponse({"detail": "Erro ao processar JSON"}, status_code=400) return JSONResponse({"hello": "world"}) @@ -50,7 +52,7 @@ def test_post_items(): assert resposta.status_code == 201 assert resposta.json() == { "message": "Item criado", - "item": {"nome": "Caderno", "quantidade": 10} + "item": {"nome": "Caderno", "quantidade": 10}, } @@ -87,13 +89,16 @@ def test_method_not_allowed(): # Testa múltiplos métodos HTTP para a mesma rota usando parametrização # Verifica se cada método responde com o status esperado -@pytest.mark.parametrize("metodo,status_esperado", [ - ("GET", 200), - ("POST", 201), - ("PUT", 405), - ("DELETE", 405), - ("PATCH", 405), -]) +@pytest.mark.parametrize( + "metodo,status_esperado", + [ + ("GET", 200), + ("POST", 201), + ("PUT", 405), + ("DELETE", 405), + ("PATCH", 405), + ], +) def test_varios_metodos(metodo, status_esperado): payload = {"nome": "Caneta", "quantidade": 1} resposta = client.request(metodo, "/items/", json=payload) diff --git a/tests/test_route_with_multiple_methods.py b/tests/test_route_with_multiple_methods.py index 97f8fc7da..e114cbf04 100644 --- a/tests/test_route_with_multiple_methods.py +++ b/tests/test_route_with_multiple_methods.py @@ -1,8 +1,9 @@ # https://github.com/fastapi/fastapi/issues/10180 -from fastapi import FastAPI, APIRouter +from fastapi import APIRouter, FastAPI from fastapi.testclient import TestClient + def test_mount_subapp_on_apirouter_should_not_work(): app = FastAPI() router = APIRouter(prefix="/api")