test_pending_upload_archive_project.py 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. """Archiving a pending upload into a project checks the project first.
  2. The project id from the request was stored on the archive and the pending
  3. upload unchecked, after the file had already been archived: an unknown project
  4. failed with a server error on PostgreSQL (foreign key) with the archive files
  5. already written, and was stored as a broken link on SQLite. 0 skipped the
  6. archive but was stored on the pending upload.
  7. """
  8. from __future__ import annotations
  9. from unittest.mock import AsyncMock, patch
  10. import pytest
  11. from httpx import AsyncClient
  12. from sqlalchemy import select
  13. from backend.app.models.archive import PrintArchive
  14. from backend.app.models.pending_upload import PendingUpload
  15. pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
  16. async def _pending(db_session, tmp_path) -> PendingUpload:
  17. file = tmp_path / "part.3mf"
  18. file.write_bytes(b"3mf")
  19. upload = PendingUpload(
  20. filename="part.3mf", file_path=str(file), file_size=3, source_ip="192.168.1.100", status="pending"
  21. )
  22. db_session.add(upload)
  23. await db_session.commit()
  24. await db_session.refresh(upload)
  25. return upload
  26. async def test_an_unknown_project_is_refused_before_anything_is_archived(
  27. async_client: AsyncClient, db_session, tmp_path
  28. ):
  29. upload_id = (await _pending(db_session, tmp_path)).id
  30. with patch("backend.app.api.routes.pending_uploads.ArchiveService.archive_print", new=AsyncMock()) as archive:
  31. response = await async_client.post(f"/api/v1/pending-uploads/{upload_id}/archive", json={"project_id": 9999})
  32. assert response.status_code == 400, response.text
  33. assert response.json()["detail"] == "Project not found"
  34. archive.assert_not_awaited()
  35. db_session.expire_all()
  36. row = (
  37. await db_session.execute(
  38. select(PendingUpload.status, PendingUpload.project_id).where(PendingUpload.id == upload_id)
  39. )
  40. ).one()
  41. assert tuple(row) == ("pending", None)
  42. assert (tmp_path / "part.3mf").exists()
  43. async def _fake_archive_print(self, **_kwargs) -> PrintArchive:
  44. """Stands in for archiving a real 3MF: one archive row in the route's session."""
  45. archive = PrintArchive(filename="part.3mf", file_path="archive/part.3mf", file_size=3, status="archived")
  46. self.db.add(archive)
  47. await self.db.flush()
  48. return archive
  49. @pytest.mark.parametrize("given", ["known", 0, None])
  50. async def test_a_known_project_or_none_is_stored(async_client: AsyncClient, db_session, tmp_path, given):
  51. upload_id = (await _pending(db_session, tmp_path)).id
  52. project_id = None
  53. if given == "known":
  54. project_id = (await async_client.post("/api/v1/projects/", json={"name": "P"})).json()["id"]
  55. with patch("backend.app.api.routes.pending_uploads.ArchiveService.archive_print", new=_fake_archive_print):
  56. response = await async_client.post(
  57. f"/api/v1/pending-uploads/{upload_id}/archive",
  58. json={"project_id": project_id if given == "known" else given},
  59. )
  60. assert response.status_code == 200, response.text
  61. db_session.expire_all()
  62. stored = (
  63. await db_session.execute(select(PrintArchive.project_id).where(PrintArchive.id == response.json()["id"]))
  64. ).scalar()
  65. pending = (await db_session.execute(select(PendingUpload.project_id).where(PendingUpload.id == upload_id))).scalar()
  66. assert stored == project_id
  67. assert pending == project_id