Files
bambuddy/backend/tests/unit/test_library_print_name.py
T
maziggy 71e58e6cf1 fix(library): show the filename, not the embedded 3MF Title (#1489)
File Manager cards, search and sort keyed off file_metadata.print_name,
  which ThreeMFParser lifts from the 3MF's <metadata name="Title">. That
  title is the in-app project title — generic "Exported 3D Model" for any
  Bambu Studio "Save As", a marketing title for a MakerWorld download —
  and almost never the filename the user saved as. A card for
  Whatever.3mf showed "Exported 3D Model"; correcting it needed a rename
  round-trip, since the Rename dialog disables Save while the name is
  unchanged.

  The slicer-output write path already dropped print_name for this exact
  reason; the four other paths that store parsed 3MF metadata onto a
  LibraryFile did not — external-folder scan, managed multipart upload,
  the multi-file ZIP-upload branch, and MakerWorld import.

  Add a shared _without_print_name() helper and apply it at all four
  import paths; switch the slicer path to it so there is one rule. A
  LibraryFile's display name is its filename — only PrintArchive carries
  a real print_name, which is untouched. Remove the now-redundant
  filename->print_name mirroring in the rename route.

  Add a one-time idempotent data migration (_migrate_drop_library_print_name,
  SQLite json_remove / PostgreSQL jsonb key-removal branched on
  is_sqlite()) so libraries imported before the fix correct themselves
  without the rename workaround. No frontend change: print_name || filename
  yields the filename once print_name is gone.

  Tests: 6 new in test_library_print_name.py cover _without_print_name and
  the migration (incl. idempotency, siblings preserved, null metadata).
  SQLite migration branch verified by test; PostgreSQL branch verified
  against a real Postgres instance.
2026-05-22 10:22:14 +02:00

96 lines
3.7 KiB
Python

"""Tests for library files displaying the filename, not the embedded 3MF Title (#1489).
The 3MF ``<metadata name="Title">`` is the in-app project title — generic
("Exported 3D Model") for a Bambu Studio "Save As", a marketing title for a
MakerWorld download — never the filename the user saved as. The FileManager
keyed its display name / search / sort off ``file_metadata.print_name``, so
storing the Title made every card show the wrong name. ``_without_print_name``
strips it on import; ``_migrate_drop_library_print_name`` clears it from rows
imported before the fix.
"""
from sqlalchemy import select
from backend.app.api.routes.library import _without_print_name
from backend.app.core.database import _migrate_drop_library_print_name
from backend.app.models.library import LibraryFile
# --- _without_print_name ---------------------------------------------------
def test_strips_print_name_keeps_siblings():
cleaned = _without_print_name({"print_name": "Exported 3D Model", "print_time_seconds": 100})
assert cleaned == {"print_time_seconds": 100}
def test_none_passes_through():
assert _without_print_name(None) is None
def test_dict_without_print_name_returned_unchanged():
meta = {"print_time_seconds": 50}
# No copy needed when there's nothing to strip — same object back.
assert _without_print_name(meta) is meta
def test_does_not_mutate_input():
original = {"print_name": "Whatever", "filament_used_grams": 12}
cleaned = _without_print_name(original)
assert original == {"print_name": "Whatever", "filament_used_grams": 12} # untouched
assert cleaned == {"filament_used_grams": 12}
def test_print_name_only_collapses_to_empty_dict():
assert _without_print_name({"print_name": "Exported 3D Model"}) == {}
# --- _migrate_drop_library_print_name --------------------------------------
async def test_migration_strips_print_name_from_existing_rows(db_session, monkeypatch):
"""Rows imported before the fix get print_name cleared; siblings and rows
that never had it are untouched. Idempotent on a second run.
The test DB is SQLite; is_sqlite() reads settings.database_url (not the
test engine), so pin it to exercise the SQLite branch deterministically.
The PostgreSQL branch is verified against a real PG instance separately."""
monkeypatch.setattr("backend.app.core.database.is_sqlite", lambda: True)
db_session.add_all(
[
LibraryFile(
filename="halloween.3mf",
file_path="/a",
file_type="3mf",
file_size=1,
file_metadata={"print_name": "Haunted House", "print_time_seconds": 100},
),
LibraryFile(
filename="no_meta.3mf",
file_path="/b",
file_type="3mf",
file_size=1,
file_metadata={"print_time_seconds": 50},
),
LibraryFile(
filename="null_meta.3mf",
file_path="/c",
file_type="3mf",
file_size=1,
file_metadata=None,
),
]
)
await db_session.commit()
conn = await db_session.connection()
await _migrate_drop_library_print_name(conn)
await _migrate_drop_library_print_name(conn) # idempotent
db_session.expire_all()
rows = (await db_session.execute(select(LibraryFile).order_by(LibraryFile.filename))).scalars().all()
by_name = {r.filename: r for r in rows}
assert by_name["halloween.3mf"].file_metadata == {"print_time_seconds": 100}
assert by_name["no_meta.3mf"].file_metadata == {"print_time_seconds": 50}
assert by_name["null_meta.3mf"].file_metadata is None