You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by ep...@apache.org on 2022/06/30 14:03:30 UTC

[airflow] 03/14: Add tests for the grid_data endpoint (#24375)

This is an automated email from the ASF dual-hosted git repository.

ephraimanierobi pushed a commit to branch v2-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit d54af711c42b62d23b84ce7377da64e90bbb7c4e
Author: Ash Berlin-Taylor <as...@apache.org>
AuthorDate: Fri Jun 10 15:35:38 2022 +0100

    Add tests for the grid_data endpoint (#24375)
    
    The one fix/change here was to include the JSON content response here so that
    `resp.json` works in the test.
    
    (cherry picked from commit 2b2d97068fa45881672dab6f2134becae246f3f3)
---
 airflow/www/views.py               |   5 +-
 tests/www/views/test_views_grid.py | 238 +++++++++++++++++++++++++++++++++++++
 2 files changed, 242 insertions(+), 1 deletion(-)

diff --git a/airflow/www/views.py b/airflow/www/views.py
index fe4217c452..7e1a80da1b 100644
--- a/airflow/www/views.py
+++ b/airflow/www/views.py
@@ -3544,7 +3544,10 @@ class Airflow(AirflowBaseView):
                 'dag_runs': encoded_runs,
             }
         # avoid spaces to reduce payload size
-        return htmlsafe_json_dumps(data, separators=(',', ':'))
+        return (
+            htmlsafe_json_dumps(data, separators=(',', ':')),
+            {'Content-Type': 'application/json; charset=utf-8'},
+        )
 
     @expose('/robots.txt')
     @action_logging
diff --git a/tests/www/views/test_views_grid.py b/tests/www/views/test_views_grid.py
new file mode 100644
index 0000000000..e5d29be8a2
--- /dev/null
+++ b/tests/www/views/test_views_grid.py
@@ -0,0 +1,238 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import freezegun
+import pendulum
+import pytest
+
+from airflow.models import DagBag
+from airflow.operators.empty import EmptyOperator
+from airflow.utils.state import DagRunState, TaskInstanceState
+from airflow.utils.task_group import TaskGroup
+from airflow.utils.types import DagRunType
+from tests.test_utils.mock_operators import MockOperator
+
+DAG_ID = 'test'
+CURRENT_TIME = pendulum.DateTime(2021, 9, 7)
+
+
+@pytest.fixture(autouse=True, scope="module")
+def examples_dag_bag():
+    # Speed up: We don't want example dags for this module
+    return DagBag(include_examples=False, read_dags_from_db=True)
+
+
+@pytest.fixture
+def dag_without_runs(dag_maker, session, app, monkeypatch):
+    with monkeypatch.context() as m:
+        # Remove global operator links for this test
+        m.setattr('airflow.plugins_manager.global_operator_extra_links', [])
+        m.setattr('airflow.plugins_manager.operator_extra_links', [])
+        m.setattr('airflow.plugins_manager.registered_operator_link_classes', {})
+
+        with dag_maker(dag_id=DAG_ID, serialized=True, session=session):
+            EmptyOperator(task_id="task1")
+            with TaskGroup(group_id='group'):
+                MockOperator.partial(task_id='mapped').expand(arg1=['a', 'b', 'c'])
+
+        m.setattr(app, 'dag_bag', dag_maker.dagbag)
+        yield dag_maker
+
+
+@pytest.fixture
+def dag_with_runs(dag_without_runs):
+    with freezegun.freeze_time(CURRENT_TIME):
+        date = dag_without_runs.dag.start_date
+        run_1 = dag_without_runs.create_dagrun(
+            run_id='run_1', state=DagRunState.SUCCESS, run_type=DagRunType.SCHEDULED, execution_date=date
+        )
+        run_2 = dag_without_runs.create_dagrun(
+            run_id='run_2',
+            run_type=DagRunType.SCHEDULED,
+            execution_date=dag_without_runs.dag.next_dagrun_info(date).logical_date,
+        )
+
+        yield run_1, run_2
+
+
+def test_no_runs(admin_client, dag_without_runs):
+    resp = admin_client.get(f'/object/grid_data?dag_id={DAG_ID}', follow_redirects=True)
+    assert resp.status_code == 200, resp.json
+    assert resp.json == {
+        'dag_runs': [],
+        'groups': {
+            'children': [
+                {
+                    'extra_links': [],
+                    'id': 'task1',
+                    'instances': [],
+                    'is_mapped': False,
+                    'label': 'task1',
+                },
+                {
+                    'children': [
+                        {
+                            'extra_links': [],
+                            'id': 'group.mapped',
+                            'instances': [],
+                            'is_mapped': True,
+                            'label': 'mapped',
+                        }
+                    ],
+                    'id': 'group',
+                    'instances': [],
+                    'label': 'group',
+                    'tooltip': '',
+                },
+            ],
+            'id': None,
+            'instances': [],
+            'label': None,
+            'tooltip': '',
+        },
+    }
+
+
+def test_one_run(admin_client, dag_with_runs, session):
+    run1, run2 = dag_with_runs
+
+    for ti in run1.task_instances:
+        ti.state = TaskInstanceState.SUCCESS
+
+    session.flush()
+
+    resp = admin_client.get(f'/object/grid_data?dag_id={DAG_ID}', follow_redirects=True)
+    assert resp.status_code == 200, resp.json
+    assert resp.json == {
+        'dag_runs': [
+            {
+                'data_interval_end': '2016-01-02T00:00:00+00:00',
+                'data_interval_start': '2016-01-01T00:00:00+00:00',
+                'end_date': '2021-09-07T00:00:00+00:00',
+                'execution_date': '2016-01-01T00:00:00+00:00',
+                'last_scheduling_decision': None,
+                'run_id': 'run_1',
+                'run_type': 'scheduled',
+                'start_date': '2016-01-01T00:00:00+00:00',
+                'state': 'success',
+            },
+            {
+                'data_interval_end': '2016-01-03T00:00:00+00:00',
+                'data_interval_start': '2016-01-02T00:00:00+00:00',
+                'end_date': None,
+                'execution_date': '2016-01-02T00:00:00+00:00',
+                'last_scheduling_decision': None,
+                'run_id': 'run_2',
+                'run_type': 'scheduled',
+                'start_date': '2016-01-01T00:00:00+00:00',
+                'state': 'running',
+            },
+        ],
+        'groups': {
+            'children': [
+                {
+                    'extra_links': [],
+                    'id': 'task1',
+                    'instances': [
+                        {
+                            'end_date': None,
+                            'map_index': -1,
+                            'run_id': 'run_1',
+                            'start_date': None,
+                            'state': 'success',
+                            'task_id': 'task1',
+                            'try_number': 1,
+                        },
+                        {
+                            'end_date': None,
+                            'map_index': -1,
+                            'run_id': 'run_2',
+                            'start_date': None,
+                            'state': None,
+                            'task_id': 'task1',
+                            'try_number': 1,
+                        },
+                    ],
+                    'is_mapped': False,
+                    'label': 'task1',
+                },
+                {
+                    'children': [
+                        {
+                            'extra_links': [],
+                            'id': 'group.mapped',
+                            'instances': [
+                                {
+                                    'end_date': None,
+                                    'mapped_states': ['success', 'success', 'success'],
+                                    'run_id': 'run_1',
+                                    'start_date': None,
+                                    'state': 'success',
+                                    'task_id': 'group.mapped',
+                                    'try_number': 1,
+                                },
+                                {
+                                    'end_date': None,
+                                    'mapped_states': [None, None, None],
+                                    'run_id': 'run_2',
+                                    'start_date': None,
+                                    'state': None,
+                                    'task_id': 'group.mapped',
+                                    'try_number': 1,
+                                },
+                            ],
+                            'is_mapped': True,
+                            'label': 'mapped',
+                        },
+                    ],
+                    'id': 'group',
+                    'instances': [
+                        {
+                            'end_date': None,
+                            'run_id': 'run_1',
+                            'start_date': None,
+                            'state': 'success',
+                            'task_id': 'group',
+                        },
+                        {
+                            'end_date': None,
+                            'run_id': 'run_2',
+                            'start_date': None,
+                            'state': None,
+                            'task_id': 'group',
+                        },
+                    ],
+                    'label': 'group',
+                    'tooltip': '',
+                },
+            ],
+            'id': None,
+            'instances': [
+                {
+                    'end_date': None,
+                    'run_id': 'run_1',
+                    'start_date': None,
+                    'state': 'success',
+                    'task_id': None,
+                },
+                {'end_date': None, 'run_id': 'run_2', 'start_date': None, 'state': None, 'task_id': None},
+            ],
+            'label': None,
+            'tooltip': '',
+        },
+    }