test_webhook_start_print.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. """Regression tests for the webhook `/printer/{id}/start` route.
  2. The previous implementation called `printer_manager.start_print()` directly
  3. with `queue_item.archive_id` (an int) as the filename arg and no print
  4. options, and used `await` on a non-async function. That route 500'd on
  5. every invocation. The fix mirrors `POST /print-queue/{item_id}/start`:
  6. clear the next pending item's `manual_start` so the scheduler picks it up
  7. with the queue's stored options (timelapse, bed_levelling, etc.) intact.
  8. """
  9. import pytest
  10. from httpx import AsyncClient
  11. @pytest.fixture
  12. async def api_key_data(async_client: AsyncClient, db_session):
  13. """Create an API key with control_printer permission."""
  14. from backend.app.core.auth import generate_api_key
  15. from backend.app.models.api_key import APIKey
  16. full_key, key_hash, key_prefix = generate_api_key()
  17. api_key = APIKey(
  18. name="webhook-test-key",
  19. key_hash=key_hash,
  20. key_prefix=key_prefix,
  21. can_queue=True,
  22. can_control_printer=True,
  23. can_read_status=True,
  24. enabled=True,
  25. )
  26. db_session.add(api_key)
  27. await db_session.commit()
  28. return full_key
  29. @pytest.fixture
  30. async def printer_with_queue(db_session):
  31. """Create a printer and a pending queue item with manual_start=True."""
  32. from backend.app.models.print_queue import PrintQueueItem
  33. from backend.app.models.printer import Printer
  34. printer = Printer(
  35. name="WebhookTest",
  36. ip_address="192.168.1.42",
  37. access_code="12345678",
  38. serial_number="00M00A000000000",
  39. model="P1S",
  40. )
  41. db_session.add(printer)
  42. await db_session.commit()
  43. item = PrintQueueItem(
  44. printer_id=printer.id,
  45. position=1,
  46. status="pending",
  47. manual_start=True,
  48. timelapse=True,
  49. bed_levelling=True,
  50. flow_cali=False,
  51. vibration_cali=True,
  52. layer_inspect=False,
  53. use_ams=True,
  54. )
  55. db_session.add(item)
  56. await db_session.commit()
  57. return printer, item
  58. class TestWebhookStartPrint:
  59. @pytest.mark.asyncio
  60. @pytest.mark.integration
  61. async def test_clears_manual_start_on_next_pending_item(
  62. self, async_client: AsyncClient, db_session, api_key_data, printer_with_queue
  63. ):
  64. """The webhook flips manual_start to False so the scheduler picks it up.
  65. Pre-fix the route called `printer_manager.start_print()` directly
  66. with no options and `archive_id` (int) as the filename — 500'd on
  67. every invocation. Now it mirrors the regular `/print-queue/{id}/start`
  68. affordance: scheduler dispatch handles FTP upload and all print
  69. options via the queue's stored fields.
  70. """
  71. printer, item = printer_with_queue
  72. resp = await async_client.post(
  73. f"/api/v1/webhook/printer/{printer.id}/start",
  74. headers={"X-API-Key": api_key_data},
  75. )
  76. assert resp.status_code == 200, resp.text
  77. assert resp.json()["queue_item_id"] == item.id
  78. await db_session.refresh(item)
  79. assert item.manual_start is False, "manual_start must be cleared so scheduler dispatches"
  80. # Stored options must be untouched so the scheduler picks the user's choice.
  81. assert item.timelapse is True
  82. assert item.bed_levelling is True
  83. assert item.vibration_cali is True
  84. @pytest.mark.asyncio
  85. @pytest.mark.integration
  86. async def test_returns_404_when_no_pending_items(self, async_client: AsyncClient, db_session, api_key_data):
  87. from backend.app.models.printer import Printer
  88. printer = Printer(
  89. name="EmptyQueue",
  90. ip_address="192.168.1.43",
  91. access_code="12345678",
  92. serial_number="00M00A000000001",
  93. model="P1S",
  94. )
  95. db_session.add(printer)
  96. await db_session.commit()
  97. resp = await async_client.post(
  98. f"/api/v1/webhook/printer/{printer.id}/start",
  99. headers={"X-API-Key": api_key_data},
  100. )
  101. assert resp.status_code == 404
  102. assert "No pending prints" in resp.json()["detail"]
  103. @pytest.mark.asyncio
  104. @pytest.mark.integration
  105. async def test_returns_404_when_printer_does_not_exist(self, async_client: AsyncClient, api_key_data):
  106. resp = await async_client.post(
  107. "/api/v1/webhook/printer/99999/start",
  108. headers={"X-API-Key": api_key_data},
  109. )
  110. assert resp.status_code == 404