test_archive_update_targets.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. """Editing an archive cannot move it to a printer or project the caller may not use.
  2. The archive edit checked only the printer the archive was on. A member of a
  3. group limited to printer A could move an archive of A to printer B, into the
  4. history of a team they have no access to, and any caller could link an
  5. archive to a printer or project that does not exist (stored dangling on
  6. SQLite, a server error on PostgreSQL). Upload already checked the printer.
  7. """
  8. from __future__ import annotations
  9. import pytest
  10. from httpx import AsyncClient
  11. from backend.tests.integration.test_printer_scope_1727 import TEAM_PERMISSIONS, _admin_token, _auth, _group, _user
  12. pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
  13. async def _member(client: AsyncClient, admin: str, name: str, printer_ids: list[int]) -> dict[str, str]:
  14. perms = await _group(client, admin, f"perms_{name}", permissions=[*TEAM_PERMISSIONS, "archives:update_all"])
  15. team = await _group(client, admin, f"team_{name}", printer_ids=printer_ids)
  16. return _auth((await _user(client, admin, name, [perms, team]))[0])
  17. async def _printer_of(client: AsyncClient, headers: dict[str, str], archive_id: int) -> int | None:
  18. response = await client.get(f"/api/v1/archives/{archive_id}", headers=headers)
  19. assert response.status_code == 200, response.text
  20. return response.json()["printer_id"]
  21. async def test_a_member_cannot_move_an_archive_to_a_printer_outside_the_team(
  22. async_client: AsyncClient, printer_factory, archive_factory
  23. ):
  24. a, b = await printer_factory(name="A"), await printer_factory(name="B")
  25. admin = await _admin_token(async_client)
  26. member = await _member(async_client, admin, "member", [a.id])
  27. other_team = await _member(async_client, admin, "other", [b.id])
  28. archive = await archive_factory(a.id, print_name="on-a")
  29. response = await async_client.patch(f"/api/v1/archives/{archive.id}", headers=member, json={"printer_id": b.id})
  30. assert response.status_code == 404, response.text
  31. assert response.json()["detail"] == "Printer not found"
  32. assert await _printer_of(async_client, _auth(admin), archive.id) == a.id
  33. listed = await async_client.get("/api/v1/archives/", headers=other_team)
  34. assert [x["print_name"] for x in listed.json()] == []
  35. async def test_a_member_can_move_an_archive_between_team_printers(
  36. async_client: AsyncClient, printer_factory, archive_factory
  37. ):
  38. a, a2 = await printer_factory(name="A"), await printer_factory(name="A2")
  39. admin = await _admin_token(async_client)
  40. member = await _member(async_client, admin, "member", [a.id, a2.id])
  41. archive = await archive_factory(a.id)
  42. response = await async_client.patch(f"/api/v1/archives/{archive.id}", headers=member, json={"printer_id": a2.id})
  43. assert response.status_code == 200, response.text
  44. assert await _printer_of(async_client, member, archive.id) == a2.id
  45. async def test_a_member_can_still_clear_the_printer(async_client: AsyncClient, printer_factory, archive_factory):
  46. # As an upload without a printer: a printer-less archive is in every scope.
  47. a = await printer_factory(name="A")
  48. admin = await _admin_token(async_client)
  49. member = await _member(async_client, admin, "member", [a.id])
  50. archive = await archive_factory(a.id)
  51. response = await async_client.patch(f"/api/v1/archives/{archive.id}", headers=member, json={"printer_id": None})
  52. assert response.status_code == 200, response.text
  53. assert await _printer_of(async_client, member, archive.id) is None
  54. async def test_an_admin_can_move_an_archive_to_any_printer(async_client: AsyncClient, printer_factory, archive_factory):
  55. a, b = await printer_factory(name="A"), await printer_factory(name="B")
  56. archive = await archive_factory(a.id)
  57. response = await async_client.patch(f"/api/v1/archives/{archive.id}", json={"printer_id": b.id})
  58. assert response.status_code == 200, response.text
  59. assert response.json()["printer_id"] == b.id
  60. @pytest.mark.parametrize("printer_id", [0, 9999])
  61. async def test_an_unknown_printer_is_refused(async_client: AsyncClient, printer_factory, archive_factory, printer_id):
  62. a = await printer_factory(name="A")
  63. archive = await archive_factory(a.id)
  64. response = await async_client.patch(f"/api/v1/archives/{archive.id}", json={"printer_id": printer_id})
  65. assert response.status_code == 400, response.text
  66. assert response.json()["detail"] == "Printer not found"
  67. assert (await async_client.get(f"/api/v1/archives/{archive.id}")).json()["printer_id"] == a.id
  68. @pytest.mark.parametrize("project_id", [0, 9999])
  69. async def test_an_unknown_project_is_refused(async_client: AsyncClient, printer_factory, archive_factory, project_id):
  70. a = await printer_factory(name="A")
  71. archive = await archive_factory(a.id)
  72. response = await async_client.patch(f"/api/v1/archives/{archive.id}", json={"project_id": project_id})
  73. assert response.status_code == 400, response.text
  74. assert response.json()["detail"] == "Project not found"
  75. assert (await async_client.get(f"/api/v1/archives/{archive.id}")).json()["project_id"] is None
  76. async def test_a_known_project_can_be_set_and_cleared(async_client: AsyncClient, printer_factory, archive_factory):
  77. a = await printer_factory(name="A")
  78. archive = await archive_factory(a.id)
  79. project = (await async_client.post("/api/v1/projects/", json={"name": "P"})).json()
  80. linked = await async_client.patch(f"/api/v1/archives/{archive.id}", json={"project_id": project["id"]})
  81. cleared = await async_client.patch(f"/api/v1/archives/{archive.id}", json={"project_id": None})
  82. assert linked.status_code == 200, linked.text
  83. assert linked.json()["project_id"] == project["id"]
  84. assert cleared.status_code == 200, cleared.text
  85. assert cleared.json()["project_id"] is None