code: def test_wait_max_attempts_exceeded(self, mock_sleep, caplog):
      mock_sleep.return_value = True
      mock_waiter = mock.MagicMock()
      error = WaiterError(
          name="test_waiter",
          reason="test_reason",
          last_response=generate_response("Pending"),
      )
      mock_waiter.wait.side_effect = [error, error, error]
      with pytest.raises(AirflowException) as exc:
          wait(
              waiter=mock_waiter,
              waiter_delay=123,
              waiter_max_attempts=2,
              args={"test_arg": "test_value"},
              failure_message="test failure message",
              status_message="test status message",
              status_args=["Status.State"],
          )
      assert "Waiter error: max attempts reached" in str(exc)
      mock_waiter.wait.assert_called_with(
          **{"test_arg": "test_value"},
          WaiterConfig={
              "MaxAttempts": 1,
          },
      )
  
      assert mock_waiter.wait.call_count == 2
      mock_sleep.assert_called_with(123)
      assert (
          caplog.record_tuples
          == [
              (
                  "airflow.providers.amazon.aws.utils.waiter_with_logging",
                  logging.INFO,
                  "test status message: Pending",
              )
          ]
          * 2
      )
  
file_path: airflow/tests/providers/amazon/aws/utils/test_waiter_with_logging.py
imports: from __future__ import annotations
  import logging
  from typing import Any
  from unittest import mock
  from unittest.mock import AsyncMock
  import pytest
  from botocore.exceptions import WaiterError
  from airflow.exceptions import AirflowException
  from airflow.providers.amazon.aws.utils.waiter_with_logging import _LazyStatusFormatter, async_wait, wait
label_desc: Use image node_id 2 for calling the TestWaiter obj's underlying member method code with example usage: obj.test_wait_max_attempts_exceeded(mock_sleep, caplog) without return types