test_maintenance_item_get_or_create.py 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. """A maintenance row created on first read is taken, not duplicated (FO-C14).
  2. Two reads of a new printer's maintenance -- two dashboards, or a dashboard
  3. and the background check -- could each create the same row. With the unique
  4. index the second insert is refused; the read then uses the row the other one
  5. created, and its session carries on.
  6. """
  7. import pytest
  8. from sqlalchemy import func, select
  9. from backend.app.api.routes.maintenance import _get_or_create_maintenance_item, ensure_default_types
  10. from backend.app.models.maintenance import MaintenanceType, PrinterMaintenance
  11. pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
  12. async def test_the_row_another_read_created_is_used(db_session, printer_factory):
  13. printer = await printer_factory()
  14. await ensure_default_types(db_session)
  15. type_id = (await db_session.scalars(select(MaintenanceType.id).limit(1))).first()
  16. theirs = PrinterMaintenance(
  17. printer_id=printer.id, maintenance_type_id=type_id, enabled=True, last_performed_hours=3.0
  18. )
  19. db_session.add(theirs)
  20. await db_session.commit()
  21. item = await _get_or_create_maintenance_item(db_session, printer.id, type_id)
  22. assert item.id == theirs.id
  23. count = await db_session.scalar(
  24. select(func.count()).select_from(PrinterMaintenance).where(PrinterMaintenance.printer_id == printer.id)
  25. )
  26. assert count == 1
  27. # The refused insert was rolled back to its savepoint only.
  28. await db_session.commit()
  29. async def test_a_missing_row_is_created(db_session, printer_factory):
  30. printer = await printer_factory()
  31. await ensure_default_types(db_session)
  32. type_id = (await db_session.scalars(select(MaintenanceType.id).limit(1))).first()
  33. item = await _get_or_create_maintenance_item(db_session, printer.id, type_id)
  34. assert item.id is not None
  35. assert (item.enabled, item.last_performed_hours) == (True, 0.0)
  36. async def test_a_second_assign_racing_the_first_is_refused_as_assigned(db_session, printer_factory):
  37. """Both pass the "already assigned?" check (a double click); the index now
  38. refuses the second insert, which answers the route's own 400, not a 500."""
  39. from unittest.mock import MagicMock, patch
  40. from fastapi import HTTPException
  41. from sqlalchemy.ext.asyncio import AsyncSession
  42. from backend.app.api.routes.maintenance import assign_maintenance_type
  43. printer = await printer_factory()
  44. await ensure_default_types(db_session)
  45. type_id = (await db_session.scalars(select(MaintenanceType.id).limit(1))).first()
  46. db_session.add(PrinterMaintenance(printer_id=printer.id, maintenance_type_id=type_id, enabled=True))
  47. await db_session.commit()
  48. real_execute = AsyncSession.execute
  49. async def check_sees_nothing(self, statement, *args, **kwargs):
  50. sql = str(statement)
  51. if "FROM printer_maintenance" in sql and "maintenance_type_id" in sql:
  52. return MagicMock(scalar_one_or_none=MagicMock(return_value=None))
  53. return await real_execute(self, statement, *args, **kwargs)
  54. with patch.object(AsyncSession, "execute", check_sees_nothing), pytest.raises(HTTPException) as refused:
  55. await assign_maintenance_type(printer.id, type_id, db_session, None)
  56. assert refused.value.status_code == 400