test_archives_api.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. """Integration tests for Archives API endpoints.
  2. Tests the full request/response cycle for /api/v1/archives/ endpoints.
  3. """
  4. import pytest
  5. from httpx import AsyncClient
  6. class TestArchivesAPI:
  7. """Integration tests for /api/v1/archives/ endpoints."""
  8. # ========================================================================
  9. # List endpoints
  10. # ========================================================================
  11. @pytest.mark.asyncio
  12. @pytest.mark.integration
  13. async def test_list_archives_empty(self, async_client: AsyncClient):
  14. """Verify empty list is returned when no archives exist."""
  15. response = await async_client.get("/api/v1/archives/")
  16. assert response.status_code == 200
  17. data = response.json()
  18. assert isinstance(data, list)
  19. assert len(data) == 0
  20. @pytest.mark.asyncio
  21. @pytest.mark.integration
  22. async def test_list_archives_with_data(
  23. self, async_client: AsyncClient, archive_factory, printer_factory, db_session
  24. ):
  25. """Verify list returns existing archives."""
  26. printer = await printer_factory()
  27. await archive_factory(printer.id, print_name="Test Archive")
  28. response = await async_client.get("/api/v1/archives/")
  29. assert response.status_code == 200
  30. data = response.json()
  31. assert isinstance(data, list)
  32. assert len(data) >= 1
  33. assert any(a["print_name"] == "Test Archive" for a in data)
  34. @pytest.mark.asyncio
  35. @pytest.mark.integration
  36. async def test_list_archives_pagination(
  37. self, async_client: AsyncClient, archive_factory, printer_factory, db_session
  38. ):
  39. """Verify pagination works correctly."""
  40. printer = await printer_factory()
  41. # Create 5 archives
  42. for i in range(5):
  43. await archive_factory(printer.id, print_name=f"Archive {i}")
  44. # Get first page with limit 2
  45. response = await async_client.get("/api/v1/archives/?limit=2&offset=0")
  46. assert response.status_code == 200
  47. data = response.json()
  48. assert isinstance(data, list)
  49. assert len(data) == 2
  50. @pytest.mark.asyncio
  51. @pytest.mark.integration
  52. async def test_list_archives_filter_by_printer(
  53. self, async_client: AsyncClient, archive_factory, printer_factory, db_session
  54. ):
  55. """Verify filtering by printer_id works."""
  56. printer1 = await printer_factory(name="Printer 1", serial_number="00M09A000000001")
  57. printer2 = await printer_factory(name="Printer 2", serial_number="00M09A000000002")
  58. await archive_factory(printer1.id, print_name="Printer 1 Archive")
  59. await archive_factory(printer2.id, print_name="Printer 2 Archive")
  60. response = await async_client.get(f"/api/v1/archives/?printer_id={printer1.id}")
  61. assert response.status_code == 200
  62. data = response.json()
  63. assert all(a["printer_id"] == printer1.id for a in data)
  64. # ========================================================================
  65. # Get single endpoint
  66. # ========================================================================
  67. @pytest.mark.asyncio
  68. @pytest.mark.integration
  69. async def test_get_archive(self, async_client: AsyncClient, archive_factory, printer_factory, db_session):
  70. """Verify single archive can be retrieved."""
  71. printer = await printer_factory()
  72. archive = await archive_factory(printer.id, print_name="Get Test Archive")
  73. response = await async_client.get(f"/api/v1/archives/{archive.id}")
  74. assert response.status_code == 200
  75. result = response.json()
  76. assert result["id"] == archive.id
  77. assert result["print_name"] == "Get Test Archive"
  78. @pytest.mark.asyncio
  79. @pytest.mark.integration
  80. async def test_get_archive_not_found(self, async_client: AsyncClient):
  81. """Verify 404 for non-existent archive."""
  82. response = await async_client.get("/api/v1/archives/9999")
  83. assert response.status_code == 404
  84. # ========================================================================
  85. # Update endpoints
  86. # ========================================================================
  87. @pytest.mark.asyncio
  88. @pytest.mark.integration
  89. async def test_update_archive_name(self, async_client: AsyncClient, archive_factory, printer_factory, db_session):
  90. """Verify archive name can be updated."""
  91. printer = await printer_factory()
  92. archive = await archive_factory(printer.id, print_name="Original Name")
  93. response = await async_client.patch(f"/api/v1/archives/{archive.id}", json={"print_name": "Updated Name"})
  94. assert response.status_code == 200
  95. assert response.json()["print_name"] == "Updated Name"
  96. @pytest.mark.asyncio
  97. @pytest.mark.integration
  98. async def test_update_archive_notes(self, async_client: AsyncClient, archive_factory, printer_factory, db_session):
  99. """Verify archive notes can be updated."""
  100. printer = await printer_factory()
  101. archive = await archive_factory(printer.id)
  102. response = await async_client.patch(f"/api/v1/archives/{archive.id}", json={"notes": "Great print!"})
  103. assert response.status_code == 200
  104. assert response.json()["notes"] == "Great print!"
  105. @pytest.mark.asyncio
  106. @pytest.mark.integration
  107. async def test_update_archive_favorite(
  108. self, async_client: AsyncClient, archive_factory, printer_factory, db_session
  109. ):
  110. """Verify archive favorite status can be updated."""
  111. printer = await printer_factory()
  112. archive = await archive_factory(printer.id)
  113. response = await async_client.patch(f"/api/v1/archives/{archive.id}", json={"is_favorite": True})
  114. assert response.status_code == 200
  115. assert response.json()["is_favorite"] is True
  116. # ========================================================================
  117. # Delete endpoints
  118. # ========================================================================
  119. @pytest.mark.asyncio
  120. @pytest.mark.integration
  121. async def test_delete_archive(self, async_client: AsyncClient, archive_factory, printer_factory, db_session):
  122. """Verify archive can be deleted."""
  123. printer = await printer_factory()
  124. archive = await archive_factory(printer.id)
  125. archive_id = archive.id
  126. response = await async_client.delete(f"/api/v1/archives/{archive_id}")
  127. assert response.status_code == 200
  128. # Verify deleted
  129. response = await async_client.get(f"/api/v1/archives/{archive_id}")
  130. assert response.status_code == 404
  131. @pytest.mark.asyncio
  132. @pytest.mark.integration
  133. async def test_delete_nonexistent_archive(self, async_client: AsyncClient):
  134. """Verify deleting non-existent archive returns 404."""
  135. response = await async_client.delete("/api/v1/archives/9999")
  136. assert response.status_code == 404
  137. # ========================================================================
  138. # Statistics endpoints
  139. # ========================================================================
  140. @pytest.mark.asyncio
  141. @pytest.mark.integration
  142. async def test_get_archive_stats(self, async_client: AsyncClient, archive_factory, printer_factory, db_session):
  143. """Verify archive statistics can be retrieved."""
  144. printer = await printer_factory()
  145. await archive_factory(
  146. printer.id,
  147. status="completed",
  148. print_time_seconds=3600,
  149. filament_used_grams=50.0,
  150. )
  151. await archive_factory(
  152. printer.id,
  153. status="completed",
  154. print_time_seconds=7200,
  155. filament_used_grams=100.0,
  156. )
  157. response = await async_client.get("/api/v1/archives/stats")
  158. assert response.status_code == 200
  159. result = response.json()
  160. # Check for actual stats fields
  161. assert "total_prints" in result
  162. assert "successful_prints" in result
  163. class TestArchiveDataIntegrity:
  164. """Tests for archive data integrity."""
  165. @pytest.mark.asyncio
  166. @pytest.mark.integration
  167. async def test_archive_linked_to_printer(
  168. self, async_client: AsyncClient, archive_factory, printer_factory, db_session
  169. ):
  170. """Verify archive is properly linked to printer."""
  171. printer = await printer_factory(name="My Printer")
  172. archive = await archive_factory(printer.id)
  173. response = await async_client.get(f"/api/v1/archives/{archive.id}")
  174. assert response.status_code == 200
  175. result = response.json()
  176. assert result["printer_id"] == printer.id
  177. @pytest.mark.asyncio
  178. @pytest.mark.integration
  179. async def test_archive_stores_print_data(
  180. self, async_client: AsyncClient, archive_factory, printer_factory, db_session
  181. ):
  182. """Verify archive stores all print data correctly."""
  183. printer = await printer_factory()
  184. archive = await archive_factory(
  185. printer.id,
  186. print_name="Test Print",
  187. filename="test.3mf",
  188. status="completed",
  189. filament_type="PLA",
  190. filament_used_grams=75.5,
  191. print_time_seconds=5400,
  192. )
  193. response = await async_client.get(f"/api/v1/archives/{archive.id}")
  194. assert response.status_code == 200
  195. result = response.json()
  196. assert result["print_name"] == "Test Print"
  197. assert result["filename"] == "test.3mf"
  198. assert result["status"] == "completed"
  199. assert result["filament_type"] == "PLA"
  200. assert result["filament_used_grams"] == 75.5
  201. assert result["print_time_seconds"] == 5400
  202. @pytest.mark.asyncio
  203. @pytest.mark.integration
  204. async def test_archive_update_persists(
  205. self, async_client: AsyncClient, archive_factory, printer_factory, db_session
  206. ):
  207. """CRITICAL: Verify archive updates persist."""
  208. printer = await printer_factory()
  209. archive = await archive_factory(printer.id, notes="Original notes")
  210. # Update
  211. await async_client.patch(f"/api/v1/archives/{archive.id}", json={"notes": "Updated notes", "is_favorite": True})
  212. # Verify persistence
  213. response = await async_client.get(f"/api/v1/archives/{archive.id}")
  214. result = response.json()
  215. assert result["notes"] == "Updated notes"
  216. assert result["is_favorite"] is True