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

pierrejeambrun pushed a commit to branch v3-0-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-0-test by this push:
     new f61d41472de [v3-0-test] Bring back mapped task extra links test 
(#51297) (#51477)
f61d41472de is described below

commit f61d41472de13a097e12e921dc6574ca0e030349
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Jul 2 11:00:01 2025 +0200

    [v3-0-test] Bring back mapped task extra links test (#51297) (#51477)
    
    * Bring back mapped task extra links test
    
    * rebase and fixing test
    (cherry picked from commit 17a5038)
    
    Co-authored-by: Amogh Desai <[email protected]>
---
 .../core_api/routes/public/test_extra_links.py     | 59 +++++++++++++---------
 .../src/tests_common/test_utils/mock_operators.py  |  7 +++
 2 files changed, 42 insertions(+), 24 deletions(-)

diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_extra_links.py
 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_extra_links.py
index 1ff1d06d961..4d84485d48a 100644
--- 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_extra_links.py
+++ 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_extra_links.py
@@ -120,6 +120,9 @@ class TestGetExtraLinks:
             CustomOperator(
                 task_id=self.task_multiple_links, 
bash_command=["TEST_LINK_VALUE_1", "TEST_LINK_VALUE_2"]
             )
+            _ = CustomOperator.partial(task_id=self.task_mapped).expand(
+                bash_command=["TEST_LINK_VALUE_1", "TEST_LINK_VALUE_2"]
+            )
         return dag
 
     @pytest.mark.parametrize(
@@ -261,29 +264,37 @@ class TestGetExtraLinks:
             ).model_dump()
         )
 
-    @pytest.mark.xfail(reason="TODO: TaskSDK need to fix this, Extra links 
should work for mapped operator")
-    def test_should_respond_200_mapped_task_instance(self, test_client):
-        map_index = 0
-        XCom.set(
-            key="search_query",
-            value="TEST_LINK_VALUE_1",
-            task_id=self.task_mapped,
-            dag_id=self.dag.dag_id,
-            run_id=self.dag_run_id,
-            map_index=map_index,
-        )
-        response = test_client.get(
-            
f"/dags/{self.dag_id}/dagRuns/{self.dag_run_id}/taskInstances/{self.task_mapped}/links",
-            params={"map_index": map_index},
-        )
-        assert response.status_code == 200
-        assert (
-            response.json()
-            == ExtraLinkCollectionResponse(
-                extra_links={"Google Custom": 
"http://google.com/custom_base_link?search=TEST_LINK_VALUE_1"},
-                total_entries=1,
-            ).model_dump()
-        )
+    def test_should_respond_200_mapped_task_instance(self, test_client, 
session):
+        for map_index, value in enumerate(["TEST_LINK_VALUE_1", 
"TEST_LINK_VALUE_2"]):
+            XCom.set(
+                key="search_query",
+                value=value,
+                task_id=self.task_mapped,
+                dag_id=self.dag_id,
+                run_id=self.dag_run_id,
+                map_index=map_index,
+            )
+            XCom.set(
+                key="_link_CustomOpLink",
+                value=f"http://google.com/custom_base_link?search={value}";,
+                task_id=self.task_mapped,
+                dag_id=self.dag_id,
+                run_id=self.dag_run_id,
+                map_index=map_index,
+            )
+            session.commit()
+            response = test_client.get(
+                
f"/dags/{self.dag_id}/dagRuns/{self.dag_run_id}/taskInstances/{self.task_mapped}/links",
+                params={"map_index": map_index},
+            )
+            assert response.status_code == 200
+            assert (
+                response.json()
+                == ExtraLinkCollectionResponse(
+                    extra_links={"Google Custom": 
f"http://google.com/custom_base_link?search={value}"},
+                    total_entries=1,
+                ).model_dump()
+            )
 
     def test_should_respond_401_unauthenticated(self, 
unauthenticated_test_client):
         response = unauthenticated_test_client.get(
@@ -305,4 +316,4 @@ class TestGetExtraLinks:
             params={"map_index": 4},
         )
         assert response.status_code == 404
-        assert response.json() == {"detail": "Task with ID = TEST_MAPPED_TASK 
not found"}
+        assert response.json() == {"detail": "TaskInstance not found"}
diff --git a/devel-common/src/tests_common/test_utils/mock_operators.py 
b/devel-common/src/tests_common/test_utils/mock_operators.py
index 123968e936d..d4fd43421f6 100644
--- a/devel-common/src/tests_common/test_utils/mock_operators.py
+++ b/devel-common/src/tests_common/test_utils/mock_operators.py
@@ -151,8 +151,15 @@ class CustomOperator(BaseOperator):
     @property
     def operator_extra_links(self):
         """Return operator extra links."""
+        # For mapped operators
+        if not hasattr(self, "bash_command"):
+            # For mapped operators, we return CustomOpLink since each mapped 
instance
+            # will get its own link during runtime
+            return (CustomOpLink(),)
+        # For non-mapped operators
         if isinstance(self.bash_command, str) or self.bash_command is None:
             return (CustomOpLink(),)
+        # For operators with multiple commands
         return (CustomBaseIndexOpLink(i) for i, _ in 
enumerate(self.bash_command))
 
     def __init__(self, bash_command=None, **kwargs):

Reply via email to