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