test_print_log_one_entry_per_run.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. """One print log entry per run, however often the run is reported.
  2. A run is its archive, printer and start time. A reprint starts at a new time
  3. and a plate printing on a second printer is a run of its own, so both get
  4. their own entries; the same run reported again replaces its entry.
  5. """
  6. from datetime import datetime, timedelta, timezone
  7. import pytest
  8. from sqlalchemy import select
  9. from backend.app.models.print_log import PrintLogEntry
  10. from backend.app.services.print_log import write_log_entry
  11. pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
  12. START = datetime(2026, 10, 19, 8, 0)
  13. @pytest.fixture
  14. async def rows(db_session, printer_factory) -> dict:
  15. """Two printers and an archive, so the foreign keys hold (PostgreSQL)."""
  16. from backend.app.models.archive import PrintArchive
  17. first, second = await printer_factory(name="A"), await printer_factory(name="B")
  18. archive = PrintArchive(
  19. printer_id=first.id, filename="a.3mf", file_path="a.3mf", file_size=1, status="printing", started_at=START
  20. )
  21. db_session.add(archive)
  22. await db_session.commit()
  23. return {"printer": first.id, "other_printer": second.id, "archive": archive}
  24. async def _write(db, rows, *, printer_id=None, started_at=START, status="completed", queue_item_id=None, cost=None):
  25. await write_log_entry(
  26. db,
  27. status=status,
  28. archive_id=rows["archive"].id,
  29. printer_id=printer_id or rows["printer"],
  30. started_at=started_at,
  31. completed_at=started_at + timedelta(hours=1),
  32. queue_item_id=queue_item_id,
  33. cost=cost,
  34. )
  35. await db.commit()
  36. async def _entries(db) -> list[tuple]:
  37. rows = (await db.scalars(select(PrintLogEntry).order_by(PrintLogEntry.id))).all()
  38. return [(r.printer_id, r.started_at, r.status, r.queue_item_id) for r in rows]
  39. async def test_the_same_run_reported_twice_is_one_entry(db_session, rows):
  40. await _write(db_session, rows, status="cancelled")
  41. await _write(db_session, rows, status="completed")
  42. assert await _entries(db_session) == [(rows["printer"], START, "completed", None)]
  43. async def test_a_reprint_is_an_entry_of_its_own(db_session, rows):
  44. await _write(db_session, rows)
  45. await _write(db_session, rows, started_at=START + timedelta(days=1))
  46. assert len(await _entries(db_session)) == 2
  47. async def test_a_plate_on_another_printer_is_an_entry_of_its_own(db_session, rows):
  48. await _write(db_session, rows)
  49. await _write(db_session, rows, printer_id=rows["other_printer"])
  50. assert len(await _entries(db_session)) == 2
  51. async def test_an_aware_start_time_is_the_same_run(db_session, rows):
  52. """The stop route holds aware times, the columns store naive UTC."""
  53. await _write(db_session, rows, status="cancelled")
  54. await _write(db_session, rows, started_at=START.replace(tzinfo=timezone.utc))
  55. assert [status for _p, _s, status, _q in await _entries(db_session)] == ["completed"]
  56. async def test_the_same_outcome_reported_again_changes_nothing(db_session, rows):
  57. """A repeat lacks the run's tracked usage; the entry may have been edited
  58. or given its energy since."""
  59. await _write(db_session, rows, cost=4.20)
  60. entry = await db_session.scalar(select(PrintLogEntry))
  61. entry.energy_kwh = 0.5
  62. entry.failure_reason = "edited"
  63. await db_session.commit()
  64. await _write(db_session, rows, cost=5.00)
  65. db_session.expire_all()
  66. entry = await db_session.scalar(select(PrintLogEntry))
  67. assert (entry.cost, entry.energy_kwh, entry.failure_reason) == (4.20, 0.5, "edited")
  68. class TestOtherRunsOf:
  69. """Whether an archive's card still shows its first run (#1378)."""
  70. async def _entry(self, db, rows, started_at, printer_id=None):
  71. db.add(
  72. PrintLogEntry(
  73. archive_id=rows["archive"].id,
  74. printer_id=printer_id or rows["printer"],
  75. status="cancelled",
  76. started_at=started_at,
  77. )
  78. )
  79. await db.commit()
  80. async def test_the_current_run_s_own_entry_is_not_another_run(self, db_session, rows):
  81. from backend.app.services.print_log import other_runs_of
  82. await self._entry(db_session, rows, START)
  83. assert await other_runs_of(db_session, rows["archive"]) == 0
  84. async def test_an_earlier_run_or_another_printer_is(self, db_session, rows):
  85. from backend.app.services.print_log import other_runs_of
  86. await self._entry(db_session, rows, START - timedelta(days=1))
  87. await self._entry(db_session, rows, START, printer_id=rows["other_printer"])
  88. assert await other_runs_of(db_session, rows["archive"]) == 2
  89. async def test_without_a_start_time_every_entry_is(self, db_session, rows):
  90. from backend.app.services.print_log import other_runs_of
  91. await self._entry(db_session, rows, START)
  92. rows["archive"].started_at = None
  93. await db_session.commit()
  94. assert await other_runs_of(db_session, rows["archive"]) == 1