test_tag.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. from datetime import datetime
  2. import uuid
  3. from httpx import AsyncClient
  4. import pytest
  5. from sqlalchemy import select
  6. from sqlalchemy.orm import Session
  7. from app.main import app
  8. from app.models import Project, Slot, SlotTag, SlotTemplate
  9. from app.tests.conftest import default_project_id, default_tag_id
  10. async def test_read_list_project_tags(
  11. client: AsyncClient,
  12. default_user_headers: dict,
  13. default_public_project: Project,
  14. session: Session,
  15. ):
  16. response = await client.get(
  17. app.url_path_for("list_project_tags", project_id=default_project_id),
  18. )
  19. assert response.status_code == 401
  20. response = await client.get(
  21. app.url_path_for("list_project_tags", project_id=default_project_id),
  22. headers=default_user_headers,
  23. )
  24. assert response.status_code == 200
  25. assert len(response.json()) == 1
  26. tag = SlotTag(title="1er tag", project_id=default_project_id)
  27. session.add(tag)
  28. session.commit()
  29. response = await client.get(
  30. app.url_path_for("list_project_tags", project_id=default_project_id),
  31. headers=default_user_headers,
  32. )
  33. assert response.status_code == 200
  34. assert len(response.json()) == 2
  35. async def test_create_tag_fail(
  36. client: AsyncClient,
  37. default_user_headers: dict,
  38. default_public_project: Project,
  39. session: Session,
  40. ):
  41. response = await client.post(
  42. app.url_path_for("create_tag", project_id="default_project_id"),
  43. headers=default_user_headers,
  44. )
  45. assert response.status_code == 422
  46. response = await client.post(
  47. app.url_path_for("create_tag", project_id=uuid.uuid4()),
  48. json={"title": "1st tag"},
  49. headers=default_user_headers,
  50. )
  51. assert response.status_code == 404
  52. response = await client.post(
  53. app.url_path_for("create_tag", project_id="default_project_id"),
  54. )
  55. assert response.status_code == 401
  56. response = await client.post(
  57. app.url_path_for("create_tag", project_id=default_project_id),
  58. json={"titl": "1st tag"},
  59. headers=default_user_headers,
  60. )
  61. assert response.status_code == 422
  62. async def test_create_tag(
  63. client: AsyncClient,
  64. default_user_headers: dict,
  65. default_project: Project,
  66. session: Session,
  67. ):
  68. response = await client.post(
  69. app.url_path_for("create_tag", project_id=default_project_id),
  70. json={"title": "1st tag"},
  71. headers=default_user_headers,
  72. )
  73. assert response.status_code == 200
  74. result = session.execute(select(SlotTag).where(SlotTag.project_id == default_project_id))
  75. tag = result.scalars().first()
  76. assert tag is not None
  77. assert tag.title == "1st tag"
  78. response = await client.post(
  79. app.url_path_for("create_tag", project_id=default_project_id),
  80. json={"title": "1st tag"},
  81. headers=default_user_headers,
  82. )
  83. result = session.execute(select(SlotTag).where(SlotTag.project_id == default_project_id))
  84. tag_count = 0
  85. for tag in result.scalars():
  86. tag_count += 1
  87. assert tag_count == 2
  88. async def test_create_tag_with_template(
  89. client: AsyncClient,
  90. default_user_headers: dict,
  91. default_public_project: Project,
  92. session: Session,
  93. ):
  94. template = SlotTemplate(
  95. project_id=default_project_id,
  96. title="coucou",
  97. description="ceci est une description",
  98. )
  99. session.add(template)
  100. session.commit()
  101. response = await client.post(
  102. app.url_path_for("create_tag", project_id=default_project_id),
  103. json={"title": "1st tag", "templates": [template.id]},
  104. headers=default_user_headers,
  105. )
  106. assert response.status_code == 200
  107. assert response.json()["templates_id"][0] == template.id
  108. session.refresh(template)
  109. assert len(template.tags) > 0
  110. template = SlotTemplate(
  111. project_id=default_project_id,
  112. title="coucou",
  113. description="ceci est une description",
  114. )
  115. session.add(template)
  116. session.commit()
  117. project = Project(name="second project", is_public=False)
  118. session.add(project)
  119. session.commit()
  120. session.refresh(project)
  121. response = await client.post(
  122. app.url_path_for("create_tag", project_id=project.id),
  123. json={"title": "1st tag", "templates": [template.id]},
  124. headers=default_user_headers,
  125. )
  126. assert response.status_code == 400
  127. async def test_update_tag_fail(
  128. client: AsyncClient,
  129. default_user_headers: dict,
  130. default_public_project: Project,
  131. session: Session,
  132. ):
  133. # no authent
  134. url = app.url_path_for("update_tag", project_id=default_project_id, tag_id=default_tag_id)
  135. response = await client.post(url, json={"title": "royaux"})
  136. assert response.status_code == 401
  137. @pytest.mark.parametrize(
  138. "payload", [{"title": [1.001, 2]}, {"title": {}}, {"templates": ["1", "2"]}]
  139. )
  140. async def test_update_tag_fail_invalid_payload(
  141. client: AsyncClient,
  142. default_user_headers: dict,
  143. default_public_project: Project,
  144. session: Session,
  145. payload: dict,
  146. ):
  147. url = app.url_path_for("update_tag", project_id=default_project_id, tag_id=default_tag_id)
  148. response = await client.post(url, json=payload, headers=default_user_headers)
  149. assert response.status_code == 422
  150. async def test_update_tag(
  151. client: AsyncClient,
  152. default_user_headers: dict,
  153. default_public_project: Project,
  154. session: Session,
  155. ):
  156. tag = session.get(SlotTag, default_tag_id)
  157. assert tag is not None
  158. url = app.url_path_for("update_tag", project_id=default_project_id, tag_id=default_tag_id)
  159. response = await client.post(url, json={"title": "new_title"}, headers=default_user_headers)
  160. assert response.status_code == 200
  161. session.refresh(tag)
  162. assert tag.title == "new_title"
  163. assert response.json()["title"] == "new_title"
  164. template = SlotTemplate(project_id=default_project_id, title="template")
  165. session.add(template)
  166. session.commit()
  167. response = await client.post(
  168. url, json={"templates": [template.id]}, headers=default_user_headers
  169. )
  170. assert response.status_code == 200
  171. session.refresh(tag)
  172. assert len(tag.templates) == 1
  173. response = await client.post(url, json={"templates": []}, headers=default_user_headers)
  174. session.refresh(tag)
  175. assert len(tag.templates) == 0
  176. async def test_update_tag_fail_template(
  177. client: AsyncClient,
  178. default_user_headers: dict,
  179. default_public_project: Project,
  180. session: Session,
  181. ):
  182. tag = session.get(SlotTag, default_tag_id)
  183. assert tag is not None
  184. project = Project(name="other project", is_public=False)
  185. template = SlotTemplate(title="template 1")
  186. project.templates.append(template)
  187. session.add(template)
  188. session.add(project)
  189. session.commit()
  190. url = app.url_path_for("update_tag", project_id=default_project_id, tag_id=default_tag_id)
  191. response = await client.post(
  192. url, json={"templates": [template.id]}, headers=default_user_headers
  193. )
  194. assert response.status_code == 400
  195. @pytest.mark.parametrize("n_slot", [1, 5, 10])
  196. async def test_list_slot(
  197. client: AsyncClient,
  198. default_user_headers: dict,
  199. session: Session,
  200. default_public_project: Project,
  201. n_slot: int,
  202. ):
  203. url = app.url_path_for("list_tagged_slot", project_id=default_project_id, tag_id=default_tag_id)
  204. response = await client.get(url, headers=default_user_headers)
  205. assert response.status_code == 200
  206. assert len(response.json()) == 0
  207. tag = session.get(SlotTag, default_tag_id)
  208. assert tag is not None
  209. for i in range(n_slot):
  210. template = SlotTemplate(project_id=default_project_id, title=f"template {i}")
  211. slot = Slot(
  212. project_id=default_project_id,
  213. title=f"Slot {i}",
  214. starting_time=datetime(2024, 9, 9, 12, 2 * i),
  215. ending_time=datetime(2024, 9, 9, 12, 2 * (i + 1)),
  216. )
  217. slot.template = template
  218. tag.templates.append(template)
  219. session.add_all([template, slot])
  220. session.commit()
  221. response = await client.get(url, headers=default_user_headers)
  222. assert response.status_code == 200
  223. assert len(response.json()) == n_slot
  224. async def test_delete_tag_fail(
  225. client: AsyncClient,
  226. default_user_headers: dict,
  227. session: Session,
  228. default_public_project: Project,
  229. ):
  230. # no authent
  231. url = app.url_path_for("delete_tag", project_id=default_project_id, tag_id=default_tag_id)
  232. response = await client.delete(url)
  233. assert response.status_code == 401
  234. # invalid tag
  235. url = app.url_path_for("delete_tag", project_id=default_project_id, tag_id="default_tag_id")
  236. response = await client.delete(url, headers=default_user_headers)
  237. assert response.status_code == 422
  238. # invalid project_id
  239. url = app.url_path_for("delete_tag", project_id="default_volunteer_id", tag_id=default_tag_id)
  240. response = await client.delete(url, headers=default_user_headers)
  241. assert response.status_code == 422
  242. async def test_delete_tag(
  243. client: AsyncClient,
  244. default_user_headers: dict,
  245. session: Session,
  246. default_public_project: Project,
  247. ):
  248. # Proper deletion
  249. url = app.url_path_for("delete_tag", project_id=default_project_id, tag_id=default_tag_id)
  250. response = await client.delete(url, headers=default_user_headers)
  251. assert response.status_code == 200
  252. result = session.execute(select(SlotTag).where(SlotTag.id == default_tag_id))
  253. slot = result.scalars().first()
  254. assert slot is None
  255. # can delete random uuid
  256. url = app.url_path_for("delete_tag", project_id=default_project_id, tag_id=uuid.uuid4())
  257. response = await client.delete(url, headers=default_user_headers)
  258. assert response.status_code == 200
  259. async def test_delete_tag_idempotent(
  260. client: AsyncClient,
  261. default_user_headers: dict,
  262. session: Session,
  263. default_public_project: Project,
  264. ):
  265. # Idempotence test
  266. url = app.url_path_for("delete_tag", project_id=default_project_id, tag_id=default_tag_id)
  267. response = await client.delete(url, headers=default_user_headers)
  268. response = await client.delete(url, headers=default_user_headers)
  269. assert response.status_code == 200