test_admin.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. import csv
  2. import io
  3. import pytest
  4. from datetime import datetime, timedelta
  5. from stor.auth.models import Group, User
  6. from stor.repository.definitions import FundingType
  7. from stor.repository.models import FundingProject
  8. DATE_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
  9. @pytest.mark.parametrize(
  10. ("username", "message"),
  11. (
  12. ("", "Access Denied"),
  13. ("vera", "Email not verified"),
  14. ("ina", "Account not activated"),
  15. ("maya", "Access Denied"),
  16. ("una", "Access Denied"),
  17. ("gobnait", "Access Denied"),
  18. ("steph", "Access Denied"),
  19. ("reeve", "Access Denied"),
  20. ),
  21. )
  22. def test_get_unauthenticated_and_unauthorized(client, auth, username, message):
  23. if username:
  24. auth.login(username)
  25. endpoints = [
  26. "/groups",
  27. "/users",
  28. "/users/4",
  29. "/users/search?q=gobnait",
  30. "/users/4/edit",
  31. "/users/4/delete",
  32. "/users/report",
  33. "/projects",
  34. "/projects/create",
  35. "/projects/1/edit",
  36. "/projects/1/delete",
  37. ]
  38. for endpoint in endpoints:
  39. assert bytes(message, "utf8") in client.get("/admin{0}".format(endpoint), follow_redirects=True).data
  40. def test_list_groups_get_authorized(client, auth):
  41. auth.login("ada")
  42. response = client.get("/admin/groups")
  43. assert response.status_code == 200
  44. assert b"admin" in response.data
  45. assert b"reviewer" in response.data
  46. assert b"staff" in response.data
  47. assert b"standard" in response.data
  48. ALL_NAMES = ["Ada", "Gobnait", "Ina", "Leo", "Maya", "Reeve", "Stan", "Steph", "Úna", "Vera"]
  49. @pytest.mark.parametrize(
  50. ("request_arg", "expected_names"),
  51. (
  52. ("", ["Ada", "Gobnait", "Ina", "Leo"]),
  53. ("?page=1", ["Ada", "Gobnait", "Ina", "Leo"]),
  54. ("?page=2", ["Maya", "Reeve", "Stan", "Steph"]),
  55. ("?page=3", ["Úna", "Vera"]),
  56. ("?page=4", []),
  57. ),
  58. )
  59. def test_list_users_get_authorized(client, auth, request_arg, expected_names):
  60. auth.login("ada")
  61. response = client.get("/admin/users{0}".format(request_arg))
  62. assert response.status_code == 200
  63. for name in ALL_NAMES:
  64. assert (bytes(name, "utf8") in response.data) == (name in expected_names)
  65. assert bytes("Total users: 10", "utf8") in response.data
  66. @pytest.mark.parametrize(
  67. ("request_args", "es_query", "es_total", "es_results", "page_no", "expected_names"),
  68. (
  69. ("?q=zzzzz", "zzzzz", 0, [], 1, []),
  70. ("?q=gobnait", "gobnait", 1, [4], 1, ["Gobnait"]),
  71. ("?q=gobnait&page=2", "gobnait", 1, [], 2, []),
  72. ("?q=gobnait&page=0", "gobnait", 1, [4], 1, ["Gobnait"]),
  73. ("?q=gobnait&page=-1", "gobnait", 1, [4], 1, ["Gobnait"]),
  74. ("?q=reeve@test.com", "reeve@test.com", 1, [2], 1, ["Reeve"]),
  75. ("?q=Úna", "Úna", 1, [6], 1, ["Úna"]),
  76. ("?q=ford", "ford", 1, [3], 1, ["Stan"]),
  77. ("?q=University", "University", 2, [1, 2], 1, ["Ada", "Reeve"]),
  78. ("?q=Úna", "Úna", 1, [6], 1, ["Úna"]),
  79. ("?q=sunshine", "sunshine", 7, [3, 4, 5, 6, 7], 1, ["Stan", "Gobnait", "Leo", "Úna", "Steph"]),
  80. ("?q=sunshine&page=1", "sunshine", 7, [3, 4, 5, 6, 7], 1, ["Stan", "Gobnait", "Leo", "Úna", "Steph"]),
  81. ("?q=sunshine&page=2", "sunshine", 7, [8, 9], 2, ["Vera", "Ina"]),
  82. ("?q=sunshine&page=3", "sunshine", 7, [], 3, []),
  83. ),
  84. )
  85. def test_search_users(app, client, auth, request_args, es_query, es_total, es_results, page_no, expected_names):
  86. auth.login("ada")
  87. app.elasticsearch.search.return_value = {
  88. "hits": {
  89. "total": {"value": es_total},
  90. "hits": [{"_id" : r} for r in es_results]
  91. }
  92. }
  93. response = client.get("/admin/users/search{0}".format(request_args), follow_redirects=True)
  94. assert response.status_code == 200
  95. for name in ALL_NAMES:
  96. assert (bytes(name, "utf8") in response.data) == (name in expected_names)
  97. assert bytes("{0} user(s) found (<a href=/admin/users>of 10 total</a>)".format(es_total), "utf8") in response.data
  98. expected_search_query = {
  99. "multi_match": {
  100. "query": es_query,
  101. "fields": ["username", "email", "first_name", "last_name", "organization_name"]
  102. }
  103. }
  104. page_size = app.config["USERS_PER_PAGE"]
  105. from_ = (page_no - 1) * page_size
  106. app.elasticsearch.search.assert_called_once_with(index="users", query=expected_search_query, from_=from_, size=page_size)
  107. @pytest.mark.parametrize(
  108. ("user_id", "expected_visible", "expected_email", "expected_text"),
  109. (
  110. (2, True, "reeve@test.com", "Reeve Ewing, University of Rain"),
  111. (4, True, "gobnait@test.com", "Gobnait Ní Mhurchú, Dept of Sunshine"),
  112. (0, False, "@test.com", "Not Found"),
  113. ),
  114. )
  115. def test_detail_user_get_authorized(client, auth, user_id, expected_visible, expected_email, expected_text):
  116. auth.login("ada")
  117. response = client.get("/admin/users/{0}".format(user_id))
  118. assert response.status_code == 200
  119. assert (bytes("User Detail", "utf8") in response.data) == expected_visible
  120. assert (bytes(expected_email, "utf8") in response.data) == expected_visible
  121. assert bytes(expected_text, "utf8") in response.data
  122. @pytest.mark.parametrize(
  123. ("user_id", "expected_visible", "expected_text"),
  124. (
  125. (1, True, "ada@test.com"),
  126. (2, True, "reeve@test.com"),
  127. (3, True, "stan@test.com"),
  128. (4, True, "gobnait@test.com"),
  129. (5, True, "leo@test.com"),
  130. (6, True, "una@test.com"),
  131. (7, True, "steph@test.com"),
  132. (8, True, "vera@test.com"),
  133. (9, True, "ina@test.com"),
  134. (0, False, "Not Found"),
  135. ),
  136. )
  137. def test_edit_user_get_authorized(client, auth, user_id, expected_visible, expected_text):
  138. auth.login("ada")
  139. response = client.get("/admin/users/{0}/edit".format(user_id))
  140. assert response.status_code == 200
  141. assert (bytes("Edit User", "utf8") in response.data) == expected_visible
  142. assert bytes(expected_text, "utf8") in response.data
  143. @pytest.mark.parametrize(
  144. ("user_id", "first_name", "last_name", "organization_name", "position_in_organization", "activated", "password_rotated", "new_groups"),
  145. (
  146. (2, "A", "A", "O1", "P1", True, True, [4]),
  147. (3, "B", "B", "O2", "P2", "", True, [3, 4]),
  148. (4, "C", "C", "O3", "P3", True, True, [1, 2, 3, 4]),
  149. (5, "D", "D", "O4", "", "", True, []),
  150. (9, "E", "E", "O5", "P5", True, True, [4]),
  151. (9, "E", "E", "O5", "P5", True, "", [4]),
  152. (10, "F", "F", "O6", "P6", True, "", [4]),
  153. ),
  154. )
  155. def test_edit_user_post_authorized_valid(app, client, auth, user_id, first_name, last_name, organization_name,
  156. position_in_organization, activated, password_rotated, new_groups):
  157. auth.login("ada")
  158. data = {
  159. "first_name" : first_name,
  160. "last_name" : last_name,
  161. "organization_name" : organization_name,
  162. "position_in_organization" : position_in_organization,
  163. "activated" : activated,
  164. "password_rotated" : password_rotated,
  165. "groups" : new_groups,
  166. }
  167. response = client.post("/admin/users/{0}/edit".format(user_id), data=data, follow_redirects=True)
  168. assert response.status_code == 200
  169. assert response.history[0].status_code == 302
  170. with app.app_context():
  171. user = User.query.filter_by(id=user_id).first()
  172. assert user is not None
  173. assert user.first_name == first_name
  174. assert user.last_name == last_name
  175. assert user.organization_name == organization_name
  176. assert user.position_in_organization == position_in_organization
  177. assert user.activated == bool(activated)
  178. assert user.password_rotated == bool(password_rotated)
  179. for i in range(1, 5):
  180. group = Group.query.filter_by(id=i).first()
  181. if i in new_groups:
  182. assert group in user.groups
  183. else:
  184. assert group not in user.groups
  185. expected_document = {
  186. "username": user.username,
  187. "email": user.email,
  188. "first_name": user.first_name,
  189. "last_name": user.last_name,
  190. "organization_name": user.organization_name,
  191. }
  192. app.elasticsearch.index.assert_any_call(index="users", id=user_id, document=expected_document)
  193. @pytest.mark.parametrize(
  194. ("user_id", "first_name", "last_name", "organization_name", "activated", "password_rotated", "new_groups", "expected_groups", "message"),
  195. (
  196. (0, "A", "A", "O1", "", "", [4], [], b"Not Found"),
  197. (2, "", "A", "O1", True, True, [4], [2, 3, 4], b"Please fill in this field."), # Missing first name
  198. (2, "A", "", "O1", True, True, [4], [2, 3, 4], b"Please fill in this field."), # Missing last name
  199. (2, "A", "A", "", True, True, [4], [2, 3, 4], b"Please fill in this field."), # Missing organization name
  200. (2, "A", "A", "O1", True, True, [17], [], b""),
  201. (2, "A", "A", "O1", True, True, [17, 4], [4], b""),
  202. (1, "A", "A", "O1", True, True, [2, 3, 4], [1, 2, 3, 4], b"A user may not remove admin permissions from themselves."),
  203. (1, "A", "A", "O1", "", True, [1, 2, 3, 4], [1, 2, 3, 4], b"A user may not deactivate their own user account."),
  204. (10, "A", "A", "O1", True, True, [2, 3], [4], b"The password rotated field may only be unset."),
  205. ),
  206. )
  207. def test_edit_user_post_authorized_invalid(app, client, auth, user_id, first_name, last_name, organization_name,
  208. activated, password_rotated, new_groups, expected_groups, message):
  209. auth.login("ada")
  210. data = {
  211. "first_name" : first_name,
  212. "last_name" : last_name,
  213. "organization_name" : organization_name,
  214. "activated" : activated,
  215. "password_rotated" : password_rotated,
  216. "groups" : new_groups,
  217. }
  218. response = client.post("/admin/users/{0}/edit".format(user_id), data=data, follow_redirects=True)
  219. assert response.status_code == 200
  220. assert message in response.data
  221. with app.app_context():
  222. user = User.query.filter_by(id=user_id).first()
  223. if user is not None:
  224. for i in range(1, 4):
  225. group = Group.query.filter_by(id=i).first()
  226. if i in expected_groups:
  227. assert group in user.groups
  228. else:
  229. assert group not in user.groups
  230. @pytest.mark.parametrize(
  231. ("user_id"),
  232. (
  233. (6),
  234. (2),
  235. ),
  236. )
  237. def test_delete_user_post_authorized_valid(app, client, auth, user_id):
  238. auth.login("ada")
  239. data = {
  240. "confirm" : True,
  241. }
  242. response = client.post("/admin/users/{0}/delete".format(user_id), data=data, follow_redirects=True)
  243. assert response.status_code == 200
  244. assert response.history[0].status_code == 302
  245. with app.app_context():
  246. assert User.query.filter_by(id=user_id).first() is None
  247. app.elasticsearch.delete.assert_any_call(index="users", id=user_id)
  248. @pytest.mark.parametrize(
  249. ("user_id", "message"),
  250. (
  251. (7, b"This user is the owner of or contact for one or more resources."),
  252. (4, b"This user is the owner of or contact for one or more resources."),
  253. (1, b"A user may not delete their own user account."),
  254. ),
  255. )
  256. def test_delete_user_post_authorized_invalid(app, client, auth, user_id, message):
  257. auth.login("ada")
  258. data = {
  259. "confirm" : True,
  260. }
  261. response = client.post("/admin/users/{0}/delete".format(user_id), data=data, follow_redirects=True)
  262. assert response.status_code == 200
  263. assert response.history[0].status_code == 302
  264. assert message in response.data
  265. with app.app_context():
  266. assert User.query.filter_by(id=user_id).first() is not None
  267. def test_report_users_get_valid(client, auth):
  268. auth.login("ada")
  269. response = client.get("/admin/users/report")
  270. assert response.status_code == 200
  271. reader = csv.DictReader(io.StringIO(response.data.decode("utf-8")))
  272. rows = list(reader)
  273. assert len(rows) == 10
  274. assert int(rows[0]["id"]) == 1
  275. assert rows[0]["username"] == "ada"
  276. assert rows[0]["email"] == "ada@test.com"
  277. assert rows[0]["email_verified"] == "True"
  278. assert rows[0]["activated"] == "True"
  279. assert rows[0]["password_rotated"] == "True"
  280. assert((datetime.utcnow() - datetime.strptime(rows[0]["joined"], DATE_FORMAT)) < timedelta(seconds=2))
  281. assert((datetime.utcnow() - datetime.strptime(rows[0]["last_seen"], DATE_FORMAT)) < timedelta(seconds=2))
  282. assert rows[0]["first_name"] == "Ada"
  283. assert rows[0]["last_name"] == "Min"
  284. assert rows[0]["organization_name"] == "University of Rain"
  285. assert rows[0]["organization_address"] == ""
  286. assert rows[0]["organization_phone"] == ""
  287. assert rows[0]["position_in_organization"] == ""
  288. assert int(rows[0]["no_of_resources"]) == 0
  289. assert int(rows[1]["id"]) == 2
  290. assert rows[1]["username"] == "reeve"
  291. assert rows[1]["email"] == "reeve@test.com"
  292. assert rows[1]["email_verified"] == "True"
  293. assert rows[1]["activated"] == "True"
  294. assert rows[1]["password_rotated"] == "True"
  295. assert((datetime.utcnow() - datetime.strptime(rows[1]["joined"], DATE_FORMAT)) < timedelta(seconds=2))
  296. assert((datetime.utcnow() - datetime.strptime(rows[1]["last_seen"], DATE_FORMAT)) < timedelta(seconds=2))
  297. assert rows[1]["first_name"] == "Reeve"
  298. assert rows[1]["last_name"] == "Ewing"
  299. assert rows[1]["organization_name"] == "University of Rain"
  300. assert rows[1]["organization_address"] == ""
  301. assert rows[1]["organization_phone"] == ""
  302. assert rows[1]["position_in_organization"] == ""
  303. assert int(rows[1]["no_of_resources"]) == 0
  304. assert int(rows[2]["id"]) == 3
  305. assert rows[2]["username"] == "stan"
  306. assert rows[2]["email"] == "stan@test.com"
  307. assert rows[2]["email_verified"] == "True"
  308. assert rows[2]["activated"] == "True"
  309. assert rows[2]["password_rotated"] == "True"
  310. assert((datetime.utcnow() - datetime.strptime(rows[2]["joined"], DATE_FORMAT)) < timedelta(seconds=2))
  311. assert((datetime.utcnow() - datetime.strptime(rows[2]["last_seen"], DATE_FORMAT)) < timedelta(seconds=2))
  312. assert rows[2]["first_name"] == "Stan"
  313. assert rows[2]["last_name"] == "Ford"
  314. assert rows[2]["organization_name"] == "Dept of Sunshine"
  315. assert rows[2]["organization_address"] == ""
  316. assert rows[2]["organization_phone"] == ""
  317. assert rows[2]["position_in_organization"] == ""
  318. assert int(rows[2]["no_of_resources"]) == 0
  319. assert int(rows[3]["id"]) == 4
  320. assert rows[3]["username"] == "gobnait"
  321. assert rows[3]["email"] == "gobnait@test.com"
  322. assert rows[3]["email_verified"] == "True"
  323. assert rows[3]["activated"] == "True"
  324. assert rows[3]["password_rotated"] == "True"
  325. assert((datetime.utcnow() - datetime.strptime(rows[3]["joined"], DATE_FORMAT)) < timedelta(seconds=2))
  326. assert((datetime.utcnow() - datetime.strptime(rows[3]["last_seen"], DATE_FORMAT)) < timedelta(seconds=2))
  327. assert rows[3]["first_name"] == "Gobnait"
  328. assert rows[3]["last_name"] == "Ní Mhurchú"
  329. assert rows[3]["organization_name"] == "Dept of Sunshine"
  330. assert rows[3]["organization_address"] == "123 Department Street"
  331. assert rows[3]["organization_phone"] == "(01)1234567"
  332. assert rows[3]["position_in_organization"] == "Officer"
  333. assert int(rows[3]["no_of_resources"]) == 5
  334. assert int(rows[4]["id"]) == 5
  335. assert rows[4]["username"] == "leo"
  336. assert rows[4]["email"] == "leo@test.com"
  337. assert rows[4]["email_verified"] == "True"
  338. assert rows[4]["activated"] == "True"
  339. assert rows[4]["password_rotated"] == "True"
  340. assert((datetime.utcnow() - datetime.strptime(rows[4]["joined"], DATE_FORMAT)) < timedelta(seconds=2))
  341. assert((datetime.utcnow() - datetime.strptime(rows[4]["last_seen"], DATE_FORMAT)) < timedelta(seconds=2))
  342. assert rows[4]["first_name"] == "Leo"
  343. assert rows[4]["last_name"] == "Cat"
  344. assert rows[4]["organization_name"] == "Dept of Sunshine"
  345. assert rows[4]["organization_address"] == ""
  346. assert rows[4]["organization_phone"] == ""
  347. assert rows[4]["position_in_organization"] == ""
  348. assert int(rows[4]["no_of_resources"]) == 2
  349. assert int(rows[5]["id"]) == 6
  350. assert rows[5]["username"] == "una"
  351. assert rows[5]["email"] == "una@test.com"
  352. assert rows[5]["email_verified"] == "True"
  353. assert rows[5]["activated"] == "True"
  354. assert rows[5]["password_rotated"] == "True"
  355. assert((datetime.utcnow() - datetime.strptime(rows[5]["joined"], DATE_FORMAT)) < timedelta(seconds=2))
  356. assert((datetime.utcnow() - datetime.strptime(rows[5]["last_seen"], DATE_FORMAT)) < timedelta(seconds=2))
  357. assert rows[5]["first_name"] == "Úna"
  358. assert rows[5]["last_name"] == "Ssigned"
  359. assert rows[5]["organization_name"] == "Dept of Sunshine"
  360. assert rows[5]["organization_address"] == ""
  361. assert rows[5]["organization_phone"] == ""
  362. assert rows[5]["position_in_organization"] == ""
  363. assert int(rows[5]["no_of_resources"]) == 0
  364. assert int(rows[6]["id"]) == 7
  365. assert rows[6]["username"] == "steph"
  366. assert rows[6]["email"] == "steph@test.com"
  367. assert rows[6]["email_verified"] == "True"
  368. assert rows[6]["activated"] == "True"
  369. assert rows[6]["password_rotated"] == "True"
  370. assert((datetime.utcnow() - datetime.strptime(rows[6]["joined"], DATE_FORMAT)) < timedelta(seconds=2))
  371. assert((datetime.utcnow() - datetime.strptime(rows[6]["last_seen"], DATE_FORMAT)) < timedelta(seconds=2))
  372. assert rows[6]["first_name"] == "Steph"
  373. assert rows[6]["last_name"] == "Anie"
  374. assert rows[6]["organization_name"] == "Dept of Sunshine"
  375. assert rows[6]["organization_address"] == ""
  376. assert rows[6]["organization_phone"] == ""
  377. assert rows[6]["position_in_organization"] == ""
  378. assert int(rows[6]["no_of_resources"]) == 3
  379. assert int(rows[7]["id"]) == 8
  380. assert rows[7]["username"] == "vera"
  381. assert rows[7]["email"] == "vera@test.com"
  382. assert rows[7]["email_verified"] == "False"
  383. assert rows[7]["activated"] == "False"
  384. assert rows[7]["password_rotated"] == "True"
  385. assert((datetime.utcnow() - datetime.strptime(rows[7]["joined"], DATE_FORMAT)) < timedelta(seconds=2))
  386. assert((datetime.utcnow() - datetime.strptime(rows[7]["last_seen"], DATE_FORMAT)) < timedelta(seconds=2))
  387. assert rows[7]["first_name"] == "Vera"
  388. assert rows[7]["last_name"] == "Fication-Pending"
  389. assert rows[7]["organization_name"] == "Dept of Sunshine"
  390. assert rows[7]["organization_address"] == ""
  391. assert rows[7]["organization_phone"] == ""
  392. assert rows[7]["position_in_organization"] == ""
  393. assert int(rows[7]["no_of_resources"]) == 0
  394. assert int(rows[8]["id"]) == 9
  395. assert rows[8]["username"] == "ina"
  396. assert rows[8]["email"] == "ina@test.com"
  397. assert rows[8]["email_verified"] == "True"
  398. assert rows[8]["activated"] == "False"
  399. assert rows[8]["password_rotated"] == "True"
  400. assert((datetime.utcnow() - datetime.strptime(rows[8]["joined"], DATE_FORMAT)) < timedelta(seconds=2))
  401. assert((datetime.utcnow() - datetime.strptime(rows[8]["last_seen"], DATE_FORMAT)) < timedelta(seconds=2))
  402. assert rows[8]["first_name"] == "Ina"
  403. assert rows[8]["last_name"] == "Ctive"
  404. assert rows[8]["organization_name"] == "Dept of Sunshine"
  405. assert rows[8]["organization_address"] == ""
  406. assert rows[8]["organization_phone"] == ""
  407. assert rows[8]["position_in_organization"] == ""
  408. assert int(rows[8]["no_of_resources"]) == 0
  409. assert int(rows[9]["id"]) == 10
  410. assert rows[9]["username"] == "maya"
  411. assert rows[9]["email"] == "maya@test.com"
  412. assert rows[9]["email_verified"] == "True"
  413. assert rows[9]["password_rotated"] == "False"
  414. assert rows[9]["activated"] == "True"
  415. assert((datetime.utcnow() - datetime.strptime(rows[9]["joined"], DATE_FORMAT)) < timedelta(seconds=2))
  416. assert((datetime.utcnow() - datetime.strptime(rows[9]["last_seen"], DATE_FORMAT)) < timedelta(seconds=2))
  417. assert rows[9]["first_name"] == "Maya"
  418. assert rows[9]["last_name"] == "Grated-User"
  419. assert rows[9]["organization_name"] == "Dept of Sunshine"
  420. assert rows[9]["organization_address"] == ""
  421. assert rows[9]["organization_phone"] == ""
  422. assert rows[9]["position_in_organization"] == ""
  423. assert int(rows[9]["no_of_resources"]) == 0
  424. def test_list_projects_authorized(client, auth):
  425. auth.login("ada")
  426. response = client.get("/admin/projects", follow_redirects=True)
  427. assert b"Funding Project A" in response.data
  428. assert b"Funding Project B" in response.data
  429. assert b"Funding Project C" in response.data
  430. def test_create_project_authorized_valid(app, client, auth):
  431. auth.login("ada")
  432. project_id = 5
  433. data = {
  434. "name" : "Project Name",
  435. "short_name" : "Project Short Name",
  436. "url" : "http://www.test.com",
  437. "funding_type" : FundingType.NATIONAL_FUNDS.name,
  438. "funder" : "Dept of Sunshine",
  439. "funding_country" : "IE",
  440. }
  441. response = client.post("/admin/projects/create", data=data, follow_redirects=True)
  442. assert response.status_code == 200
  443. assert response.history[0].status_code == 302
  444. with app.app_context():
  445. project = FundingProject.query.filter_by(id=project_id).first()
  446. assert project.name == "Project Name"
  447. assert project.short_name == "Project Short Name"
  448. assert project.url == "http://www.test.com"
  449. assert project.funding_type == FundingType.NATIONAL_FUNDS
  450. assert project.funder == "Dept of Sunshine"
  451. assert project.funding_country == "IE"
  452. @pytest.mark.parametrize(
  453. ("new_name", "new_funding_type", "new_funding_country", "message"),
  454. (
  455. ("", FundingType.NATIONAL_FUNDS.name, "IE", b"Please fill in this field."),
  456. ("Funding Project A", FundingType.NATIONAL_FUNDS.name, "IE", b"A project with this name already exists."),
  457. ("New Name", "UNKNOWN", "IE", b"Not a valid choice."),
  458. ("New Name", "", "IE", b"Not a valid choice."),
  459. ("New Name", FundingType.NATIONAL_FUNDS.name, "UNKNOWN", b"Not a valid choice."),
  460. ),
  461. )
  462. def test_create_project_authorized_invalid(app, client, auth, new_name, new_funding_type, new_funding_country, message):
  463. auth.login("ada")
  464. project_id = 5
  465. data = {
  466. "name" : new_name,
  467. "short_name" : "Project Short Name",
  468. "url" : "http://www.test.com",
  469. "funding_type" : new_funding_type,
  470. "funder" : "New Funder",
  471. "funding_country" : new_funding_country,
  472. }
  473. response = client.post("/admin/projects/create", data=data, follow_redirects=True)
  474. assert response.status_code == 200
  475. assert len(response.history) == 0
  476. assert message in response.data
  477. with app.app_context():
  478. assert FundingProject.query.filter_by(id=project_id).first() == None
  479. @pytest.mark.parametrize(
  480. ("new_name", "new_short_name", "new_funding_type", "new_funding_country"),
  481. (
  482. ("Funding Project A", "FPA", FundingType.NATIONAL_FUNDS, "IE"),
  483. ("updated name", "updated short name", FundingType.OWN_FUNDS, "SK"),
  484. ("updated name", "", FundingType.OTHER, "EU"),
  485. ),
  486. )
  487. def test_edit_project_authorized_valid(app, client, auth, new_name, new_short_name, new_funding_type, new_funding_country):
  488. auth.login("ada")
  489. project_id = 1
  490. data = {
  491. "name" : new_name,
  492. "short_name" : new_short_name,
  493. "url" : "http://www.test.com",
  494. "funding_type" : new_funding_type.name,
  495. "funder" : "Dept of Sunshine",
  496. "funding_country" : new_funding_country,
  497. }
  498. response = client.post("/admin/projects/{0}/edit".format(project_id), data=data, follow_redirects=True)
  499. assert response.status_code == 200
  500. assert response.history[0].status_code == 302
  501. with app.app_context():
  502. project = FundingProject.query.filter_by(id=project_id).first()
  503. assert project.name == new_name
  504. assert project.short_name == new_short_name
  505. assert project.url == "http://www.test.com"
  506. assert project.funding_type == new_funding_type
  507. assert project.funder == "Dept of Sunshine"
  508. assert project.funding_country == new_funding_country
  509. @pytest.mark.parametrize(
  510. ("new_name", "new_funding_type", "new_funding_country", "message"),
  511. (
  512. ("", FundingType.OWN_FUNDS.name, "EU", b"Please fill in this field."),
  513. ("Funding Project B", FundingType.OWN_FUNDS.name, "EU", b"A project with this name already exists."),
  514. ("New Name", "UNKNOWN", "EU", b"Not a valid choice."),
  515. ("New Name", "", "EU", b"Not a valid choice."),
  516. ("New Name", FundingType.OWN_FUNDS.name, "UNKNOWN", b"Not a valid choice."),
  517. ),
  518. )
  519. def test_edit_project_authorized_invalid(app, client, auth, new_name, new_funding_type, new_funding_country, message):
  520. auth.login("ada")
  521. data = {
  522. "name" : new_name,
  523. "short_name" : "fp2",
  524. "url" : "http://www.test.com",
  525. "funding_type" : new_funding_type,
  526. "funder" : "New Funder",
  527. "funding_country" : new_funding_country,
  528. }
  529. response = client.post("/admin/projects/1/edit", data=data, follow_redirects=True)
  530. assert response.status_code == 200
  531. assert len(response.history) == 0
  532. assert message in response.data
  533. with app.app_context():
  534. project = FundingProject.query.filter_by(id=1).first()
  535. assert project.name == "Funding Project A"
  536. assert project.short_name == "FPA"
  537. assert project.url == None
  538. assert project.funding_type == FundingType.NATIONAL_FUNDS
  539. assert project.funder == None
  540. def test_delete_project_authorized_valid(app, client, auth):
  541. auth.login("ada")
  542. project_id = 4
  543. data = {
  544. "confirm" : True,
  545. }
  546. response = client.post("/admin/projects/{0}/delete".format(project_id), data=data, follow_redirects=True)
  547. assert response.status_code == 200
  548. assert response.history[0].status_code == 302
  549. with app.app_context():
  550. assert FundingProject.query.filter_by(id=project_id).first() is None
  551. def test_delete_project_authorized_invalid(app, client, auth):
  552. auth.login("ada")
  553. project_id = 1
  554. data = {
  555. "confirm" : True,
  556. }
  557. response = client.post("/admin/projects/{0}/delete".format(project_id), data=data, follow_redirects=True)
  558. assert response.status_code == 200
  559. assert response.history[0].status_code == 302
  560. assert b"This project cannot be deleted because there are resources associated with it." in response.data
  561. with app.app_context():
  562. assert FundingProject.query.filter_by(id=project_id).first() is not None