test_stuck_budget_reservations.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. """Reservations no print can use any more are released at start.
  2. Before b2ed37b6c, billing a print without a user (authentication off, or a
  3. user deleted while it printed) stopped before releasing the print's budget
  4. reservation, so it held that much of the cost centre's budget for good. A
  5. reservation whose queue entry has finished or is gone can never be charged.
  6. """
  7. from __future__ import annotations
  8. import pytest
  9. from sqlalchemy import select
  10. from backend.app.core.database import _release_stuck_budget_reservations
  11. from backend.app.models.finance import BudgetReservation, CostCenter
  12. from backend.app.models.print_queue import PrintQueueItem
  13. pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
  14. CASES = {
  15. # queue entry status (None: entry deleted) -> reservation released?
  16. "completed": True,
  17. "failed": True,
  18. "cancelled": True,
  19. None: True,
  20. "printing": False,
  21. "pending": False,
  22. "skipped": False, # can go back to pending
  23. }
  24. async def test_reservations_of_finished_or_deleted_entries_are_released(db_session, printer_factory, archive_factory):
  25. printer = await printer_factory()
  26. archive = await archive_factory(printer.id)
  27. center = CostCenter(name="Lab", total_budget=100)
  28. db_session.add(center)
  29. await db_session.flush()
  30. reservation_ids = {}
  31. for status in CASES:
  32. if status is None:
  33. source_id = 999_999
  34. else:
  35. item = PrintQueueItem(printer_id=printer.id, archive_id=archive.id, status=status)
  36. db_session.add(item)
  37. await db_session.flush()
  38. source_id = item.id
  39. reservation = BudgetReservation(
  40. cost_center_id=center.id, amount=5, status="active", source_type="print_queue", source_id=source_id
  41. )
  42. db_session.add(reservation)
  43. await db_session.flush()
  44. reservation_ids[status] = reservation.id
  45. other = BudgetReservation(cost_center_id=center.id, amount=5, status="active", source_type="manual", source_id=1)
  46. db_session.add(other)
  47. await db_session.commit()
  48. other_id = other.id
  49. await _release_stuck_budget_reservations(await db_session.connection())
  50. await _release_stuck_budget_reservations(await db_session.connection()) # a restart changes nothing
  51. await db_session.commit()
  52. db_session.expire_all()
  53. rows = dict((await db_session.execute(select(BudgetReservation.id, BudgetReservation.status))).all())
  54. released = {status: rows[rid] == "released" for status, rid in reservation_ids.items()}
  55. assert released == CASES
  56. assert rows[other_id] == "active"