Improve 17track tests (#112734)

* 17Track tests

* add 17Track sensor to coverage

* extract repeated code

* 1. _goto_future - call tick only once
2. change test name to reflect test
3. remove ifs from test

* remove undersocre from _goto_future
This commit is contained in:
Shai Ungar 2024-03-10 19:57:28 +02:00 committed by GitHub
parent 049f0f5e3b
commit eb81bf1d49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 317 additions and 325 deletions

View File

@ -1183,7 +1183,6 @@ omit =
homeassistant/components/serial_pm/sensor.py
homeassistant/components/sesame/lock.py
homeassistant/components/seven_segments/image_processing.py
homeassistant/components/seventeentrack/sensor.py
homeassistant/components/shodan/sensor.py
homeassistant/components/sia/__init__.py
homeassistant/components/sia/alarm_control_panel.py

View File

@ -1 +1,25 @@
"""Tests for the seventeentrack component."""
from datetime import timedelta
from freezegun.api import FrozenDateTimeFactory
from homeassistant.components.seventeentrack.sensor import DEFAULT_SCAN_INTERVAL
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import async_setup_component
from tests.common import async_fire_time_changed
async def init_integration(hass: HomeAssistant, config: ConfigType):
"""Set up the seventeentrack integration in Home Assistant."""
assert await async_setup_component(hass, "sensor", config)
await hass.async_block_till_done()
async def goto_future(hass: HomeAssistant, freezer: FrozenDateTimeFactory):
"""Move to future."""
freezer.tick(DEFAULT_SCAN_INTERVAL + timedelta(minutes=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()

View File

@ -0,0 +1,108 @@
"""Configuration for 17Track tests."""
from typing import Optional
from unittest.mock import AsyncMock, patch
from py17track.package import Package
import pytest
from homeassistant.components.seventeentrack.sensor import (
CONF_SHOW_ARCHIVED,
CONF_SHOW_DELIVERED,
)
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
VALID_CONFIG_MINIMAL = {
"sensor": {
"platform": "seventeentrack",
CONF_USERNAME: "test",
CONF_PASSWORD: "test",
}
}
INVALID_CONFIG = {"sensor": {"platform": "seventeentrack", "boom": "test"}}
VALID_CONFIG_FULL = {
"sensor": {
"platform": "seventeentrack",
CONF_USERNAME: "test",
CONF_PASSWORD: "test",
CONF_SHOW_ARCHIVED: True,
CONF_SHOW_DELIVERED: True,
}
}
VALID_CONFIG_FULL_NO_DELIVERED = {
"sensor": {
"platform": "seventeentrack",
CONF_USERNAME: "test",
CONF_PASSWORD: "test",
CONF_SHOW_ARCHIVED: False,
CONF_SHOW_DELIVERED: False,
}
}
DEFAULT_SUMMARY = {
"Not Found": 0,
"In Transit": 0,
"Expired": 0,
"Ready to be Picked Up": 0,
"Undelivered": 0,
"Delivered": 0,
"Returned": 0,
}
NEW_SUMMARY_DATA = {
"Not Found": 1,
"In Transit": 1,
"Expired": 1,
"Ready to be Picked Up": 1,
"Undelivered": 1,
"Delivered": 1,
"Returned": 1,
}
@pytest.fixture
def mock_seventeentrack():
"""Build a fixture for the 17Track API."""
mock_seventeentrack_api = AsyncMock()
with (
patch(
"homeassistant.components.seventeentrack.sensor.SeventeenTrackClient",
return_value=mock_seventeentrack_api,
) as mock_seventeentrack_api,
):
mock_seventeentrack_api.return_value.profile.login.return_value = True
mock_seventeentrack_api.return_value.profile.packages.return_value = []
mock_seventeentrack_api.return_value.profile.summary.return_value = (
DEFAULT_SUMMARY
)
yield mock_seventeentrack_api
def get_package(
tracking_number: str = "456",
destination_country: int = 206,
friendly_name: Optional[str] = "friendly name 1",
info_text: str = "info text 1",
location: str = "location 1",
timestamp: str = "2020-08-10 10:32",
origin_country: int = 206,
package_type: int = 2,
status: int = 0,
tz: str = "UTC",
):
"""Build a Package of the 17Track API."""
return Package(
tracking_number=tracking_number,
destination_country=destination_country,
friendly_name=friendly_name,
info_text=info_text,
location=location,
timestamp=timestamp,
origin_country=origin_country,
package_type=package_type,
status=status,
tz=tz,
)

View File

@ -2,283 +2,169 @@
from __future__ import annotations
import datetime
from unittest.mock import patch
from unittest.mock import AsyncMock, patch
from py17track.package import Package
import pytest
from freezegun.api import FrozenDateTimeFactory
from py17track.errors import SeventeenTrackError
from homeassistant.components.seventeentrack.sensor import (
CONF_SHOW_ARCHIVED,
CONF_SHOW_DELIVERED,
)
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from homeassistant.util import utcnow
from tests.common import async_fire_time_changed
VALID_CONFIG_MINIMAL = {
"sensor": {
"platform": "seventeentrack",
CONF_USERNAME: "test",
CONF_PASSWORD: "test",
}
}
INVALID_CONFIG = {"sensor": {"platform": "seventeentrack", "boom": "test"}}
VALID_CONFIG_FULL = {
"sensor": {
"platform": "seventeentrack",
CONF_USERNAME: "test",
CONF_PASSWORD: "test",
CONF_SHOW_ARCHIVED: True,
CONF_SHOW_DELIVERED: True,
}
}
VALID_CONFIG_FULL_NO_DELIVERED = {
"sensor": {
"platform": "seventeentrack",
CONF_USERNAME: "test",
CONF_PASSWORD: "test",
CONF_SHOW_ARCHIVED: False,
CONF_SHOW_DELIVERED: False,
}
}
DEFAULT_SUMMARY = {
"Not Found": 0,
"In Transit": 0,
"Expired": 0,
"Ready to be Picked Up": 0,
"Undelivered": 0,
"Delivered": 0,
"Returned": 0,
}
NEW_SUMMARY_DATA = {
"Not Found": 1,
"In Transit": 1,
"Expired": 1,
"Ready to be Picked Up": 1,
"Undelivered": 1,
"Delivered": 1,
"Returned": 1,
}
from . import goto_future, init_integration
from .conftest import (
DEFAULT_SUMMARY,
INVALID_CONFIG,
NEW_SUMMARY_DATA,
VALID_CONFIG_FULL,
VALID_CONFIG_FULL_NO_DELIVERED,
VALID_CONFIG_MINIMAL,
get_package,
)
class ClientMock:
"""Mock the py17track client to inject the ProfileMock."""
def __init__(self, session) -> None:
"""Mock the profile."""
self.profile = ProfileMock()
class ProfileMock:
"""ProfileMock will mock data coming from 17track."""
package_list = []
login_result = True
summary_data = DEFAULT_SUMMARY
account_id = "123"
@classmethod
def reset(cls):
"""Reset data to defaults."""
cls.package_list = []
cls.login_result = True
cls.summary_data = DEFAULT_SUMMARY
cls.account_id = "123"
def __init__(self) -> None:
"""Override Account id."""
self.account_id = self.__class__.account_id
async def login(self, email: str, password: str) -> bool:
"""Login mock."""
return self.__class__.login_result
async def packages(
self,
package_state: int | str = "",
show_archived: bool = False,
tz: str = "UTC",
) -> list:
"""Packages mock.""" # noqa: D401
return self.__class__.package_list[:]
async def summary(self, show_archived: bool = False) -> dict:
"""Summary mock."""
return self.__class__.summary_data
@pytest.fixture(autouse=True, name="mock_client")
def fixture_mock_client():
"""Mock py17track client."""
with patch(
"homeassistant.components.seventeentrack.sensor.SeventeenTrackClient",
new=ClientMock,
):
yield
ProfileMock.reset()
async def _setup_seventeentrack(hass, config=None, summary_data=None):
"""Set up component using config."""
if not config:
config = VALID_CONFIG_MINIMAL
if not summary_data:
summary_data = {}
ProfileMock.summary_data = summary_data
assert await async_setup_component(hass, "sensor", config)
await hass.async_block_till_done()
async def _goto_future(hass: HomeAssistant, future=None):
"""Move to future."""
if not future:
future = utcnow() + datetime.timedelta(minutes=10)
with patch("homeassistant.util.utcnow", return_value=future):
async_fire_time_changed(hass, future)
await hass.async_block_till_done(wait_background_tasks=True)
async_fire_time_changed(hass, future)
await hass.async_block_till_done(wait_background_tasks=True)
async def test_full_valid_config(hass: HomeAssistant) -> None:
async def test_full_valid_config(
hass: HomeAssistant, mock_seventeentrack: AsyncMock
) -> None:
"""Ensure everything starts correctly."""
assert await async_setup_component(hass, "sensor", VALID_CONFIG_FULL)
await hass.async_block_till_done()
assert len(hass.states.async_entity_ids()) == len(ProfileMock.summary_data.keys())
await init_integration(hass, VALID_CONFIG_MINIMAL)
assert len(hass.states.async_entity_ids()) == len(DEFAULT_SUMMARY.keys())
async def test_valid_config(hass: HomeAssistant) -> None:
async def test_valid_config(
hass: HomeAssistant, mock_seventeentrack: AsyncMock
) -> None:
"""Ensure everything starts correctly."""
assert await async_setup_component(hass, "sensor", VALID_CONFIG_MINIMAL)
await hass.async_block_till_done()
assert len(hass.states.async_entity_ids()) == len(ProfileMock.summary_data.keys())
await init_integration(hass, VALID_CONFIG_FULL)
assert len(hass.states.async_entity_ids()) == len(DEFAULT_SUMMARY.keys())
async def test_invalid_config(hass: HomeAssistant) -> None:
"""Ensure nothing is created when config is wrong."""
assert await async_setup_component(hass, "sensor", INVALID_CONFIG)
await init_integration(hass, INVALID_CONFIG)
assert not hass.states.async_entity_ids("sensor")
async def test_add_package(hass: HomeAssistant) -> None:
"""Ensure package is added correctly when user add a new package."""
package = Package(
tracking_number="456",
destination_country=206,
friendly_name="friendly name 1",
info_text="info text 1",
location="location 1",
timestamp="2020-08-10 10:32",
origin_country=206,
package_type=2,
async def test_login_exception(
hass: HomeAssistant, mock_seventeentrack: AsyncMock
) -> None:
"""Ensure everything starts correctly."""
mock_seventeentrack.return_value.profile.login.side_effect = SeventeenTrackError(
"Error"
)
ProfileMock.package_list = [package]
await init_integration(hass, VALID_CONFIG_FULL)
assert not hass.states.async_entity_ids("sensor")
await _setup_seventeentrack(hass)
async def test_add_package(
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_seventeentrack: AsyncMock
) -> None:
"""Ensure package is added correctly when user add a new package."""
package = get_package()
mock_seventeentrack.return_value.profile.packages.return_value = [package]
mock_seventeentrack.return_value.profile.summary.return_value = {}
await init_integration(hass, VALID_CONFIG_FULL)
assert hass.states.get("sensor.seventeentrack_package_456") is not None
assert len(hass.states.async_entity_ids()) == 1
package2 = Package(
package2 = get_package(
tracking_number="789",
destination_country=206,
friendly_name="friendly name 2",
info_text="info text 2",
location="location 2",
timestamp="2020-08-10 14:25",
origin_country=206,
package_type=2,
)
ProfileMock.package_list = [package, package2]
mock_seventeentrack.return_value.profile.packages.return_value = [package, package2]
await _goto_future(hass)
await goto_future(hass, freezer)
assert hass.states.get("sensor.seventeentrack_package_789") is not None
assert len(hass.states.async_entity_ids()) == 2
async def test_remove_package(hass: HomeAssistant) -> None:
async def test_add_package_default_friendly_name(
hass: HomeAssistant, mock_seventeentrack: AsyncMock
) -> None:
"""Ensure package is added correctly with default friendly name when user add a new package without his own friendly name."""
package = get_package(friendly_name=None)
mock_seventeentrack.return_value.profile.packages.return_value = [package]
mock_seventeentrack.return_value.profile.summary.return_value = {}
await init_integration(hass, VALID_CONFIG_FULL)
state_456 = hass.states.get("sensor.seventeentrack_package_456")
assert state_456 is not None
assert state_456.attributes["friendly_name"] == "Seventeentrack Package: 456"
assert len(hass.states.async_entity_ids()) == 1
async def test_remove_package(
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_seventeentrack: AsyncMock
) -> None:
"""Ensure entity is not there anymore if package is not there."""
package1 = Package(
tracking_number="456",
destination_country=206,
friendly_name="friendly name 1",
info_text="info text 1",
location="location 1",
timestamp="2020-08-10 10:32",
origin_country=206,
package_type=2,
)
package2 = Package(
package1 = get_package()
package2 = get_package(
tracking_number="789",
destination_country=206,
friendly_name="friendly name 2",
info_text="info text 2",
location="location 2",
timestamp="2020-08-10 14:25",
origin_country=206,
package_type=2,
)
ProfileMock.package_list = [package1, package2]
mock_seventeentrack.return_value.profile.packages.return_value = [
package1,
package2,
]
mock_seventeentrack.return_value.profile.summary.return_value = {}
await _setup_seventeentrack(hass)
await init_integration(hass, VALID_CONFIG_FULL)
assert hass.states.get("sensor.seventeentrack_package_456") is not None
assert hass.states.get("sensor.seventeentrack_package_789") is not None
assert len(hass.states.async_entity_ids()) == 2
ProfileMock.package_list = [package2]
mock_seventeentrack.return_value.profile.packages.return_value = [package2]
await _goto_future(hass)
await goto_future(hass, freezer)
assert hass.states.get("sensor.seventeentrack_package_456").state == "unavailable"
assert len(hass.states.async_entity_ids()) == 2
await goto_future(hass, freezer)
assert hass.states.get("sensor.seventeentrack_package_456") is None
assert hass.states.get("sensor.seventeentrack_package_789") is not None
assert len(hass.states.async_entity_ids()) == 1
async def test_friendly_name_changed(hass: HomeAssistant) -> None:
"""Test friendly name change."""
package = Package(
tracking_number="456",
destination_country=206,
friendly_name="friendly name 1",
info_text="info text 1",
location="location 1",
timestamp="2020-08-10 10:32",
origin_country=206,
package_type=2,
async def test_package_error(
hass: HomeAssistant, mock_seventeentrack: AsyncMock
) -> None:
"""Ensure package is added correctly when user add a new package."""
mock_seventeentrack.return_value.profile.packages.side_effect = SeventeenTrackError(
"Error"
)
ProfileMock.package_list = [package]
mock_seventeentrack.return_value.profile.summary.return_value = {}
await _setup_seventeentrack(hass)
await init_integration(hass, VALID_CONFIG_FULL)
assert hass.states.get("sensor.seventeentrack_package_456") is None
async def test_friendly_name_changed(
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_seventeentrack: AsyncMock
) -> None:
"""Test friendly name change."""
package = get_package()
mock_seventeentrack.return_value.profile.packages.return_value = [package]
mock_seventeentrack.return_value.profile.summary.return_value = {}
await init_integration(hass, VALID_CONFIG_FULL)
assert hass.states.get("sensor.seventeentrack_package_456") is not None
assert len(hass.states.async_entity_ids()) == 1
package = Package(
tracking_number="456",
destination_country=206,
friendly_name="friendly name 2",
info_text="info text 1",
location="location 1",
timestamp="2020-08-10 10:32",
origin_country=206,
package_type=2,
)
ProfileMock.package_list = [package]
package = get_package(friendly_name="friendly name 2")
mock_seventeentrack.return_value.profile.packages.return_value = [package]
await _goto_future(hass)
await goto_future(hass, freezer)
assert hass.states.get("sensor.seventeentrack_package_456") is not None
entity = hass.data["entity_components"]["sensor"].get_entity(
@ -288,169 +174,144 @@ async def test_friendly_name_changed(hass: HomeAssistant) -> None:
assert len(hass.states.async_entity_ids()) == 1
async def test_delivered_not_shown(hass: HomeAssistant) -> None:
async def test_delivered_not_shown(
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_seventeentrack: AsyncMock
) -> None:
"""Ensure delivered packages are not shown."""
package = Package(
tracking_number="456",
destination_country=206,
friendly_name="friendly name 1",
info_text="info text 1",
location="location 1",
timestamp="2020-08-10 10:32",
origin_country=206,
package_type=2,
status=40,
)
ProfileMock.package_list = [package]
package = get_package(status=40)
mock_seventeentrack.return_value.profile.packages.return_value = [package]
mock_seventeentrack.return_value.profile.summary.return_value = {}
with patch(
"homeassistant.components.seventeentrack.sensor.persistent_notification"
) as persistent_notification_mock:
await _setup_seventeentrack(hass, VALID_CONFIG_FULL_NO_DELIVERED)
await _goto_future(hass)
await init_integration(hass, VALID_CONFIG_FULL_NO_DELIVERED)
await goto_future(hass, freezer)
assert not hass.states.async_entity_ids()
persistent_notification_mock.create.assert_called()
async def test_delivered_shown(hass: HomeAssistant) -> None:
async def test_delivered_shown(
hass: HomeAssistant, mock_seventeentrack: AsyncMock
) -> None:
"""Ensure delivered packages are show when user choose to show them."""
package = Package(
tracking_number="456",
destination_country=206,
friendly_name="friendly name 1",
info_text="info text 1",
location="location 1",
timestamp="2020-08-10 10:32",
origin_country=206,
package_type=2,
status=40,
)
ProfileMock.package_list = [package]
package = get_package(status=40)
mock_seventeentrack.return_value.profile.packages.return_value = [package]
mock_seventeentrack.return_value.profile.summary.return_value = {}
with patch(
"homeassistant.components.seventeentrack.sensor.persistent_notification"
) as persistent_notification_mock:
await _setup_seventeentrack(hass, VALID_CONFIG_FULL)
await init_integration(hass, VALID_CONFIG_FULL)
assert hass.states.get("sensor.seventeentrack_package_456") is not None
assert len(hass.states.async_entity_ids()) == 1
persistent_notification_mock.create.assert_not_called()
async def test_becomes_delivered_not_shown_notification(hass: HomeAssistant) -> None:
async def test_becomes_delivered_not_shown_notification(
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_seventeentrack: AsyncMock
) -> None:
"""Ensure notification is triggered when package becomes delivered."""
package = Package(
tracking_number="456",
destination_country=206,
friendly_name="friendly name 1",
info_text="info text 1",
location="location 1",
timestamp="2020-08-10 10:32",
origin_country=206,
package_type=2,
)
ProfileMock.package_list = [package]
package = get_package()
mock_seventeentrack.return_value.profile.packages.return_value = [package]
mock_seventeentrack.return_value.profile.summary.return_value = {}
await _setup_seventeentrack(hass, VALID_CONFIG_FULL_NO_DELIVERED)
await init_integration(hass, VALID_CONFIG_FULL_NO_DELIVERED)
assert hass.states.get("sensor.seventeentrack_package_456") is not None
assert len(hass.states.async_entity_ids()) == 1
package_delivered = Package(
tracking_number="456",
destination_country=206,
friendly_name="friendly name 1",
info_text="info text 1",
location="location 1",
timestamp="2020-08-10 10:32",
origin_country=206,
package_type=2,
status=40,
)
ProfileMock.package_list = [package_delivered]
package_delivered = get_package(status=40)
mock_seventeentrack.return_value.profile.packages.return_value = [package_delivered]
with patch(
"homeassistant.components.seventeentrack.sensor.persistent_notification"
) as persistent_notification_mock:
await _goto_future(hass)
await goto_future(hass, freezer)
await goto_future(hass, freezer)
persistent_notification_mock.create.assert_called()
assert not hass.states.async_entity_ids()
async def test_summary_correctly_updated(hass: HomeAssistant) -> None:
async def test_summary_correctly_updated(
hass: HomeAssistant, freezer: FrozenDateTimeFactory, mock_seventeentrack: AsyncMock
) -> None:
"""Ensure summary entities are not duplicated."""
package = Package(
tracking_number="456",
destination_country=206,
friendly_name="friendly name 1",
info_text="info text 1",
location="location 1",
timestamp="2020-08-10 10:32",
origin_country=206,
package_type=2,
status=30,
)
ProfileMock.package_list = [package]
package = get_package(status=30)
mock_seventeentrack.return_value.profile.packages.return_value = [package]
mock_seventeentrack.return_value.profile.summary.return_value = DEFAULT_SUMMARY
await _setup_seventeentrack(hass, summary_data=DEFAULT_SUMMARY)
await init_integration(hass, VALID_CONFIG_FULL)
assert len(hass.states.async_entity_ids()) == 8
for state in hass.states.async_all():
if state.entity_id == "sensor.seventeentrack_package_456":
break
assert state.state == "0"
assert (
len(
hass.states.get(
"sensor.seventeentrack_packages_ready_to_be_picked_up"
).attributes["packages"]
)
== 1
state_ready_picked = hass.states.get(
"sensor.seventeentrack_packages_ready_to_be_picked_up"
)
assert state_ready_picked is not None
assert len(state_ready_picked.attributes["packages"]) == 1
ProfileMock.package_list = []
ProfileMock.summary_data = NEW_SUMMARY_DATA
mock_seventeentrack.return_value.profile.packages.return_value = []
mock_seventeentrack.return_value.profile.summary.return_value = NEW_SUMMARY_DATA
await _goto_future(hass)
await goto_future(hass, freezer)
await goto_future(hass, freezer)
assert len(hass.states.async_entity_ids()) == 7
for state in hass.states.async_all():
assert state.state == "1"
state_ready_picked = hass.states.get(
"sensor.seventeentrack_packages_ready_to_be_picked_up"
)
assert state_ready_picked is not None
assert state_ready_picked.attributes["packages"] is None
async def test_summary_error(
hass: HomeAssistant, mock_seventeentrack: AsyncMock
) -> None:
"""Test summary empty if error."""
package = get_package(status=30)
mock_seventeentrack.return_value.profile.packages.return_value = [package]
mock_seventeentrack.return_value.profile.summary.side_effect = SeventeenTrackError(
"Error"
)
await init_integration(hass, VALID_CONFIG_FULL)
assert len(hass.states.async_entity_ids()) == 1
assert (
hass.states.get(
"sensor.seventeentrack_packages_ready_to_be_picked_up"
).attributes["packages"]
is None
hass.states.get("sensor.seventeentrack_packages_ready_to_be_picked_up") is None
)
async def test_utc_timestamp(hass: HomeAssistant) -> None:
async def test_utc_timestamp(
hass: HomeAssistant, mock_seventeentrack: AsyncMock
) -> None:
"""Ensure package timestamp is converted correctly from HA-defined time zone to UTC."""
package = Package(
tracking_number="456",
destination_country=206,
friendly_name="friendly name 1",
info_text="info text 1",
location="location 1",
timestamp="2020-08-10 10:32",
origin_country=206,
package_type=2,
tz="Asia/Jakarta",
)
ProfileMock.package_list = [package]
await _setup_seventeentrack(hass)
package = get_package(tz="Asia/Jakarta")
mock_seventeentrack.return_value.profile.packages.return_value = [package]
mock_seventeentrack.return_value.profile.summary.return_value = {}
await init_integration(hass, VALID_CONFIG_FULL)
assert hass.states.get("sensor.seventeentrack_package_456") is not None
assert len(hass.states.async_entity_ids()) == 1
assert (
str(
hass.states.get("sensor.seventeentrack_package_456").attributes.get(
"timestamp"
)
)
== "2020-08-10 03:32:00+00:00"
)
state_456 = hass.states.get("sensor.seventeentrack_package_456")
assert state_456 is not None
assert str(state_456.attributes.get("timestamp")) == "2020-08-10 03:32:00+00:00"
async def test_non_valid_platform_config(
hass: HomeAssistant, mock_seventeentrack: AsyncMock
) -> None:
"""Test if login fails."""
mock_seventeentrack.return_value.profile.login.return_value = False
await init_integration(hass, VALID_CONFIG_FULL)
assert len(hass.states.async_entity_ids()) == 0