test_octoeverywhere_actions.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. """OctoEverywhere uses the existing printer controls and AI event subscription."""
  2. from unittest.mock import AsyncMock, MagicMock, patch
  3. import pytest
  4. from backend.app.models.notification_template import DEFAULT_TEMPLATES, NotificationTemplate
  5. from backend.app.services.octoeverywhere_actions import execute_action
  6. @pytest.mark.parametrize("action", ["notify", "pause", "pause_and_off"])
  7. async def test_dispatches_action_with_quality_without_fake_confidence(action):
  8. with patch("backend.app.services.octoeverywhere_actions.execute_detection_action", new_callable=AsyncMock) as run:
  9. await execute_action(1, action, "benchy", 2)
  10. run.assert_awaited_once_with(1, action, "benchy", None, print_quality=2)
  11. async def test_pause_and_power_off_also_notify():
  12. from backend.app.services.obico_actions import execute_action as run
  13. with (
  14. patch("backend.app.services.obico_actions._get_printer_name", return_value="Printer"),
  15. patch("backend.app.services.obico_actions._pause_print") as pause,
  16. patch("backend.app.services.obico_actions._turn_off_linked_plugs", new_callable=AsyncMock) as off,
  17. patch("backend.app.services.obico_actions._notify", new_callable=AsyncMock) as notify,
  18. ):
  19. await run(1, "pause_and_off", "benchy", None, print_quality=1)
  20. pause.assert_called_once_with(1)
  21. off.assert_awaited_once_with(1)
  22. notify.assert_awaited_once_with(1, "Printer", "benchy", None, "pause_and_off", None, print_quality=1)
  23. async def test_notification_shows_quality_and_preserves_event_subscription():
  24. from backend.app.services.notification_service import NotificationService
  25. service = NotificationService()
  26. db = MagicMock()
  27. template = NotificationTemplate(**next(t for t in DEFAULT_TEMPLATES if t["event_type"] == "ai_failure_detection"))
  28. original_body = template.body_template
  29. with (
  30. patch.object(service, "_get_providers_for_event", return_value=["provider"]) as providers,
  31. patch.object(service, "_get_template", return_value=template),
  32. patch.object(service, "_send_to_providers", new_callable=AsyncMock) as send,
  33. ):
  34. await service.on_ai_failure_detection(1, "Printer", "benchy", None, "notify", db, print_quality=2)
  35. providers.assert_awaited_once_with(db, "on_ai_failure_detection", 1)
  36. assert send.await_args.args[1] == "Possible Print Failure Detected"
  37. assert send.await_args.args[2] == "Printer: benchy\nOctoEverywhere print quality: 2/10\nAction taken: notify"
  38. assert send.await_args.kwargs["variables"]["confidence"] == "N/A"
  39. assert template.body_template == original_body
  40. @pytest.mark.parametrize(
  41. ("body", "expected"),
  42. [
  43. ("Please check {printer}.", "Please check Printer."),
  44. ("{provider}: {task_name}, quality {print_quality}", "OctoEverywhere: benchy, quality 2/10"),
  45. ],
  46. )
  47. async def test_notification_preserves_custom_title_and_body(body, expected):
  48. from backend.app.services.notification_service import NotificationService
  49. service = NotificationService()
  50. template = NotificationTemplate(
  51. event_type="ai_failure_detection",
  52. name="AI Failure Detection",
  53. title_template="Check {printer}",
  54. body_template=body,
  55. )
  56. with (
  57. patch.object(service, "_get_providers_for_event", return_value=["provider"]),
  58. patch.object(service, "_get_template", return_value=template),
  59. patch.object(service, "_send_to_providers", new_callable=AsyncMock) as send,
  60. ):
  61. await service.on_ai_failure_detection(1, "Printer", "benchy", None, "notify", MagicMock(), print_quality=2)
  62. assert send.await_args.args[1:3] == ("Check Printer", expected)
  63. assert template.body_template == body
  64. async def test_obico_default_notification_keeps_confidence():
  65. from backend.app.services.notification_service import NotificationService
  66. service = NotificationService()
  67. template = NotificationTemplate(**next(t for t in DEFAULT_TEMPLATES if t["event_type"] == "ai_failure_detection"))
  68. with (
  69. patch.object(service, "_get_providers_for_event", return_value=["provider"]),
  70. patch.object(service, "_get_template", return_value=template),
  71. patch.object(service, "_send_to_providers", new_callable=AsyncMock) as send,
  72. ):
  73. await service.on_ai_failure_detection(1, "Printer", "benchy", 0.85, "pause", MagicMock())
  74. assert send.await_args.args[2] == "Printer: benchy\nConfidence: 0.85\nAction taken: pause"
  75. async def test_ai_notification_template_preview_includes_quality_variables():
  76. from backend.app.api.routes.notification_templates import preview_template
  77. from backend.app.schemas.notification_template import EVENT_VARIABLES, EventType, TemplatePreviewRequest
  78. assert {"provider", "print_quality", "task_name", "confidence", "action"} <= set(
  79. EVENT_VARIABLES[EventType.AI_FAILURE_DETECTION]
  80. )
  81. result = await preview_template(
  82. TemplatePreviewRequest(
  83. event_type=EventType.AI_FAILURE_DETECTION,
  84. title_template="{provider}: {printer}",
  85. body_template="{task_name}: {print_quality}, {confidence}, {action}",
  86. )
  87. )
  88. assert result.title == "OctoEverywhere: Bambu X1C"
  89. assert result.body == "Benchy.3mf: 2/10, N/A, pause"