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

shahar1 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 5bc738efafe Add tests for the Google Cloud Tasks links (#72846)
5bc738efafe is described below

commit 5bc738efafe1802dcdf5527b476a83cfc00aae25
Author: Xiaolong Song <[email protected]>
AuthorDate: Sun Sep 27 23:13:01 2026 +0200

    Add tests for the Google Cloud Tasks links (#72846)
    
    `CloudTasksQueueLink` and `CloudTasksLink` had no unit tests, so they were
    suppressed by the `OVERLOOKED_TESTS` allowlist. `CloudTasksQueueLink` is
    attached to eleven operators in `google.cloud.operators.tasks` and its
    `extract_parts()` helper parses the queue resource name that every one of
    those links is built from, but none of that ran under test.
---
 .../tests/unit/always/test_project_structure.py    |   1 -
 .../unit/google/cloud/links/test_cloud_tasks.py    | 155 +++++++++++++++++++++
 2 files changed, 155 insertions(+), 1 deletion(-)

diff --git a/airflow-core/tests/unit/always/test_project_structure.py 
b/airflow-core/tests/unit/always/test_project_structure.py
index ed83b0aae0a..33827962068 100644
--- a/airflow-core/tests/unit/always/test_project_structure.py
+++ b/airflow-core/tests/unit/always/test_project_structure.py
@@ -99,7 +99,6 @@ class TestProjectStructure:
             
"providers/google/tests/unit/google/cloud/links/test_cloud_functions.py",
             
"providers/google/tests/unit/google/cloud/links/test_cloud_memorystore.py",
             "providers/google/tests/unit/google/cloud/links/test_cloud_sql.py",
-            
"providers/google/tests/unit/google/cloud/links/test_cloud_tasks.py",
             "providers/google/tests/unit/google/cloud/links/test_compute.py",
             
"providers/google/tests/unit/google/cloud/links/test_data_loss_prevention.py",
             "providers/google/tests/unit/google/cloud/links/test_dataflow.py",
diff --git a/providers/google/tests/unit/google/cloud/links/test_cloud_tasks.py 
b/providers/google/tests/unit/google/cloud/links/test_cloud_tasks.py
new file mode 100644
index 00000000000..56a98892ba1
--- /dev/null
+++ b/providers/google/tests/unit/google/cloud/links/test_cloud_tasks.py
@@ -0,0 +1,155 @@
+#
+# 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.
+from __future__ import annotations
+
+from unittest import mock
+
+import pytest
+from google.cloud.tasks_v2.types import Queue
+
+from airflow.providers.google.cloud.links.cloud_tasks import (
+    CLOUD_TASKS_LINK,
+    CLOUD_TASKS_QUEUE_LINK,
+    CloudTasksLink,
+    CloudTasksQueueLink,
+)
+from airflow.providers.google.cloud.operators.tasks import (
+    CloudTasksQueueCreateOperator,
+    CloudTasksQueuesListOperator,
+)
+
+from tests_common.test_utils.version_compat import AIRFLOW_V_3_0_PLUS
+
+if AIRFLOW_V_3_0_PLUS:
+    from airflow.sdk.execution_time.comms import XComResult
+
+PROJECT_ID = "test-project"
+LOCATION = "asia-east2"
+QUEUE_ID = "test-queue"
+FULL_QUEUE_PATH = 
f"projects/{PROJECT_ID}/locations/{LOCATION}/queues/{QUEUE_ID}"
+TEST_QUEUE = Queue(name=FULL_QUEUE_PATH)
+
+EXPECTED_CLOUD_TASKS_QUEUE_URL = (
+    
f"https://console.cloud.google.com/cloudtasks/queue/{LOCATION}/{QUEUE_ID}/tasks?project={PROJECT_ID}";
+)
+EXPECTED_CLOUD_TASKS_URL = 
f"https://console.cloud.google.com/cloudtasks?project={PROJECT_ID}";
+
+
+def mock_context():
+    """Build a context whose ``task`` exposes no extra link params, as 
``GoogleCloudBaseOperator`` does."""
+    return {"ti": mock.MagicMock(), "task": 
mock.MagicMock(extra_links_params={})}
+
+
+class TestCloudTasksQueueLink:
+    def test_class_attributes(self):
+        assert CloudTasksQueueLink.key == "cloud_task_queue"
+        assert CloudTasksQueueLink.name == "Cloud Tasks Queue"
+        assert CloudTasksQueueLink.format_str == CLOUD_TASKS_QUEUE_LINK
+
+    @pytest.mark.parametrize(
+        ("queue_name", "expected_parts"),
+        [
+            pytest.param(FULL_QUEUE_PATH, (PROJECT_ID, LOCATION, QUEUE_ID), 
id="full-resource-name"),
+            pytest.param(None, ("", "", ""), id="none"),
+            pytest.param("", ("", "", ""), id="empty-string"),
+        ],
+    )
+    def test_extract_parts(self, queue_name, expected_parts):
+        assert CloudTasksQueueLink.extract_parts(queue_name) == expected_parts
+
+    def test_persist(self):
+        context = mock_context()
+
+        CloudTasksQueueLink.persist(context=context, 
queue_name=FULL_QUEUE_PATH)
+
+        context["ti"].xcom_push.assert_called_once_with(
+            key=CloudTasksQueueLink.key,
+            value={"project_id": PROJECT_ID, "location": LOCATION, "queue_id": 
QUEUE_ID},
+        )
+
+    def test_persist_without_queue_name(self):
+        context = mock_context()
+
+        CloudTasksQueueLink.persist(context=context)
+
+        context["ti"].xcom_push.assert_called_once_with(
+            key=CloudTasksQueueLink.key,
+            value={"project_id": "", "location": "", "queue_id": ""},
+        )
+
+    @pytest.mark.db_test
+    def test_get_link(self, dag_maker, create_task_instance_of_operator, 
session, mock_supervisor_comms):
+        link = CloudTasksQueueLink()
+        ti = create_task_instance_of_operator(
+            CloudTasksQueueCreateOperator,
+            dag_id="test_cloud_tasks_queue_link_dag",
+            task_id="test_cloud_tasks_queue_link_task",
+            location=LOCATION,
+            project_id=PROJECT_ID,
+            task_queue=TEST_QUEUE,
+        )
+        task = dag_maker.dag.get_task(ti.task_id)
+
+        link.persist(context={"ti": ti, "task": task}, 
queue_name=FULL_QUEUE_PATH)
+
+        if AIRFLOW_V_3_0_PLUS and mock_supervisor_comms:
+            mock_supervisor_comms.send.return_value = XComResult(
+                key="key",
+                value={"project_id": PROJECT_ID, "location": LOCATION, 
"queue_id": QUEUE_ID},
+            )
+        actual_url = link.get_link(operator=task, ti_key=ti.key)
+        assert actual_url == EXPECTED_CLOUD_TASKS_QUEUE_URL
+
+
+class TestCloudTasksLink:
+    def test_class_attributes(self):
+        assert CloudTasksLink.key == "cloud_task"
+        assert CloudTasksLink.name == "Cloud Tasks"
+        assert CloudTasksLink.format_str == CLOUD_TASKS_LINK
+
+    def test_persist(self):
+        context = mock_context()
+
+        CloudTasksLink.persist(context=context, project_id=PROJECT_ID)
+
+        context["ti"].xcom_push.assert_called_once_with(
+            key=CloudTasksLink.key,
+            value={"project_id": PROJECT_ID},
+        )
+
+    @pytest.mark.db_test
+    def test_get_link(self, dag_maker, create_task_instance_of_operator, 
session, mock_supervisor_comms):
+        link = CloudTasksLink()
+        ti = create_task_instance_of_operator(
+            CloudTasksQueuesListOperator,
+            dag_id="test_cloud_tasks_link_dag",
+            task_id="test_cloud_tasks_link_task",
+            location=LOCATION,
+            project_id=PROJECT_ID,
+        )
+        task = dag_maker.dag.get_task(ti.task_id)
+
+        link.persist(context={"ti": ti, "task": task}, project_id=PROJECT_ID)
+
+        if AIRFLOW_V_3_0_PLUS and mock_supervisor_comms:
+            mock_supervisor_comms.send.return_value = XComResult(
+                key="key",
+                value={"project_id": PROJECT_ID},
+            )
+        actual_url = link.get_link(operator=task, ti_key=ti.key)
+        assert actual_url == EXPECTED_CLOUD_TASKS_URL

Reply via email to