mirror of
https://github.com/esphome/esphome.git
synced 2026-10-07 11:26:39 +00:00
Merge remote-tracking branch 'upstream/dev' into integration
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
uart:
|
||||
rx_pin: ${rx_pin}
|
||||
baud_rate: 9600
|
||||
|
||||
sensor:
|
||||
- platform: wts01
|
||||
id: wts01_sensor
|
||||
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO16
|
||||
rx_pin: GPIO17
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO6
|
||||
rx_pin: GPIO7
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO6
|
||||
rx_pin: GPIO7
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO16
|
||||
rx_pin: GPIO17
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO1
|
||||
rx_pin: GPIO3
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO0
|
||||
rx_pin: GPIO1
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
|
||||
from aioesphomeapi import APIConnectionError
|
||||
from aioesphomeapi import APIConnectionError, InvalidAuthAPIError
|
||||
import pytest
|
||||
|
||||
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||
@@ -48,6 +48,22 @@ async def test_host_mode_api_password(
|
||||
assert len(states) > 0
|
||||
|
||||
# Test with wrong password - should fail
|
||||
with pytest.raises(APIConnectionError, match="Invalid password"):
|
||||
async with api_client_connected(password="wrong_password"):
|
||||
pass # Should not reach here
|
||||
# Try connecting with wrong password
|
||||
try:
|
||||
async with api_client_connected(
|
||||
password="wrong_password", timeout=5
|
||||
) as client:
|
||||
# If we get here without exception, try to use the connection
|
||||
# which should fail if auth failed
|
||||
await client.device_info_and_list_entities()
|
||||
# If we successfully got device info and entities, auth didn't fail properly
|
||||
pytest.fail("Connection succeeded with wrong password")
|
||||
except (InvalidAuthAPIError, APIConnectionError) as e:
|
||||
# Expected - auth should fail
|
||||
# Accept either InvalidAuthAPIError or generic APIConnectionError
|
||||
# since the client might not always distinguish
|
||||
assert (
|
||||
"password" in str(e).lower()
|
||||
or "auth" in str(e).lower()
|
||||
or "invalid" in str(e).lower()
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ from esphome import platformio_api
|
||||
from esphome.__main__ import (
|
||||
Purpose,
|
||||
choose_upload_log_host,
|
||||
command_clean_platform,
|
||||
command_clean_all,
|
||||
command_rename,
|
||||
command_update_all,
|
||||
command_wizard,
|
||||
@@ -1857,33 +1857,31 @@ esp32:
|
||||
assert "can only concatenate str" not in clean_output
|
||||
|
||||
|
||||
def test_command_clean_platform_success(
|
||||
def test_command_clean_all_success(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test command_clean_platform when writer.clean_platform() succeeds."""
|
||||
args = MockArgs()
|
||||
config = {}
|
||||
"""Test command_clean_all when writer.clean_all() succeeds."""
|
||||
args = MockArgs(configuration=["/path/to/config1", "/path/to/config2"])
|
||||
|
||||
# Set logger level to capture INFO messages
|
||||
with (
|
||||
caplog.at_level(logging.INFO),
|
||||
patch("esphome.writer.clean_platform") as mock_clean_platform,
|
||||
patch("esphome.writer.clean_all") as mock_clean_all,
|
||||
):
|
||||
result = command_clean_platform(args, config)
|
||||
result = command_clean_all(args)
|
||||
|
||||
assert result == 0
|
||||
mock_clean_platform.assert_called_once()
|
||||
mock_clean_all.assert_called_once_with(["/path/to/config1", "/path/to/config2"])
|
||||
|
||||
# Check that success message was logged
|
||||
assert "Done!" in caplog.text
|
||||
|
||||
|
||||
def test_command_clean_platform_oserror(
|
||||
def test_command_clean_all_oserror(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test command_clean_platform when writer.clean_platform() raises OSError."""
|
||||
args = MockArgs()
|
||||
config = {}
|
||||
"""Test command_clean_all when writer.clean_all() raises OSError."""
|
||||
args = MockArgs(configuration=["/path/to/config1"])
|
||||
|
||||
# Create a mock OSError with a specific message
|
||||
mock_error = OSError("Permission denied: cannot delete directory")
|
||||
@@ -1891,30 +1889,27 @@ def test_command_clean_platform_oserror(
|
||||
# Set logger level to capture ERROR and INFO messages
|
||||
with (
|
||||
caplog.at_level(logging.INFO),
|
||||
patch(
|
||||
"esphome.writer.clean_platform", side_effect=mock_error
|
||||
) as mock_clean_platform,
|
||||
patch("esphome.writer.clean_all", side_effect=mock_error) as mock_clean_all,
|
||||
):
|
||||
result = command_clean_platform(args, config)
|
||||
result = command_clean_all(args)
|
||||
|
||||
assert result == 1
|
||||
mock_clean_platform.assert_called_once()
|
||||
mock_clean_all.assert_called_once_with(["/path/to/config1"])
|
||||
|
||||
# Check that error message was logged
|
||||
assert (
|
||||
"Error deleting platform files: Permission denied: cannot delete directory"
|
||||
"Error cleaning all files: Permission denied: cannot delete directory"
|
||||
in caplog.text
|
||||
)
|
||||
# Should not have success message
|
||||
assert "Done!" not in caplog.text
|
||||
|
||||
|
||||
def test_command_clean_platform_oserror_no_message(
|
||||
def test_command_clean_all_oserror_no_message(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test command_clean_platform when writer.clean_platform() raises OSError without message."""
|
||||
args = MockArgs()
|
||||
config = {}
|
||||
"""Test command_clean_all when writer.clean_all() raises OSError without message."""
|
||||
args = MockArgs(configuration=["/path/to/config1"])
|
||||
|
||||
# Create a mock OSError without a message
|
||||
mock_error = OSError()
|
||||
@@ -1922,34 +1917,33 @@ def test_command_clean_platform_oserror_no_message(
|
||||
# Set logger level to capture ERROR and INFO messages
|
||||
with (
|
||||
caplog.at_level(logging.INFO),
|
||||
patch(
|
||||
"esphome.writer.clean_platform", side_effect=mock_error
|
||||
) as mock_clean_platform,
|
||||
patch("esphome.writer.clean_all", side_effect=mock_error) as mock_clean_all,
|
||||
):
|
||||
result = command_clean_platform(args, config)
|
||||
result = command_clean_all(args)
|
||||
|
||||
assert result == 1
|
||||
mock_clean_platform.assert_called_once()
|
||||
mock_clean_all.assert_called_once_with(["/path/to/config1"])
|
||||
|
||||
# Check that error message was logged (should show empty string for OSError without message)
|
||||
assert "Error deleting platform files:" in caplog.text
|
||||
assert "Error cleaning all files:" in caplog.text
|
||||
# Should not have success message
|
||||
assert "Done!" not in caplog.text
|
||||
|
||||
|
||||
def test_command_clean_platform_args_and_config_ignored() -> None:
|
||||
"""Test that command_clean_platform ignores args and config parameters."""
|
||||
# Test with various args and config to ensure they don't affect the function
|
||||
args1 = MockArgs(name="test1", file="test.bin")
|
||||
config1 = {"wifi": {"ssid": "test"}}
|
||||
def test_command_clean_all_args_used() -> None:
|
||||
"""Test that command_clean_all uses args.configuration parameter."""
|
||||
# Test with different configuration paths
|
||||
args1 = MockArgs(configuration=["/path/to/config1"])
|
||||
args2 = MockArgs(configuration=["/path/to/config2", "/path/to/config3"])
|
||||
|
||||
args2 = MockArgs(name="test2", dashboard=True)
|
||||
config2 = {"api": {}, "ota": {}}
|
||||
|
||||
with patch("esphome.writer.clean_platform") as mock_clean_platform:
|
||||
result1 = command_clean_platform(args1, config1)
|
||||
result2 = command_clean_platform(args2, config2)
|
||||
with patch("esphome.writer.clean_all") as mock_clean_all:
|
||||
result1 = command_clean_all(args1)
|
||||
result2 = command_clean_all(args2)
|
||||
|
||||
assert result1 == 0
|
||||
assert result2 == 0
|
||||
assert mock_clean_platform.call_count == 2
|
||||
assert mock_clean_all.call_count == 2
|
||||
|
||||
# Verify the correct configuration paths were passed
|
||||
mock_clean_all.assert_any_call(["/path/to/config1"])
|
||||
mock_clean_all.assert_any_call(["/path/to/config2", "/path/to/config3"])
|
||||
|
||||
@@ -739,16 +739,24 @@ def test_write_cpp_with_duplicate_markers(
|
||||
|
||||
|
||||
@patch("esphome.writer.CORE")
|
||||
def test_clean_platform(
|
||||
def test_clean_all(
|
||||
mock_core: MagicMock,
|
||||
tmp_path: Path,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test clean_platform removes build and PlatformIO dirs."""
|
||||
# Create build directory
|
||||
build_dir = tmp_path / "build"
|
||||
build_dir.mkdir()
|
||||
(build_dir / "dummy.txt").write_text("x")
|
||||
"""Test clean_all removes build and PlatformIO dirs."""
|
||||
# Create build directories for multiple configurations
|
||||
config1_dir = tmp_path / "config1"
|
||||
config2_dir = tmp_path / "config2"
|
||||
config1_dir.mkdir()
|
||||
config2_dir.mkdir()
|
||||
|
||||
build_dir1 = config1_dir / ".esphome"
|
||||
build_dir2 = config2_dir / ".esphome"
|
||||
build_dir1.mkdir()
|
||||
build_dir2.mkdir()
|
||||
(build_dir1 / "dummy.txt").write_text("x")
|
||||
(build_dir2 / "dummy.txt").write_text("x")
|
||||
|
||||
# Create PlatformIO directories
|
||||
pio_cache = tmp_path / "pio_cache"
|
||||
@@ -759,9 +767,6 @@ def test_clean_platform(
|
||||
d.mkdir()
|
||||
(d / "keep").write_text("x")
|
||||
|
||||
# Setup CORE
|
||||
mock_core.build_path = build_dir
|
||||
|
||||
# Mock ProjectConfig
|
||||
with patch(
|
||||
"platformio.project.config.ProjectConfig.get_instance"
|
||||
@@ -781,13 +786,14 @@ def test_clean_platform(
|
||||
mock_config.get.side_effect = cfg_get
|
||||
|
||||
# Call
|
||||
from esphome.writer import clean_platform
|
||||
from esphome.writer import clean_all
|
||||
|
||||
with caplog.at_level("INFO"):
|
||||
clean_platform()
|
||||
clean_all([str(config1_dir), str(config2_dir)])
|
||||
|
||||
# Verify deletions
|
||||
assert not build_dir.exists()
|
||||
assert not build_dir1.exists()
|
||||
assert not build_dir2.exists()
|
||||
assert not pio_cache.exists()
|
||||
assert not pio_packages.exists()
|
||||
assert not pio_platforms.exists()
|
||||
@@ -795,7 +801,8 @@ def test_clean_platform(
|
||||
|
||||
# Verify logging mentions each
|
||||
assert "Deleting" in caplog.text
|
||||
assert str(build_dir) in caplog.text
|
||||
assert str(build_dir1) in caplog.text
|
||||
assert str(build_dir2) in caplog.text
|
||||
assert "PlatformIO cache" in caplog.text
|
||||
assert "PlatformIO packages" in caplog.text
|
||||
assert "PlatformIO platforms" in caplog.text
|
||||
@@ -803,28 +810,29 @@ def test_clean_platform(
|
||||
|
||||
|
||||
@patch("esphome.writer.CORE")
|
||||
def test_clean_platform_platformio_not_available(
|
||||
def test_clean_all_platformio_not_available(
|
||||
mock_core: MagicMock,
|
||||
tmp_path: Path,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test clean_platform when PlatformIO is not available."""
|
||||
# Build dir
|
||||
build_dir = tmp_path / "build"
|
||||
"""Test clean_all when PlatformIO is not available."""
|
||||
# Build dirs
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
build_dir = config_dir / ".esphome"
|
||||
build_dir.mkdir()
|
||||
mock_core.build_path = build_dir
|
||||
|
||||
# PlatformIO dirs that should remain untouched
|
||||
pio_cache = tmp_path / "pio_cache"
|
||||
pio_cache.mkdir()
|
||||
|
||||
from esphome.writer import clean_platform
|
||||
from esphome.writer import clean_all
|
||||
|
||||
with (
|
||||
patch.dict("sys.modules", {"platformio.project.config": None}),
|
||||
caplog.at_level("INFO"),
|
||||
):
|
||||
clean_platform()
|
||||
clean_all([str(config_dir)])
|
||||
|
||||
# Build dir removed, PlatformIO dirs remain
|
||||
assert not build_dir.exists()
|
||||
@@ -835,14 +843,15 @@ def test_clean_platform_platformio_not_available(
|
||||
|
||||
|
||||
@patch("esphome.writer.CORE")
|
||||
def test_clean_platform_partial_exists(
|
||||
def test_clean_all_partial_exists(
|
||||
mock_core: MagicMock,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test clean_platform when only build dir exists."""
|
||||
build_dir = tmp_path / "build"
|
||||
"""Test clean_all when only some build dirs exist."""
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
build_dir = config_dir / ".esphome"
|
||||
build_dir.mkdir()
|
||||
mock_core.build_path = build_dir
|
||||
|
||||
with patch(
|
||||
"platformio.project.config.ProjectConfig.get_instance"
|
||||
@@ -854,8 +863,8 @@ def test_clean_platform_partial_exists(
|
||||
tmp_path / "does_not_exist"
|
||||
)
|
||||
|
||||
from esphome.writer import clean_platform
|
||||
from esphome.writer import clean_all
|
||||
|
||||
clean_platform()
|
||||
clean_all([str(config_dir)])
|
||||
|
||||
assert not build_dir.exists()
|
||||
|
||||
Reference in New Issue
Block a user