test_slot.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. from datetime import datetime, timedelta, timezone
  2. import uuid
  3. from httpx import AsyncClient
  4. from sqlalchemy import select
  5. from sqlalchemy.orm import Session
  6. from app.main import app
  7. from app.models import Project, Slot, Volunteer
  8. from app.tests.conftest import (
  9. default_project_id,
  10. default_slot_id,
  11. default_volunteer_id,
  12. )
  13. async def test_read_list_project_slots(
  14. client: AsyncClient, default_user_headers: dict, default_public_project: Project
  15. ):
  16. response = await client.get(
  17. app.url_path_for("list_project_slots", project_id=default_project_id),
  18. )
  19. assert response.status_code == 401
  20. response = await client.get(
  21. app.url_path_for("list_project_slots", project_id=uuid.uuid4()),
  22. headers=default_user_headers,
  23. )
  24. assert response.status_code == 404
  25. response = await client.get(
  26. app.url_path_for("list_project_slots", project_id="pas un uuid valid"),
  27. headers=default_user_headers,
  28. )
  29. assert response.status_code == 422
  30. response = await client.get(
  31. app.url_path_for("list_project_slots", project_id=default_project_id),
  32. headers=default_user_headers,
  33. )
  34. assert response.status_code == 200
  35. data = response.json()
  36. assert len(data) == 1
  37. slot_response = data[0]
  38. assert slot_response["title"] == "être roi"
  39. assert slot_response["id"] == default_slot_id
  40. assert "created_at" in slot_response
  41. async def test_create_slot(
  42. client: AsyncClient,
  43. default_public_project: Project,
  44. default_user_headers: dict,
  45. session: Session,
  46. ):
  47. # Test without autentication
  48. response = await client.post(
  49. app.url_path_for("create_slot", project_id=default_project_id)
  50. )
  51. assert response.status_code == 401
  52. starting_time = datetime(1900, 1, 1)
  53. payload = {
  54. "title": "être mort",
  55. "starting_time": starting_time.isoformat(),
  56. "ending_time": (starting_time + timedelta(minutes=60)).isoformat(),
  57. }
  58. # test invalid project_id
  59. response = await client.post(
  60. app.url_path_for("create_slot", project_id=uuid.uuid4()),
  61. json=payload,
  62. headers=default_user_headers,
  63. )
  64. assert response.status_code == 404
  65. # Test normal payload
  66. response = await client.post(
  67. app.url_path_for("create_slot", project_id=default_project_id),
  68. json=payload,
  69. headers=default_user_headers,
  70. )
  71. assert response.status_code == 200
  72. assert response.json()["id"] != default_slot_id
  73. assert response.json()["title"] == "être mort"
  74. result = session.execute(select(Slot).where(Slot.project_id == default_project_id))
  75. slots = result.scalars().all()
  76. assert len(slots) > 1
  77. slot = [s for s in slots if s.id != default_slot_id][0]
  78. assert slot.title == "être mort"
  79. assert abs(
  80. slot.starting_time - starting_time.replace(tzinfo=timezone.utc)
  81. ) < timedelta(minutes=30)
  82. # test invalid payload
  83. del payload["title"]
  84. response = await client.post(
  85. app.url_path_for("create_slot", project_id=default_project_id),
  86. json=payload,
  87. headers=default_user_headers,
  88. )
  89. assert response.status_code == 422
  90. async def test_update_slot(
  91. client: AsyncClient,
  92. default_public_project: Project,
  93. default_user_headers: dict,
  94. session: Session,
  95. ):
  96. # Test without autentication
  97. response = await client.post(
  98. app.url_path_for(
  99. "update_slot",
  100. project_id=default_project_id,
  101. slot_id=default_slot_id,
  102. )
  103. )
  104. assert response.status_code == 401
  105. starting_time = datetime(2000, 1, 1, tzinfo=timezone.utc)
  106. payload = {
  107. "title": "être mort 2 fois",
  108. "place": "être mort 2 fois",
  109. "responsible_contact": "être mort 2 fois",
  110. "starting_time": starting_time.isoformat(),
  111. "ending_time": (starting_time + timedelta(minutes=60)).isoformat(),
  112. }
  113. # test invalid project_id
  114. response = await client.post(
  115. app.url_path_for(
  116. "update_slot",
  117. project_id=uuid.uuid4(),
  118. slot_id=default_slot_id,
  119. ),
  120. json=payload,
  121. headers=default_user_headers,
  122. )
  123. assert response.status_code == 404
  124. # test invalid slot_id
  125. response = await client.post(
  126. app.url_path_for(
  127. "update_slot",
  128. project_id=default_project_id,
  129. slot_id=uuid.uuid4(),
  130. ),
  131. json=payload,
  132. headers=default_user_headers,
  133. )
  134. assert response.status_code == 404
  135. # Test normal payload
  136. for k, v in payload.items():
  137. response = await client.post(
  138. app.url_path_for(
  139. "update_slot",
  140. project_id=default_project_id,
  141. slot_id=default_slot_id,
  142. ),
  143. json={k: v},
  144. headers=default_user_headers,
  145. )
  146. assert response.status_code == 200
  147. assert response.json()["id"] == default_slot_id
  148. if "time" in k:
  149. assert datetime.fromisoformat(response.json()[k]) == datetime.fromisoformat(
  150. v
  151. )
  152. else:
  153. assert response.json()[k] == v
  154. async def test_update_slot_volunteers(
  155. client: AsyncClient,
  156. default_public_project: Project,
  157. default_user_headers: dict,
  158. session: Session,
  159. ):
  160. response = await client.post(
  161. app.url_path_for(
  162. "update_slot",
  163. project_id=default_project_id,
  164. slot_id=default_slot_id,
  165. ),
  166. json={"volunteers": []},
  167. headers=default_user_headers,
  168. )
  169. assert response.status_code == 200
  170. result = session.execute(
  171. select(Volunteer).where(Volunteer.id == default_volunteer_id)
  172. )
  173. volunteer = result.scalars().first()
  174. assert volunteer is not None
  175. assert volunteer.slots_id == []
  176. response = await client.post(
  177. app.url_path_for(
  178. "update_slot",
  179. project_id=default_project_id,
  180. slot_id=default_slot_id,
  181. ),
  182. json={"volunteers": [default_volunteer_id]},
  183. headers=default_user_headers,
  184. )
  185. assert response.status_code == 200
  186. session.refresh(volunteer)
  187. assert volunteer is not None
  188. assert volunteer.slots_id == [default_slot_id]
  189. # An invalid volunteer list
  190. response = await client.post(
  191. app.url_path_for(
  192. "update_slot",
  193. project_id=default_project_id,
  194. slot_id=default_slot_id,
  195. ),
  196. json={"volunteers": [str(uuid.uuid4())]},
  197. headers=default_user_headers,
  198. )
  199. assert response.status_code == 400
  200. # An invalid volunteer list
  201. response = await client.post(
  202. app.url_path_for(
  203. "update_slot",
  204. project_id=default_project_id,
  205. slot_id=default_slot_id,
  206. ),
  207. json={"volunteers": ["not uuid str"]},
  208. headers=default_user_headers,
  209. )
  210. assert response.status_code == 422
  211. async def test_delete_slot(
  212. client: AsyncClient,
  213. default_user_headers: dict,
  214. session: Session,
  215. default_public_project: Project,
  216. ):
  217. # Fail deleting the project due to not logged in
  218. response = await client.delete(
  219. app.url_path_for(
  220. "delete_slot",
  221. project_id=default_project_id,
  222. slot_id=default_slot_id,
  223. )
  224. )
  225. assert response.status_code == 401
  226. result = session.execute(select(Slot).where(Slot.id == default_slot_id))
  227. slot = result.scalars().first()
  228. assert slot is not None
  229. # Proper deletion
  230. response = await client.delete(
  231. app.url_path_for(
  232. "delete_slot",
  233. project_id=default_project_id,
  234. slot_id=default_slot_id,
  235. ),
  236. headers=default_user_headers,
  237. )
  238. assert response.status_code == 200
  239. result = session.execute(select(Slot).where(Slot.id == default_slot_id))
  240. slot = result.scalars().first()
  241. assert slot is None
  242. # check deletion is cascaded to volunteers
  243. result = session.execute(
  244. select(Volunteer).where(Volunteer.id == default_volunteer_id)
  245. )
  246. volunteer: Volunteer | None = result.scalars().first()
  247. assert default_slot_id not in volunteer.slots_id
  248. # Idempotence test
  249. response = await client.delete(
  250. app.url_path_for(
  251. "delete_slot",
  252. project_id=default_project_id,
  253. slot_id=default_slot_id,
  254. ),
  255. headers=default_user_headers,
  256. )
  257. assert response.status_code == 200
  258. # can delete random uuid
  259. response = await client.delete(
  260. app.url_path_for(
  261. "delete_slot", project_id=default_project_id, slot_id=uuid.uuid4()
  262. ),
  263. headers=default_user_headers,
  264. )
  265. assert response.status_code == 200
  266. # Cannot delete non uuid string
  267. response = await client.delete(
  268. app.url_path_for(
  269. "delete_slot", project_id=default_project_id, slot_id="not uidstr"
  270. ),
  271. headers=default_user_headers,
  272. )
  273. assert response.status_code == 422