mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-25 13:13:48 +01:00 
			
		
		
		
	Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
		
			
				
	
	
		
			661 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			661 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """Tests for storage_json.py path functions."""
 | |
| 
 | |
| from datetime import datetime
 | |
| import json
 | |
| from pathlib import Path
 | |
| import sys
 | |
| from unittest.mock import MagicMock, Mock, patch
 | |
| 
 | |
| import pytest
 | |
| 
 | |
| from esphome import storage_json
 | |
| from esphome.const import CONF_DISABLED, CONF_MDNS
 | |
| from esphome.core import CORE
 | |
| 
 | |
| 
 | |
| def test_storage_path(setup_core: Path) -> None:
 | |
|     """Test storage_path returns correct path for current config."""
 | |
|     CORE.config_path = setup_core / "my_device.yaml"
 | |
| 
 | |
|     result = storage_json.storage_path()
 | |
| 
 | |
|     data_dir = Path(CORE.data_dir)
 | |
|     expected = data_dir / "storage" / "my_device.yaml.json"
 | |
|     assert result == expected
 | |
| 
 | |
| 
 | |
| def test_ext_storage_path(setup_core: Path) -> None:
 | |
|     """Test ext_storage_path returns correct path for given filename."""
 | |
|     result = storage_json.ext_storage_path("other_device.yaml")
 | |
| 
 | |
|     data_dir = Path(CORE.data_dir)
 | |
|     expected = data_dir / "storage" / "other_device.yaml.json"
 | |
|     assert result == expected
 | |
| 
 | |
| 
 | |
| def test_ext_storage_path_handles_various_extensions(setup_core: Path) -> None:
 | |
|     """Test ext_storage_path works with different file extensions."""
 | |
|     result_yml = storage_json.ext_storage_path("device.yml")
 | |
|     assert str(result_yml).endswith("device.yml.json")
 | |
| 
 | |
|     result_no_ext = storage_json.ext_storage_path("device")
 | |
|     assert str(result_no_ext).endswith("device.json")
 | |
| 
 | |
|     result_path = storage_json.ext_storage_path("my/device.yaml")
 | |
|     assert str(result_path).endswith("device.yaml.json")
 | |
| 
 | |
| 
 | |
| def test_esphome_storage_path(setup_core: Path) -> None:
 | |
|     """Test esphome_storage_path returns correct path."""
 | |
|     result = storage_json.esphome_storage_path()
 | |
| 
 | |
|     data_dir = Path(CORE.data_dir)
 | |
|     expected = data_dir / "esphome.json"
 | |
|     assert result == expected
 | |
| 
 | |
| 
 | |
| def test_ignored_devices_storage_path(setup_core: Path) -> None:
 | |
|     """Test ignored_devices_storage_path returns correct path."""
 | |
|     result = storage_json.ignored_devices_storage_path()
 | |
| 
 | |
|     data_dir = Path(CORE.data_dir)
 | |
|     expected = data_dir / "ignored-devices.json"
 | |
|     assert result == expected
 | |
| 
 | |
| 
 | |
| def test_trash_storage_path(setup_core: Path) -> None:
 | |
|     """Test trash_storage_path returns correct path."""
 | |
|     CORE.config_path = setup_core / "configs" / "device.yaml"
 | |
| 
 | |
|     result = storage_json.trash_storage_path()
 | |
| 
 | |
|     expected = setup_core / "configs" / "trash"
 | |
|     assert result == expected
 | |
| 
 | |
| 
 | |
| def test_archive_storage_path(setup_core: Path) -> None:
 | |
|     """Test archive_storage_path returns correct path."""
 | |
|     CORE.config_path = setup_core / "configs" / "device.yaml"
 | |
| 
 | |
|     result = storage_json.archive_storage_path()
 | |
| 
 | |
|     expected = setup_core / "configs" / "archive"
 | |
|     assert result == expected
 | |
| 
 | |
| 
 | |
| def test_storage_path_with_subdirectory(setup_core: Path) -> None:
 | |
|     """Test storage paths work correctly when config is in subdirectory."""
 | |
|     subdir = setup_core / "configs" / "basement"
 | |
|     subdir.mkdir(parents=True, exist_ok=True)
 | |
|     CORE.config_path = subdir / "sensor.yaml"
 | |
| 
 | |
|     result = storage_json.storage_path()
 | |
| 
 | |
|     data_dir = Path(CORE.data_dir)
 | |
|     expected = data_dir / "storage" / "sensor.yaml.json"
 | |
|     assert result == expected
 | |
| 
 | |
| 
 | |
| def test_storage_json_firmware_bin_path_property(setup_core: Path) -> None:
 | |
|     """Test StorageJSON firmware_bin_path property."""
 | |
|     storage = storage_json.StorageJSON(
 | |
|         storage_version=1,
 | |
|         name="test_device",
 | |
|         friendly_name="Test Device",
 | |
|         comment=None,
 | |
|         esphome_version="2024.1.0",
 | |
|         src_version=None,
 | |
|         address="192.168.1.100",
 | |
|         web_port=80,
 | |
|         target_platform="ESP32",
 | |
|         build_path="build/test_device",
 | |
|         firmware_bin_path="/path/to/firmware.bin",
 | |
|         loaded_integrations={"wifi", "api"},
 | |
|         loaded_platforms=set(),
 | |
|         no_mdns=False,
 | |
|     )
 | |
| 
 | |
|     assert storage.firmware_bin_path == "/path/to/firmware.bin"
 | |
| 
 | |
| 
 | |
| def test_storage_json_save_creates_directory(
 | |
|     setup_core: Path, tmp_path: Path, mock_write_file_if_changed: Mock
 | |
| ) -> None:
 | |
|     """Test StorageJSON.save creates storage directory if it doesn't exist."""
 | |
|     storage_dir = tmp_path / "new_data" / "storage"
 | |
|     storage_file = storage_dir / "test.json"
 | |
| 
 | |
|     assert not storage_dir.exists()
 | |
| 
 | |
|     storage = storage_json.StorageJSON(
 | |
|         storage_version=1,
 | |
|         name="test",
 | |
|         friendly_name="Test",
 | |
|         comment=None,
 | |
|         esphome_version="2024.1.0",
 | |
|         src_version=None,
 | |
|         address="test.local",
 | |
|         web_port=None,
 | |
|         target_platform="ESP8266",
 | |
|         build_path=None,
 | |
|         firmware_bin_path=None,
 | |
|         loaded_integrations=set(),
 | |
|         loaded_platforms=set(),
 | |
|         no_mdns=False,
 | |
|     )
 | |
| 
 | |
|     storage.save(str(storage_file))
 | |
|     mock_write_file_if_changed.assert_called_once()
 | |
|     call_args = mock_write_file_if_changed.call_args[0]
 | |
|     assert call_args[0] == str(storage_file)
 | |
| 
 | |
| 
 | |
| def test_storage_json_from_wizard(setup_core: Path) -> None:
 | |
|     """Test StorageJSON.from_wizard creates correct storage object."""
 | |
|     storage = storage_json.StorageJSON.from_wizard(
 | |
|         name="my_device",
 | |
|         friendly_name="My Device",
 | |
|         address="my_device.local",
 | |
|         platform="ESP32",
 | |
|     )
 | |
| 
 | |
|     assert storage.name == "my_device"
 | |
|     assert storage.friendly_name == "My Device"
 | |
|     assert storage.address == "my_device.local"
 | |
|     assert storage.target_platform == "ESP32"
 | |
|     assert storage.build_path is None
 | |
|     assert storage.firmware_bin_path is None
 | |
| 
 | |
| 
 | |
| @pytest.mark.skipif(sys.platform == "win32", reason="HA addons don't run on Windows")
 | |
| @patch("esphome.core.is_ha_addon")
 | |
| def test_storage_paths_with_ha_addon(mock_is_ha_addon: bool, tmp_path: Path) -> None:
 | |
|     """Test storage paths when running as Home Assistant addon."""
 | |
|     mock_is_ha_addon.return_value = True
 | |
| 
 | |
|     CORE.config_path = tmp_path / "test.yaml"
 | |
| 
 | |
|     result = storage_json.storage_path()
 | |
|     # When is_ha_addon is True, CORE.data_dir returns "/data"
 | |
|     # This is the standard mount point for HA addon containers
 | |
|     expected = Path("/data") / "storage" / "test.yaml.json"
 | |
|     assert result == expected
 | |
| 
 | |
|     result = storage_json.esphome_storage_path()
 | |
|     expected = Path("/data") / "esphome.json"
 | |
|     assert result == expected
 | |
| 
 | |
| 
 | |
| def test_storage_json_as_dict() -> None:
 | |
|     """Test StorageJSON.as_dict returns correct dictionary."""
 | |
|     storage = storage_json.StorageJSON(
 | |
|         storage_version=1,
 | |
|         name="test_device",
 | |
|         friendly_name="Test Device",
 | |
|         comment="Test comment",
 | |
|         esphome_version="2024.1.0",
 | |
|         src_version=1,
 | |
|         address="192.168.1.100",
 | |
|         web_port=80,
 | |
|         target_platform="ESP32",
 | |
|         build_path="/path/to/build",
 | |
|         firmware_bin_path="/path/to/firmware.bin",
 | |
|         loaded_integrations={"wifi", "api", "ota"},
 | |
|         loaded_platforms={"sensor", "binary_sensor"},
 | |
|         no_mdns=True,
 | |
|         framework="arduino",
 | |
|         core_platform="esp32",
 | |
|     )
 | |
| 
 | |
|     result = storage.as_dict()
 | |
| 
 | |
|     assert result["storage_version"] == 1
 | |
|     assert result["name"] == "test_device"
 | |
|     assert result["friendly_name"] == "Test Device"
 | |
|     assert result["comment"] == "Test comment"
 | |
|     assert result["esphome_version"] == "2024.1.0"
 | |
|     assert result["src_version"] == 1
 | |
|     assert result["address"] == "192.168.1.100"
 | |
|     assert result["web_port"] == 80
 | |
|     assert result["esp_platform"] == "ESP32"
 | |
|     assert result["build_path"] == "/path/to/build"
 | |
|     assert result["firmware_bin_path"] == "/path/to/firmware.bin"
 | |
|     assert "api" in result["loaded_integrations"]
 | |
|     assert "wifi" in result["loaded_integrations"]
 | |
|     assert "ota" in result["loaded_integrations"]
 | |
|     assert result["loaded_integrations"] == sorted(
 | |
|         ["wifi", "api", "ota"]
 | |
|     )  # Should be sorted
 | |
|     assert "sensor" in result["loaded_platforms"]
 | |
|     assert result["loaded_platforms"] == sorted(
 | |
|         ["sensor", "binary_sensor"]
 | |
|     )  # Should be sorted
 | |
|     assert result["no_mdns"] is True
 | |
|     assert result["framework"] == "arduino"
 | |
|     assert result["core_platform"] == "esp32"
 | |
| 
 | |
| 
 | |
| def test_storage_json_to_json() -> None:
 | |
|     """Test StorageJSON.to_json returns valid JSON string."""
 | |
|     storage = storage_json.StorageJSON(
 | |
|         storage_version=1,
 | |
|         name="test",
 | |
|         friendly_name="Test",
 | |
|         comment=None,
 | |
|         esphome_version="2024.1.0",
 | |
|         src_version=None,
 | |
|         address="test.local",
 | |
|         web_port=None,
 | |
|         target_platform="ESP8266",
 | |
|         build_path=None,
 | |
|         firmware_bin_path=None,
 | |
|         loaded_integrations=set(),
 | |
|         loaded_platforms=set(),
 | |
|         no_mdns=False,
 | |
|     )
 | |
| 
 | |
|     json_str = storage.to_json()
 | |
| 
 | |
|     # Should be valid JSON
 | |
|     parsed = json.loads(json_str)
 | |
|     assert parsed["name"] == "test"
 | |
|     assert parsed["storage_version"] == 1
 | |
| 
 | |
|     # Should end with newline
 | |
|     assert json_str.endswith("\n")
 | |
| 
 | |
| 
 | |
| def test_storage_json_save(tmp_path: Path) -> None:
 | |
|     """Test StorageJSON.save writes file correctly."""
 | |
|     storage = storage_json.StorageJSON(
 | |
|         storage_version=1,
 | |
|         name="test",
 | |
|         friendly_name="Test",
 | |
|         comment=None,
 | |
|         esphome_version="2024.1.0",
 | |
|         src_version=None,
 | |
|         address="test.local",
 | |
|         web_port=None,
 | |
|         target_platform="ESP32",
 | |
|         build_path=None,
 | |
|         firmware_bin_path=None,
 | |
|         loaded_integrations=set(),
 | |
|         loaded_platforms=set(),
 | |
|         no_mdns=False,
 | |
|     )
 | |
| 
 | |
|     save_path = tmp_path / "test.json"
 | |
| 
 | |
|     with patch("esphome.storage_json.write_file_if_changed") as mock_write:
 | |
|         storage.save(str(save_path))
 | |
|         mock_write.assert_called_once_with(str(save_path), storage.to_json())
 | |
| 
 | |
| 
 | |
| def test_storage_json_from_esphome_core(setup_core: Path) -> None:
 | |
|     """Test StorageJSON.from_esphome_core creates correct storage object."""
 | |
|     # Mock CORE object
 | |
|     mock_core = MagicMock()
 | |
|     mock_core.name = "my_device"
 | |
|     mock_core.friendly_name = "My Device"
 | |
|     mock_core.comment = "A test device"
 | |
|     mock_core.address = "192.168.1.50"
 | |
|     mock_core.web_port = 8080
 | |
|     mock_core.target_platform = "esp32"
 | |
|     mock_core.is_esp32 = True
 | |
|     mock_core.build_path = "/build/my_device"
 | |
|     mock_core.firmware_bin = "/build/my_device/firmware.bin"
 | |
|     mock_core.loaded_integrations = {"wifi", "api"}
 | |
|     mock_core.loaded_platforms = {"sensor"}
 | |
|     mock_core.config = {CONF_MDNS: {CONF_DISABLED: True}}
 | |
|     mock_core.target_framework = "esp-idf"
 | |
| 
 | |
|     with patch("esphome.components.esp32.get_esp32_variant") as mock_variant:
 | |
|         mock_variant.return_value = "ESP32-C3"
 | |
| 
 | |
|         result = storage_json.StorageJSON.from_esphome_core(mock_core, old=None)
 | |
| 
 | |
|     assert result.name == "my_device"
 | |
|     assert result.friendly_name == "My Device"
 | |
|     assert result.comment == "A test device"
 | |
|     assert result.address == "192.168.1.50"
 | |
|     assert result.web_port == 8080
 | |
|     assert result.target_platform == "ESP32-C3"
 | |
|     assert result.build_path == "/build/my_device"
 | |
|     assert result.firmware_bin_path == "/build/my_device/firmware.bin"
 | |
|     assert result.loaded_integrations == {"wifi", "api"}
 | |
|     assert result.loaded_platforms == {"sensor"}
 | |
|     assert result.no_mdns is True
 | |
|     assert result.framework == "esp-idf"
 | |
|     assert result.core_platform == "esp32"
 | |
| 
 | |
| 
 | |
| def test_storage_json_from_esphome_core_mdns_enabled(setup_core: Path) -> None:
 | |
|     """Test from_esphome_core with mDNS enabled."""
 | |
|     mock_core = MagicMock()
 | |
|     mock_core.name = "test"
 | |
|     mock_core.friendly_name = "Test"
 | |
|     mock_core.comment = None
 | |
|     mock_core.address = "test.local"
 | |
|     mock_core.web_port = None
 | |
|     mock_core.target_platform = "esp8266"
 | |
|     mock_core.is_esp32 = False
 | |
|     mock_core.build_path = "/build"
 | |
|     mock_core.firmware_bin = "/build/firmware.bin"
 | |
|     mock_core.loaded_integrations = set()
 | |
|     mock_core.loaded_platforms = set()
 | |
|     mock_core.config = {}  # No MDNS config means enabled
 | |
|     mock_core.target_framework = "arduino"
 | |
| 
 | |
|     result = storage_json.StorageJSON.from_esphome_core(mock_core, old=None)
 | |
| 
 | |
|     assert result.no_mdns is False
 | |
| 
 | |
| 
 | |
| def test_storage_json_load_valid_file(tmp_path: Path) -> None:
 | |
|     """Test StorageJSON.load with valid JSON file."""
 | |
|     storage_data = {
 | |
|         "storage_version": 1,
 | |
|         "name": "loaded_device",
 | |
|         "friendly_name": "Loaded Device",
 | |
|         "comment": "Loaded from file",
 | |
|         "esphome_version": "2024.1.0",
 | |
|         "src_version": 2,
 | |
|         "address": "10.0.0.1",
 | |
|         "web_port": 8080,
 | |
|         "esp_platform": "ESP32",
 | |
|         "build_path": "/loaded/build",
 | |
|         "firmware_bin_path": "/loaded/firmware.bin",
 | |
|         "loaded_integrations": ["wifi", "api"],
 | |
|         "loaded_platforms": ["sensor"],
 | |
|         "no_mdns": True,
 | |
|         "framework": "arduino",
 | |
|         "core_platform": "esp32",
 | |
|     }
 | |
| 
 | |
|     file_path = tmp_path / "storage.json"
 | |
|     file_path.write_text(json.dumps(storage_data))
 | |
| 
 | |
|     result = storage_json.StorageJSON.load(file_path)
 | |
| 
 | |
|     assert result is not None
 | |
|     assert result.name == "loaded_device"
 | |
|     assert result.friendly_name == "Loaded Device"
 | |
|     assert result.comment == "Loaded from file"
 | |
|     assert result.esphome_version == "2024.1.0"
 | |
|     assert result.src_version == 2
 | |
|     assert result.address == "10.0.0.1"
 | |
|     assert result.web_port == 8080
 | |
|     assert result.target_platform == "ESP32"
 | |
|     assert result.build_path == Path("/loaded/build")
 | |
|     assert result.firmware_bin_path == Path("/loaded/firmware.bin")
 | |
|     assert result.loaded_integrations == {"wifi", "api"}
 | |
|     assert result.loaded_platforms == {"sensor"}
 | |
|     assert result.no_mdns is True
 | |
|     assert result.framework == "arduino"
 | |
|     assert result.core_platform == "esp32"
 | |
| 
 | |
| 
 | |
| def test_storage_json_load_invalid_file(tmp_path: Path) -> None:
 | |
|     """Test StorageJSON.load with invalid JSON file."""
 | |
|     file_path = tmp_path / "invalid.json"
 | |
|     file_path.write_text("not valid json{")
 | |
| 
 | |
|     result = storage_json.StorageJSON.load(file_path)
 | |
| 
 | |
|     assert result is None
 | |
| 
 | |
| 
 | |
| def test_storage_json_load_nonexistent_file() -> None:
 | |
|     """Test StorageJSON.load with non-existent file."""
 | |
|     result = storage_json.StorageJSON.load("/nonexistent/file.json")
 | |
| 
 | |
|     assert result is None
 | |
| 
 | |
| 
 | |
| def test_storage_json_equality() -> None:
 | |
|     """Test StorageJSON equality comparison."""
 | |
|     storage1 = storage_json.StorageJSON(
 | |
|         storage_version=1,
 | |
|         name="test",
 | |
|         friendly_name="Test",
 | |
|         comment=None,
 | |
|         esphome_version="2024.1.0",
 | |
|         src_version=1,
 | |
|         address="test.local",
 | |
|         web_port=80,
 | |
|         target_platform="ESP32",
 | |
|         build_path="/build",
 | |
|         firmware_bin_path="/firmware.bin",
 | |
|         loaded_integrations={"wifi"},
 | |
|         loaded_platforms=set(),
 | |
|         no_mdns=False,
 | |
|     )
 | |
| 
 | |
|     storage2 = storage_json.StorageJSON(
 | |
|         storage_version=1,
 | |
|         name="test",
 | |
|         friendly_name="Test",
 | |
|         comment=None,
 | |
|         esphome_version="2024.1.0",
 | |
|         src_version=1,
 | |
|         address="test.local",
 | |
|         web_port=80,
 | |
|         target_platform="ESP32",
 | |
|         build_path="/build",
 | |
|         firmware_bin_path="/firmware.bin",
 | |
|         loaded_integrations={"wifi"},
 | |
|         loaded_platforms=set(),
 | |
|         no_mdns=False,
 | |
|     )
 | |
| 
 | |
|     storage3 = storage_json.StorageJSON(
 | |
|         storage_version=1,
 | |
|         name="different",  # Different name
 | |
|         friendly_name="Test",
 | |
|         comment=None,
 | |
|         esphome_version="2024.1.0",
 | |
|         src_version=1,
 | |
|         address="test.local",
 | |
|         web_port=80,
 | |
|         target_platform="ESP32",
 | |
|         build_path="/build",
 | |
|         firmware_bin_path="/firmware.bin",
 | |
|         loaded_integrations={"wifi"},
 | |
|         loaded_platforms=set(),
 | |
|         no_mdns=False,
 | |
|     )
 | |
| 
 | |
|     assert storage1 == storage2
 | |
|     assert storage1 != storage3
 | |
|     assert storage1 != "not a storage object"
 | |
| 
 | |
| 
 | |
| def test_esphome_storage_json_as_dict() -> None:
 | |
|     """Test EsphomeStorageJSON.as_dict returns correct dictionary."""
 | |
|     storage = storage_json.EsphomeStorageJSON(
 | |
|         storage_version=1,
 | |
|         cookie_secret="secret123",
 | |
|         last_update_check="2024-01-15T10:30:00",
 | |
|         remote_version="2024.1.1",
 | |
|     )
 | |
| 
 | |
|     result = storage.as_dict()
 | |
| 
 | |
|     assert result["storage_version"] == 1
 | |
|     assert result["cookie_secret"] == "secret123"
 | |
|     assert result["last_update_check"] == "2024-01-15T10:30:00"
 | |
|     assert result["remote_version"] == "2024.1.1"
 | |
| 
 | |
| 
 | |
| def test_esphome_storage_json_last_update_check_property() -> None:
 | |
|     """Test EsphomeStorageJSON.last_update_check property."""
 | |
|     storage = storage_json.EsphomeStorageJSON(
 | |
|         storage_version=1,
 | |
|         cookie_secret="secret",
 | |
|         last_update_check="2024-01-15T10:30:00",
 | |
|         remote_version=None,
 | |
|     )
 | |
| 
 | |
|     # Test getter
 | |
|     result = storage.last_update_check
 | |
|     assert isinstance(result, datetime)
 | |
|     assert result.year == 2024
 | |
|     assert result.month == 1
 | |
|     assert result.day == 15
 | |
|     assert result.hour == 10
 | |
|     assert result.minute == 30
 | |
| 
 | |
|     # Test setter
 | |
|     new_date = datetime(2024, 2, 20, 15, 45, 30)
 | |
|     storage.last_update_check = new_date
 | |
|     assert storage.last_update_check_str == "2024-02-20T15:45:30"
 | |
| 
 | |
| 
 | |
| def test_esphome_storage_json_last_update_check_invalid() -> None:
 | |
|     """Test EsphomeStorageJSON.last_update_check with invalid date."""
 | |
|     storage = storage_json.EsphomeStorageJSON(
 | |
|         storage_version=1,
 | |
|         cookie_secret="secret",
 | |
|         last_update_check="invalid date",
 | |
|         remote_version=None,
 | |
|     )
 | |
| 
 | |
|     result = storage.last_update_check
 | |
|     assert result is None
 | |
| 
 | |
| 
 | |
| def test_esphome_storage_json_to_json() -> None:
 | |
|     """Test EsphomeStorageJSON.to_json returns valid JSON string."""
 | |
|     storage = storage_json.EsphomeStorageJSON(
 | |
|         storage_version=1,
 | |
|         cookie_secret="mysecret",
 | |
|         last_update_check="2024-01-15T10:30:00",
 | |
|         remote_version="2024.1.1",
 | |
|     )
 | |
| 
 | |
|     json_str = storage.to_json()
 | |
| 
 | |
|     # Should be valid JSON
 | |
|     parsed = json.loads(json_str)
 | |
|     assert parsed["cookie_secret"] == "mysecret"
 | |
|     assert parsed["storage_version"] == 1
 | |
| 
 | |
|     # Should end with newline
 | |
|     assert json_str.endswith("\n")
 | |
| 
 | |
| 
 | |
| def test_esphome_storage_json_save(tmp_path: Path) -> None:
 | |
|     """Test EsphomeStorageJSON.save writes file correctly."""
 | |
|     storage = storage_json.EsphomeStorageJSON(
 | |
|         storage_version=1,
 | |
|         cookie_secret="secret",
 | |
|         last_update_check=None,
 | |
|         remote_version=None,
 | |
|     )
 | |
| 
 | |
|     save_path = tmp_path / "esphome.json"
 | |
| 
 | |
|     with patch("esphome.storage_json.write_file_if_changed") as mock_write:
 | |
|         storage.save(str(save_path))
 | |
|         mock_write.assert_called_once_with(str(save_path), storage.to_json())
 | |
| 
 | |
| 
 | |
| def test_esphome_storage_json_load_valid_file(tmp_path: Path) -> None:
 | |
|     """Test EsphomeStorageJSON.load with valid JSON file."""
 | |
|     storage_data = {
 | |
|         "storage_version": 1,
 | |
|         "cookie_secret": "loaded_secret",
 | |
|         "last_update_check": "2024-01-20T14:30:00",
 | |
|         "remote_version": "2024.1.2",
 | |
|     }
 | |
| 
 | |
|     file_path = tmp_path / "esphome.json"
 | |
|     file_path.write_text(json.dumps(storage_data))
 | |
| 
 | |
|     result = storage_json.EsphomeStorageJSON.load(str(file_path))
 | |
| 
 | |
|     assert result is not None
 | |
|     assert result.storage_version == 1
 | |
|     assert result.cookie_secret == "loaded_secret"
 | |
|     assert result.last_update_check_str == "2024-01-20T14:30:00"
 | |
|     assert result.remote_version == "2024.1.2"
 | |
| 
 | |
| 
 | |
| def test_esphome_storage_json_load_invalid_file(tmp_path: Path) -> None:
 | |
|     """Test EsphomeStorageJSON.load with invalid JSON file."""
 | |
|     file_path = tmp_path / "invalid.json"
 | |
|     file_path.write_text("not valid json{")
 | |
| 
 | |
|     result = storage_json.EsphomeStorageJSON.load(str(file_path))
 | |
| 
 | |
|     assert result is None
 | |
| 
 | |
| 
 | |
| def test_esphome_storage_json_load_nonexistent_file() -> None:
 | |
|     """Test EsphomeStorageJSON.load with non-existent file."""
 | |
|     result = storage_json.EsphomeStorageJSON.load("/nonexistent/file.json")
 | |
| 
 | |
|     assert result is None
 | |
| 
 | |
| 
 | |
| def test_esphome_storage_json_get_default() -> None:
 | |
|     """Test EsphomeStorageJSON.get_default creates default storage."""
 | |
|     with patch("esphome.storage_json.os.urandom") as mock_urandom:
 | |
|         # Mock urandom to return predictable bytes
 | |
|         mock_urandom.return_value = b"test" * 16  # 64 bytes
 | |
| 
 | |
|         result = storage_json.EsphomeStorageJSON.get_default()
 | |
| 
 | |
|     assert result.storage_version == 1
 | |
|     assert len(result.cookie_secret) == 128  # 64 bytes hex = 128 chars
 | |
|     assert result.last_update_check is None
 | |
|     assert result.remote_version is None
 | |
| 
 | |
| 
 | |
| def test_esphome_storage_json_equality() -> None:
 | |
|     """Test EsphomeStorageJSON equality comparison."""
 | |
|     storage1 = storage_json.EsphomeStorageJSON(
 | |
|         storage_version=1,
 | |
|         cookie_secret="secret",
 | |
|         last_update_check="2024-01-15T10:30:00",
 | |
|         remote_version="2024.1.1",
 | |
|     )
 | |
| 
 | |
|     storage2 = storage_json.EsphomeStorageJSON(
 | |
|         storage_version=1,
 | |
|         cookie_secret="secret",
 | |
|         last_update_check="2024-01-15T10:30:00",
 | |
|         remote_version="2024.1.1",
 | |
|     )
 | |
| 
 | |
|     storage3 = storage_json.EsphomeStorageJSON(
 | |
|         storage_version=1,
 | |
|         cookie_secret="different",  # Different secret
 | |
|         last_update_check="2024-01-15T10:30:00",
 | |
|         remote_version="2024.1.1",
 | |
|     )
 | |
| 
 | |
|     assert storage1 == storage2
 | |
|     assert storage1 != storage3
 | |
|     assert storage1 != "not a storage object"
 | |
| 
 | |
| 
 | |
| def test_storage_json_load_legacy_esphomeyaml_version(tmp_path: Path) -> None:
 | |
|     """Test loading storage with legacy esphomeyaml_version field."""
 | |
|     storage_data = {
 | |
|         "storage_version": 1,
 | |
|         "name": "legacy_device",
 | |
|         "friendly_name": "Legacy Device",
 | |
|         "esphomeyaml_version": "1.14.0",  # Legacy field name
 | |
|         "address": "legacy.local",
 | |
|         "esp_platform": "ESP8266",
 | |
|     }
 | |
| 
 | |
|     file_path = tmp_path / "legacy.json"
 | |
|     file_path.write_text(json.dumps(storage_data))
 | |
| 
 | |
|     result = storage_json.StorageJSON.load(file_path)
 | |
| 
 | |
|     assert result is not None
 | |
|     assert result.esphome_version == "1.14.0"  # Should map to esphome_version
 |