1
0
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:
Jesse Hills
2025-09-20 00:59:48 +12:00
committed by GitHub
parent de617c85c7
commit 9ea3643b74
57 changed files with 808 additions and 938 deletions

View File

@@ -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()

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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