Fix imap sensor in case of alternative empty search response (#132081)
parent
c6468aca2b
commit
ab5165fdfa
|
@ -332,7 +332,17 @@ class ImapDataUpdateCoordinator(DataUpdateCoordinator[int | None]):
|
||||||
raise UpdateFailed(
|
raise UpdateFailed(
|
||||||
f"Invalid response for search '{self.config_entry.data[CONF_SEARCH]}': {result} / {lines[0]}"
|
f"Invalid response for search '{self.config_entry.data[CONF_SEARCH]}': {result} / {lines[0]}"
|
||||||
)
|
)
|
||||||
if not (count := len(message_ids := lines[0].split())):
|
# Check we do have returned items.
|
||||||
|
#
|
||||||
|
# In rare cases, when no UID's are returned,
|
||||||
|
# only the status line is returned, and not an empty line.
|
||||||
|
# See: https://github.com/home-assistant/core/issues/132042
|
||||||
|
#
|
||||||
|
# Strictly the RfC notes that 0 or more numbers should be returned
|
||||||
|
# delimited by a space.
|
||||||
|
#
|
||||||
|
# See: https://datatracker.ietf.org/doc/html/rfc3501#section-7.2.5
|
||||||
|
if len(lines) == 1 or not (count := len(message_ids := lines[0].split())):
|
||||||
self._last_message_uid = None
|
self._last_message_uid = None
|
||||||
return 0
|
return 0
|
||||||
last_message_uid = (
|
last_message_uid = (
|
||||||
|
|
|
@ -141,6 +141,8 @@ TEST_CONTENT_MULTIPART_BASE64_INVALID = (
|
||||||
)
|
)
|
||||||
|
|
||||||
EMPTY_SEARCH_RESPONSE = ("OK", [b"", b"Search completed (0.0001 + 0.000 secs)."])
|
EMPTY_SEARCH_RESPONSE = ("OK", [b"", b"Search completed (0.0001 + 0.000 secs)."])
|
||||||
|
EMPTY_SEARCH_RESPONSE_ALT = ("OK", [b"Search completed (0.0001 + 0.000 secs)."])
|
||||||
|
|
||||||
BAD_RESPONSE = ("BAD", [b"", b"Unexpected error"])
|
BAD_RESPONSE = ("BAD", [b"", b"Unexpected error"])
|
||||||
|
|
||||||
TEST_SEARCH_RESPONSE = ("OK", [b"1", b"Search completed (0.0001 + 0.000 secs)."])
|
TEST_SEARCH_RESPONSE = ("OK", [b"1", b"Search completed (0.0001 + 0.000 secs)."])
|
||||||
|
|
|
@ -20,6 +20,7 @@ from homeassistant.util.dt import utcnow
|
||||||
from .const import (
|
from .const import (
|
||||||
BAD_RESPONSE,
|
BAD_RESPONSE,
|
||||||
EMPTY_SEARCH_RESPONSE,
|
EMPTY_SEARCH_RESPONSE,
|
||||||
|
EMPTY_SEARCH_RESPONSE_ALT,
|
||||||
TEST_BADLY_ENCODED_CONTENT,
|
TEST_BADLY_ENCODED_CONTENT,
|
||||||
TEST_FETCH_RESPONSE_BINARY,
|
TEST_FETCH_RESPONSE_BINARY,
|
||||||
TEST_FETCH_RESPONSE_HTML,
|
TEST_FETCH_RESPONSE_HTML,
|
||||||
|
@ -517,6 +518,11 @@ async def test_fetch_number_of_messages(
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"empty_search_reponse",
|
||||||
|
[EMPTY_SEARCH_RESPONSE, EMPTY_SEARCH_RESPONSE_ALT],
|
||||||
|
ids=["regular_empty_search_response", "alt_empty_search_response"],
|
||||||
|
)
|
||||||
@pytest.mark.parametrize("imap_search", [TEST_SEARCH_RESPONSE])
|
@pytest.mark.parametrize("imap_search", [TEST_SEARCH_RESPONSE])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("imap_fetch", "valid_date"),
|
("imap_fetch", "valid_date"),
|
||||||
|
@ -525,7 +531,10 @@ async def test_fetch_number_of_messages(
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize("imap_has_capability", [True, False], ids=["push", "poll"])
|
@pytest.mark.parametrize("imap_has_capability", [True, False], ids=["push", "poll"])
|
||||||
async def test_reset_last_message(
|
async def test_reset_last_message(
|
||||||
hass: HomeAssistant, mock_imap_protocol: MagicMock, valid_date: bool
|
hass: HomeAssistant,
|
||||||
|
mock_imap_protocol: MagicMock,
|
||||||
|
valid_date: bool,
|
||||||
|
empty_search_reponse: tuple[str, list[bytes]],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test receiving a message successfully."""
|
"""Test receiving a message successfully."""
|
||||||
event = asyncio.Event() # needed for pushed coordinator to make a new loop
|
event = asyncio.Event() # needed for pushed coordinator to make a new loop
|
||||||
|
@ -580,7 +589,7 @@ async def test_reset_last_message(
|
||||||
)
|
)
|
||||||
|
|
||||||
# Simulate an update where no messages are found (needed for pushed coordinator)
|
# Simulate an update where no messages are found (needed for pushed coordinator)
|
||||||
mock_imap_protocol.search.return_value = Response(*EMPTY_SEARCH_RESPONSE)
|
mock_imap_protocol.search.return_value = Response(*empty_search_reponse)
|
||||||
|
|
||||||
# Make sure we have an update
|
# Make sure we have an update
|
||||||
async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
|
async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
|
||||||
|
|
Loading…
Reference in New Issue