test_tag.py 9.9 KB

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