mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 14:43:51 +00:00 
			
		
		
		
	[core] os.path -> Path (#10654)
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>
This commit is contained in:
		| @@ -22,7 +22,7 @@ def create_cache_key() -> tuple[int, int, float, int]: | ||||
| def setup_core(): | ||||
|     """Set up CORE for testing.""" | ||||
|     with tempfile.TemporaryDirectory() as tmpdir: | ||||
|         CORE.config_path = str(Path(tmpdir) / "test.yaml") | ||||
|         CORE.config_path = Path(tmpdir) / "test.yaml" | ||||
|         yield | ||||
|         CORE.reset() | ||||
|  | ||||
| @@ -44,7 +44,7 @@ async def dashboard_entries(mock_settings: MagicMock) -> DashboardEntries: | ||||
|  | ||||
| def test_dashboard_entry_path_initialization() -> None: | ||||
|     """Test DashboardEntry initializes with path correctly.""" | ||||
|     test_path = "/test/config/device.yaml" | ||||
|     test_path = Path("/test/config/device.yaml") | ||||
|     cache_key = create_cache_key() | ||||
|  | ||||
|     entry = DashboardEntry(test_path, cache_key) | ||||
| @@ -59,21 +59,21 @@ def test_dashboard_entry_path_with_absolute_path() -> None: | ||||
|     test_path = Path.cwd() / "absolute" / "path" / "to" / "config.yaml" | ||||
|     cache_key = create_cache_key() | ||||
|  | ||||
|     entry = DashboardEntry(str(test_path), cache_key) | ||||
|     entry = DashboardEntry(test_path, cache_key) | ||||
|  | ||||
|     assert entry.path == str(test_path) | ||||
|     assert Path(entry.path).is_absolute() | ||||
|     assert entry.path == test_path | ||||
|     assert entry.path.is_absolute() | ||||
|  | ||||
|  | ||||
| def test_dashboard_entry_path_with_relative_path() -> None: | ||||
|     """Test DashboardEntry handles relative paths.""" | ||||
|     test_path = "configs/device.yaml" | ||||
|     test_path = Path("configs/device.yaml") | ||||
|     cache_key = create_cache_key() | ||||
|  | ||||
|     entry = DashboardEntry(test_path, cache_key) | ||||
|  | ||||
|     assert entry.path == test_path | ||||
|     assert not Path(entry.path).is_absolute() | ||||
|     assert not entry.path.is_absolute() | ||||
|  | ||||
|  | ||||
| @pytest.mark.asyncio | ||||
| @@ -81,12 +81,12 @@ async def test_dashboard_entries_get_by_path( | ||||
|     dashboard_entries: DashboardEntries, | ||||
| ) -> None: | ||||
|     """Test getting entry by path.""" | ||||
|     test_path = "/test/config/device.yaml" | ||||
|     test_path = Path("/test/config/device.yaml") | ||||
|     entry = DashboardEntry(test_path, create_cache_key()) | ||||
|  | ||||
|     dashboard_entries._entries[test_path] = entry | ||||
|     dashboard_entries._entries[str(test_path)] = entry | ||||
|  | ||||
|     result = dashboard_entries.get(test_path) | ||||
|     result = dashboard_entries.get(str(test_path)) | ||||
|     assert result == entry | ||||
|  | ||||
|  | ||||
| @@ -104,12 +104,12 @@ async def test_dashboard_entries_path_normalization( | ||||
|     dashboard_entries: DashboardEntries, | ||||
| ) -> None: | ||||
|     """Test that paths are handled consistently.""" | ||||
|     path1 = "/test/config/device.yaml" | ||||
|     path1 = Path("/test/config/device.yaml") | ||||
|  | ||||
|     entry = DashboardEntry(path1, create_cache_key()) | ||||
|     dashboard_entries._entries[path1] = entry | ||||
|     dashboard_entries._entries[str(path1)] = entry | ||||
|  | ||||
|     result = dashboard_entries.get(path1) | ||||
|     result = dashboard_entries.get(str(path1)) | ||||
|     assert result == entry | ||||
|  | ||||
|  | ||||
| @@ -118,12 +118,12 @@ async def test_dashboard_entries_path_with_spaces( | ||||
|     dashboard_entries: DashboardEntries, | ||||
| ) -> None: | ||||
|     """Test handling paths with spaces.""" | ||||
|     test_path = "/test/config/my device.yaml" | ||||
|     test_path = Path("/test/config/my device.yaml") | ||||
|     entry = DashboardEntry(test_path, create_cache_key()) | ||||
|  | ||||
|     dashboard_entries._entries[test_path] = entry | ||||
|     dashboard_entries._entries[str(test_path)] = entry | ||||
|  | ||||
|     result = dashboard_entries.get(test_path) | ||||
|     result = dashboard_entries.get(str(test_path)) | ||||
|     assert result == entry | ||||
|     assert result.path == test_path | ||||
|  | ||||
| @@ -133,18 +133,18 @@ async def test_dashboard_entries_path_with_special_chars( | ||||
|     dashboard_entries: DashboardEntries, | ||||
| ) -> None: | ||||
|     """Test handling paths with special characters.""" | ||||
|     test_path = "/test/config/device-01_test.yaml" | ||||
|     test_path = Path("/test/config/device-01_test.yaml") | ||||
|     entry = DashboardEntry(test_path, create_cache_key()) | ||||
|  | ||||
|     dashboard_entries._entries[test_path] = entry | ||||
|     dashboard_entries._entries[str(test_path)] = entry | ||||
|  | ||||
|     result = dashboard_entries.get(test_path) | ||||
|     result = dashboard_entries.get(str(test_path)) | ||||
|     assert result == entry | ||||
|  | ||||
|  | ||||
| def test_dashboard_entries_windows_path() -> None: | ||||
|     """Test handling Windows-style paths.""" | ||||
|     test_path = r"C:\Users\test\esphome\device.yaml" | ||||
|     test_path = Path(r"C:\Users\test\esphome\device.yaml") | ||||
|     cache_key = create_cache_key() | ||||
|  | ||||
|     entry = DashboardEntry(test_path, cache_key) | ||||
| @@ -157,28 +157,28 @@ async def test_dashboard_entries_path_to_cache_key_mapping( | ||||
|     dashboard_entries: DashboardEntries, | ||||
| ) -> None: | ||||
|     """Test internal entries storage with paths and cache keys.""" | ||||
|     path1 = "/test/config/device1.yaml" | ||||
|     path2 = "/test/config/device2.yaml" | ||||
|     path1 = Path("/test/config/device1.yaml") | ||||
|     path2 = Path("/test/config/device2.yaml") | ||||
|  | ||||
|     entry1 = DashboardEntry(path1, create_cache_key()) | ||||
|     entry2 = DashboardEntry(path2, (1, 1, 1.0, 1)) | ||||
|  | ||||
|     dashboard_entries._entries[path1] = entry1 | ||||
|     dashboard_entries._entries[path2] = entry2 | ||||
|     dashboard_entries._entries[str(path1)] = entry1 | ||||
|     dashboard_entries._entries[str(path2)] = entry2 | ||||
|  | ||||
|     assert path1 in dashboard_entries._entries | ||||
|     assert path2 in dashboard_entries._entries | ||||
|     assert dashboard_entries._entries[path1].cache_key == create_cache_key() | ||||
|     assert dashboard_entries._entries[path2].cache_key == (1, 1, 1.0, 1) | ||||
|     assert str(path1) in dashboard_entries._entries | ||||
|     assert str(path2) in dashboard_entries._entries | ||||
|     assert dashboard_entries._entries[str(path1)].cache_key == create_cache_key() | ||||
|     assert dashboard_entries._entries[str(path2)].cache_key == (1, 1, 1.0, 1) | ||||
|  | ||||
|  | ||||
| def test_dashboard_entry_path_property() -> None: | ||||
|     """Test that path property returns expected value.""" | ||||
|     test_path = "/test/config/device.yaml" | ||||
|     test_path = Path("/test/config/device.yaml") | ||||
|     entry = DashboardEntry(test_path, create_cache_key()) | ||||
|  | ||||
|     assert entry.path == test_path | ||||
|     assert isinstance(entry.path, str) | ||||
|     assert isinstance(entry.path, Path) | ||||
|  | ||||
|  | ||||
| @pytest.mark.asyncio | ||||
| @@ -187,14 +187,14 @@ async def test_dashboard_entries_all_returns_entries_with_paths( | ||||
| ) -> None: | ||||
|     """Test that all() returns entries with their paths intact.""" | ||||
|     paths = [ | ||||
|         "/test/config/device1.yaml", | ||||
|         "/test/config/device2.yaml", | ||||
|         "/test/config/subfolder/device3.yaml", | ||||
|         Path("/test/config/device1.yaml"), | ||||
|         Path("/test/config/device2.yaml"), | ||||
|         Path("/test/config/subfolder/device3.yaml"), | ||||
|     ] | ||||
|  | ||||
|     for path in paths: | ||||
|         entry = DashboardEntry(path, create_cache_key()) | ||||
|         dashboard_entries._entries[path] = entry | ||||
|         dashboard_entries._entries[str(path)] = entry | ||||
|  | ||||
|     all_entries = dashboard_entries.async_all() | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
|  | ||||
| from __future__ import annotations | ||||
|  | ||||
| import os | ||||
| from pathlib import Path | ||||
| import tempfile | ||||
|  | ||||
| @@ -17,7 +16,7 @@ def dashboard_settings(tmp_path: Path) -> DashboardSettings: | ||||
|     settings = DashboardSettings() | ||||
|     # Resolve symlinks to ensure paths match | ||||
|     resolved_dir = tmp_path.resolve() | ||||
|     settings.config_dir = str(resolved_dir) | ||||
|     settings.config_dir = resolved_dir | ||||
|     settings.absolute_config_dir = resolved_dir | ||||
|     return settings | ||||
|  | ||||
| @@ -26,7 +25,7 @@ def test_rel_path_simple(dashboard_settings: DashboardSettings) -> None: | ||||
|     """Test rel_path with simple relative path.""" | ||||
|     result = dashboard_settings.rel_path("config.yaml") | ||||
|  | ||||
|     expected = str(Path(dashboard_settings.config_dir) / "config.yaml") | ||||
|     expected = dashboard_settings.config_dir / "config.yaml" | ||||
|     assert result == expected | ||||
|  | ||||
|  | ||||
| @@ -34,9 +33,7 @@ def test_rel_path_multiple_components(dashboard_settings: DashboardSettings) -> | ||||
|     """Test rel_path with multiple path components.""" | ||||
|     result = dashboard_settings.rel_path("subfolder", "device", "config.yaml") | ||||
|  | ||||
|     expected = str( | ||||
|         Path(dashboard_settings.config_dir) / "subfolder" / "device" / "config.yaml" | ||||
|     ) | ||||
|     expected = dashboard_settings.config_dir / "subfolder" / "device" / "config.yaml" | ||||
|     assert result == expected | ||||
|  | ||||
|  | ||||
| @@ -55,7 +52,7 @@ def test_rel_path_absolute_path_within_config( | ||||
|  | ||||
|     internal_path.touch() | ||||
|     result = dashboard_settings.rel_path("internal.yaml") | ||||
|     expected = str(Path(dashboard_settings.config_dir) / "internal.yaml") | ||||
|     expected = dashboard_settings.config_dir / "internal.yaml" | ||||
|     assert result == expected | ||||
|  | ||||
|  | ||||
| @@ -80,7 +77,7 @@ def test_rel_path_with_pathlib_path(dashboard_settings: DashboardSettings) -> No | ||||
|     path_obj = Path("subfolder") / "config.yaml" | ||||
|     result = dashboard_settings.rel_path(path_obj) | ||||
|  | ||||
|     expected = str(Path(dashboard_settings.config_dir) / "subfolder" / "config.yaml") | ||||
|     expected = dashboard_settings.config_dir / "subfolder" / "config.yaml" | ||||
|     assert result == expected | ||||
|  | ||||
|  | ||||
| @@ -93,9 +90,7 @@ def test_rel_path_normalizes_slashes(dashboard_settings: DashboardSettings) -> N | ||||
|     assert result1 == result2 | ||||
|  | ||||
|     # Also test that the result is as expected | ||||
|     expected = os.path.join( | ||||
|         dashboard_settings.config_dir, "folder", "subfolder", "file.yaml" | ||||
|     ) | ||||
|     expected = dashboard_settings.config_dir / "folder" / "subfolder" / "file.yaml" | ||||
|     assert result1 == expected | ||||
|  | ||||
|  | ||||
| @@ -103,7 +98,7 @@ def test_rel_path_handles_spaces(dashboard_settings: DashboardSettings) -> None: | ||||
|     """Test rel_path handles paths with spaces.""" | ||||
|     result = dashboard_settings.rel_path("my folder", "my config.yaml") | ||||
|  | ||||
|     expected = str(Path(dashboard_settings.config_dir) / "my folder" / "my config.yaml") | ||||
|     expected = dashboard_settings.config_dir / "my folder" / "my config.yaml" | ||||
|     assert result == expected | ||||
|  | ||||
|  | ||||
| @@ -111,15 +106,13 @@ def test_rel_path_handles_special_chars(dashboard_settings: DashboardSettings) - | ||||
|     """Test rel_path handles paths with special characters.""" | ||||
|     result = dashboard_settings.rel_path("device-01_test", "config.yaml") | ||||
|  | ||||
|     expected = str( | ||||
|         Path(dashboard_settings.config_dir) / "device-01_test" / "config.yaml" | ||||
|     ) | ||||
|     expected = dashboard_settings.config_dir / "device-01_test" / "config.yaml" | ||||
|     assert result == expected | ||||
|  | ||||
|  | ||||
| def test_config_dir_as_path_property(dashboard_settings: DashboardSettings) -> None: | ||||
|     """Test that config_dir can be accessed and used with Path operations.""" | ||||
|     config_path = Path(dashboard_settings.config_dir) | ||||
|     config_path = dashboard_settings.config_dir | ||||
|  | ||||
|     assert config_path.exists() | ||||
|     assert config_path.is_dir() | ||||
| @@ -141,7 +134,7 @@ def test_rel_path_symlink_inside_config(dashboard_settings: DashboardSettings) - | ||||
|     symlink = dashboard_settings.absolute_config_dir / "link.yaml" | ||||
|     symlink.symlink_to(target) | ||||
|     result = dashboard_settings.rel_path("link.yaml") | ||||
|     expected = str(Path(dashboard_settings.config_dir) / "link.yaml") | ||||
|     expected = dashboard_settings.config_dir / "link.yaml" | ||||
|     assert result == expected | ||||
|  | ||||
|  | ||||
| @@ -157,12 +150,12 @@ def test_rel_path_symlink_outside_config(dashboard_settings: DashboardSettings) | ||||
| def test_rel_path_with_none_arg(dashboard_settings: DashboardSettings) -> None: | ||||
|     """Test rel_path handles None arguments gracefully.""" | ||||
|     result = dashboard_settings.rel_path("None") | ||||
|     expected = str(Path(dashboard_settings.config_dir) / "None") | ||||
|     expected = dashboard_settings.config_dir / "None" | ||||
|     assert result == expected | ||||
|  | ||||
|  | ||||
| def test_rel_path_with_numeric_args(dashboard_settings: DashboardSettings) -> None: | ||||
|     """Test rel_path handles numeric arguments.""" | ||||
|     result = dashboard_settings.rel_path("123", "456.789") | ||||
|     expected = str(Path(dashboard_settings.config_dir) / "123" / "456.789") | ||||
|     expected = dashboard_settings.config_dir / "123" / "456.789" | ||||
|     assert result == expected | ||||
|   | ||||
| @@ -49,7 +49,7 @@ def mock_trash_storage_path(tmp_path: Path) -> Generator[MagicMock]: | ||||
|     """Fixture to mock trash_storage_path.""" | ||||
|     trash_dir = tmp_path / "trash" | ||||
|     with patch( | ||||
|         "esphome.dashboard.web_server.trash_storage_path", return_value=str(trash_dir) | ||||
|         "esphome.dashboard.web_server.trash_storage_path", return_value=trash_dir | ||||
|     ) as mock: | ||||
|         yield mock | ||||
|  | ||||
| @@ -60,7 +60,7 @@ def mock_archive_storage_path(tmp_path: Path) -> Generator[MagicMock]: | ||||
|     archive_dir = tmp_path / "archive" | ||||
|     with patch( | ||||
|         "esphome.dashboard.web_server.archive_storage_path", | ||||
|         return_value=str(archive_dir), | ||||
|         return_value=archive_dir, | ||||
|     ) as mock: | ||||
|         yield mock | ||||
|  | ||||
| @@ -257,7 +257,7 @@ async def test_download_binary_handler_with_file( | ||||
|     # Mock storage JSON | ||||
|     mock_storage = Mock() | ||||
|     mock_storage.name = "test_device" | ||||
|     mock_storage.firmware_bin_path = str(firmware_file) | ||||
|     mock_storage.firmware_bin_path = firmware_file | ||||
|     mock_storage_json.load.return_value = mock_storage | ||||
|  | ||||
|     response = await dashboard.fetch( | ||||
| @@ -289,7 +289,7 @@ async def test_download_binary_handler_compressed( | ||||
|     # Mock storage JSON | ||||
|     mock_storage = Mock() | ||||
|     mock_storage.name = "test_device" | ||||
|     mock_storage.firmware_bin_path = str(firmware_file) | ||||
|     mock_storage.firmware_bin_path = firmware_file | ||||
|     mock_storage_json.load.return_value = mock_storage | ||||
|  | ||||
|     response = await dashboard.fetch( | ||||
| @@ -321,7 +321,7 @@ async def test_download_binary_handler_custom_download_name( | ||||
|     # Mock storage JSON | ||||
|     mock_storage = Mock() | ||||
|     mock_storage.name = "test_device" | ||||
|     mock_storage.firmware_bin_path = str(firmware_file) | ||||
|     mock_storage.firmware_bin_path = firmware_file | ||||
|     mock_storage_json.load.return_value = mock_storage | ||||
|  | ||||
|     response = await dashboard.fetch( | ||||
| @@ -355,7 +355,7 @@ async def test_download_binary_handler_idedata_fallback( | ||||
|     # Mock storage JSON | ||||
|     mock_storage = Mock() | ||||
|     mock_storage.name = "test_device" | ||||
|     mock_storage.firmware_bin_path = str(firmware_file) | ||||
|     mock_storage.firmware_bin_path = firmware_file | ||||
|     mock_storage_json.load.return_value = mock_storage | ||||
|  | ||||
|     # Mock idedata response | ||||
| @@ -402,7 +402,7 @@ async def test_edit_request_handler_post_existing( | ||||
|     test_file.write_text("esphome:\n  name: original\n") | ||||
|  | ||||
|     # Configure the mock settings | ||||
|     mock_dashboard_settings.rel_path.return_value = str(test_file) | ||||
|     mock_dashboard_settings.rel_path.return_value = test_file | ||||
|     mock_dashboard_settings.absolute_config_dir = test_file.parent | ||||
|  | ||||
|     new_content = "esphome:\n  name: modified\n" | ||||
| @@ -426,7 +426,7 @@ async def test_unarchive_request_handler( | ||||
| ) -> None: | ||||
|     """Test the UnArchiveRequestHandler.post method.""" | ||||
|     # Set up an archived file | ||||
|     archive_dir = Path(mock_archive_storage_path.return_value) | ||||
|     archive_dir = mock_archive_storage_path.return_value | ||||
|     archive_dir.mkdir(parents=True, exist_ok=True) | ||||
|     archived_file = archive_dir / "archived.yaml" | ||||
|     archived_file.write_text("test content") | ||||
| @@ -435,7 +435,7 @@ async def test_unarchive_request_handler( | ||||
|     config_dir = tmp_path / "config" | ||||
|     config_dir.mkdir(parents=True, exist_ok=True) | ||||
|     destination_file = config_dir / "archived.yaml" | ||||
|     mock_dashboard_settings.rel_path.return_value = str(destination_file) | ||||
|     mock_dashboard_settings.rel_path.return_value = destination_file | ||||
|  | ||||
|     response = await dashboard.fetch( | ||||
|         "/unarchive?configuration=archived.yaml", | ||||
| @@ -474,7 +474,7 @@ async def test_secret_keys_handler_with_file( | ||||
|  | ||||
|     # Configure mock to return our temp secrets file | ||||
|     # Since the file actually exists, os.path.isfile will return True naturally | ||||
|     mock_dashboard_settings.rel_path.return_value = str(secrets_file) | ||||
|     mock_dashboard_settings.rel_path.return_value = secrets_file | ||||
|  | ||||
|     response = await dashboard.fetch("/secret_keys", method="GET") | ||||
|     assert response.code == 200 | ||||
| @@ -538,8 +538,8 @@ def test_start_web_server_with_address_port( | ||||
| ) -> None: | ||||
|     """Test the start_web_server function with address and port.""" | ||||
|     app = Mock() | ||||
|     trash_dir = Path(mock_trash_storage_path.return_value) | ||||
|     archive_dir = Path(mock_archive_storage_path.return_value) | ||||
|     trash_dir = mock_trash_storage_path.return_value | ||||
|     archive_dir = mock_archive_storage_path.return_value | ||||
|  | ||||
|     # Create trash dir to test migration | ||||
|     trash_dir.mkdir() | ||||
| @@ -643,12 +643,12 @@ async def test_archive_handler_with_build_folder( | ||||
|     (build_folder / ".pioenvs").mkdir() | ||||
|  | ||||
|     mock_dashboard_settings.config_dir = str(config_dir) | ||||
|     mock_dashboard_settings.rel_path.return_value = str(test_config) | ||||
|     mock_archive_storage_path.return_value = str(archive_dir) | ||||
|     mock_dashboard_settings.rel_path.return_value = test_config | ||||
|     mock_archive_storage_path.return_value = archive_dir | ||||
|  | ||||
|     mock_storage = MagicMock() | ||||
|     mock_storage.name = "test_device" | ||||
|     mock_storage.build_path = str(build_folder) | ||||
|     mock_storage.build_path = build_folder | ||||
|     mock_storage_json.load.return_value = mock_storage | ||||
|  | ||||
|     response = await dashboard.fetch( | ||||
| @@ -686,8 +686,8 @@ async def test_archive_handler_no_build_folder( | ||||
|     test_config.write_text("esphome:\n  name: test_device\n") | ||||
|  | ||||
|     mock_dashboard_settings.config_dir = str(config_dir) | ||||
|     mock_dashboard_settings.rel_path.return_value = str(test_config) | ||||
|     mock_archive_storage_path.return_value = str(archive_dir) | ||||
|     mock_dashboard_settings.rel_path.return_value = test_config | ||||
|     mock_archive_storage_path.return_value = archive_dir | ||||
|  | ||||
|     mock_storage = MagicMock() | ||||
|     mock_storage.name = "test_device" | ||||
|   | ||||
| @@ -13,14 +13,14 @@ from esphome.dashboard import web_server | ||||
| def test_get_base_frontend_path_production() -> None: | ||||
|     """Test get_base_frontend_path in production mode.""" | ||||
|     mock_module = MagicMock() | ||||
|     mock_module.where.return_value = "/usr/local/lib/esphome_dashboard" | ||||
|     mock_module.where.return_value = Path("/usr/local/lib/esphome_dashboard") | ||||
|  | ||||
|     with ( | ||||
|         patch.dict(os.environ, {}, clear=True), | ||||
|         patch.dict("sys.modules", {"esphome_dashboard": mock_module}), | ||||
|     ): | ||||
|         result = web_server.get_base_frontend_path() | ||||
|         assert result == "/usr/local/lib/esphome_dashboard" | ||||
|         assert result == Path("/usr/local/lib/esphome_dashboard") | ||||
|         mock_module.where.assert_called_once() | ||||
|  | ||||
|  | ||||
| @@ -31,13 +31,12 @@ def test_get_base_frontend_path_dev_mode() -> None: | ||||
|     with patch.dict(os.environ, {"ESPHOME_DASHBOARD_DEV": test_path}): | ||||
|         result = web_server.get_base_frontend_path() | ||||
|  | ||||
|         # The function uses os.path.abspath which doesn't resolve symlinks | ||||
|         # We need to match that behavior | ||||
|         # The function uses Path.resolve() which resolves symlinks | ||||
|         # The actual function adds "/" to the path, so we simulate that | ||||
|         test_path_with_slash = test_path if test_path.endswith("/") else test_path + "/" | ||||
|         expected = os.path.abspath( | ||||
|             os.path.join(os.getcwd(), test_path_with_slash, "esphome_dashboard") | ||||
|         ) | ||||
|         expected = ( | ||||
|             Path(os.getcwd()) / test_path_with_slash / "esphome_dashboard" | ||||
|         ).resolve() | ||||
|         assert result == expected | ||||
|  | ||||
|  | ||||
| @@ -48,8 +47,8 @@ def test_get_base_frontend_path_dev_mode_with_trailing_slash() -> None: | ||||
|     with patch.dict(os.environ, {"ESPHOME_DASHBOARD_DEV": test_path}): | ||||
|         result = web_server.get_base_frontend_path() | ||||
|  | ||||
|         # The function uses os.path.abspath which doesn't resolve symlinks | ||||
|         expected = os.path.abspath(str(Path.cwd() / test_path / "esphome_dashboard")) | ||||
|         # The function uses Path.resolve() which resolves symlinks | ||||
|         expected = (Path.cwd() / test_path / "esphome_dashboard").resolve() | ||||
|         assert result == expected | ||||
|  | ||||
|  | ||||
| @@ -60,76 +59,72 @@ def test_get_base_frontend_path_dev_mode_relative_path() -> None: | ||||
|     with patch.dict(os.environ, {"ESPHOME_DASHBOARD_DEV": test_path}): | ||||
|         result = web_server.get_base_frontend_path() | ||||
|  | ||||
|         # The function uses os.path.abspath which doesn't resolve symlinks | ||||
|         # We need to match that behavior | ||||
|         # The function uses Path.resolve() which resolves symlinks | ||||
|         # The actual function adds "/" to the path, so we simulate that | ||||
|         test_path_with_slash = test_path if test_path.endswith("/") else test_path + "/" | ||||
|         expected = os.path.abspath( | ||||
|             os.path.join(os.getcwd(), test_path_with_slash, "esphome_dashboard") | ||||
|         ) | ||||
|         expected = ( | ||||
|             Path(os.getcwd()) / test_path_with_slash / "esphome_dashboard" | ||||
|         ).resolve() | ||||
|         assert result == expected | ||||
|         assert Path(result).is_absolute() | ||||
|         assert result.is_absolute() | ||||
|  | ||||
|  | ||||
| def test_get_static_path_single_component() -> None: | ||||
|     """Test get_static_path with single path component.""" | ||||
|     with patch("esphome.dashboard.web_server.get_base_frontend_path") as mock_base: | ||||
|         mock_base.return_value = "/base/frontend" | ||||
|         mock_base.return_value = Path("/base/frontend") | ||||
|  | ||||
|         result = web_server.get_static_path("file.js") | ||||
|  | ||||
|         assert result == os.path.join("/base/frontend", "static", "file.js") | ||||
|         assert result == Path("/base/frontend") / "static" / "file.js" | ||||
|  | ||||
|  | ||||
| def test_get_static_path_multiple_components() -> None: | ||||
|     """Test get_static_path with multiple path components.""" | ||||
|     with patch("esphome.dashboard.web_server.get_base_frontend_path") as mock_base: | ||||
|         mock_base.return_value = "/base/frontend" | ||||
|         mock_base.return_value = Path("/base/frontend") | ||||
|  | ||||
|         result = web_server.get_static_path("js", "esphome", "index.js") | ||||
|  | ||||
|         assert result == os.path.join( | ||||
|             "/base/frontend", "static", "js", "esphome", "index.js" | ||||
|         assert ( | ||||
|             result == Path("/base/frontend") / "static" / "js" / "esphome" / "index.js" | ||||
|         ) | ||||
|  | ||||
|  | ||||
| def test_get_static_path_empty_args() -> None: | ||||
|     """Test get_static_path with no arguments.""" | ||||
|     with patch("esphome.dashboard.web_server.get_base_frontend_path") as mock_base: | ||||
|         mock_base.return_value = "/base/frontend" | ||||
|         mock_base.return_value = Path("/base/frontend") | ||||
|  | ||||
|         result = web_server.get_static_path() | ||||
|  | ||||
|         assert result == os.path.join("/base/frontend", "static") | ||||
|         assert result == Path("/base/frontend") / "static" | ||||
|  | ||||
|  | ||||
| def test_get_static_path_with_pathlib_path() -> None: | ||||
|     """Test get_static_path with Path objects.""" | ||||
|     with patch("esphome.dashboard.web_server.get_base_frontend_path") as mock_base: | ||||
|         mock_base.return_value = "/base/frontend" | ||||
|         mock_base.return_value = Path("/base/frontend") | ||||
|  | ||||
|         path_obj = Path("js") / "app.js" | ||||
|         result = web_server.get_static_path(str(path_obj)) | ||||
|  | ||||
|         assert result == os.path.join("/base/frontend", "static", "js", "app.js") | ||||
|         assert result == Path("/base/frontend") / "static" / "js" / "app.js" | ||||
|  | ||||
|  | ||||
| def test_get_static_file_url_production() -> None: | ||||
|     """Test get_static_file_url in production mode.""" | ||||
|     web_server.get_static_file_url.cache_clear() | ||||
|     mock_module = MagicMock() | ||||
|     mock_file = MagicMock() | ||||
|     mock_file.read.return_value = b"test content" | ||||
|     mock_file.__enter__ = MagicMock(return_value=mock_file) | ||||
|     mock_file.__exit__ = MagicMock(return_value=None) | ||||
|     mock_path = MagicMock(spec=Path) | ||||
|     mock_path.read_bytes.return_value = b"test content" | ||||
|  | ||||
|     with ( | ||||
|         patch.dict(os.environ, {}, clear=True), | ||||
|         patch.dict("sys.modules", {"esphome_dashboard": mock_module}), | ||||
|         patch("esphome.dashboard.web_server.get_static_path") as mock_get_path, | ||||
|         patch("esphome.dashboard.web_server.open", create=True, return_value=mock_file), | ||||
|     ): | ||||
|         mock_get_path.return_value = "/fake/path/js/app.js" | ||||
|         mock_get_path.return_value = mock_path | ||||
|         result = web_server.get_static_file_url("js/app.js") | ||||
|         assert result.startswith("./static/js/app.js?hash=") | ||||
|  | ||||
| @@ -182,26 +177,26 @@ def test_load_file_compressed_path(tmp_path: Path) -> None: | ||||
| def test_path_normalization_in_static_path() -> None: | ||||
|     """Test that paths are normalized correctly.""" | ||||
|     with patch("esphome.dashboard.web_server.get_base_frontend_path") as mock_base: | ||||
|         mock_base.return_value = "/base/frontend" | ||||
|         mock_base.return_value = Path("/base/frontend") | ||||
|  | ||||
|         # Test with separate components | ||||
|         result1 = web_server.get_static_path("js", "app.js") | ||||
|         result2 = web_server.get_static_path("js", "app.js") | ||||
|  | ||||
|         assert result1 == result2 | ||||
|         assert result1 == os.path.join("/base/frontend", "static", "js", "app.js") | ||||
|         assert result1 == Path("/base/frontend") / "static" / "js" / "app.js" | ||||
|  | ||||
|  | ||||
| def test_windows_path_handling() -> None: | ||||
|     """Test handling of Windows-style paths.""" | ||||
|     with patch("esphome.dashboard.web_server.get_base_frontend_path") as mock_base: | ||||
|         mock_base.return_value = r"C:\Program Files\esphome\frontend" | ||||
|         mock_base.return_value = Path(r"C:\Program Files\esphome\frontend") | ||||
|  | ||||
|         result = web_server.get_static_path("js", "app.js") | ||||
|  | ||||
|         # os.path.join should handle this correctly on the platform | ||||
|         expected = os.path.join( | ||||
|             r"C:\Program Files\esphome\frontend", "static", "js", "app.js" | ||||
|         # Path should handle this correctly on the platform | ||||
|         expected = ( | ||||
|             Path(r"C:\Program Files\esphome\frontend") / "static" / "js" / "app.js" | ||||
|         ) | ||||
|         assert result == expected | ||||
|  | ||||
| @@ -209,22 +204,20 @@ def test_windows_path_handling() -> None: | ||||
| def test_path_with_special_characters() -> None: | ||||
|     """Test paths with special characters.""" | ||||
|     with patch("esphome.dashboard.web_server.get_base_frontend_path") as mock_base: | ||||
|         mock_base.return_value = "/base/frontend" | ||||
|         mock_base.return_value = Path("/base/frontend") | ||||
|  | ||||
|         result = web_server.get_static_path("js-modules", "app_v1.0.js") | ||||
|  | ||||
|         assert result == os.path.join( | ||||
|             "/base/frontend", "static", "js-modules", "app_v1.0.js" | ||||
|         assert ( | ||||
|             result == Path("/base/frontend") / "static" / "js-modules" / "app_v1.0.js" | ||||
|         ) | ||||
|  | ||||
|  | ||||
| def test_path_with_spaces() -> None: | ||||
|     """Test paths with spaces.""" | ||||
|     with patch("esphome.dashboard.web_server.get_base_frontend_path") as mock_base: | ||||
|         mock_base.return_value = "/base/my frontend" | ||||
|         mock_base.return_value = Path("/base/my frontend") | ||||
|  | ||||
|         result = web_server.get_static_path("my js", "my app.js") | ||||
|  | ||||
|         assert result == os.path.join( | ||||
|             "/base/my frontend", "static", "my js", "my app.js" | ||||
|         ) | ||||
|         assert result == Path("/base/my frontend") / "static" / "my js" / "my app.js" | ||||
|   | ||||
| @@ -1,56 +0,0 @@ | ||||
| import os | ||||
| from pathlib import Path | ||||
| from unittest.mock import patch | ||||
|  | ||||
| import py | ||||
| import pytest | ||||
|  | ||||
| from esphome.dashboard.util.file import write_file, write_utf8_file | ||||
|  | ||||
|  | ||||
| def test_write_utf8_file(tmp_path: Path) -> None: | ||||
|     write_utf8_file(tmp_path.joinpath("foo.txt"), "foo") | ||||
|     assert tmp_path.joinpath("foo.txt").read_text() == "foo" | ||||
|  | ||||
|     with pytest.raises(OSError): | ||||
|         write_utf8_file(Path("/dev/not-writable"), "bar") | ||||
|  | ||||
|  | ||||
| def test_write_file(tmp_path: Path) -> None: | ||||
|     write_file(tmp_path.joinpath("foo.txt"), b"foo") | ||||
|     assert tmp_path.joinpath("foo.txt").read_text() == "foo" | ||||
|  | ||||
|  | ||||
| def test_write_utf8_file_fails_at_rename( | ||||
|     tmpdir: py.path.local, caplog: pytest.LogCaptureFixture | ||||
| ) -> None: | ||||
|     """Test that if rename fails not not remove, we do not log the failed cleanup.""" | ||||
|     test_dir = tmpdir.mkdir("files") | ||||
|     test_file = Path(test_dir / "test.json") | ||||
|  | ||||
|     with ( | ||||
|         pytest.raises(OSError), | ||||
|         patch("esphome.dashboard.util.file.os.replace", side_effect=OSError), | ||||
|     ): | ||||
|         write_utf8_file(test_file, '{"some":"data"}', False) | ||||
|  | ||||
|     assert not os.path.exists(test_file) | ||||
|  | ||||
|     assert "File replacement cleanup failed" not in caplog.text | ||||
|  | ||||
|  | ||||
| def test_write_utf8_file_fails_at_rename_and_remove( | ||||
|     tmpdir: py.path.local, caplog: pytest.LogCaptureFixture | ||||
| ) -> None: | ||||
|     """Test that if rename and remove both fail, we log the failed cleanup.""" | ||||
|     test_dir = tmpdir.mkdir("files") | ||||
|     test_file = Path(test_dir / "test.json") | ||||
|  | ||||
|     with ( | ||||
|         pytest.raises(OSError), | ||||
|         patch("esphome.dashboard.util.file.os.remove", side_effect=OSError), | ||||
|         patch("esphome.dashboard.util.file.os.replace", side_effect=OSError), | ||||
|     ): | ||||
|         write_utf8_file(test_file, '{"some":"data"}', False) | ||||
|  | ||||
|     assert "File replacement cleanup failed" in caplog.text | ||||
		Reference in New Issue
	
	Block a user