mirror of
https://github.com/esphome/esphome.git
synced 2025-09-16 10:12:21 +01:00
[dashboard] Fix archive handler incorrectly deleting build folders instead of archiving them
This commit is contained in:
@@ -1039,11 +1039,11 @@ class ArchiveRequestHandler(BaseHandler):
|
|||||||
|
|
||||||
storage_json = StorageJSON.load(storage_path)
|
storage_json = StorageJSON.load(storage_path)
|
||||||
if storage_json is not None:
|
if storage_json is not None:
|
||||||
# Delete build folder (if exists)
|
# Move build folder to archive (if exists)
|
||||||
name = storage_json.name
|
name = storage_json.name
|
||||||
build_folder = os.path.join(settings.config_dir, name)
|
build_folder = os.path.join(settings.config_dir, name)
|
||||||
if build_folder is not None:
|
if os.path.exists(build_folder):
|
||||||
shutil.rmtree(build_folder, os.path.join(archive_path, name))
|
shutil.move(build_folder, os.path.join(archive_path, name))
|
||||||
|
|
||||||
|
|
||||||
class UnArchiveRequestHandler(BaseHandler):
|
class UnArchiveRequestHandler(BaseHandler):
|
||||||
|
@@ -589,7 +589,7 @@ async def test_archive_request_handler_post(
|
|||||||
mock_ext_storage_path: MagicMock,
|
mock_ext_storage_path: MagicMock,
|
||||||
tmp_path: Path,
|
tmp_path: Path,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test ArchiveRequestHandler.post method."""
|
"""Test ArchiveRequestHandler.post method without storage_json."""
|
||||||
|
|
||||||
# Set up temp directories
|
# Set up temp directories
|
||||||
config_dir = Path(get_fixture_path("conf"))
|
config_dir = Path(get_fixture_path("conf"))
|
||||||
@@ -599,14 +599,18 @@ async def test_archive_request_handler_post(
|
|||||||
test_config = config_dir / "test_archive.yaml"
|
test_config = config_dir / "test_archive.yaml"
|
||||||
test_config.write_text("esphome:\n name: test_archive\n")
|
test_config.write_text("esphome:\n name: test_archive\n")
|
||||||
|
|
||||||
# Archive the configuration
|
# Mock storage_json to return None (no storage)
|
||||||
response = await dashboard.fetch(
|
with patch("esphome.dashboard.web_server.StorageJSON.load") as mock_load:
|
||||||
"/archive",
|
mock_load.return_value = None
|
||||||
method="POST",
|
|
||||||
body="configuration=test_archive.yaml",
|
# Archive the configuration
|
||||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
response = await dashboard.fetch(
|
||||||
)
|
"/archive",
|
||||||
assert response.code == 200
|
method="POST",
|
||||||
|
body="configuration=test_archive.yaml",
|
||||||
|
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
|
)
|
||||||
|
assert response.code == 200
|
||||||
|
|
||||||
# Verify file was moved to archive
|
# Verify file was moved to archive
|
||||||
assert not test_config.exists()
|
assert not test_config.exists()
|
||||||
@@ -616,6 +620,112 @@ async def test_archive_request_handler_post(
|
|||||||
).read_text() == "esphome:\n name: test_archive\n"
|
).read_text() == "esphome:\n name: test_archive\n"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_archive_handler_with_build_folder(
|
||||||
|
dashboard: DashboardTestHelper,
|
||||||
|
mock_archive_storage_path: MagicMock,
|
||||||
|
mock_ext_storage_path: MagicMock,
|
||||||
|
mock_dashboard_settings: MagicMock,
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
"""Test ArchiveRequestHandler.post with storage_json and build folder."""
|
||||||
|
# Set up temp directories
|
||||||
|
config_dir = tmp_path / "config"
|
||||||
|
config_dir.mkdir()
|
||||||
|
archive_dir = tmp_path / "archive"
|
||||||
|
archive_dir.mkdir()
|
||||||
|
|
||||||
|
# Create a test configuration file
|
||||||
|
configuration = "test_device.yaml"
|
||||||
|
test_config = config_dir / configuration
|
||||||
|
test_config.write_text("esphome:\n name: test_device\n")
|
||||||
|
|
||||||
|
# Create build folder with content
|
||||||
|
build_folder = config_dir / "test_device"
|
||||||
|
build_folder.mkdir()
|
||||||
|
(build_folder / "firmware.bin").write_text("binary content")
|
||||||
|
(build_folder / ".pioenvs").mkdir()
|
||||||
|
|
||||||
|
# Mock settings to use our temp directory
|
||||||
|
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 storage_json with device name
|
||||||
|
with patch("esphome.dashboard.web_server.StorageJSON.load") as mock_load:
|
||||||
|
mock_storage = MagicMock()
|
||||||
|
mock_storage.name = "test_device"
|
||||||
|
mock_load.return_value = mock_storage
|
||||||
|
|
||||||
|
# Archive the configuration
|
||||||
|
response = await dashboard.fetch(
|
||||||
|
"/archive",
|
||||||
|
method="POST",
|
||||||
|
body=f"configuration={configuration}",
|
||||||
|
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
|
)
|
||||||
|
assert response.code == 200
|
||||||
|
|
||||||
|
# Verify config file was moved to archive
|
||||||
|
assert not test_config.exists()
|
||||||
|
assert (archive_dir / configuration).exists()
|
||||||
|
|
||||||
|
# Verify build folder was moved to archive
|
||||||
|
assert not build_folder.exists()
|
||||||
|
assert (archive_dir / "test_device").exists()
|
||||||
|
assert (archive_dir / "test_device" / "firmware.bin").exists()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_archive_handler_no_build_folder(
|
||||||
|
dashboard: DashboardTestHelper,
|
||||||
|
mock_archive_storage_path: MagicMock,
|
||||||
|
mock_ext_storage_path: MagicMock,
|
||||||
|
mock_dashboard_settings: MagicMock,
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
"""Test ArchiveRequestHandler.post with storage_json but no build folder."""
|
||||||
|
# Set up temp directories
|
||||||
|
config_dir = tmp_path / "config"
|
||||||
|
config_dir.mkdir()
|
||||||
|
archive_dir = tmp_path / "archive"
|
||||||
|
archive_dir.mkdir()
|
||||||
|
|
||||||
|
# Create a test configuration file
|
||||||
|
configuration = "test_device.yaml"
|
||||||
|
test_config = config_dir / configuration
|
||||||
|
test_config.write_text("esphome:\n name: test_device\n")
|
||||||
|
|
||||||
|
# Note: No build folder created
|
||||||
|
|
||||||
|
# Mock settings to use our temp directory
|
||||||
|
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 storage_json with device name
|
||||||
|
with patch("esphome.dashboard.web_server.StorageJSON.load") as mock_load:
|
||||||
|
mock_storage = MagicMock()
|
||||||
|
mock_storage.name = "test_device"
|
||||||
|
mock_load.return_value = mock_storage
|
||||||
|
|
||||||
|
# Archive the configuration (should not fail even without build folder)
|
||||||
|
response = await dashboard.fetch(
|
||||||
|
"/archive",
|
||||||
|
method="POST",
|
||||||
|
body=f"configuration={configuration}",
|
||||||
|
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
|
)
|
||||||
|
assert response.code == 200
|
||||||
|
|
||||||
|
# Verify config file was moved to archive
|
||||||
|
assert not test_config.exists()
|
||||||
|
assert (archive_dir / configuration).exists()
|
||||||
|
|
||||||
|
# Verify no build folder in archive (since it didn't exist)
|
||||||
|
assert not (archive_dir / "test_device").exists()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(os.name == "nt", reason="Unix sockets are not supported on Windows")
|
@pytest.mark.skipif(os.name == "nt", reason="Unix sockets are not supported on Windows")
|
||||||
@pytest.mark.usefixtures("mock_trash_storage_path", "mock_archive_storage_path")
|
@pytest.mark.usefixtures("mock_trash_storage_path", "mock_archive_storage_path")
|
||||||
def test_start_web_server_with_unix_socket(tmp_path: Path) -> None:
|
def test_start_web_server_with_unix_socket(tmp_path: Path) -> None:
|
||||||
|
Reference in New Issue
Block a user