test_volunteer.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. import uuid
  2. import pytest
  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, Sms, Volunteer
  8. from app.tests.conftest import (
  9. default_project_id,
  10. default_slot_id,
  11. default_volunteer_id,
  12. )
  13. class TestListVolunteer:
  14. async def test_read_list_not_authenticated(
  15. self,
  16. client: AsyncClient,
  17. default_user_headers: dict,
  18. default_public_project: Project,
  19. ):
  20. response = await client.get(
  21. app.url_path_for("list_project_volunteers", project_id=default_project_id),
  22. )
  23. assert response.status_code == 401
  24. @pytest.mark.parametrize(
  25. "project_id,status_code",
  26. [(uuid.uuid4(), 404), ("pas un uuid valid", 422)],
  27. )
  28. async def test_read_list_fails(
  29. self,
  30. client: AsyncClient,
  31. default_user_headers: dict,
  32. default_public_project: Project,
  33. make_org_user,
  34. project_id,
  35. status_code,
  36. ):
  37. _, headers = make_org_user()
  38. response = await client.get(
  39. app.url_path_for("list_project_volunteers", project_id=project_id),
  40. headers=headers,
  41. )
  42. assert response.status_code == status_code
  43. async def test_real_project(
  44. self, client: AsyncClient, default_user_headers: dict, default_public_project: Project
  45. ):
  46. response = await client.get(
  47. app.url_path_for("list_project_volunteers", project_id=default_project_id),
  48. headers=default_user_headers,
  49. )
  50. assert response.status_code == 200
  51. data = response.json()
  52. assert len(data) == 1
  53. volunteer_response = data[0]
  54. assert volunteer_response["name"] == "Arthur"
  55. assert volunteer_response["id"] == default_volunteer_id
  56. assert "created_at" in volunteer_response
  57. class TestCreateVolunteer:
  58. async def test_create_volunteer_without_auth(
  59. self,
  60. client: AsyncClient,
  61. ):
  62. # Test without authentication
  63. response = await client.post(
  64. app.url_path_for("create_volunteer", project_id=default_project_id)
  65. )
  66. assert response.status_code == 401
  67. async def test_create_volunteer_invalid_project_id(
  68. self,
  69. client: AsyncClient,
  70. default_user_headers: dict,
  71. ):
  72. payload = {
  73. "name": "Lancelot",
  74. "email": "lancelot@dulac.fr",
  75. "phone_number": "03 14 15 92 65",
  76. }
  77. # test invalid project_id
  78. response = await client.post(
  79. app.url_path_for("create_volunteer", project_id=uuid.uuid4()),
  80. json=payload,
  81. headers=default_user_headers,
  82. )
  83. assert response.status_code == 404
  84. async def test_create_volunteer_invalid_payload(
  85. self,
  86. client: AsyncClient,
  87. default_user_headers: dict,
  88. ):
  89. payload = {
  90. "email": "lancelot@dulac.fr",
  91. "phone_number": "03 14 15 92 65",
  92. }
  93. response = await client.post(
  94. app.url_path_for("create_volunteer", project_id=default_project_id),
  95. json=payload,
  96. headers=default_user_headers,
  97. )
  98. assert response.status_code == 422
  99. async def test_create_volunteer_valid_payload(
  100. self,
  101. client: AsyncClient,
  102. default_public_project: Project,
  103. default_user_headers: dict,
  104. session: Session,
  105. ):
  106. payload = {
  107. "name": "Lancelot",
  108. "email": "lancelot@dulac.fr",
  109. "phone_number": "03 14 15 92 65",
  110. }
  111. response = await client.post(
  112. app.url_path_for("create_volunteer", project_id=default_project_id),
  113. json=payload,
  114. headers=default_user_headers,
  115. )
  116. assert response.status_code == 200
  117. assert response.json()["id"] != default_project_id
  118. assert response.json()["name"] == "Lancelot"
  119. assert response.json()["comment"] == ""
  120. result = session.execute(
  121. select(Volunteer).where(Volunteer.project_id == default_project_id)
  122. )
  123. volunteers = result.scalars().all()
  124. assert len(volunteers) > 1
  125. async def test_create_volunteer_with_slots(
  126. self,
  127. client: AsyncClient,
  128. default_public_project: Project,
  129. default_user_headers: dict,
  130. session: Session,
  131. ):
  132. payload = {
  133. "name": "Lancelot",
  134. "email": "lancelot@dulac.fr",
  135. "phone_number": "03 14 15 92 65",
  136. "slots": [default_slot_id],
  137. }
  138. response = await client.post(
  139. app.url_path_for("create_volunteer", project_id=default_project_id),
  140. json=payload,
  141. headers=default_user_headers,
  142. )
  143. assert response.status_code == 200
  144. result = session.execute(
  145. select(Volunteer).where(Volunteer.project_id == default_project_id)
  146. )
  147. volunteers = result.scalars().all()
  148. assert len(volunteers) > 1
  149. assert len(volunteers[0].slots) == 1
  150. async def test_create_volunteer_comment(
  151. client: AsyncClient,
  152. default_public_project: Project,
  153. default_user_headers: dict,
  154. session: Session,
  155. ):
  156. payload = {
  157. "name": "Lancelot",
  158. "email": "lancelot@dulac.fr",
  159. "phone_number": "03 14 15 92 65",
  160. "comment": "it's a knight",
  161. }
  162. # Test normal payload
  163. response = await client.post(
  164. app.url_path_for("create_volunteer", project_id=default_project_id),
  165. json=payload,
  166. headers=default_user_headers,
  167. )
  168. assert response.status_code == 200
  169. assert response.json()["id"] != default_project_id
  170. assert response.json()["name"] == "Lancelot"
  171. assert response.json()["comment"] != ""
  172. new_id = response.json()["id"]
  173. result = session.execute(select(Volunteer).where(Volunteer.id == new_id))
  174. volunteer = result.scalar_one_or_none()
  175. assert volunteer is not None
  176. assert volunteer.comment == "it's a knight"
  177. async def test_update_volunteer(
  178. client: AsyncClient,
  179. default_public_project: Project,
  180. default_user_headers: dict,
  181. session: Session,
  182. ):
  183. # Test without autentication
  184. response = await client.post(
  185. app.url_path_for(
  186. "update_volunteer",
  187. project_id=default_project_id,
  188. volunteer_id=default_volunteer_id,
  189. )
  190. )
  191. assert response.status_code == 401
  192. payload = {
  193. "name": "Lancelot",
  194. "email": "lancelot@dulac.fr",
  195. "phone_number": "03 14 15 92 65",
  196. "automatic_sms": False,
  197. "comment": "new comment",
  198. }
  199. # test invalid project_id
  200. response = await client.post(
  201. app.url_path_for(
  202. "update_volunteer",
  203. project_id=uuid.uuid4(),
  204. volunteer_id=default_volunteer_id,
  205. ),
  206. json=payload,
  207. headers=default_user_headers,
  208. )
  209. assert response.status_code == 404
  210. # test invalid volunteer_id
  211. response = await client.post(
  212. app.url_path_for(
  213. "update_volunteer",
  214. project_id=default_project_id,
  215. volunteer_id=uuid.uuid4(),
  216. ),
  217. json=payload,
  218. headers=default_user_headers,
  219. )
  220. assert response.status_code == 404
  221. # Test normal payload
  222. for k, v in payload.items():
  223. response = await client.post(
  224. app.url_path_for(
  225. "update_volunteer",
  226. project_id=default_project_id,
  227. volunteer_id=default_volunteer_id,
  228. ),
  229. json={k: v},
  230. headers=default_user_headers,
  231. )
  232. assert response.status_code == 200
  233. assert response.json()["id"] == default_volunteer_id
  234. assert response.json()[k] == v
  235. async def test_update_volunteer_slots(
  236. client: AsyncClient,
  237. default_public_project: Project,
  238. default_user_headers: dict,
  239. session: Session,
  240. ):
  241. response = await client.post(
  242. app.url_path_for(
  243. "update_volunteer",
  244. project_id=default_project_id,
  245. volunteer_id=default_volunteer_id,
  246. ),
  247. json={"slots": []},
  248. headers=default_user_headers,
  249. )
  250. assert response.status_code == 200
  251. result = session.execute(select(Slot).where(Slot.id == default_slot_id))
  252. slot = result.scalars().first()
  253. assert slot is not None
  254. assert slot.volunteers_id == []
  255. response = await client.post(
  256. app.url_path_for(
  257. "update_volunteer",
  258. project_id=default_project_id,
  259. volunteer_id=default_volunteer_id,
  260. ),
  261. json={"slots": [default_slot_id]},
  262. headers=default_user_headers,
  263. )
  264. assert response.status_code == 200
  265. session.refresh(slot)
  266. assert slot is not None
  267. assert slot.volunteers_id == [default_volunteer_id]
  268. # An invalid slot list
  269. response = await client.post(
  270. app.url_path_for(
  271. "update_volunteer",
  272. project_id=default_project_id,
  273. volunteer_id=default_volunteer_id,
  274. ),
  275. json={"slots": [str(uuid.uuid4())]},
  276. headers=default_user_headers,
  277. )
  278. assert response.status_code == 400
  279. # An invalid slot list
  280. response = await client.post(
  281. app.url_path_for(
  282. "update_volunteer",
  283. project_id=default_project_id,
  284. volunteer_id=default_volunteer_id,
  285. ),
  286. json={"slots": ["not uuid str"]},
  287. headers=default_user_headers,
  288. )
  289. assert response.status_code == 422
  290. async def test_delete_volunteer(
  291. client: AsyncClient,
  292. default_user_headers: dict,
  293. session: Session,
  294. default_public_project: Project,
  295. ):
  296. # Fail deleting the project due to not logged in
  297. response = await client.delete(
  298. app.url_path_for(
  299. "delete_volunteer",
  300. project_id=default_project_id,
  301. volunteer_id=default_volunteer_id,
  302. )
  303. )
  304. assert response.status_code == 401
  305. result = session.execute(select(Volunteer).where(Volunteer.id == default_volunteer_id))
  306. volunteer = result.scalars().first()
  307. assert volunteer is not None
  308. # Proper deletion
  309. response = await client.delete(
  310. app.url_path_for(
  311. "delete_volunteer",
  312. project_id=default_project_id,
  313. volunteer_id=default_volunteer_id,
  314. ),
  315. headers=default_user_headers,
  316. )
  317. assert response.status_code == 200
  318. result = session.execute(select(Volunteer).where(Volunteer.id == default_volunteer_id))
  319. volunteer = result.scalars().first()
  320. assert volunteer is None
  321. # check deletion is cascaded to slots
  322. result = session.execute(select(Slot).where(Slot.id == default_slot_id))
  323. slot: Slot | None = result.scalar_one_or_none()
  324. assert slot is not None
  325. assert default_volunteer_id not in slot.volunteers_id
  326. # Idempotence test
  327. response = await client.delete(
  328. app.url_path_for(
  329. "delete_volunteer",
  330. project_id=default_project_id,
  331. volunteer_id=default_volunteer_id,
  332. ),
  333. headers=default_user_headers,
  334. )
  335. assert response.status_code == 200
  336. async def test_delete_volunteer_any_uuid(
  337. client: AsyncClient,
  338. default_user_headers: dict,
  339. session: Session,
  340. default_public_project: Project,
  341. ):
  342. # can delete random uuid
  343. response = await client.delete(
  344. app.url_path_for(
  345. "delete_volunteer", project_id=default_project_id, volunteer_id=uuid.uuid4()
  346. ),
  347. headers=default_user_headers,
  348. )
  349. assert response.status_code == 200
  350. # Cannot delete non uuid string
  351. response = await client.delete(
  352. app.url_path_for(
  353. "delete_volunteer", project_id=default_project_id, volunteer_id="not uidstr"
  354. ),
  355. headers=default_user_headers,
  356. )
  357. assert response.status_code == 422
  358. async def test_delete_volunteer_with_sms(
  359. client: AsyncClient,
  360. default_user_headers: dict,
  361. session: Session,
  362. default_public_project: Project,
  363. ):
  364. sms = Sms()
  365. sms.project_id = default_project_id
  366. sms.content = "coucou"
  367. sms.phone_number = "02 66 66 66 66 66"
  368. sms.volunteer_id = default_volunteer_id
  369. session.add(sms)
  370. session.commit()
  371. session.refresh(sms)
  372. response = await client.delete(
  373. app.url_path_for(
  374. "delete_volunteer",
  375. project_id=default_project_id,
  376. volunteer_id=default_volunteer_id,
  377. ),
  378. headers=default_user_headers,
  379. )
  380. assert response.status_code == 200
  381. # Volunteer must be deleted
  382. result = session.execute(select(Volunteer).where(Volunteer.id == default_volunteer_id))
  383. assert result.scalar_one_or_none() is None
  384. # Sms must be deleted
  385. assert session.execute(select(Sms).where(Sms.id == sms.id)).scalar_one_or_none() is None