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

eladkal 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 04194b1de1f Fix flaky Kafka trigger tests by awaiting the event 
instead of a fixed sleep (#74124)
04194b1de1f is described below

commit 04194b1de1fabb9581430210a01e5d7f9cf431e3
Author: Shahar Epstein <[email protected]>
AuthorDate: Sat Oct 3 05:20:27 2026 +0300

    Fix flaky Kafka trigger tests by awaiting the event instead of a fixed 
sleep (#74124)
    
    The trigger run loop crosses several sync_to_async hops on asgiref's single
    shared thread. Under CI load they can take longer than the fixed 1 s sleep,
    so `task.done()` was False and the tests failed. Await the first event with 
a
    generous timeout instead, and rewrite the tombstone test to feed a tombstone
    followed by a regular message so it asserts on the yielded event and the
    commit calls rather than on timing.
---
 .../apache/kafka/triggers/test_await_message.py    | 49 ++++++++++------------
 .../unit/apache/kafka/triggers/test_msg_queue.py   |  5 +--
 2 files changed, 24 insertions(+), 30 deletions(-)

diff --git 
a/providers/apache/kafka/tests/unit/apache/kafka/triggers/test_await_message.py 
b/providers/apache/kafka/tests/unit/apache/kafka/triggers/test_await_message.py
index ed950bea9ab..905f14f8cc0 100644
--- 
a/providers/apache/kafka/tests/unit/apache/kafka/triggers/test_await_message.py
+++ 
b/providers/apache/kafka/tests/unit/apache/kafka/triggers/test_await_message.py
@@ -138,14 +138,14 @@ class TestTrigger:
         )
 
     @pytest.mark.parametrize(
-        "apply_function",
+        ("apply_function", "expected"),
         [
-            
"unit.apache.kafka.triggers.test_await_message.apply_function_true",
-            None,
+            
("unit.apache.kafka.triggers.test_await_message.apply_function_true", True),
+            (None, "test_message"),
         ],
     )
     @pytest.mark.asyncio
-    async def test_trigger_run_good(self, mocker, apply_function):
+    async def test_trigger_run_good(self, mocker, apply_function, expected):
         mocker.patch.object(KafkaConsumerHook, "get_consumer", 
return_value=MockedConsumer)
 
         trigger = AwaitMessageTrigger(
@@ -156,10 +156,8 @@ class TestTrigger:
             poll_interval=5,
         )
 
-        task = asyncio.create_task(trigger.run().__anext__())
-        await asyncio.sleep(1.0)
-        assert task.done() is True
-        asyncio.get_event_loop().stop()
+        event = await asyncio.wait_for(trigger.run().__anext__(), timeout=10)
+        assert event.payload == expected
 
     @pytest.mark.asyncio
     async def test_trigger_run_bad(self, mocker):
@@ -179,18 +177,20 @@ class TestTrigger:
         asyncio.get_event_loop().stop()
 
     @pytest.mark.asyncio
-    async def test_trigger_run_tombstone_message_keeps_polling(self, mocker):
+    async def test_trigger_run_skips_tombstone_and_yields_next_message(self, 
mocker):
         """
         A tombstone (null-value) message must not crash the trigger when no 
apply_function is set.
 
         Regression test: the trigger previously raised
         ``AttributeError: 'NoneType' object has no attribute 'decode'`` on 
``message.value()``
         returning ``None``. A tombstone carries no payload to emit, so the 
trigger should
-        commit the offset (when ``commit_offset`` is enabled) and keep polling.
+        commit its offset (when ``commit_offset`` is enabled) and keep polling 
until a regular
+        message arrives.
         """
-        message = MockedTombstoneMessage()
+        tombstone = MockedTombstoneMessage()
+        regular = MockedMessage()
         consumer = MockedConsumer()
-        mocker.patch.object(consumer, "poll", return_value=message)
+        mocker.patch.object(consumer, "poll", side_effect=[tombstone, regular])
         commit_mock = mocker.patch.object(consumer, "commit")
         mocker.patch.object(KafkaConsumerHook, "get_consumer", 
return_value=consumer)
 
@@ -199,19 +199,16 @@ class TestTrigger:
             apply_function=None,
             topics=["noop"],
             poll_timeout=0.0001,
-            poll_interval=5,
+            poll_interval=0,
         )
 
-        task = asyncio.create_task(trigger.run().__anext__())
-        await asyncio.sleep(1.0)
-        try:
-            # The task must neither raise nor yield an event: the tombstone is 
skipped
-            # and the trigger sleeps through poll_interval waiting for the 
next message.
-            assert task.done() is False
-            # The tombstone offset is still committed, like any other 
non-matching message.
-            assert commit_mock.mock_calls == [call(message=message, 
asynchronous=False)]
-        finally:
-            task.cancel()
+        event = await asyncio.wait_for(trigger.run().__anext__(), timeout=10)
+
+        assert event.payload == "test_message"
+        assert commit_mock.mock_calls == [
+            call(message=tombstone, asynchronous=False),
+            call(message=regular, asynchronous=False),
+        ]
 
     @pytest.mark.asyncio
     async def 
test_trigger_run_without_apply_function_yields_message_value(self, mocker):
@@ -229,10 +226,8 @@ class TestTrigger:
             poll_interval=5,
         )
 
-        task = asyncio.create_task(trigger.run().__anext__())
-        await asyncio.sleep(1.0)
-        assert task.done() is True
-        assert task.result().payload == "test_message"
+        event = await asyncio.wait_for(trigger.run().__anext__(), timeout=10)
+        assert event.payload == "test_message"
 
     @pytest.mark.asyncio
     async def test_cleanup_closes_consumer(self, mocker):
diff --git 
a/providers/apache/kafka/tests/unit/apache/kafka/triggers/test_msg_queue.py 
b/providers/apache/kafka/tests/unit/apache/kafka/triggers/test_msg_queue.py
index 372925e757d..2234cd28c11 100644
--- a/providers/apache/kafka/tests/unit/apache/kafka/triggers/test_msg_queue.py
+++ b/providers/apache/kafka/tests/unit/apache/kafka/triggers/test_msg_queue.py
@@ -130,9 +130,8 @@ class TestMessageQueueTrigger:
             poll_interval=5,
         )
 
-        task = asyncio.create_task(trigger.run().__anext__())
-        await asyncio.sleep(1.0)
-        assert task.done() is True
+        event = await asyncio.wait_for(trigger.run().__anext__(), timeout=10)
+        assert event.payload is True
 
     @pytest.mark.skipif(not AIRFLOW_V_3_0_PLUS, reason="Requires Airflow 
3.0.+")
     @pytest.mark.asyncio

Reply via email to