Browse Source
When a user returns a Response with a background parameter and also uses injected BackgroundTasks, the injected tasks were being silently discarded. This fix merges both sets of background tasks so all tasks are executed. Changes: - Modified fastapi/routing.py to merge BackgroundTasks when both exist - Added comprehensive tests in test_background_tasks_merge.py - Handles both BackgroundTask and BackgroundTasks types - Maintains backward compatibility Fixes #11215pull/14868/head
2 changed files with 136 additions and 0 deletions
@ -0,0 +1,111 @@ |
|||
""" |
|||
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 |
|||
|
|||
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 |
|||
Loading…
Reference in new issue