def testRedirectPhoto(self): """Test that a photo redirection returns the signed S3 URL and does *not* set the cookie.""" next_url = "?next=/episodes/%s/photos/%s.f" % (self._new_ep_id, self._photo_ids[0]) response = self._SendRequest(self._invitation_url + next_url) self.assertEqual(response.code, 302) self.assertIn('/fileobjstore/photo/%s.f' % self._photo_ids[0], response.headers['location']) self.assertIsNone(self._tester.GetCookieFromResponse(response)) self._tester.PutPhotoImage( self._cookie, self._new_ep_id, self._photo_ids[0], '.f', 'full image data', content_md5=util.ComputeMD5Base64('full image data')) response = self._SendRequest(response.headers['location'], self._cookie) self.assertEqual(response.code, 200) self.assertEqual(response.body, 'full image data') # Ensure that any cookie passed is not disturbed. response = self._SendRequest(self._invitation_url + next_url, self._cookie3) response_cookie = self._tester.GetCookieFromResponse(response) # Get only first value part of each cookie, which excludes the timestamp & sig parts, which may change. self.assertEqual( response_cookie.split('|')[0], self._cookie3.split('|')[0])
def testUploadMismatch(self): """Upload photo image data with a different MD5 than was originally provided to upload_episode. Because the photo image data does not yet exist, the metadata should be overwritten with the new values. Then try to upload a different MD5 again, expecting an error this time. """ for attr_name, suffix, image_data in [ ('tn_md5', '.t', 'new thumbnail image data'), ('med_md5', '.m', 'new medium image data'), ('full_md5', '.f', 'new full image data'), ('orig_md5', '.o', 'new original image data') ]: # Expect success on first upload. response = self._PutPhoto( self._cookie, self._episode_id, self._photo_ids[0], suffix, image_data, content_md5=util.ComputeMD5Base64(image_data), etag=util.ComputeMD5Hex(image_data)) self.assertEqual(response.code, 200) # Validate that the photo's MD5 was updated. ph_dict = { 'photo_id': self._photo_ids[0], attr_name: util.ComputeMD5Hex(image_data) } self._validator.ValidateUpdateDBObject(Photo, **ph_dict) # Expect failure on second upload with different MD5. new_image_data = 'really ' + image_data response = self._PutPhoto( self._cookie, self._episode_id, self._photo_ids[0], suffix, new_image_data, content_md5=util.ComputeMD5Base64(new_image_data), etag=util.ComputeMD5Hex(new_image_data)) self.assertEqual(response.code, 400)
def _PutPhotoAndVerify(self, user_cookie, exp_code, episode_id, photo_id, suffix, image_data): """Call _PutPhoto and verify return code equals "exp_code".""" response = self._PutPhoto( user_cookie, episode_id, photo_id, suffix, image_data, content_md5=util.ComputeMD5Base64(image_data)) self.assertEqual(response.code, exp_code) return response
def testReUpload(self): """Upload a new photo and attempt to re-upload using If-None-Match header to simulate a phone reinstall where the client uses the /photos/<photo_id> interface to get a redirect to a PUT URL. In the case of the photo existing, the Etag should match and result in a 304 response, saving the client the upload bandwidth. """ full_image_data = 'full image data' for photo_id in self._photo_ids: response = self._PutPhoto( self._cookie, self._episode_id, photo_id, '.f', full_image_data, content_md5=util.ComputeMD5Base64(full_image_data), etag=util.ComputeMD5Hex(full_image_data)) self.assertEqual(response.code, 200) for photo_id in self._photo_ids: response = self._PutPhoto( self._cookie, self._episode_id, photo_id, '.f', full_image_data, content_md5=util.ComputeMD5Base64(full_image_data), etag='"%s"' % util.ComputeMD5Hex(full_image_data)) self.assertEqual(response.code, 304) response = self._PutPhoto( self._cookie, self._episode_id, photo_id, '.f', full_image_data, content_md5=util.ComputeMD5Base64(full_image_data), etag='*') self.assertEqual(response.code, 304)
def _UploadImageFile(self, url, image_data): """Makes a PUT request to the specified 'url' to upload 'image_data'.""" md5_hex = util.ComputeMD5Base64(image_data) response = self._RunAsync(self._tester.http_client.fetch, url, method='PUT', body=image_data, headers={ 'Content-Type': 'image/jpeg', 'Content-MD5': md5_hex }) assert response.code == 200, response return response.body
def setUp(self): super(RemoveViewpointTestCase, self).setUp() self._CreateSimpleTestAssets() self._existing_vp_id, existing_ep_ids = self._tester.ShareNew( self._cookie, [(self._episode_id2, self._photo_ids2)], [self._user2.user_id], **self._CreateViewpointDict(self._cookie)) self._new_vp_id, new_ep_ids = self._tester.ShareNew( self._cookie, [(self._episode_id, self._photo_ids)], [self._user2.user_id], **self._CreateViewpointDict(self._cookie)) self._existing_ep_id = existing_ep_ids[0] self._new_ep_id = new_ep_ids[0] image_data = 'original image data' self._tester.PutPhotoImage( self._cookie, self._new_ep_id, self._photo_ids[0], '.o', image_data, content_md5=util.ComputeMD5Base64(image_data))
def testUploadMD5Mismatch(self): """Call upload_episode once. Then call it again, but with different MD5 image values. Because the photo image data does not yet exist, the metadata should be overwritten with the new values. Then actually upload the image data and try to overwrite the MD5 values again, expecting an error this time. """ upload_data = [('tn_md5', '.t', 'new thumbnail image data'), ('med_md5', '.m', 'new medium image data'), ('full_md5', '.f', 'new full image data'), ('orig_md5', '.o', 'new original image data')] ph_dict = { 'aspect_ratio': 0.75, 'timestamp': time.time(), 'tn_size': 5 * 1024, 'med_size': 10 * 1024, 'full_size': 150 * 1024, 'orig_size': 1200 * 1024 } # Do first upload. for data in upload_data: attr_name, suffix, image_data = data ph_dict[attr_name] = util.ComputeMD5Hex(image_data) ep_dict = {'timestamp': time.time()} ep_id, ph_ids = self._UploadEpisode(self._cookie, [ph_dict], ep_dict) ph_dict['photo_id'] = ph_ids[0] # Update the photo MD5 values and upload again. for data in upload_data: attr_name, suffix, image_data = data ph_dict[attr_name] = util.ComputeMD5Hex('really ' + image_data) ep_dict['episode_id'] = ep_id ep_id, ph_ids = self._UploadEpisode(self._cookie, [ph_dict], ep_dict) assert ph_dict['photo_id'] == ph_ids[0], (ph_dict, ph_ids) for data in upload_data: # Upload the image data for this size of photo. attr_name, suffix, image_data = data etag = util.ComputeMD5Hex('really ' + image_data) self._tester.PutPhotoImage( self._cookie, ep_id, ph_ids[0], suffix, 'really ' + image_data, content_md5=util.ComputeMD5Base64('really ' + image_data), etag=etag) # Validate that the photo's MD5 was updated. update_ph_dict = {'photo_id': ph_ids[0], attr_name: etag} self._validator.ValidateUpdateDBObject(Photo, **update_ph_dict) # Verify the MD5 value can no longer be updated. copy_ph_dict = deepcopy(ph_dict) copy_ph_dict[attr_name] = util.ComputeMD5Hex('bad ' + image_data) self.assertRaisesHttpError(403, self._UploadEpisode, self._cookie, [copy_ph_dict], ep_dict) # Test changing one MD5 and not others. if suffix == '.m': copy_ph_dict = deepcopy(ph_dict) copy_ph_dict['full_md5'] = util.ComputeMD5Hex('only this ' + image_data) self._UploadEpisode(self._cookie, [copy_ph_dict], ep_dict)
def testUploadAndGetPut(self): """Upload a photo, PUT the photo image data, then access it in various ways. """ episode_id = self._episode_id photo_id = self._photo_ids[0] orig_image_data = 'original image data' # Same as used in self._UploadEpisode self._PutPhotoAndVerify(self._cookie, 200, episode_id, photo_id, '.o', orig_image_data) self._PutPhotoAndVerify(self._cookie, 200, episode_id, photo_id, '.f', 'full image data') self._PutPhotoAndVerify(self._cookie, 200, episode_id, photo_id, '.t', 'thumbnail image data') # Test legit downloads. self._GetPhotoAndVerify(self._cookie, 200, episode_id, photo_id, '.o') self._GetPhotoAndVerify(self._cookie, 200, episode_id, photo_id, '.f') self._GetPhotoAndVerify(self._cookie, 200, episode_id, photo_id, '.t') # Try get and put with no cookie. self._PutPhotoAndVerify(None, 401, episode_id, photo_id, '.o', orig_image_data) self._GetPhotoAndVerify(None, 401, episode_id, photo_id, '.o') # Try get and put of missing photo. self._PutPhotoAndVerify(self._cookie, 404, episode_id, 'p-unk', '.m', orig_image_data) self._GetPhotoAndVerify(self._cookie, 404, episode_id, 'p-unk', '.m') # Try get and put without permission. self._PutPhotoAndVerify(self._cookie2, 404, episode_id, photo_id, '.o', orig_image_data) self._GetPhotoAndVerify(self._cookie2, 404, episode_id, photo_id, '.o') # Omit the Content-MD5 header. response = self._PutPhoto(self._cookie, episode_id, photo_id, '.o', orig_image_data) assert response.code == 400, response # Try to use a non well-formed Content-MD5 header. response = self._PutPhoto(self._cookie, episode_id, photo_id, '.o', orig_image_data, content_md5='not well-formed') assert response.code == 400, response # Try to use a Content-MD5 header that does not match the data. response = self._PutPhoto( self._cookie, episode_id, photo_id, '.o', orig_image_data, content_md5=util.ComputeMD5Base64('mismatched md5')) assert response.code == 400, response # Try put with user that is not episode owner. new_vp_id, new_ep_ids = self._tester.ShareNew( self._cookie, [(episode_id, [photo_id])], [self._user2.user_id]) self._PutPhotoAndVerify(self._cookie2, 403, new_ep_ids[0], photo_id, '.o', orig_image_data) # Try get of photo using removed follower. self._tester.RemoveFollowers(self._cookie2, new_vp_id, [self._user2.user_id]) self._GetPhotoAndVerify(self._cookie2, 404, new_ep_ids[0], photo_id, '.o') # Try get and put of unshared photo. self._tester.Unshare(self._cookie, new_vp_id, [(new_ep_ids[0], [photo_id])]) self._PutPhotoAndVerify(self._cookie, 403, new_ep_ids[0], photo_id, '.o', orig_image_data) self._GetPhotoAndVerify(self._cookie, 403, new_ep_ids[0], photo_id, '.o') # Try get and put of photo that has been shared again in order to override unshare. self._tester.ShareExisting(self._cookie, new_vp_id, [(self._episode_id, self._photo_ids)]) self._PutPhotoAndVerify(self._cookie, 200, self._episode_id, self._photo_ids[0], '.o', orig_image_data) self._GetPhotoAndVerify(self._cookie, 200, self._episode_id, self._photo_ids[0], '.o') # Try get and put of hidden photo. self._tester.HidePhotos(self._cookie, [(self._episode_id, self._photo_ids)]) self._PutPhotoAndVerify(self._cookie, 200, self._episode_id, self._photo_ids[0], '.o', orig_image_data) self._GetPhotoAndVerify(self._cookie, 200, self._episode_id, self._photo_ids[0], '.o') # Try get and put of removed photo. self._tester.RemovePhotos(self._cookie, [(self._episode_id, self._photo_ids)]) self._PutPhotoAndVerify(self._cookie, 200, self._episode_id, self._photo_ids[0], '.o', orig_image_data) self._GetPhotoAndVerify(self._cookie, 200, self._episode_id, self._photo_ids[0], '.o')
def testAccessAfterRemoveViewpoint(self): """Error: Remove a viewpoint and then attempt other operations against that viewpoint.""" self._UpdateOrAllocateDBObject(Follower, user_id=self._user.user_id, viewpoint_id=self._new_vp_id, labels=[]) # Remove the viewpoint. self._tester.RemoveViewpoint(self._cookie, self._new_vp_id) # Try to add a follower and observe that it fails. self.assertRaisesHttpError(403, self._tester.AddFollowers, self._cookie, self._new_vp_id, ['Email:[email protected]']) # Try to post a comment and observe that it fails. self.assertRaisesHttpError(403, self._tester.PostComment, self._cookie, self._new_vp_id, "my message") # Try to hide photos from episode in a removed viewpoint and observe that it fails. self.assertRaisesHttpError(403, self._tester.HidePhotos, self._cookie, [(self._new_ep_id, self._photo_ids[:1])]) # Unshare episode from removed viewpoint should fail. self.assertRaisesHttpError(403, self._tester.Unshare, self._cookie, self._new_vp_id, [(self._new_ep_id, self._photo_ids[:1])]) # Try to save photos to default viewpoint from episode in a removed viewpoint and observe that it fails. self.assertRaisesHttpError(403, self._tester.SavePhotos, self._cookie, [(self._new_ep_id, self._photo_ids[:1])]) # Try to share an existing episode into an episode in a removed viewpoint and observe that it fails. self.assertRaisesHttpError( 403, self._tester.ShareExisting, self._cookie, self._new_vp_id, [(self._existing_ep_id, self._photo_ids2[:1])]) # Try to share from an episode in a removed viewpoint to an existing viewpoint and observe that it fails. self.assertRaisesHttpError(403, self._tester.ShareExisting, self._cookie, self._existing_vp_id, [(self._new_ep_id, self._photo_ids[:1])]) # Try creating a new share from an episode in a removed viewpoint and observe that it fails. self.assertRaisesHttpError(403, self._tester.ShareNew, self._cookie, [(self._new_ep_id, self._photo_ids[:1])], [self._user2.user_id], **self._CreateViewpointDict(self._cookie)) # Try getting photo from episode in removed viewpoint and observe that we get a 404 (Not Found) response code. response = self._tester.GetPhotoImage(self._cookie, self._new_ep_id, self._photo_ids[0], '.o') self.assertEqual(response.code, 404) # Try putting photo for episode in removed viewpoint and observe that we get a 404 (Not Found) response code. image_data = 'original image data' response = self._tester.PutPhotoImage( self._cookie, self._new_ep_id, self._photo_ids[1], '.o', image_data, content_md5=util.ComputeMD5Base64(image_data)) self.assertEqual(response.code, 404) # Try QueryViewpoints against a removed viewpoint and observe that it's empty. result = self._tester.QueryViewpoints( self._cookie, [self._tester.CreateViewpointSelection(self._new_vp_id)]) # QueryViewpoints should return viewpoint metadata without any content. self.assertIsNone(result['viewpoints'][0].get('activities', None)) self.assertIsNone(result['viewpoints'][0].get('comments', None)) self.assertIsNone(result['viewpoints'][0].get('episodes', None)) self.assertIsNone(result['viewpoints'][0].get('followers', None)) # Try UpdateViewpoint on a removed viewpoint and expect that it fails. self.assertRaisesHttpError(403, self._tester.UpdateViewpoint, self._cookie, self._new_vp_id, title='a new title') # Try UpdateEpisode on an episode in a removed viewpoint and expect failure. self.assertRaisesHttpError(403, self._tester.UpdateEpisode, self._cookie, self._new_ep_id, description='A newly added description') # Try QueryEpisodes for an episode in a removed viewpoint and expect and empty episode list in the response. result = self._tester.QueryEpisodes( self._cookie, [self._tester.CreateEpisodeSelection(self._new_ep_id)]) self.assertEquals(len(result['episodes']), 0)