integration tests

This commit is contained in:
J. Nick Koston
2025-06-06 21:56:16 -05:00
parent 2f230f32fa
commit a89c60cf10
5 changed files with 635 additions and 0 deletions

View File

@@ -0,0 +1,322 @@
esphome:
name: host-mode-many-entities
friendly_name: "Host Mode Many Entities Test"
logger:
host:
api:
sensor:
# 50 test sensors with predictable values for batching test
- platform: template
name: "Test Sensor 1"
lambda: return 1.0;
update_interval: 2s
- platform: template
name: "Test Sensor 2"
lambda: return 2.0;
update_interval: 2s
- platform: template
name: "Test Sensor 3"
lambda: return 3.0;
update_interval: 2s
- platform: template
name: "Test Sensor 4"
lambda: return 4.0;
update_interval: 2s
- platform: template
name: "Test Sensor 5"
lambda: return 5.0;
update_interval: 2s
- platform: template
name: "Test Sensor 6"
lambda: return 6.0;
update_interval: 2s
- platform: template
name: "Test Sensor 7"
lambda: return 7.0;
update_interval: 2s
- platform: template
name: "Test Sensor 8"
lambda: return 8.0;
update_interval: 2s
- platform: template
name: "Test Sensor 9"
lambda: return 9.0;
update_interval: 2s
- platform: template
name: "Test Sensor 10"
lambda: return 10.0;
update_interval: 2s
- platform: template
name: "Test Sensor 11"
lambda: return 11.0;
update_interval: 2s
- platform: template
name: "Test Sensor 12"
lambda: return 12.0;
update_interval: 2s
- platform: template
name: "Test Sensor 13"
lambda: return 13.0;
update_interval: 2s
- platform: template
name: "Test Sensor 14"
lambda: return 14.0;
update_interval: 2s
- platform: template
name: "Test Sensor 15"
lambda: return 15.0;
update_interval: 2s
- platform: template
name: "Test Sensor 16"
lambda: return 16.0;
update_interval: 2s
- platform: template
name: "Test Sensor 17"
lambda: return 17.0;
update_interval: 2s
- platform: template
name: "Test Sensor 18"
lambda: return 18.0;
update_interval: 2s
- platform: template
name: "Test Sensor 19"
lambda: return 19.0;
update_interval: 2s
- platform: template
name: "Test Sensor 20"
lambda: return 20.0;
update_interval: 2s
- platform: template
name: "Test Sensor 21"
lambda: return 21.0;
update_interval: 2s
- platform: template
name: "Test Sensor 22"
lambda: return 22.0;
update_interval: 2s
- platform: template
name: "Test Sensor 23"
lambda: return 23.0;
update_interval: 2s
- platform: template
name: "Test Sensor 24"
lambda: return 24.0;
update_interval: 2s
- platform: template
name: "Test Sensor 25"
lambda: return 25.0;
update_interval: 2s
- platform: template
name: "Test Sensor 26"
lambda: return 26.0;
update_interval: 2s
- platform: template
name: "Test Sensor 27"
lambda: return 27.0;
update_interval: 2s
- platform: template
name: "Test Sensor 28"
lambda: return 28.0;
update_interval: 2s
- platform: template
name: "Test Sensor 29"
lambda: return 29.0;
update_interval: 2s
- platform: template
name: "Test Sensor 30"
lambda: return 30.0;
update_interval: 2s
- platform: template
name: "Test Sensor 31"
lambda: return 31.0;
update_interval: 2s
- platform: template
name: "Test Sensor 32"
lambda: return 32.0;
update_interval: 2s
- platform: template
name: "Test Sensor 33"
lambda: return 33.0;
update_interval: 2s
- platform: template
name: "Test Sensor 34"
lambda: return 34.0;
update_interval: 2s
- platform: template
name: "Test Sensor 35"
lambda: return 35.0;
update_interval: 2s
- platform: template
name: "Test Sensor 36"
lambda: return 36.0;
update_interval: 2s
- platform: template
name: "Test Sensor 37"
lambda: return 37.0;
update_interval: 2s
- platform: template
name: "Test Sensor 38"
lambda: return 38.0;
update_interval: 2s
- platform: template
name: "Test Sensor 39"
lambda: return 39.0;
update_interval: 2s
- platform: template
name: "Test Sensor 40"
lambda: return 40.0;
update_interval: 2s
- platform: template
name: "Test Sensor 41"
lambda: return 41.0;
update_interval: 2s
- platform: template
name: "Test Sensor 42"
lambda: return 42.0;
update_interval: 2s
- platform: template
name: "Test Sensor 43"
lambda: return 43.0;
update_interval: 2s
- platform: template
name: "Test Sensor 44"
lambda: return 44.0;
update_interval: 2s
- platform: template
name: "Test Sensor 45"
lambda: return 45.0;
update_interval: 2s
- platform: template
name: "Test Sensor 46"
lambda: return 46.0;
update_interval: 2s
- platform: template
name: "Test Sensor 47"
lambda: return 47.0;
update_interval: 2s
- platform: template
name: "Test Sensor 48"
lambda: return 48.0;
update_interval: 2s
- platform: template
name: "Test Sensor 49"
lambda: return 49.0;
update_interval: 2s
- platform: template
name: "Test Sensor 50"
lambda: return 50.0;
update_interval: 2s
# Mixed entity types for comprehensive batching test
binary_sensor:
- platform: template
name: "Test Binary Sensor 1"
lambda: return true;
- platform: template
name: "Test Binary Sensor 2"
lambda: return false;
switch:
- platform: template
name: "Test Switch 1"
lambda: return true;
turn_on_action:
- logger.log: "Switch 1 ON"
turn_off_action:
- logger.log: "Switch 1 OFF"
- platform: template
name: "Test Switch 2"
lambda: return false;
turn_on_action:
- logger.log: "Switch 2 ON"
turn_off_action:
- logger.log: "Switch 2 OFF"
text_sensor:
- platform: template
name: "Test Text Sensor 1"
lambda: return std::string("Test Value 1");
- platform: template
name: "Test Text Sensor 2"
lambda: return std::string("Test Value 2");
- platform: version
name: "ESPHome Version"
number:
- platform: template
name: "Test Number"
min_value: 0
max_value: 100
step: 1
lambda: return 50.0;
set_action:
- logger.log: "Number set"
select:
- platform: template
name: "Test Select"
options:
- "Option 1"
- "Option 2"
initial_option: "Option 1"
optimistic: true
set_action:
- logger.log: "Select changed"
text:
- platform: template
name: "Test Text"
mode: text
initial_value: "Hello"
set_action:
- logger.log: "Text changed"
valve:
- platform: template
name: "Test Valve"
open_action:
- logger.log: "Valve opening"
close_action:
- logger.log: "Valve closing"
stop_action:
- logger.log: "Valve stopping"
alarm_control_panel:
- platform: template
name: "Test Alarm"
codes:
- "1234"
arming_away_time: 0s
arming_home_time: 0s
pending_time: 0s
trigger_time: 300s
restore_mode: ALWAYS_DISARMED
on_disarmed:
- logger.log: "Alarm disarmed"
on_arming:
- logger.log: "Alarm arming"
on_armed_away:
- logger.log: "Alarm armed away"
on_armed_home:
- logger.log: "Alarm armed home"
on_pending:
- logger.log: "Alarm pending"
on_triggered:
- logger.log: "Alarm triggered"
event:
- platform: template
name: "Test Event"
event_types:
- first_event
- second_event
button:
- platform: template
name: "Test Button"
on_press:
- logger.log: "Button pressed"

View File

@@ -0,0 +1,136 @@
esphome:
name: host-mode-many-entities-multi
friendly_name: "Host Mode Many Entities Multiple Connections Test"
logger:
host:
api:
sensor:
# 20 test sensors for faster testing with multiple connections
- platform: template
name: "Test Sensor 1"
lambda: return 1.0;
update_interval: 2s
- platform: template
name: "Test Sensor 2"
lambda: return 2.0;
update_interval: 2s
- platform: template
name: "Test Sensor 3"
lambda: return 3.0;
update_interval: 2s
- platform: template
name: "Test Sensor 4"
lambda: return 4.0;
update_interval: 2s
- platform: template
name: "Test Sensor 5"
lambda: return 5.0;
update_interval: 2s
- platform: template
name: "Test Sensor 6"
lambda: return 6.0;
update_interval: 2s
- platform: template
name: "Test Sensor 7"
lambda: return 7.0;
update_interval: 2s
- platform: template
name: "Test Sensor 8"
lambda: return 8.0;
update_interval: 2s
- platform: template
name: "Test Sensor 9"
lambda: return 9.0;
update_interval: 2s
- platform: template
name: "Test Sensor 10"
lambda: return 10.0;
update_interval: 2s
- platform: template
name: "Test Sensor 11"
lambda: return 11.0;
update_interval: 2s
- platform: template
name: "Test Sensor 12"
lambda: return 12.0;
update_interval: 2s
- platform: template
name: "Test Sensor 13"
lambda: return 13.0;
update_interval: 2s
- platform: template
name: "Test Sensor 14"
lambda: return 14.0;
update_interval: 2s
- platform: template
name: "Test Sensor 15"
lambda: return 15.0;
update_interval: 2s
- platform: template
name: "Test Sensor 16"
lambda: return 16.0;
update_interval: 2s
- platform: template
name: "Test Sensor 17"
lambda: return 17.0;
update_interval: 2s
- platform: template
name: "Test Sensor 18"
lambda: return 18.0;
update_interval: 2s
- platform: template
name: "Test Sensor 19"
lambda: return 19.0;
update_interval: 2s
- platform: template
name: "Test Sensor 20"
lambda: return 20.0;
update_interval: 2s
# Mixed entity types for comprehensive batching test
binary_sensor:
- platform: template
name: "Test Binary Sensor 1"
lambda: return true;
- platform: template
name: "Test Binary Sensor 2"
lambda: return false;
text_sensor:
- platform: template
name: "Test Text Sensor 1"
lambda: return std::string("Test Value 1");
- platform: template
name: "Test Text Sensor 2"
lambda: return std::string("Test Value 2");
- platform: version
name: "ESPHome Version"
switch:
- platform: template
name: "Test Switch 1"
lambda: return true;
turn_on_action:
- logger.log: "Switch 1 ON"
turn_off_action:
- logger.log: "Switch 1 OFF"
button:
- platform: template
name: "Test Button"
on_press:
- logger.log: "Button pressed"
number:
- platform: template
name: "Test Number"
min_value: 0
max_value: 100
step: 1
lambda: return 50.0;
set_action:
- logger.log: "Number set"

View File

@@ -5,3 +5,46 @@ api:
encryption:
key: N4Yle5YirwZhPiHHsdZLdOA73ndj/84veVaLhTvxCuU=
logger:
# Test sensors to verify batching works with noise encryption
sensor:
- platform: template
name: "Noise Test Sensor 1"
lambda: return 1.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 2"
lambda: return 2.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 3"
lambda: return 3.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 4"
lambda: return 4.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 5"
lambda: return 5.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 6"
lambda: return 6.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 7"
lambda: return 7.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 8"
lambda: return 8.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 9"
lambda: return 9.0;
update_interval: 2s
- platform: template
name: "Noise Test Sensor 10"
lambda: return 10.0;
update_interval: 2s

View File

@@ -0,0 +1,64 @@
"""Integration test for many entities to test API batching."""
from __future__ import annotations
import asyncio
from aioesphomeapi import EntityState
import pytest
from .types import APIClientConnectedFactory, RunCompiledFunction
@pytest.mark.asyncio
async def test_host_mode_many_entities(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Test API batching with many entities of different types."""
# Write, compile and run the ESPHome device, then connect to API
async with run_compiled(yaml_config), api_client_connected() as client:
# Subscribe to state changes
states: dict[int, EntityState] = {}
entity_count_future: asyncio.Future[int] = asyncio.Future()
def on_state(state: EntityState) -> None:
states[state.key] = state
# When we have received states from a good number of entities, resolve the future
if len(states) >= 50 and not entity_count_future.done():
entity_count_future.set_result(len(states))
client.subscribe_states(on_state)
# Wait for states from at least 50 entities with timeout
try:
entity_count = await asyncio.wait_for(entity_count_future, timeout=10.0)
except asyncio.TimeoutError:
pytest.fail(
f"Did not receive states from at least 50 entities within 10 seconds. "
f"Received {len(states)} states: {list(states.keys())}"
)
# Verify we received a good number of entity states
assert entity_count >= 50, f"Expected at least 50 entities, got {entity_count}"
assert len(states) >= 50, f"Expected at least 50 states, got {len(states)}"
# Verify we have different entity types by checking some expected values
sensor_states = [
s
for s in states.values()
if hasattr(s, "state") and isinstance(s.state, float)
]
binary_sensor_states = [
s
for s in states.values()
if hasattr(s, "state") and isinstance(s.state, bool)
]
assert len(sensor_states) >= 50, (
f"Expected at least 50 sensor states, got {len(sensor_states)}"
)
assert len(binary_sensor_states) >= 2, (
f"Expected at least 2 binary sensor states, got {len(binary_sensor_states)}"
)

View File

@@ -0,0 +1,70 @@
"""Integration test for shared buffer optimization with multiple API connections."""
from __future__ import annotations
import asyncio
from aioesphomeapi import EntityState
import pytest
from .types import APIClientConnectedFactory, RunCompiledFunction
@pytest.mark.asyncio
async def test_host_mode_many_entities_multiple_connections(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Test shared buffer optimization with multiple API connections."""
# Write, compile and run the ESPHome device
async with (
run_compiled(yaml_config),
api_client_connected() as client1,
api_client_connected() as client2,
):
# Subscribe both clients to state changes
states1: dict[int, EntityState] = {}
states2: dict[int, EntityState] = {}
client1_ready = asyncio.Future()
client2_ready = asyncio.Future()
def on_state1(state: EntityState) -> None:
states1[state.key] = state
if len(states1) >= 20 and not client1_ready.done():
client1_ready.set_result(len(states1))
def on_state2(state: EntityState) -> None:
states2[state.key] = state
if len(states2) >= 20 and not client2_ready.done():
client2_ready.set_result(len(states2))
client1.subscribe_states(on_state1)
client2.subscribe_states(on_state2)
# Wait for both clients to receive states
try:
count1, count2 = await asyncio.gather(
asyncio.wait_for(client1_ready, timeout=10.0),
asyncio.wait_for(client2_ready, timeout=10.0),
)
except asyncio.TimeoutError:
pytest.fail(
f"One or both clients did not receive enough states within 10 seconds. "
f"Client1: {len(states1)}, Client2: {len(states2)}"
)
# Verify both clients received states successfully
assert count1 >= 20, (
f"Client 1 should have received at least 20 states, got {count1}"
)
assert count2 >= 20, (
f"Client 2 should have received at least 20 states, got {count2}"
)
# Verify both clients received the same entity keys (same device state)
common_keys = set(states1.keys()) & set(states2.keys())
assert len(common_keys) >= 20, (
f"Expected at least 20 common entity keys, got {len(common_keys)}"
)