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.
208 lines
6.8 KiB
208 lines
6.8 KiB
"""
|
|
Test that background tasks from injected BackgroundTasks and Response.background are both executed.
|
|
Related to issue #11215
|
|
"""
|
|
|
|
from fastapi import BackgroundTasks, FastAPI
|
|
from fastapi.responses import Response
|
|
from fastapi.testclient import TestClient
|
|
from starlette.background import BackgroundTask
|
|
|
|
app = FastAPI()
|
|
|
|
# Track which background tasks were executed
|
|
executed_tasks = []
|
|
|
|
|
|
def task_from_injected():
|
|
executed_tasks.append("injected")
|
|
|
|
|
|
def task_from_response():
|
|
executed_tasks.append("response")
|
|
|
|
|
|
@app.get("/both-tasks")
|
|
async def endpoint_with_both_tasks(tasks: BackgroundTasks):
|
|
"""Endpoint that uses both injected BackgroundTasks and Response.background"""
|
|
tasks.add_task(task_from_injected)
|
|
return Response(
|
|
content="Custom response",
|
|
background=BackgroundTasks(tasks=[]),
|
|
)
|
|
|
|
|
|
@app.get("/both-tasks-populated")
|
|
async def endpoint_with_both_tasks_populated(tasks: BackgroundTasks):
|
|
"""Endpoint where both injected and response have background tasks"""
|
|
tasks.add_task(task_from_injected)
|
|
response_bg = BackgroundTasks()
|
|
response_bg.add_task(task_from_response)
|
|
return Response(
|
|
content="Custom response",
|
|
background=response_bg,
|
|
)
|
|
|
|
|
|
@app.get("/only-injected")
|
|
async def endpoint_with_only_injected(tasks: BackgroundTasks):
|
|
"""Endpoint that only uses injected BackgroundTasks"""
|
|
tasks.add_task(task_from_injected)
|
|
return Response(content="Custom response")
|
|
|
|
|
|
@app.get("/only-response")
|
|
async def endpoint_with_only_response():
|
|
"""Endpoint that only uses Response.background"""
|
|
response_bg = BackgroundTasks()
|
|
response_bg.add_task(task_from_response)
|
|
return Response(
|
|
content="Custom response",
|
|
background=response_bg,
|
|
)
|
|
|
|
|
|
def test_injected_tasks_not_lost_when_response_has_empty_background():
|
|
"""Test that injected background tasks are preserved even when Response has empty background"""
|
|
global executed_tasks
|
|
executed_tasks = []
|
|
|
|
client = TestClient(app)
|
|
response = client.get("/both-tasks")
|
|
|
|
assert response.status_code == 200
|
|
# Both tasks should be executed
|
|
assert "injected" in executed_tasks, "Injected task was not executed"
|
|
|
|
|
|
def test_both_tasks_executed_when_both_populated():
|
|
"""Test that both injected and response background tasks are executed"""
|
|
global executed_tasks
|
|
executed_tasks = []
|
|
|
|
client = TestClient(app)
|
|
response = client.get("/both-tasks-populated")
|
|
|
|
assert response.status_code == 200
|
|
# Both tasks should be executed
|
|
assert "injected" in executed_tasks, "Injected task was not executed"
|
|
assert "response" in executed_tasks, "Response task was not executed"
|
|
|
|
|
|
def test_only_injected_tasks_work():
|
|
"""Test that only injected background tasks work when no Response.background"""
|
|
global executed_tasks
|
|
executed_tasks = []
|
|
|
|
client = TestClient(app)
|
|
response = client.get("/only-injected")
|
|
|
|
assert response.status_code == 200
|
|
assert "injected" in executed_tasks
|
|
|
|
|
|
def test_only_response_tasks_work():
|
|
"""Test that only response background tasks work"""
|
|
global executed_tasks
|
|
executed_tasks = []
|
|
|
|
client = TestClient(app)
|
|
response = client.get("/only-response")
|
|
|
|
assert response.status_code == 200
|
|
assert "response" in executed_tasks
|
|
|
|
|
|
@app.get("/single-background-task-with-injected")
|
|
async def endpoint_with_single_background_task_and_injected(tasks: BackgroundTasks):
|
|
"""Endpoint with a single BackgroundTask in Response and injected BackgroundTasks"""
|
|
tasks.add_task(task_from_injected)
|
|
return Response(
|
|
content="Custom response",
|
|
background=BackgroundTask(task_from_response),
|
|
)
|
|
|
|
|
|
@app.get("/single-background-task-with-single-injected")
|
|
async def endpoint_with_single_background_task_and_single_injected(
|
|
tasks: BackgroundTasks,
|
|
):
|
|
"""Endpoint with a single BackgroundTask in Response and single injected task"""
|
|
# Add just one task to injected BackgroundTasks to test the single+single case
|
|
tasks.add_task(task_from_injected)
|
|
return Response(
|
|
content="Custom response",
|
|
background=BackgroundTask(task_from_response),
|
|
)
|
|
|
|
|
|
def test_single_background_task_with_injected_tasks():
|
|
"""Test that injected BackgroundTasks are merged with a single BackgroundTask from Response"""
|
|
global executed_tasks
|
|
executed_tasks = []
|
|
|
|
client = TestClient(app)
|
|
response = client.get("/single-background-task-with-injected")
|
|
|
|
assert response.status_code == 200
|
|
# Both tasks should be executed
|
|
assert "injected" in executed_tasks, "Injected task was not executed"
|
|
assert "response" in executed_tasks, "Response task was not executed"
|
|
# Injected tasks should run before response tasks
|
|
assert executed_tasks.index("injected") < executed_tasks.index(
|
|
"response"
|
|
), "Task execution order is wrong"
|
|
|
|
|
|
def test_single_background_task_with_single_injected_task():
|
|
"""Test that two single BackgroundTasks are merged correctly"""
|
|
global executed_tasks
|
|
executed_tasks = []
|
|
|
|
client = TestClient(app)
|
|
response = client.get("/single-background-task-with-single-injected")
|
|
|
|
assert response.status_code == 200
|
|
# Both tasks should be executed
|
|
assert "injected" in executed_tasks, "Injected task was not executed"
|
|
assert "response" in executed_tasks, "Response task was not executed"
|
|
# Injected tasks should run before response tasks
|
|
assert executed_tasks.index("injected") < executed_tasks.index(
|
|
"response"
|
|
), "Task execution order is wrong"
|
|
|
|
|
|
@app.get("/response-background-tasks-with-single-injected")
|
|
async def endpoint_with_response_background_tasks_and_single_injected_task(
|
|
tasks: BackgroundTasks,
|
|
):
|
|
"""Endpoint where Response has BackgroundTasks but injected has single task
|
|
|
|
This tests the code path where the nested if checks:
|
|
- raw_response.background is StarletteBackgroundTasks: True
|
|
- solved_result.background_tasks is StarletteBackgroundTasks: False (it's a single task)
|
|
|
|
This is an uncovered path that needs testing.
|
|
"""
|
|
# Add just one task to injected BackgroundTasks
|
|
tasks.add_task(task_from_injected)
|
|
response_bg = BackgroundTasks()
|
|
response_bg.add_task(task_from_response)
|
|
return Response(
|
|
content="Custom response",
|
|
background=response_bg,
|
|
)
|
|
|
|
|
|
def test_response_background_tasks_with_single_injected_task():
|
|
"""Test merging single injected BackgroundTask with Response BackgroundTasks"""
|
|
global executed_tasks
|
|
executed_tasks = []
|
|
|
|
client = TestClient(app)
|
|
response = client.get("/response-background-tasks-with-single-injected")
|
|
|
|
assert response.status_code == 200
|
|
# Both tasks should be executed
|
|
assert "injected" in executed_tasks, "Injected task was not executed"
|
|
assert "response" in executed_tasks, "Response task was not executed"
|
|
|