test_controllers.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. from datetime import date
  2. import pytest
  3. from flask import get_flashed_messages, url_for
  4. from flask_login import current_user, login_user
  5. from werkzeug.datastructures import MultiDict
  6. from flaskbb.core.changesets import ChangeSetHandler
  7. from flaskbb.core.exceptions import PersistenceError, StopValidation
  8. from flaskbb.core.user.update import (EmailUpdate, PasswordUpdate,
  9. SettingsUpdate, UserDetailsChange)
  10. from flaskbb.user.forms import (ChangeEmailForm, ChangePasswordForm,
  11. ChangeUserDetailsForm, GeneralSettingsForm)
  12. from flaskbb.user.views import (ChangeEmail, ChangePassword, ChangeUserDetails,
  13. UserSettings)
  14. @pytest.fixture(scope="function", autouse=True)
  15. def setup_request(user, default_settings, post_request_context):
  16. login_user(user)
  17. class TestUserSettingsView(object):
  18. def test_renders_get_okay(self, mocker):
  19. form = self.produce_form({})
  20. handler = mocker.Mock(spec=ChangeSetHandler)
  21. handler = UserSettings(form=form, settings_update_handler=handler)
  22. handler.get()
  23. def test_update_user_settings_successfully(self, user, mocker):
  24. form = self.produce_form(
  25. data={"language": "python", "theme": "solarized"}
  26. )
  27. handler = mocker.Mock(spec=ChangeSetHandler)
  28. view = UserSettings(form=form, settings_update_handler=handler)
  29. result = view.post()
  30. flashed = get_flashed_messages(with_categories=True)
  31. assert len(flashed) == 1
  32. assert flashed[0] == ("success", "Settings updated.")
  33. assert result.status_code == 302
  34. assert result.headers["Location"] == url_for("user.settings")
  35. handler.apply_changeset.assert_called_once_with(
  36. user, SettingsUpdate(language="python", theme="solarized")
  37. )
  38. def test_update_user_settings_fail_with_not_valid(self, mocker):
  39. form = self.produce_form(
  40. data={"language": "ruby", "theme": "solarized"}
  41. )
  42. handler = mocker.Mock(spec=ChangeSetHandler)
  43. view = UserSettings(form=form, settings_update_handler=handler)
  44. view.post()
  45. flashed = get_flashed_messages()
  46. assert not len(flashed)
  47. handler.apply_changeset.assert_not_called()
  48. assert form.errors
  49. def test_update_user_settings_fail_with_stopvalidation_error(self, mocker):
  50. form = self.produce_form(
  51. data={"language": "python", "theme": "molokai"}
  52. )
  53. handler = mocker.Mock(spec=ChangeSetHandler)
  54. handler.apply_changeset.side_effect = StopValidation(
  55. [("theme", "Solarized is better")]
  56. )
  57. view = UserSettings(form=form, settings_update_handler=handler)
  58. view.post()
  59. flashed = get_flashed_messages()
  60. assert not (len(flashed))
  61. assert form.errors["theme"] == ["Solarized is better"]
  62. def test_update_user_settings_fails_with_persistence_error(self, mocker):
  63. form = self.produce_form(
  64. data={"language": "python", "theme": "molokai"}
  65. )
  66. handler = mocker.Mock(spec=ChangeSetHandler)
  67. handler.apply_changeset.side_effect = PersistenceError("Nope")
  68. view = UserSettings(form=form, settings_update_handler=handler)
  69. result = view.post()
  70. flashed = get_flashed_messages(with_categories=True)
  71. assert len(flashed) == 1
  72. assert flashed[0] == ("danger", "Error while updating user settings")
  73. assert result.status_code == 302
  74. assert result.headers["Location"] == url_for("user.settings")
  75. def produce_form(self, data):
  76. form = GeneralSettingsForm(
  77. formdata=MultiDict(data), meta={"csrf": False}
  78. )
  79. form.language.choices = [
  80. ("python", "python"),
  81. ("ecmascript", "ecmascript"),
  82. ]
  83. form.theme.choices = [
  84. ("molokai", "molokai"),
  85. ("solarized", "solarized"),
  86. ]
  87. return form
  88. class TestChangePasswordView(object):
  89. def test_renders_get_okay(self, mocker):
  90. form = self.produce_form()
  91. handler = mocker.Mock(spec=ChangeSetHandler)
  92. view = ChangePassword(form=form, password_update_handler=handler)
  93. view.get()
  94. def test_updates_user_password_okay(self, user, mocker):
  95. form = self.produce_form(
  96. old_password="password",
  97. new_password="newpassword",
  98. confirm_new_password="newpassword",
  99. )
  100. handler = mocker.Mock(spec=ChangeSetHandler)
  101. view = ChangePassword(form=form, password_update_handler=handler)
  102. result = view.post()
  103. flashed = get_flashed_messages(with_categories=True)
  104. assert len(flashed) == 1
  105. assert flashed[0] == ("success", "Password updated.")
  106. assert result.status_code == 302
  107. assert result.headers["Location"] == url_for("user.change_password")
  108. handler.apply_changeset.assert_called_once_with(
  109. user,
  110. PasswordUpdate(old_password="password", new_password="newpassword"),
  111. )
  112. def test_updates_user_password_fails_with_invalid_inpit(self, mocker, user):
  113. form = self.produce_form(
  114. old_password="password",
  115. new_password="newpassword",
  116. confirm_new_password="whoops",
  117. )
  118. handler = mocker.Mock(spec=ChangeSetHandler)
  119. view = ChangePassword(form=form, password_update_handler=handler)
  120. view.post()
  121. handler.apply_changeset.assert_not_called()
  122. assert "new_password" in form.errors
  123. def test_update_user_password_fails_with_stopvalidation_error(self, mocker):
  124. form = self.produce_form(
  125. old_password="password",
  126. new_password="newpassword",
  127. confirm_new_password="newpassword",
  128. )
  129. handler = mocker.Mock(spec=ChangeSetHandler)
  130. handler.apply_changeset.side_effect = StopValidation(
  131. [("new_password", "That's not a very strong password")]
  132. )
  133. view = ChangePassword(form=form, password_update_handler=handler)
  134. view.post()
  135. assert form.errors["new_password"] == [
  136. "That's not a very strong password"
  137. ]
  138. def test_update_user_password_fails_with_persistence_error(self, mocker):
  139. form = self.produce_form(
  140. old_password="password",
  141. new_password="newpassword",
  142. confirm_new_password="newpassword",
  143. )
  144. handler = mocker.Mock(spec=ChangeSetHandler)
  145. handler.apply_changeset.side_effect = PersistenceError("no")
  146. view = ChangePassword(form=form, password_update_handler=handler)
  147. result = view.post()
  148. flashed = get_flashed_messages(with_categories=True)
  149. assert flashed == [("danger", "Error while changing password")]
  150. assert result.status_code == 302
  151. assert result.headers["Location"] == url_for("user.change_password")
  152. def produce_form(self, **kwargs):
  153. return ChangePasswordForm(
  154. formdata=MultiDict(kwargs), meta={"csrf": False}, obj=current_user
  155. )
  156. class TestChangeEmailView(object):
  157. def test_renders_get_okay(self, mocker):
  158. form = self.produce_form()
  159. handler = mocker.Mock(spec=ChangeSetHandler)
  160. view = ChangeEmail(form=form, update_email_handler=handler)
  161. view.get()
  162. def test_update_user_email_successfully(self, user, mocker):
  163. form = self.produce_form(
  164. old_email=user.email,
  165. new_email="new@email.mail",
  166. confirm_new_email="new@email.mail",
  167. )
  168. handler = mocker.Mock(spec=ChangeSetHandler)
  169. view = ChangeEmail(form=form, update_email_handler=handler)
  170. result = view.post()
  171. flashed = get_flashed_messages(with_categories=True)
  172. assert flashed == [("success", "Email address updated.")]
  173. handler.apply_changeset.assert_called_once_with(
  174. user, EmailUpdate(old_email=user.email, new_email="new@email.mail")
  175. )
  176. assert result.status_code == 302
  177. assert result.headers["Location"] == url_for("user.change_email")
  178. def test_update_user_email_fails_with_invalid_input(self, user, mocker):
  179. form = self.produce_form(old_email=user.email, new_email="new@e.mail")
  180. handler = mocker.Mock(spec=ChangeSetHandler)
  181. view = ChangeEmail(form=form, update_email_handler=handler)
  182. view.post()
  183. assert form.errors
  184. handler.apply_changeset.assert_not_called()
  185. def test_update_user_email_fails_with_stopvalidation(self, user, mocker):
  186. form = self.produce_form(
  187. old_email=user.email,
  188. new_email="new@e.mail",
  189. confirm_new_email="new@e.mail",
  190. )
  191. handler = mocker.Mock(spec=ChangeSetHandler)
  192. handler.apply_changeset.side_effect = StopValidation(
  193. [("new_email", "bad email")]
  194. )
  195. view = ChangeEmail(form=form, update_email_handler=handler)
  196. view.post()
  197. assert form.errors == {"new_email": ["bad email"]}
  198. def test_update_email_fails_with_persistence_error(self, user, mocker):
  199. form = self.produce_form(
  200. old_email=user.email,
  201. new_email="new@e.mail",
  202. confirm_new_email="new@e.mail",
  203. )
  204. handler = mocker.Mock(spec=ChangeSetHandler)
  205. handler.apply_changeset.side_effect = PersistenceError("nope")
  206. view = ChangeEmail(form=form, update_email_handler=handler)
  207. result = view.post()
  208. flashed = get_flashed_messages(with_categories=True)
  209. assert flashed == [("danger", "Error while updating email")]
  210. assert result.status_code == 302
  211. assert result.headers["Location"] == url_for("user.change_email")
  212. def produce_form(self, **data):
  213. return ChangeEmailForm(
  214. formdata=MultiDict(data), user=current_user, meta={"csrf": False}
  215. )
  216. class TestChangeUserDetailsView(object):
  217. def test_renders_get_okay(self, mocker):
  218. form = self.produce_form()
  219. handler = mocker.Mock(spec=ChangeSetHandler)
  220. view = ChangeUserDetails(form=form, details_update_handler=handler)
  221. view.get()
  222. def test_update_user_details_successfully_updates(self, user, mocker):
  223. form = self.produce_form(
  224. birthday="25 04 2000",
  225. gender="awesome",
  226. location="here",
  227. website="http://web.site",
  228. avatar="http://web.site/avatar.png",
  229. signature="use a cursive font",
  230. notes="got 'em",
  231. )
  232. handler = mocker.Mock(spec=ChangeSetHandler)
  233. view = ChangeUserDetails(form=form, details_update_handler=handler)
  234. result = view.post()
  235. flashed = get_flashed_messages(with_categories=True)
  236. assert flashed == [("success", "User details updated.")]
  237. assert result.status_code == 302
  238. assert result.headers["Location"] == url_for("user.change_user_details")
  239. handler.apply_changeset.assert_called_once_with(
  240. user,
  241. UserDetailsChange(
  242. birthday=date(2000, 4, 25),
  243. gender="awesome",
  244. location="here",
  245. website="http://web.site",
  246. avatar="http://web.site/avatar.png",
  247. signature="use a cursive font",
  248. notes="got 'em",
  249. ),
  250. )
  251. def test_update_user_fails_with_invalid_input(self, mocker):
  252. form = self.produce_form(birthday="99 99 999999")
  253. handler = mocker.Mock(spec=ChangeSetHandler)
  254. view = ChangeUserDetails(form=form, details_update_handler=handler)
  255. view.post()
  256. assert form.errors == {"birthday": ["Not a valid date value"]}
  257. def test_update_user_fails_with_stopvalidation(self, mocker):
  258. form = self.produce_form(birthday="25 04 2000")
  259. handler = mocker.Mock(spec=ChangeSetHandler)
  260. handler.apply_changeset.side_effect = StopValidation(
  261. [("birthday", "I just want you to know that's a great birthday")]
  262. )
  263. view = ChangeUserDetails(form=form, details_update_handler=handler)
  264. view.post()
  265. assert form.errors == {
  266. "birthday": ["I just want you to know that's a great birthday"]
  267. }
  268. def test_update_user_fails_with_persistence_error(self, mocker):
  269. form = self.produce_form(birthday="25 04 2000")
  270. handler = mocker.Mock(spec=ChangeSetHandler)
  271. handler.apply_changeset.side_effect = PersistenceError("no")
  272. view = ChangeUserDetails(form=form, details_update_handler=handler)
  273. result = view.post()
  274. flashed = get_flashed_messages(with_categories=True)
  275. assert flashed == [("danger", "Error while updating user details")]
  276. assert result.status_code == 302
  277. assert result.headers["Location"] == url_for("user.change_user_details")
  278. def produce_form(self, **kwargs):
  279. return ChangeUserDetailsForm(
  280. obj=current_user, formdata=MultiDict(kwargs), meta={"csrf": False}
  281. )