This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-1-test by this push:
new 2d3be8741bd Add number of queries guard for ui grid (#57977)
2d3be8741bd is described below
commit 2d3be8741bd2c5b0d6fd0983cdb096ed55122815
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Fri Nov 7 15:54:05 2025 +0100
Add number of queries guard for ui grid (#57977)
(cherry picked from commit 56ddebe108c35f36cd3c2128b2a01f2a79d94122)
---
.../api_fastapi/core_api/routes/ui/test_grid.py | 70 +++++++++++++++-------
1 file changed, 47 insertions(+), 23 deletions(-)
diff --git
a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
index 9bd06f764d5..80f11a2c341 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
@@ -35,6 +35,7 @@ from airflow.utils.session import provide_session
from airflow.utils.state import DagRunState, TaskInstanceState
from airflow.utils.types import DagRunTriggeredByType, DagRunType
+from tests_common.test_utils.asserts import assert_queries_count
from tests_common.test_utils.db import clear_db_assets, clear_db_dags,
clear_db_runs, clear_db_serialized_dags
from tests_common.test_utils.mock_operators import MockOperator
@@ -273,7 +274,8 @@ def _freeze_time_for_dagruns(time_machine):
@pytest.mark.usefixtures("_freeze_time_for_dagruns")
class TestGetGridDataEndpoint:
def test_should_response_200(self, test_client):
- response = test_client.get(f"/grid/runs/{DAG_ID}")
+ with assert_queries_count(5):
+ response = test_client.get(f"/grid/runs/{DAG_ID}")
assert response.status_code == 200
assert response.json() == [
GRID_RUN_1,
@@ -314,7 +316,8 @@ class TestGetGridDataEndpoint:
],
)
def test_should_response_200_order_by(self, test_client, order_by,
expected):
- response = test_client.get(f"/grid/runs/{DAG_ID}", params={"order_by":
order_by})
+ with assert_queries_count(5):
+ response = test_client.get(f"/grid/runs/{DAG_ID}",
params={"order_by": order_by})
assert response.status_code == 200
assert response.json() == expected
@@ -332,7 +335,8 @@ class TestGetGridDataEndpoint:
],
)
def test_should_response_200_limit(self, test_client, limit, expected):
- response = test_client.get(f"/grid/runs/{DAG_ID}", params={"limit":
limit})
+ with assert_queries_count(5):
+ response = test_client.get(f"/grid/runs/{DAG_ID}",
params={"limit": limit})
assert response.status_code == 200
assert response.json() == expected
@@ -356,15 +360,16 @@ class TestGetGridDataEndpoint:
],
)
def test_runs_should_response_200_date_filters(self, test_client, params,
expected):
- response = test_client.get(
- f"/grid/runs/{DAG_ID}",
- params=params,
- )
+ with assert_queries_count(5):
+ response = test_client.get(
+ f"/grid/runs/{DAG_ID}",
+ params=params,
+ )
assert response.status_code == 200
assert response.json() == expected
@pytest.mark.parametrize(
- "params, expected",
+ ("params, expected, expected_queries_count"),
[
(
{
@@ -372,6 +377,7 @@ class TestGetGridDataEndpoint:
"run_after_lte": timezone.datetime(2024, 11, 30),
},
GRID_NODES,
+ 7,
),
(
{
@@ -379,14 +385,18 @@ class TestGetGridDataEndpoint:
"run_after_lte": timezone.datetime(2024, 10, 30),
},
GRID_NODES,
+ 5,
),
],
)
- def test_structure_should_response_200_date_filters(self, test_client,
params, expected):
- response = test_client.get(
- f"/grid/structure/{DAG_ID}",
- params=params,
- )
+ def test_structure_should_response_200_date_filters(
+ self, test_client, params, expected, expected_queries_count
+ ):
+ with assert_queries_count(expected_queries_count):
+ response = test_client.get(
+ f"/grid/structure/{DAG_ID}",
+ params=params,
+ )
assert response.status_code == 200
assert response.json() == expected
@@ -407,12 +417,14 @@ class TestGetGridDataEndpoint:
assert response.json() == {"detail": "Dag with id invalid_dag was not
found"}
def test_structure_should_response_200_without_dag_run(self, test_client):
- response = test_client.get(f"/grid/structure/{DAG_ID_2}")
+ with assert_queries_count(5):
+ response = test_client.get(f"/grid/structure/{DAG_ID_2}")
assert response.status_code == 200
assert response.json() == [{"id": "task2", "label": "task2"}]
def test_runs_should_response_200_without_dag_run(self, test_client):
- response = test_client.get(f"/grid/runs/{DAG_ID_2}")
+ with assert_queries_count(5):
+ response = test_client.get(f"/grid/runs/{DAG_ID_2}")
assert response.status_code == 200
assert response.json() == []
@@ -426,7 +438,8 @@ class TestGetGridDataEndpoint:
ti.dag_version = session.scalar(select(DagModel).where(DagModel.dag_id
== DAG_ID_3)).dag_versions[-1]
session.commit()
- response = test_client.get(f"/grid/structure/{DAG_ID_3}")
+ with assert_queries_count(7):
+ response = test_client.get(f"/grid/structure/{DAG_ID_3}")
assert response.status_code == 200
assert response.json() == [
{"id": "task3", "label": "task3"},
@@ -439,7 +452,8 @@ class TestGetGridDataEndpoint:
]
# Also verify that TI summaries include a leaf entry for the removed
task
- ti_resp = test_client.get(f"/grid/ti_summaries/{DAG_ID_3}/run_3")
+ with assert_queries_count(4):
+ ti_resp = test_client.get(f"/grid/ti_summaries/{DAG_ID_3}/run_3")
assert ti_resp.status_code == 200
ti_payload = ti_resp.json()
assert ti_payload["dag_id"] == DAG_ID_3
@@ -473,7 +487,9 @@ class TestGetGridDataEndpoint:
def test_get_dag_structure(self, session, test_client):
session.commit()
- response = test_client.get(f"/grid/structure/{DAG_ID}?limit=5")
+
+ with assert_queries_count(7):
+ response = test_client.get(f"/grid/structure/{DAG_ID}?limit=5")
assert response.status_code == 200
assert response.json() == [
{
@@ -506,7 +522,8 @@ class TestGetGridDataEndpoint:
def test_get_grid_runs(self, session, test_client):
session.commit()
- response = test_client.get(f"/grid/runs/{DAG_ID}?limit=5")
+ with assert_queries_count(5):
+ response = test_client.get(f"/grid/runs/{DAG_ID}?limit=5")
assert response.status_code == 200
assert response.json() == [
{
@@ -562,14 +579,17 @@ class TestGetGridDataEndpoint:
def test_get_grid_runs_filter_by_run_type_and_triggering_user(self,
session, test_client):
session.commit()
- response =
test_client.get(f"/grid/runs/{DAG_ID}?run_type=manual&triggering_user=user2")
+ with assert_queries_count(5):
+ response =
test_client.get(f"/grid/runs/{DAG_ID}?run_type=manual&triggering_user=user2")
assert response.status_code == 200
assert response.json() == [GRID_RUN_2]
def test_grid_ti_summaries_group(self, session, test_client):
run_id = "run_4-1"
session.commit()
- response = test_client.get(f"/grid/ti_summaries/{DAG_ID_4}/{run_id}")
+
+ with assert_queries_count(4):
+ response =
test_client.get(f"/grid/ti_summaries/{DAG_ID_4}/{run_id}")
assert response.status_code == 200
actual = response.json()
expected = {
@@ -649,7 +669,9 @@ class TestGetGridDataEndpoint:
def test_grid_ti_summaries_mapped(self, session, test_client):
run_id = "run_2"
session.commit()
- response = test_client.get(f"/grid/ti_summaries/{DAG_ID}/{run_id}")
+
+ with assert_queries_count(4):
+ response = test_client.get(f"/grid/ti_summaries/{DAG_ID}/{run_id}")
assert response.status_code == 200
data = response.json()
actual = data["task_instances"]
@@ -726,7 +748,9 @@ class TestGetGridDataEndpoint:
def
test_structure_includes_historical_removed_task_with_proper_shape(self,
session, test_client):
# Ensure the structure endpoint returns synthetic node for
historical/removed task
- response = test_client.get(f"/grid/structure/{DAG_ID_3}")
+
+ with assert_queries_count(7):
+ response = test_client.get(f"/grid/structure/{DAG_ID_3}")
assert response.status_code == 200
nodes = response.json()
# Find the historical removed task id