|
@@ -0,0 +1,116 @@
|
|
|
|
|
+"""A backup from a Windows install restored on Linux finds its files (#3501).
|
|
|
|
|
+
|
|
|
|
|
+Windows stores paths relative to the data folder with backslashes; Linux reads
|
|
|
|
|
+them as part of one file name. The restore turns them into slashes.
|
|
|
|
|
+"""
|
|
|
|
|
+
|
|
|
|
|
+import pytest
|
|
|
|
|
+from sqlalchemy import select
|
|
|
|
|
+
|
|
|
|
|
+from backend.app.models.archive import PrintArchive
|
|
|
|
|
+from backend.app.models.library import LibraryFile
|
|
|
|
|
+from backend.app.models.print_log import PrintLogEntry
|
|
|
|
|
+from backend.app.services.backup_paths import convert_windows_paths
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _windows_rows():
|
|
|
|
|
+ archive = PrintArchive(
|
|
|
|
|
+ filename="Brachy.3mf",
|
|
|
|
|
+ file_path="archive\\12\\Brachy.3mf",
|
|
|
|
|
+ file_size=1,
|
|
|
|
|
+ thumbnail_path="archive\\12\\thumbnail.png",
|
|
|
|
|
+ timelapse_path="archive\\12\\timelapse.mp4",
|
|
|
|
|
+ source_3mf_path="archive\\12\\source\\Brachy.3mf",
|
|
|
|
|
+ f3d_path="archive\\12\\design.f3d",
|
|
|
|
|
+ )
|
|
|
|
|
+ library_file = LibraryFile(
|
|
|
|
|
+ filename="Brachy.3mf",
|
|
|
|
|
+ file_path="archive\\library\\files\\abc.3mf",
|
|
|
|
|
+ file_type="3mf",
|
|
|
|
|
+ file_size=1,
|
|
|
|
|
+ thumbnail_path="archive\\library\\thumbnails\\abc.png",
|
|
|
|
|
+ )
|
|
|
|
|
+ log_entry = PrintLogEntry(
|
|
|
|
|
+ status="completed",
|
|
|
|
|
+ thumbnail_path="archive\\12\\thumbnail.png",
|
|
|
|
|
+ timelapse_path="archive\\12\\timelapse.mp4",
|
|
|
|
|
+ )
|
|
|
|
|
+ return archive, library_file, log_entry
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+async def _convert(db_session, base_dir, **kwargs) -> int:
|
|
|
|
|
+ await db_session.flush()
|
|
|
|
|
+ changed = await convert_windows_paths(await db_session.connection(), base_dir, **kwargs)
|
|
|
|
|
+ db_session.expire_all()
|
|
|
|
|
+ return changed
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+@pytest.mark.asyncio
|
|
|
|
|
+async def test_windows_paths_get_slashes(db_session, tmp_path):
|
|
|
|
|
+ db_session.add_all(_windows_rows())
|
|
|
|
|
+
|
|
|
|
|
+ assert await _convert(db_session, tmp_path, windows=False) == 3
|
|
|
|
|
+
|
|
|
|
|
+ archive = (await db_session.execute(select(PrintArchive))).scalar_one()
|
|
|
|
|
+ assert (
|
|
|
|
|
+ archive.file_path,
|
|
|
|
|
+ archive.thumbnail_path,
|
|
|
|
|
+ archive.timelapse_path,
|
|
|
|
|
+ archive.source_3mf_path,
|
|
|
|
|
+ archive.f3d_path,
|
|
|
|
|
+ ) == (
|
|
|
|
|
+ "archive/12/Brachy.3mf",
|
|
|
|
|
+ "archive/12/thumbnail.png",
|
|
|
|
|
+ "archive/12/timelapse.mp4",
|
|
|
|
|
+ "archive/12/source/Brachy.3mf",
|
|
|
|
|
+ "archive/12/design.f3d",
|
|
|
|
|
+ )
|
|
|
|
|
+ library_file = (await db_session.execute(select(LibraryFile))).scalar_one()
|
|
|
|
|
+ assert (library_file.file_path, library_file.thumbnail_path) == (
|
|
|
|
|
+ "archive/library/files/abc.3mf",
|
|
|
|
|
+ "archive/library/thumbnails/abc.png",
|
|
|
|
|
+ )
|
|
|
|
|
+ log_entry = (await db_session.execute(select(PrintLogEntry))).scalar_one()
|
|
|
|
|
+ assert (log_entry.thumbnail_path, log_entry.timelapse_path) == (
|
|
|
|
|
+ "archive/12/thumbnail.png",
|
|
|
|
|
+ "archive/12/timelapse.mp4",
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+@pytest.mark.asyncio
|
|
|
|
|
+async def test_a_file_whose_name_has_a_backslash_is_kept(db_session, tmp_path):
|
|
|
|
|
+ # On Linux a backslash can be part of a real file name.
|
|
|
|
|
+ (tmp_path / "archive" / "7").mkdir(parents=True)
|
|
|
|
|
+ (tmp_path / "archive" / "7" / "a\\b.3mf").write_bytes(b"x")
|
|
|
|
|
+ db_session.add(PrintArchive(filename="a\\b.3mf", file_path="archive/7/a\\b.3mf", file_size=1))
|
|
|
|
|
+
|
|
|
|
|
+ assert await _convert(db_session, tmp_path, windows=False) == 0
|
|
|
|
|
+
|
|
|
|
|
+ archive = (await db_session.execute(select(PrintArchive))).scalar_one()
|
|
|
|
|
+ assert archive.file_path == "archive/7/a\\b.3mf"
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+@pytest.mark.asyncio
|
|
|
|
|
+async def test_nothing_changes_on_windows(db_session, tmp_path):
|
|
|
|
|
+ db_session.add_all(_windows_rows())
|
|
|
|
|
+
|
|
|
|
|
+ assert await _convert(db_session, tmp_path, windows=True) == 0
|
|
|
|
|
+
|
|
|
|
|
+ archive = (await db_session.execute(select(PrintArchive))).scalar_one()
|
|
|
|
|
+ assert archive.file_path == "archive\\12\\Brachy.3mf"
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+@pytest.mark.asyncio
|
|
|
|
|
+async def test_linux_paths_and_empty_columns_are_left_alone(db_session, tmp_path):
|
|
|
|
|
+ db_session.add(PrintArchive(filename="x.3mf", file_path="archive/3/x.3mf", file_size=1, thumbnail_path=None))
|
|
|
|
|
+ db_session.add(PrintLogEntry(status="completed"))
|
|
|
|
|
+
|
|
|
|
|
+ assert await _convert(db_session, tmp_path, windows=False) == 0
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+@pytest.mark.asyncio
|
|
|
|
|
+async def test_a_second_run_changes_nothing(db_session, tmp_path):
|
|
|
|
|
+ db_session.add_all(_windows_rows())
|
|
|
|
|
+ await _convert(db_session, tmp_path, windows=False)
|
|
|
|
|
+
|
|
|
|
|
+ assert await _convert(db_session, tmp_path, windows=False) == 0
|