From 842f354a05c9dc4c56adc2f4eb339ad0d3799804 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 7 Sep 2026 10:59:03 +0200 Subject: [PATCH] [api] Add an integration test for field free messages Ping, device info, list entities done and disconnect all travel through the ProtoMessage static entry points now that the no-op thunk is gone. --- .../fixtures/api_empty_message_roundtrip.yaml | 11 ++++++ .../test_api_empty_message_roundtrip.py | 36 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 tests/integration/fixtures/api_empty_message_roundtrip.yaml create mode 100644 tests/integration/test_api_empty_message_roundtrip.py diff --git a/tests/integration/fixtures/api_empty_message_roundtrip.yaml b/tests/integration/fixtures/api_empty_message_roundtrip.yaml new file mode 100644 index 0000000000..daa4d5e60c --- /dev/null +++ b/tests/integration/fixtures/api_empty_message_roundtrip.yaml @@ -0,0 +1,11 @@ +esphome: + name: api-empty-message-test +host: +api: +logger: + level: DEBUG + +switch: + - platform: template + name: "Empty Message Switch" + optimistic: true diff --git a/tests/integration/test_api_empty_message_roundtrip.py b/tests/integration/test_api_empty_message_roundtrip.py new file mode 100644 index 0000000000..ec0c95ab48 --- /dev/null +++ b/tests/integration/test_api_empty_message_roundtrip.py @@ -0,0 +1,36 @@ +"""Messages without fields go through the shared ProtoMessage entry points on both directions.""" + +from __future__ import annotations + +from aioesphomeapi import api_pb2 +import pytest + +from .raw_api_client import MESSAGE_TYPE_OF, RawApiClient +from .types import RunCompiledFunction + + +@pytest.mark.asyncio +async def test_api_empty_message_roundtrip( + yaml_config: str, + run_compiled: RunCompiledFunction, + unused_tcp_port: int, +) -> None: + async with run_compiled(yaml_config), RawApiClient(unused_tcp_port) as client: + await client.connect() + + # Field free request and reply on the plain send path + await client.send_message(api_pb2.PingRequest()) + await client.read_until_frame(MESSAGE_TYPE_OF[api_pb2.PingResponse]) + + # Field free request answered by a message with fields, and a list that ends with + # the field free ListEntitiesDoneResponse through the batching path + await client.send_message(api_pb2.DeviceInfoRequest()) + await client.read_until_frame(MESSAGE_TYPE_OF[api_pb2.DeviceInfoResponse]) + await client.send_message(api_pb2.ListEntitiesRequest()) + await client.read_until_frame( + MESSAGE_TYPE_OF[api_pb2.ListEntitiesSwitchResponse] + ) + await client.read_until_frame(MESSAGE_TYPE_OF[api_pb2.ListEntitiesDoneResponse]) + + await client.send_message(api_pb2.DisconnectRequest()) + await client.read_until_frame(MESSAGE_TYPE_OF[api_pb2.DisconnectResponse])