mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-21 10:15:04 +02:00
fix(vp): show target printer's serial on proxy-mode VP card
The runtime services (SSDP, MQTT bind identity, cert subject) already advertise the target printer's serial via target_printer_serial or self.serial in proxy mode, but the API response that drives the VP settings card always returned the self-generated suffix-based serial. The card therefore displayed a serial that didn't match what slicers see, breaking the "one identity per VP" mental model. _vp_to_dict now resolves vp.target_printer_id -> Printer.serial_number when mode == VP_MODE_PROXY and substitutes the result into the response serial field. Archive / queue / review keep the self-generated serial (those modes never speak the target's identity). Orphaned target falls back to self-generated so the card still renders.
This commit is contained in:
@@ -599,3 +599,83 @@ class TestVirtualPrinterAccessCodeInheritance:
|
||||
|
||||
vp = (await db_session.execute(select(VirtualPrinter).where(VirtualPrinter.id == vp_id))).scalar_one()
|
||||
assert vp.access_code == "REALCODE"
|
||||
|
||||
|
||||
class TestVirtualPrinterSerialSurface:
|
||||
"""Proxy-mode VPs must surface the target printer's serial in API responses.
|
||||
|
||||
The bridge advertises the target's serial over SSDP and forwards the
|
||||
target's identity to the slicer; the VP-settings card should show the
|
||||
same serial so the user sees one consistent identity per VP. Archive /
|
||||
queue / review VPs keep the self-generated suffix-based serial since
|
||||
those modes never speak the target's identity.
|
||||
"""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_proxy_vp_response_uses_target_printer_serial(self, async_client: AsyncClient, printer_factory):
|
||||
target = await printer_factory(name="Real X1C", access_code="REALCODE", serial_number="00M09A123456789")
|
||||
|
||||
create_resp = await async_client.post(
|
||||
"/api/v1/virtual-printers",
|
||||
json={
|
||||
"name": "ProxyVP",
|
||||
"mode": "proxy",
|
||||
"target_printer_id": target.id,
|
||||
},
|
||||
)
|
||||
assert create_resp.status_code == 200
|
||||
vp_id = create_resp.json()["id"]
|
||||
assert create_resp.json()["serial"] == "00M09A123456789"
|
||||
|
||||
get_resp = await async_client.get(f"/api/v1/virtual-printers/{vp_id}")
|
||||
assert get_resp.status_code == 200
|
||||
assert get_resp.json()["serial"] == "00M09A123456789"
|
||||
|
||||
list_resp = await async_client.get("/api/v1/virtual-printers")
|
||||
assert list_resp.status_code == 200
|
||||
listed = next(p for p in list_resp.json()["printers"] if p["id"] == vp_id)
|
||||
assert listed["serial"] == "00M09A123456789"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_non_proxy_vp_keeps_self_generated_serial(self, async_client: AsyncClient, printer_factory):
|
||||
# Even with a target printer set (#1429 access-code inheritance flow),
|
||||
# archive / queue / review VPs are NOT bridging the target's identity
|
||||
# to the slicer — they synthesise their own. Self-generated serial.
|
||||
target = await printer_factory(name="Real X1C", access_code="REALCODE", serial_number="00M09A123456789")
|
||||
|
||||
create_resp = await async_client.post(
|
||||
"/api/v1/virtual-printers",
|
||||
json={
|
||||
"name": "QueueVP",
|
||||
"mode": "queue",
|
||||
"target_printer_id": target.id,
|
||||
},
|
||||
)
|
||||
assert create_resp.status_code == 200
|
||||
assert create_resp.json()["serial"] != "00M09A123456789"
|
||||
# The synthesised serial follows _get_serial_for_model's `<prefix><suffix>`
|
||||
# shape — model-specific prefix + 8-char hex suffix from `vp.serial_suffix`.
|
||||
assert len(create_resp.json()["serial"]) >= 8
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_proxy_vp_falls_back_to_self_generated_when_target_missing(
|
||||
self, async_client: AsyncClient, db_session
|
||||
):
|
||||
# Defensive fallback: a proxy VP whose target_printer_id points at a
|
||||
# row that no longer exists (printer deleted mid-config, manual SQL
|
||||
# tweak, race) must not 500 — it returns the self-generated serial
|
||||
# so the card still renders and the user can fix the target.
|
||||
from backend.app.models.virtual_printer import VirtualPrinter
|
||||
|
||||
vp = VirtualPrinter(name="OrphanProxy", mode="proxy", target_printer_id=99999, enabled=False)
|
||||
db_session.add(vp)
|
||||
await db_session.commit()
|
||||
|
||||
get_resp = await async_client.get(f"/api/v1/virtual-printers/{vp.id}")
|
||||
assert get_resp.status_code == 200
|
||||
body = get_resp.json()
|
||||
assert body["serial"] # non-empty
|
||||
assert len(body["serial"]) >= 8
|
||||
|
||||
Reference in New Issue
Block a user