pythonasyncioapiasyncfastapiframeworkjsonjson-schemaopenapiopenapi3pydanticpython-typespython3redocreststarletteswaggerswagger-uiuvicornweb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
4.2 KiB
142 lines
4.2 KiB
"""Tests for merging injected BackgroundTasks with Response.background.
|
|
|
|
When a route uses both the injected ``BackgroundTasks`` dependency and returns
|
|
a ``Response`` with its own ``background`` parameter, both sets of tasks should
|
|
execute. Previously, the Response's background task silently overwrote the
|
|
injected tasks.
|
|
|
|
See: https://github.com/fastapi/fastapi/issues/11215
|
|
"""
|
|
|
|
from fastapi import FastAPI, BackgroundTasks
|
|
from fastapi.testclient import TestClient
|
|
from starlette.background import BackgroundTask
|
|
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
|
|
from starlette.responses import Response
|
|
|
|
|
|
def test_injected_tasks_not_lost_when_response_has_background():
|
|
"""Core bug: injected BackgroundTasks must not be discarded."""
|
|
results: list[str] = []
|
|
app = FastAPI()
|
|
|
|
@app.get("/")
|
|
async def endpoint(tasks: BackgroundTasks):
|
|
tasks.add_task(lambda: results.append("injected"))
|
|
return Response(
|
|
content="ok",
|
|
background=BackgroundTask(lambda: results.append("response")),
|
|
)
|
|
|
|
client = TestClient(app)
|
|
response = client.get("/")
|
|
assert response.status_code == 200
|
|
assert "injected" in results
|
|
assert "response" in results
|
|
|
|
|
|
def test_injected_tasks_run_before_response_tasks():
|
|
"""Injected tasks should run first (dependency order), then response tasks."""
|
|
results: list[str] = []
|
|
app = FastAPI()
|
|
|
|
@app.get("/")
|
|
async def endpoint(tasks: BackgroundTasks):
|
|
tasks.add_task(lambda: results.append("first"))
|
|
return Response(
|
|
content="ok",
|
|
background=BackgroundTask(lambda: results.append("second")),
|
|
)
|
|
|
|
TestClient(app).get("/")
|
|
assert results == ["first", "second"]
|
|
|
|
|
|
def test_multiple_injected_with_single_response_task():
|
|
results: list[str] = []
|
|
app = FastAPI()
|
|
|
|
@app.get("/")
|
|
async def endpoint(tasks: BackgroundTasks):
|
|
tasks.add_task(lambda: results.append("a"))
|
|
tasks.add_task(lambda: results.append("b"))
|
|
return Response(
|
|
content="ok",
|
|
background=BackgroundTask(lambda: results.append("c")),
|
|
)
|
|
|
|
TestClient(app).get("/")
|
|
assert results == ["a", "b", "c"]
|
|
|
|
|
|
def test_injected_with_response_background_tasks():
|
|
"""Response.background can also be a BackgroundTasks (not just single task)."""
|
|
results: list[str] = []
|
|
app = FastAPI()
|
|
|
|
@app.get("/")
|
|
async def endpoint(tasks: BackgroundTasks):
|
|
tasks.add_task(lambda: results.append("injected"))
|
|
bg = StarletteBackgroundTasks()
|
|
bg.add_task(lambda: results.append("resp1"))
|
|
bg.add_task(lambda: results.append("resp2"))
|
|
return Response(content="ok", background=bg)
|
|
|
|
TestClient(app).get("/")
|
|
assert results == ["injected", "resp1", "resp2"]
|
|
|
|
|
|
def test_only_injected_tasks_still_works():
|
|
"""No regression: injected tasks without Response.background still work."""
|
|
results: list[str] = []
|
|
app = FastAPI()
|
|
|
|
@app.get("/")
|
|
async def endpoint(tasks: BackgroundTasks):
|
|
tasks.add_task(lambda: results.append("only"))
|
|
return Response(content="ok")
|
|
|
|
TestClient(app).get("/")
|
|
assert results == ["only"]
|
|
|
|
|
|
def test_only_response_background_still_works():
|
|
"""No regression: Response.background without injected tasks still works."""
|
|
results: list[str] = []
|
|
app = FastAPI()
|
|
|
|
@app.get("/")
|
|
async def endpoint():
|
|
return Response(
|
|
content="ok",
|
|
background=BackgroundTask(lambda: results.append("only")),
|
|
)
|
|
|
|
TestClient(app).get("/")
|
|
assert results == ["only"]
|
|
|
|
|
|
def test_no_background_tasks_at_all():
|
|
"""No regression: routes without any background tasks work normally."""
|
|
app = FastAPI()
|
|
|
|
@app.get("/")
|
|
async def endpoint():
|
|
return Response(content="ok")
|
|
|
|
response = TestClient(app).get("/")
|
|
assert response.status_code == 200
|
|
|
|
|
|
def test_normal_return_with_injected_tasks():
|
|
"""No regression: non-Response returns with injected tasks still work."""
|
|
results: list[str] = []
|
|
app = FastAPI()
|
|
|
|
@app.get("/")
|
|
async def endpoint(tasks: BackgroundTasks):
|
|
tasks.add_task(lambda: results.append("normal"))
|
|
return {"ok": True}
|
|
|
|
TestClient(app).get("/")
|
|
assert results == ["normal"]
|
|
|