mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-22 03:49:18 +02:00
Merge remote-tracking branch 'upstream/dev' into feature/billing
This commit is contained in:
@@ -438,6 +438,211 @@ class TestPrintQueueAPI:
|
||||
assert result["archive_id"] == archive.id
|
||||
assert result["ams_mapping"] == [5, -1, 2, -1]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_add_to_queue_falls_back_to_archive_slicer_ams_mapping_when_unset(
|
||||
self, async_client: AsyncClient, printer_factory, archive_factory, db_session
|
||||
):
|
||||
"""When the caller sends no explicit ams_mapping, but the archive
|
||||
carries the slicer's own saved pick for this exact printer
|
||||
(extra_data.slicer_ams_mapping, written by a VP with "Save AMS
|
||||
mapping" on), the queue item should inherit it — the same
|
||||
exact-physical-spool reuse the "Mapping" button gives you, but
|
||||
automatic when nothing was hand-edited.
|
||||
"""
|
||||
printer = await printer_factory()
|
||||
archive = await archive_factory(
|
||||
extra_data={"slicer_ams_mapping": {"mapping": [5, -1, 2, -1], "printer_id": printer.id}}
|
||||
)
|
||||
|
||||
data = {
|
||||
"printer_id": printer.id,
|
||||
"archive_id": archive.id,
|
||||
}
|
||||
response = await async_client.post("/api/v1/queue/", json=data)
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["ams_mapping"] == [5, -1, 2, -1]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_add_to_queue_ignores_archive_slicer_ams_mapping_for_different_printer(
|
||||
self, async_client: AsyncClient, printer_factory, archive_factory, db_session
|
||||
):
|
||||
"""A saved mapping's tray IDs only mean something relative to the
|
||||
printer they were resolved against. Reprinting the same archive on a
|
||||
*different* printer must not inherit it — tray 5 on printer A can
|
||||
hold a completely different spool than tray 5 on printer B.
|
||||
"""
|
||||
origin_printer = await printer_factory()
|
||||
other_printer = await printer_factory()
|
||||
archive = await archive_factory(
|
||||
extra_data={"slicer_ams_mapping": {"mapping": [5, -1, 2, -1], "printer_id": origin_printer.id}}
|
||||
)
|
||||
|
||||
data = {
|
||||
"printer_id": other_printer.id,
|
||||
"archive_id": archive.id,
|
||||
}
|
||||
response = await async_client.post("/api/v1/queue/", json=data)
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["ams_mapping"] is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_add_to_queue_ignores_archive_slicer_ams_mapping_for_model_based_dispatch(
|
||||
self, async_client: AsyncClient, printer_factory, archive_factory, db_session
|
||||
):
|
||||
"""A model-based item (no fixed printer_id) can't know in advance
|
||||
which printer the scheduler will pick, so a saved mapping resolved
|
||||
against one specific printer must never be inherited here either.
|
||||
"""
|
||||
origin_printer = await printer_factory()
|
||||
archive = await archive_factory(
|
||||
extra_data={"slicer_ams_mapping": {"mapping": [5, -1, 2, -1], "printer_id": origin_printer.id}}
|
||||
)
|
||||
|
||||
data = {
|
||||
"target_model": "X1C",
|
||||
"archive_id": archive.id,
|
||||
}
|
||||
response = await async_client.post("/api/v1/queue/", json=data)
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["ams_mapping"] is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_add_to_queue_explicit_ams_mapping_wins_over_archive_fallback(
|
||||
self, async_client: AsyncClient, printer_factory, archive_factory, db_session
|
||||
):
|
||||
"""An explicit ams_mapping in the request (e.g. from the filament
|
||||
mapping panel) must take priority over the archive's saved slicer
|
||||
pick — the fallback only fires when the caller sent nothing at all.
|
||||
"""
|
||||
printer = await printer_factory()
|
||||
archive = await archive_factory(
|
||||
extra_data={"slicer_ams_mapping": {"mapping": [5, -1, 2, -1], "printer_id": printer.id}}
|
||||
)
|
||||
|
||||
data = {
|
||||
"printer_id": printer.id,
|
||||
"archive_id": archive.id,
|
||||
"ams_mapping": [9, -1, 1, -1],
|
||||
}
|
||||
response = await async_client.post("/api/v1/queue/", json=data)
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["ams_mapping"] == [9, -1, 1, -1]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_add_to_queue_archive_extra_data_without_slicer_mapping_key_not_used(
|
||||
self, async_client: AsyncClient, printer_factory, archive_factory, db_session
|
||||
):
|
||||
"""extra_data present but without a slicer_ams_mapping key (the
|
||||
common case — most archives have other metadata but no saved slicer
|
||||
mapping) must not accidentally trip the fallback."""
|
||||
printer = await printer_factory()
|
||||
archive = await archive_factory(extra_data={"filament_slots": []})
|
||||
|
||||
data = {
|
||||
"printer_id": printer.id,
|
||||
"archive_id": archive.id,
|
||||
}
|
||||
response = await async_client.post("/api/v1/queue/", json=data)
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["ams_mapping"] is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_add_to_queue_force_color_match_overrides_beat_the_archive_fallback(
|
||||
self, async_client: AsyncClient, printer_factory, archive_factory, db_session
|
||||
):
|
||||
"""Force-color-match overrides are the caller asking the scheduler to
|
||||
match strictly against the printer's live trays, and they are only ever
|
||||
applied inside `_compute_ams_mapping_for_printer` — the function a
|
||||
stored mapping makes the scheduler skip. Inheriting the saved mapping
|
||||
here would silently retire the strictness that was just requested
|
||||
(#2700 review).
|
||||
"""
|
||||
printer = await printer_factory()
|
||||
archive = await archive_factory(
|
||||
extra_data={"slicer_ams_mapping": {"mapping": [5, -1, 2, -1], "printer_id": printer.id}}
|
||||
)
|
||||
|
||||
data = {
|
||||
"printer_id": printer.id,
|
||||
"archive_id": archive.id,
|
||||
"filament_overrides": [
|
||||
{"slot_id": 1, "type": "PLA", "color": "#FF0000", "force_color_match": True},
|
||||
],
|
||||
}
|
||||
response = await async_client.post("/api/v1/queue/", json=data)
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["ams_mapping"] is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_add_to_queue_plain_overrides_still_allow_the_archive_fallback(
|
||||
self, async_client: AsyncClient, printer_factory, archive_factory, db_session
|
||||
):
|
||||
"""Only force_color_match stands the fallback down. A plain preference
|
||||
override is a filament swap, not a request for live colour matching, so
|
||||
the saved mapping is still the best starting point.
|
||||
"""
|
||||
printer = await printer_factory()
|
||||
archive = await archive_factory(
|
||||
extra_data={"slicer_ams_mapping": {"mapping": [5, -1, 2, -1], "printer_id": printer.id}}
|
||||
)
|
||||
|
||||
data = {
|
||||
"printer_id": printer.id,
|
||||
"archive_id": archive.id,
|
||||
"filament_overrides": [{"slot_id": 1, "type": "PLA", "color": "#FF0000"}],
|
||||
}
|
||||
response = await async_client.post("/api/v1/queue/", json=data)
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["ams_mapping"] == [5, -1, 2, -1]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_queue_response_flags_saved_mapping_only_for_its_own_printer(
|
||||
self, async_client: AsyncClient, printer_factory, archive_factory, db_session
|
||||
):
|
||||
"""`archive_has_slicer_ams_mapping` drives a badge that claims the
|
||||
print reuses the slicer's exact trays. Global tray IDs mean nothing on
|
||||
another printer, so the flag must be false for a row targeting one —
|
||||
otherwise the badge is there while nothing is reused (#2700 review).
|
||||
"""
|
||||
origin_printer = await printer_factory()
|
||||
other_printer = await printer_factory()
|
||||
archive = await archive_factory(
|
||||
extra_data={"slicer_ams_mapping": {"mapping": [5, -1, 2, -1], "printer_id": origin_printer.id}}
|
||||
)
|
||||
|
||||
own = await async_client.post(
|
||||
"/api/v1/queue/", json={"printer_id": origin_printer.id, "archive_id": archive.id}
|
||||
)
|
||||
assert own.status_code == 200
|
||||
assert own.json()["archive_has_slicer_ams_mapping"] is True
|
||||
|
||||
foreign = await async_client.post(
|
||||
"/api/v1/queue/", json={"printer_id": other_printer.id, "archive_id": archive.id}
|
||||
)
|
||||
assert foreign.status_code == 200
|
||||
assert foreign.json()["archive_has_slicer_ams_mapping"] is False
|
||||
|
||||
# Model-based: the scheduler hasn't picked a printer yet, so the
|
||||
# mapping is not reused there either.
|
||||
model_based = await async_client.post("/api/v1/queue/", json={"target_model": "X1C", "archive_id": archive.id})
|
||||
assert model_based.status_code == 200
|
||||
assert model_based.json()["archive_has_slicer_ams_mapping"] is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_add_to_queue_with_plate_id(
|
||||
|
||||
Reference in New Issue
Block a user