test_bulk_archive_routes_scope.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. """Bulk archive actions keep to the caller's printers.
  2. Recalculating costs went over every archive, so a member of a group limited
  3. to some printers rewrote other printers' archives.
  4. """
  5. from __future__ import annotations
  6. import pytest
  7. from httpx import AsyncClient
  8. from sqlalchemy import select
  9. from backend.app.models.archive import PrintArchive
  10. from backend.tests.integration.test_printer_scope_1727 import TEAM_PERMISSIONS, _admin_token, _auth, _group, _user
  11. pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
  12. async def _member(client: AsyncClient, printer_ids: list[int]) -> dict[str, str]:
  13. admin = await _admin_token(client)
  14. perms = await _group(client, admin, "perms", permissions=[*TEAM_PERMISSIONS, "archives:update_all"])
  15. team = await _group(client, admin, "team", printer_ids=printer_ids)
  16. return _auth((await _user(client, admin, "member", [perms, team]))[0])
  17. async def test_recalculating_costs_leaves_other_printers_alone(
  18. async_client: AsyncClient, db_session, printer_factory, archive_factory
  19. ):
  20. a, b = await printer_factory(name="A"), await printer_factory(name="B")
  21. on_a = await archive_factory(a.id, cost=99.0, filament_used_grams=100, filament_type="PLA")
  22. on_b = await archive_factory(b.id, cost=99.0, filament_used_grams=100, filament_type="PLA")
  23. ids = (on_a.id, on_b.id)
  24. member = await _member(async_client, [a.id])
  25. response = await async_client.post("/api/v1/archives/recalculate-costs", headers=member)
  26. assert response.status_code == 200, response.text
  27. db_session.expire_all()
  28. costs = dict(
  29. (await db_session.execute(select(PrintArchive.id, PrintArchive.cost).where(PrintArchive.id.in_(ids)))).all()
  30. )
  31. assert costs[ids[0]] == 2.5 # 100 g at the default 25.00 per kg
  32. assert costs[ids[1]] == 99.0
  33. async def test_tags_keep_to_the_team_printers(async_client: AsyncClient, db_session, printer_factory, archive_factory):
  34. a, b = await printer_factory(name="A"), await printer_factory(name="B")
  35. on_a = await archive_factory(a.id, tags="shared, alpha")
  36. on_b = await archive_factory(b.id, tags="shared, beta")
  37. ids = (on_a.id, on_b.id)
  38. member = await _member(async_client, [a.id])
  39. listed = await async_client.get("/api/v1/archives/tags", headers=member)
  40. renamed = await async_client.put("/api/v1/archives/tags/shared", headers=member, json={"new_name": "common"})
  41. deleted = await async_client.delete("/api/v1/archives/tags/common", headers=member)
  42. assert sorted(t["name"] for t in listed.json()) == ["alpha", "shared"]
  43. assert (renamed.json()["affected"], deleted.json()["affected"]) == (1, 1)
  44. db_session.expire_all()
  45. tags = dict(
  46. (await db_session.execute(select(PrintArchive.id, PrintArchive.tags).where(PrintArchive.id.in_(ids)))).all()
  47. )
  48. assert tags[ids[1]] == "shared, beta"
  49. async def test_duplicates_and_similar_show_only_visible_archives(
  50. async_client: AsyncClient, printer_factory, archive_factory
  51. ):
  52. a, b = await printer_factory(name="A"), await printer_factory(name="B")
  53. mine = await archive_factory(a.id, print_name="Benchy", content_hash="h1")
  54. same_team = await archive_factory(a.id, print_name="Benchy", content_hash="h1")
  55. await archive_factory(b.id, print_name="Benchy", content_hash="h1")
  56. member = await _member(async_client, [a.id])
  57. duplicates = await async_client.get(f"/api/v1/archives/{mine.id}/duplicates", headers=member)
  58. detail = await async_client.get(f"/api/v1/archives/{mine.id}", headers=member)
  59. similar = await async_client.get(f"/api/v1/archives/{mine.id}/similar", headers=member)
  60. assert [d["id"] for d in duplicates.json()["duplicates"]] == [same_team.id]
  61. assert [d["id"] for d in detail.json()["duplicates"]] == [same_team.id]
  62. assert {s["archive"]["id"] for s in similar.json()} == {same_team.id}
  63. async def test_backfill_hashes_reports_only_the_team_printers_archives(
  64. async_client: AsyncClient, printer_factory, archive_factory
  65. ):
  66. a, b = await printer_factory(name="A"), await printer_factory(name="B")
  67. on_a = await archive_factory(a.id, file_path="archives/none-a.3mf", content_hash=None)
  68. await archive_factory(b.id, file_path="archives/none-b.3mf", content_hash=None)
  69. member = await _member(async_client, [a.id])
  70. response = await async_client.post("/api/v1/archives/backfill-hashes", headers=member)
  71. assert [e["id"] for e in response.json()["errors"]] == [on_a.id]
  72. async def test_uploading_a_source_by_name_matches_only_the_team_printers(
  73. async_client: AsyncClient, printer_factory, archive_factory
  74. ):
  75. a, b = await printer_factory(name="A"), await printer_factory(name="B")
  76. await archive_factory(b.id, print_name="Secret Part")
  77. member = await _member(async_client, [a.id])
  78. response = await async_client.post(
  79. "/api/v1/archives/upload-source",
  80. headers=member,
  81. params={"print_name": "Secret"},
  82. files={"file": ("Secret Part.3mf", b"PK\x03\x04", "application/octet-stream")},
  83. )
  84. assert response.status_code == 404, response.text
  85. assert "Secret Part" not in response.text
  86. async def test_the_list_counts_duplicates_among_visible_archives_only(
  87. async_client: AsyncClient, printer_factory, archive_factory
  88. ):
  89. a, b = await printer_factory(name="A"), await printer_factory(name="B")
  90. await archive_factory(b.id, print_name="Benchy", content_hash="h2") # older, on B
  91. mine = await archive_factory(a.id, print_name="Benchy", content_hash="h2")
  92. member = await _member(async_client, [a.id])
  93. listed = {row["id"]: row for row in (await async_client.get("/api/v1/archives/", headers=member)).json()}
  94. assert listed[mine.id]["duplicate_count"] in (0, None)
  95. assert listed[mine.id]["original_archive_id"] is None
  96. async def test_the_no_3mf_warning_ignores_other_printers(async_client: AsyncClient, printer_factory, archive_factory):
  97. a, b = await printer_factory(name="A"), await printer_factory(name="B")
  98. await archive_factory(b.id, extra_data={"no_3mf_available": True, "no_3mf_reason": "internal_storage"})
  99. member = await _member(async_client, [a.id])
  100. response = await async_client.get("/api/v1/archives/no-3mf-warning", headers=member)
  101. assert response.json() == {"has_fallback": False, "reason": None}