PageRenderTime 19ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/appengine/apps/tests.py

https://code.google.com/p/pageforest/
Python | 323 lines | 295 code | 12 blank | 16 comment | 0 complexity | bcafb3a5504d86720d12de38e46cc05a MD5 | raw file
  1. import re
  2. import logging
  3. import datetime
  4. from mock import Mock
  5. from django.conf import settings
  6. from django.test import TestCase, Client
  7. from django.utils import simplejson as json
  8. from auth.models import User
  9. from apps.models import App
  10. from docs.models import Doc
  11. from blobs.models import Blob
  12. from apps.middleware import app_id_from_trusted_domain
  13. from utils.middleware import RequestMiddleware
  14. TAG_REGEX = re.compile(r'<[/!\w][^>]*>')
  15. class MockDatetime(datetime.datetime):
  16. orig_datetime = datetime.datetime
  17. base_time = datetime.datetime(2010, 11, 12, 13, 14, 15)
  18. delta_secs = 0
  19. @classmethod
  20. def now(cls):
  21. return cls.base_time + datetime.timedelta(0, cls.delta_secs)
  22. @classmethod
  23. def advance_time(cls, secs):
  24. cls.delta_secs += secs
  25. @classmethod
  26. def reset_time(cls):
  27. cls.delta_secs = 0
  28. class AppTestCase(TestCase):
  29. """
  30. Reusable TestCase with automatic users, apps, documents.
  31. Users: peter, paul (not email verified)
  32. Apps: www, myapp
  33. Docs: myapp/mydoc (owned by peter)
  34. Blobs: apps/myapp/index.html/
  35. myapp/mydoc/ (Doc's Blob)
  36. myapp/mydoc/myblob (Child Blob)
  37. Clients: www, myapp, admin.myapp
  38. """
  39. def setUp(self):
  40. # Reset the RequestMiddleware.
  41. RequestMiddleware.thread_local = None
  42. # Mock the datetime object.
  43. datetime.datetime = MockDatetime
  44. MockDatetime.reset_time()
  45. # Create some users.
  46. self.peter = User(key_name='peter', username='Peter',
  47. email='peter@example.com',
  48. email_verified=datetime.datetime.now())
  49. self.peter.set_password('peter_secret')
  50. self.peter.put()
  51. self.paul = User(key_name='paul', username='Paul',
  52. email='paul@example.com')
  53. self.paul.set_password('paul_secret')
  54. self.paul.put()
  55. # Create a test application.
  56. self.www = App.lookup('www')
  57. self.app = App(
  58. key_name='myapp',
  59. title='My Test App',
  60. url='http://myapp.pageforest.com/',
  61. referers=['http://trusted.com'],
  62. owner='peter',
  63. readers=['public'],
  64. tags=['mytag', 'pf:featured'],
  65. secret="myapp_secret")
  66. self.app.put()
  67. # Create a test document.
  68. self.doc = Doc(
  69. key_name='myapp/mydoc',
  70. doc_id='MyDoc',
  71. title="My Document",
  72. tags='one two three'.split(),
  73. owner='peter',
  74. readers=['public'],
  75. )
  76. self.doc.put()
  77. self.private_doc = Doc(
  78. key_name='myapp/private',
  79. doc_id='private',
  80. title="My Secret Document",
  81. owner='peter',
  82. )
  83. self.private_doc.put()
  84. # Create some test blobs.
  85. Blob(key_name='apps/myapp/index.html/', value='<html>').put()
  86. Blob(key_name='myapp/mydoc/', value='{"int": 123}').put()
  87. self.blob = Blob(key_name='myapp/mydoc/myblob/', value='["json"]')
  88. self.blob.put()
  89. # Create test clients for www and app.
  90. self.www_client = Client(
  91. HTTP_HOST='www.pageforest.com',
  92. HTTP_REFERER='http://www.pageforest.com/')
  93. self.app_client = Client(
  94. HTTP_HOST='myapp.pageforest.com',
  95. HTTP_REFERER='http://myapp.pageforest.com/')
  96. self.admin_client = Client(
  97. HTTP_HOST='%s.myapp.pageforest.com' % settings.ADMIN_SUBDOMAIN,
  98. HTTP_REFERER='http://myapp.pageforest.com/')
  99. def tearDown(self):
  100. datetime.datetime = MockDatetime.orig_datetime
  101. def sign_in(self, user):
  102. """
  103. Generate sessionkey cookies for this user.
  104. """
  105. self.www_client.cookies[settings.SESSION_COOKIE_NAME] = \
  106. user.generate_session_key(self.www)
  107. self.app_client.cookies[settings.SESSION_COOKIE_NAME] = \
  108. user.generate_session_key(self.app)
  109. self.admin_client.cookies[settings.SESSION_COOKIE_NAME] = \
  110. user.generate_session_key(
  111. self.app, subdomain=settings.ADMIN_SUBDOMAIN)
  112. def sign_out(self):
  113. """
  114. Remove sessionkey cookies.
  115. """
  116. if settings.SESSION_COOKIE_NAME in self.www_client.cookies:
  117. del self.www_client.cookies[settings.SESSION_COOKIE_NAME]
  118. if settings.SESSION_COOKIE_NAME in self.app_client.cookies:
  119. del self.app_client.cookies[settings.SESSION_COOKIE_NAME]
  120. if settings.SESSION_COOKIE_NAME in self.admin_client.cookies:
  121. del self.admin_client.cookies[settings.SESSION_COOKIE_NAME]
  122. def extract_content(self, response):
  123. """Extract the most meaningful parts from the response."""
  124. text = TAG_REGEX.sub(' ', response.content)
  125. lines = [line.strip() for line in text.splitlines() if line.strip()]
  126. return '\n'.join(lines[:20])
  127. def assertContains(self, response, *args, **kwargs):
  128. """Show the response if the test fails."""
  129. try:
  130. super(AppTestCase, self).assertContains(response, *args, **kwargs)
  131. except:
  132. logging.error("assertContains:\n" + self.extract_content(response))
  133. raise
  134. def assertRedirects(self, response, *args, **kwargs):
  135. """Show the response if the test fails."""
  136. try:
  137. super(AppTestCase, self).assertRedirects(response, *args, **kwargs)
  138. except:
  139. logging.error("assertRedirects:\n" + self.extract_content(response))
  140. raise
  141. class AppJsonTest(AppTestCase):
  142. def test_app_json_update(self):
  143. """HTTP PUT app.json should update meta info."""
  144. url = '/app.json'
  145. self.sign_in(self.peter)
  146. response = self.admin_client.put(url, """\
  147. {
  148. "title": "My Application",
  149. "tags": ["test", "myapp"]
  150. }
  151. """, content_type='text/plain')
  152. self.assertContains(response, '"statusText": "Saved"')
  153. # Retrieve updated meta info.
  154. response = self.admin_client.get(url)
  155. decoded = json.loads(response.content)
  156. self.assertEqual(decoded['title'], "My Application")
  157. self.assertTrue('myapp' in decoded['tags'])
  158. self.assertTrue('test' in decoded['tags'])
  159. def test_http_method_not_allowed(self):
  160. """The /doc_id/ URL should report allowed methods."""
  161. self.sign_in(self.peter)
  162. response = self.admin_client.post('/app.json', {'key': 'value'})
  163. self.assertEqual(response.status_code, 405)
  164. self.assertEqual(response['Allow'], 'DELETE, GET, PUT')
  165. def test_delete(self):
  166. self.sign_in(self.peter)
  167. response = self.admin_client.delete('/app.json')
  168. self.assertEqual(response.status_code, 200)
  169. response = self.admin_client.get('/app.json')
  170. self.assertEqual(response.status_code, 404, "NOT FOUND")
  171. def test_app_json_get_auth(self):
  172. """The app_json_get view function should check read permissions."""
  173. url = '/app.json'
  174. self.app.readers = []
  175. self.app.put()
  176. # Application owner should have read permission.
  177. self.admin_client.cookies[settings.SESSION_COOKIE_NAME] = \
  178. self.peter.generate_session_key(
  179. self.app, subdomain=settings.ADMIN_SUBDOMAIN)
  180. response = self.admin_client.get(url)
  181. self.assertContains(response, '"url": "http://myapp.pageforest.com/"')
  182. # Session key for other subdomain should not have read permission.
  183. self.admin_client.cookies[settings.SESSION_COOKIE_NAME] = \
  184. self.peter.generate_session_key(self.app, subdomain='docs')
  185. response = self.admin_client.get(url)
  186. self.assertContains(response, "Different subdomain.", status_code=403)
  187. # Session key without subdomain should not have read permission.
  188. self.admin_client.cookies[settings.SESSION_COOKIE_NAME] = \
  189. self.peter.generate_session_key(self.app)
  190. response = self.admin_client.get(url)
  191. self.assertContains(response, "Missing subdomain.", status_code=403)
  192. # Other users should not have read permission.
  193. self.admin_client.cookies[settings.SESSION_COOKIE_NAME] = \
  194. self.paul.generate_session_key(
  195. self.app, subdomain=settings.ADMIN_SUBDOMAIN)
  196. response = self.admin_client.get(url)
  197. self.assertContains(response, "Read permission denied.",
  198. status_code=403)
  199. # Invalid session key should return a helpful error message.
  200. self.admin_client.cookies[settings.SESSION_COOKIE_NAME] = 'bogus'
  201. response = self.admin_client.get(url)
  202. self.assertContains(
  203. response, "Invalid sessionkey cookie: Expected 4 parts.",
  204. status_code=403)
  205. # Anonymous should not have read permission.
  206. del self.admin_client.cookies[settings.SESSION_COOKIE_NAME]
  207. response = self.admin_client.get(url)
  208. self.assertContains(response, "Read permission denied.",
  209. status_code=403)
  210. def test_app_json_put_auth(self):
  211. """The app_json_put view function should check write permissions."""
  212. url = '/app.json'
  213. # Application owner should have write permission.
  214. self.admin_client.cookies[settings.SESSION_COOKIE_NAME] = \
  215. self.peter.generate_session_key(
  216. self.app, subdomain=settings.ADMIN_SUBDOMAIN)
  217. response = self.admin_client.put(url, '{}', content_type='text/plain')
  218. self.assertContains(response, '"statusText": "Saved"')
  219. # Session key for other subdomain should not have write permission.
  220. self.admin_client.cookies[settings.SESSION_COOKIE_NAME] = \
  221. self.peter.generate_session_key(
  222. self.app, subdomain=settings.ADMIN_SUBDOMAIN + 's')
  223. response = self.admin_client.put(url, '{}', content_type='text/plain')
  224. self.assertContains(response, "Different subdomain.", status_code=403)
  225. # Session key without subdomain should not have write permission.
  226. self.admin_client.cookies[settings.SESSION_COOKIE_NAME] = \
  227. self.peter.generate_session_key(self.app)
  228. response = self.admin_client.put(url, '{}', content_type='text/plain')
  229. self.assertContains(response, "Missing subdomain.", status_code=403)
  230. # Other users should not have write permission.
  231. self.admin_client.cookies[settings.SESSION_COOKIE_NAME] = \
  232. self.paul.generate_session_key(
  233. self.app, subdomain=settings.ADMIN_SUBDOMAIN)
  234. response = self.admin_client.put(url, '{}', content_type='text/plain')
  235. self.assertContains(response, "Write permission denied.",
  236. status_code=403)
  237. # Invalid session key should return a helpful error message.
  238. self.admin_client.cookies[settings.SESSION_COOKIE_NAME] = 'bogus'
  239. response = self.admin_client.put(url, '{}', content_type='text/plain')
  240. self.assertContains(
  241. response, "Invalid sessionkey cookie: Expected 4 parts.",
  242. status_code=403)
  243. # Anonymous should not have write permission.
  244. del self.admin_client.cookies[settings.SESSION_COOKIE_NAME]
  245. response = self.admin_client.put(url, '{}', content_type='text/plain')
  246. self.assertContains(response, "Write permission denied.",
  247. status_code=403)
  248. def test_app_json_put_tags(self):
  249. """The app_json_put view function should update non-reserved tags."""
  250. self.sign_in(self.peter)
  251. self.assertEquals(
  252. App.get_by_key_name('myapp').tags, ['mytag', 'pf:featured'])
  253. self.admin_client.put(
  254. '/app.json', '{"tags": ["newtag", "pf:ignorethis"]}',
  255. content_type='application/json')
  256. self.assertEquals(
  257. App.get_by_key_name('myapp').tags, ['pf:featured', 'newtag'])
  258. class HostnameTest(TestCase):
  259. def test_trusted_domain(self):
  260. """The app_id_from_trusted_domain function should find myapp."""
  261. for hostname in [
  262. 'myapp.pageforest.com',
  263. 'myapp.pageforest.appspot.com',
  264. 'myapp.dev.latest.pageforest.appspot.com',
  265. 'myapp.2010-05-12.latest.pageforest.appspot.com',
  266. 'myapp.pgfr.st',
  267. 'myapp.pgfrst.com',
  268. 'myapp.pageforest',
  269. 'myapp.localhost',
  270. 'myapp.localhost:8080',
  271. ]:
  272. self.assertEqual(app_id_from_trusted_domain(hostname), 'myapp')
  273. def test_unknown_domain(self):
  274. """Untrusted domains should return None."""
  275. for hostname in [
  276. 'malicious.com',
  277. 'www.malicious.com',
  278. 'myapp.malicious.com',
  279. ]:
  280. self.assertEqual(app_id_from_trusted_domain(hostname), None)
  281. class AppErrorsTest(TestCase):
  282. def setUp(self):
  283. self.noapp_client = Client(HTTP_HOST="nosuch.pageforest.com")
  284. def test_missing_app(self):
  285. """A missing app should return 404 Not Found."""
  286. response = self.noapp_client.get('/')
  287. self.assertEqual(response.status_code, 404)