PageRenderTime 39ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/test_handlers.py

https://bitbucket.org/angryshortone/grump-o-nado
Python | 418 lines | 394 code | 10 blank | 14 comment | 0 complexity | f16b393353be9112cf5784249b03a9c6 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. # This Source Code Form is subject to the terms of the Mozilla Public
  2. # License, v. 2.0. If a copy of the MPL was not distributed with this
  3. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. """Unit tests for the handlers"""
  5. import json
  6. from datetime import datetime
  7. from datetime import timedelta as tdelta
  8. import jwt
  9. from sqlalchemy import create_engine
  10. from sqlalchemy.orm import sessionmaker
  11. from tornado import testing, escape
  12. from tornado.web import Application
  13. from tornado_sqlalchemy import make_session_factory
  14. from grumponado.app import assign_handlers
  15. from grumponado.models import Users, BASE, Tokens
  16. PW = b'password'
  17. PW_HASH = b'$2b$04$eA9hGoyi5xAp0SKE49RW2eMpOA69u0ObcmQoV/M82UtzHlDydjHwy'
  18. class HandlerTests(testing.AsyncHTTPTestCase):
  19. """Base class for the setup and tear down of handler tests. It defines the
  20. basic setup for the database and also provides the tear down method
  21. """
  22. db = 'postgres://localhost:5432/py_unit_test'
  23. engine = create_engine(db)
  24. def setUp(self):
  25. """Set up for the database"""
  26. super(HandlerTests, self).setUp()
  27. BASE.metadata.create_all(self.engine)
  28. def tearDown(self):
  29. """Tear down for the database"""
  30. super(HandlerTests, self).tearDown()
  31. BASE.metadata.drop_all(self.engine)
  32. def get_app(self):
  33. """Get the tornado app to run the tests. This is a default
  34. configuration
  35. """
  36. routes = assign_handlers('tests')
  37. session_factory = make_session_factory(self.db)
  38. return Application(routes,
  39. session_factory=session_factory,
  40. cookie_secret='pytest',
  41. hashing_salt='pysalt')
  42. class UserHandlerPreps(HandlerTests):
  43. """Setup different users"""
  44. def setUp(self):
  45. """Database set up with a default user. Base class is overwritten"""
  46. super(UserHandlerPreps, self).setUp()
  47. session = sessionmaker()
  48. session.configure(bind=self.engine)
  49. users_sammy = Users(
  50. user='sammy',
  51. email='sammy@gmx.net',
  52. password=PW_HASH,
  53. avatar=''
  54. )
  55. users_notsammy = Users(
  56. user='notsammy',
  57. email='notsammy@gmx.net',
  58. password=PW_HASH,
  59. avatar=''
  60. )
  61. dbsession = session()
  62. try:
  63. dbsession.add(users_notsammy)
  64. dbsession.commit()
  65. dbsession.add(users_sammy)
  66. dbsession.commit()
  67. token = Tokens(users_notsammy.id, 'yay', 'nope', 'nope')
  68. delta = datetime.utcnow() - tdelta(days=15)
  69. token2 = Tokens(users_notsammy.id, 'yay2', 'nope', 'nope', delta)
  70. token2.last_refresh = delta
  71. dbsession.add(token)
  72. dbsession.commit()
  73. dbsession.add(token2)
  74. dbsession.commit()
  75. finally:
  76. dbsession.close()
  77. def get_app(self):
  78. """Create an app for testing"""
  79. routes = assign_handlers('tests')
  80. session_factory = make_session_factory(self.db)
  81. return Application(routes,
  82. session_factory=session_factory,
  83. cookie_secret='pytest',
  84. login_url='/404',
  85. hashing_salt='pysalt')
  86. class TestMainHandlers(HandlerTests):
  87. """Tests for the main entry point"""
  88. def test_get_main(self):
  89. """Get the base route, without a trailing slash"""
  90. response = self.fetch('/tests')
  91. self.assertEqual(response.code, 200)
  92. def test_get_main_trailing_slash(self):
  93. """Get the base route, without a trailing slash"""
  94. response = self.fetch('/tests/')
  95. self.assertEqual(response.code, 200)
  96. class TestUserHandlers(HandlerTests):
  97. """The user handler tests to test the creation, deletion and update of
  98. users.
  99. """
  100. def setUp(self):
  101. """Database set up with a default user. Base class is overwritten"""
  102. super(TestUserHandlers, self).setUp()
  103. session = sessionmaker()
  104. session.configure(bind=self.engine)
  105. user = Users(
  106. user='notsammy',
  107. email='notsammy@gmx.net',
  108. password=PW,
  109. avatar=''
  110. )
  111. dbsession = session()
  112. try:
  113. dbsession.add(user)
  114. dbsession.commit()
  115. finally:
  116. dbsession.close()
  117. def test_user_get_inexistent_user(self):
  118. """Get the infos of a particular user, that does not exist"""
  119. response = self.fetch('/tests/users/sammy')
  120. self.assertEqual(response.code, 404)
  121. def test_user_get_user(self):
  122. """Get the infos of a particular user, that does exist"""
  123. response = self.fetch('/tests/users/notsammy')
  124. self.assertEqual(response.code, 200)
  125. def test_user_create_validation(self):
  126. """Test the validation when creating a user"""
  127. response = self.fetch('/tests/users/', method='POST', body='{}')
  128. self.assertEqual(response.code, 400)
  129. def test_user_create_not_json(self):
  130. """Test if the validation fails if the supplied data is not json"""
  131. response = self.fetch('/tests/users/', method='POST', body='_')
  132. self.assertEqual(response.code, 400)
  133. def test_user_create_user_exists(self):
  134. """Test handling if someone tries to create a user, that already
  135. exists
  136. """
  137. test_user = {'name': 'sammy',
  138. 'email': 'sammy@domain.tld',
  139. 'password': '12345678abcd'}
  140. self.fetch('/tests/users/', method='POST', body=json.dumps(test_user))
  141. response = self.fetch('/tests/users/',
  142. method='POST',
  143. body=json.dumps(test_user))
  144. self.assertEqual(response.code, 400)
  145. def test_user_create_type_error(self):
  146. """Test exception handling, when the supplied data are of the wrong
  147. type
  148. """
  149. test_user = {'name': 102,
  150. 'email': 'samm2y@domain.tld',
  151. 'password': '12345678abcd'}
  152. response = self.fetch('/tests/users/', method='POST', body=json.dumps(test_user))
  153. self.assertEqual(response.code, 400)
  154. class TestLoginLogoutHandlers(UserHandlerPreps):
  155. """Login and logout tests"""
  156. def test_login_login_errors(self):
  157. """Login with an error, missing user or email"""
  158. body = json.dumps({'email': 'notsammy@gmx.net'})
  159. response = self.fetch('/tests/login', method='POST', body=body)
  160. self.assertEqual(response.code, 400)
  161. body2 = json.dumps({'password': 'password'})
  162. response2 = self.fetch('/tests/login', method='POST', body=body2)
  163. self.assertEqual(response2.code, 400)
  164. body3 = json.dumps({'password': None})
  165. response3 = self.fetch('/tests/login', method='POST', body=body3)
  166. self.assertEqual(response3.code, 400)
  167. def test_login_format_errors(self):
  168. """Test login with a format error"""
  169. body = "{'email': 'notsammy@gmx.net'})2"
  170. response = self.fetch('/tests/login', method='POST', body=body)
  171. self.assertEqual(400, response.code)
  172. def test_login_wrong_password(self):
  173. """Test login with a wrong password"""
  174. body = json.dumps({'email': 'notsammy@gmx.net',
  175. 'password': 'password2'})
  176. response = self.fetch('/tests/login', method='POST', body=body)
  177. self.assertEqual(400, response.code)
  178. body = response.body.decode('utf-8')
  179. self.assertTrue('2004' in body)
  180. def test_login_wrong_name(self):
  181. """Test login with a wrong name"""
  182. body = json.dumps({'email': 'notsammy@gmx.net2',
  183. 'password': 'password'})
  184. response = self.fetch('/tests/login', method='POST', body=body)
  185. self.assertEqual(400, response.code)
  186. body = response.body.decode('utf-8')
  187. self.assertTrue('2004' in body)
  188. def test_login_success(self):
  189. """Test the successfull login"""
  190. body = json.dumps({'email': 'notsammy@gmx.net',
  191. 'password': 'password'})
  192. response = self.fetch('/tests/login', method='POST', body=body)
  193. body = response.body.decode('utf-8')
  194. self.assertEqual(200, response.code)
  195. self.assertTrue('5002' in body)
  196. self.assertTrue('user_name' in body)
  197. def test_logout(self):
  198. """Test to logout users"""
  199. token = jwt.encode({
  200. 'sub': 0,
  201. 'name': 'notsammy',
  202. 'iat': datetime.utcnow().timestamp(),
  203. 'exp': datetime.utcnow().timestamp() + 86000,
  204. 'refresh': 'yay'
  205. }, key='pytest', algorithm='HS256').decode('utf-8')
  206. response = self.fetch('/tests/logout', method='GET', headers={'Authorization': 'Bearer ' + token})
  207. self.assertEqual(200, response.code)
  208. decoded_body = response.body.decode('utf-8')
  209. found = '5001' in decoded_body
  210. self.assertTrue(found)
  211. def test_refresh_valid_jwt(self):
  212. """Test the refresh with a valid token"""
  213. token = jwt.encode({
  214. 'sub': 0,
  215. 'name': 'notsammy',
  216. 'iat': datetime.utcnow().timestamp(),
  217. 'exp': datetime.utcnow().timestamp() + 86000,
  218. 'refresh': 'yay'
  219. }, key='pytest', algorithm='HS256').decode('utf-8')
  220. response = self.fetch('/tests/me', method='GET', headers={'Authorization': 'Bearer ' + token})
  221. body = escape.json_decode(response.body.decode('utf-8'))
  222. self.assertIsNotNone(body['auth'])
  223. self.assertEqual(body['auth'].split('.')[0], 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9')
  224. def test_refresh_old_jwt(self):
  225. """Test the refresh with an expired token"""
  226. token = jwt.encode({
  227. 'sub': 0,
  228. 'name': 'notsammy',
  229. 'iat': datetime.utcnow().timestamp(),
  230. 'exp': datetime.utcnow().timestamp() - 86000,
  231. 'refresh': 'yay'
  232. }, key='pytest', algorithm='HS256').decode('utf-8')
  233. response = self.fetch('/tests/me', method='GET', headers={'Authorization': 'Bearer ' + token})
  234. body = escape.json_decode(response.body.decode('utf-8'))
  235. self.assertIsNotNone(body['auth'])
  236. self.assertEqual(body['auth'].split('.')[0], 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9')
  237. def test_refresh_old_refresh(self):
  238. """Test the refresh with an expired token"""
  239. token = jwt.encode({
  240. 'sub': 0,
  241. 'name': 'notsammy',
  242. 'iat': datetime.utcnow().timestamp(),
  243. 'exp': datetime.utcnow().timestamp() - 86000,
  244. 'refresh': 'yay2'
  245. }, key='pytest', algorithm='HS256').decode('utf-8')
  246. response = self.fetch('/tests/me', method='GET', headers={'Authorization': 'Bearer ' + token})
  247. self.assertEqual(response.code, 404)
  248. def test_missing_inexisting_user(self):
  249. """Test the refresh with an expired token"""
  250. token = jwt.encode({
  251. 'sub': 0,
  252. 'name': 'nope',
  253. 'iat': datetime.utcnow().timestamp(),
  254. 'exp': datetime.utcnow().timestamp() + 86000,
  255. 'refresh': 'yay'
  256. }, key='pytest', algorithm='HS256').decode('utf-8')
  257. response = self.fetch('/tests/me', method='GET', headers={'Authorization': 'Bearer ' + token})
  258. self.assertEqual(response.code, 401)
  259. def test_missing_refresh(self):
  260. """Test the refresh with an expired token"""
  261. token = jwt.encode({
  262. 'sub': 0,
  263. 'name': 'notsammy',
  264. 'iat': datetime.utcnow().timestamp(),
  265. 'exp': datetime.utcnow().timestamp() - 86000,
  266. }, key='pytest', algorithm='HS256').decode('utf-8')
  267. response = self.fetch('/tests/me', method='GET', headers={'Authorization': 'Bearer ' + token})
  268. self.assertEqual(response.code, 404)
  269. def test_malicious_token(self):
  270. """Test the refresh with an expired token"""
  271. response = self.fetch('/tests/me', method='GET', headers={'Authorization': 'Bearer ' + 'aaX'})
  272. self.assertEqual(response.code, 404)
  273. class TestRating(UserHandlerPreps):
  274. """Rating tests"""
  275. jwt_token = jwt.encode({
  276. 'sub': 1,
  277. 'name': 'sammy',
  278. 'iat': datetime.utcnow().timestamp(),
  279. 'exp': datetime.utcnow().timestamp() + 86000,
  280. 'refresh': 'yay'
  281. }, key='pytest', algorithm='HS256').decode('utf-8')
  282. def get_app(self):
  283. """Create an app for testing"""
  284. routes = assign_handlers('tests')
  285. session_factory = make_session_factory(self.db)
  286. return Application(routes,
  287. session_factory=session_factory,
  288. cookie_secret='pytest',
  289. login_url='/404',
  290. hashing_salt='pysalt')
  291. def test_increase_ratings(self):
  292. """Test of increasing ratings"""
  293. responses_community = []
  294. responses_self = []
  295. for _ in range(5):
  296. responses_community.append(self.fetch('/tests/users/sammy', method='PUT', body='action=increase',
  297. headers={'Authorization': 'Bearer ' + self.jwt_token,
  298. 'Content-Type': 'application/x-www-form-urlencoded'}))
  299. responses_self.append(self.fetch('/tests/users/notsammy', method='PUT', body='action=increase',
  300. headers={'Authorization': 'Bearer ' + self.jwt_token,
  301. 'Content-Type': 'application/x-www-form-urlencoded'}))
  302. response_bodies_community = [escape.json_decode(i.body.decode('utf-8')) for i in responses_community]
  303. response_bodies_self = [escape.json_decode(i.body.decode('utf-8')) for i in responses_self]
  304. try:
  305. ratings_community = [i['result']['community_rating'] for i in response_bodies_community]
  306. ratings_self = [i['result']['self_rating'] for i in response_bodies_self]
  307. except KeyError:
  308. raise KeyError
  309. self.assertGreater(ratings_community[4], ratings_community[3])
  310. self.assertGreater(ratings_self[4], ratings_self[3])
  311. def test_decrease_ratings(self):
  312. """Test of decreasing ratings"""
  313. responses_community = []
  314. responses_self = []
  315. action = 'action=increase'
  316. for i in range(5):
  317. if i > 3:
  318. action = 'action=decrease'
  319. responses_community.append(self.fetch('/tests/users/sammy', method='PUT', body=action,
  320. headers={'Authorization': 'Bearer ' + self.jwt_token,
  321. 'Content-Type': 'application/x-www-form-urlencoded'}))
  322. responses_self.append(self.fetch('/tests/users/notsammy', method='PUT', body=action,
  323. headers={'Authorization': 'Bearer ' + self.jwt_token,
  324. 'Content-Type': 'application/x-www-form-urlencoded'}))
  325. response_bodies_community = [escape.json_decode(i.body.decode('utf-8')) for i in responses_community]
  326. response_bodies_self = [escape.json_decode(i.body.decode('utf-8')) for i in responses_self]
  327. try:
  328. ratings_community = [i['result']['community_rating'] for i in response_bodies_community]
  329. ratings_self = [i['result']['self_rating'] for i in response_bodies_self]
  330. except KeyError:
  331. raise KeyError
  332. self.assertLess(ratings_community[4], ratings_community[3])
  333. self.assertLess(ratings_self[4], ratings_self[3])
  334. def test_missing_arguments(self):
  335. """Test of missing arguments when ratings"""
  336. responses = self.fetch('/tests/users/notsammy', method='PUT', body=b'',
  337. headers={'Authorization': 'Bearer ' + self.jwt_token,
  338. 'Content-Type': 'application/x-www-form-urlencoded'})
  339. self.assertEqual(responses.code, 403)
  340. class TestUserProfileHandler(UserHandlerPreps):
  341. """Profile tests"""
  342. jwt_token = jwt.encode({
  343. 'sub': 1,
  344. 'name': 'sammy',
  345. 'iat': datetime.utcnow().timestamp(),
  346. 'exp': datetime.utcnow().timestamp() + 86000,
  347. 'refresh': 'yay'
  348. }, key='pytest', algorithm='HS256').decode('utf-8')
  349. def get_app(self):
  350. """Create an app for testing"""
  351. routes = assign_handlers('tests')
  352. session_factory = make_session_factory(self.db)
  353. return Application(routes,
  354. session_factory=session_factory,
  355. cookie_secret='pytest',
  356. login_url='/404',
  357. hashing_salt='pysalt')
  358. def test_delete_user(self):
  359. """Test for the deletion of users"""
  360. response_before_delete = self.fetch('/tests/users/sammy')
  361. self.assertEqual(response_before_delete.code, 200)
  362. responses_delete = self.fetch('/tests/me', method='DELETE',
  363. headers={'Authorization': 'Bearer ' + self.jwt_token,
  364. 'Content-Type': 'application/x-www-form-urlencoded'})
  365. body = escape.json_decode(responses_delete.body.decode('utf-8'))
  366. self.assertEqual(body['code'], 5006)
  367. response_after_delete = self.fetch('/tests/users/sammy')
  368. self.assertEqual(response_after_delete.code, 404)