test_controllers.py 12 KB

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