blob: a7c01d09cd501c63c1d2e49dfd39471e89827bd2 [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001# Copyright 2016 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file or at
4# https://developers.google.com/open-source/licenses/bsd
5
6"""Unit tests for the framework_helpers module."""
7from __future__ import print_function
8from __future__ import division
9from __future__ import absolute_import
10
11import mock
12import unittest
Copybara854996b2021-09-07 19:36:02 +000013
Copybara854996b2021-09-07 19:36:02 +000014from google.appengine.api import urlfetch
15from google.appengine.ext import testbed
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020016from google.cloud import storage
Copybara854996b2021-09-07 19:36:02 +000017
Copybara854996b2021-09-07 19:36:02 +000018from framework import gcs_helpers
Copybara854996b2021-09-07 19:36:02 +000019from testing import testing_helpers
20
21
22class GcsHelpersTest(unittest.TestCase):
23
24 def setUp(self):
Copybara854996b2021-09-07 19:36:02 +000025 self.testbed = testbed.Testbed()
26 self.testbed.activate()
27 self.testbed.init_memcache_stub()
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020028 self.testbed.init_app_identity_stub()
29
30 self.test_storage_client = mock.create_autospec(
31 storage.Client, instance=True)
32 mock.patch.object(
33 storage, 'Client', return_value=self.test_storage_client).start()
Copybara854996b2021-09-07 19:36:02 +000034
35 def tearDown(self):
Copybara854996b2021-09-07 19:36:02 +000036 self.testbed.deactivate()
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020037 self.test_storage_client = None
38 mock.patch.stopall()
Copybara854996b2021-09-07 19:36:02 +000039
40 def testDeleteObjectFromGCS(self):
41 object_id = 'aaaaa'
Copybara854996b2021-09-07 19:36:02 +000042 gcs_helpers.DeleteObjectFromGCS(object_id)
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020043 # Verify order of client calls.
44 self.test_storage_client.assert_has_calls(
45 [
46 mock.call.bucket().get_blob(object_id),
47 mock.call.bucket().get_blob().delete()
48 ])
Copybara854996b2021-09-07 19:36:02 +000049
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020050 def testDeleteLegacyObjectFromGCS(self):
51 # A previous python module expected object ids with leading '/'
52 object_id = '/aaaaa'
53 object_id_without_leading_slash = 'aaaaa'
54 gcs_helpers.DeleteObjectFromGCS(object_id)
55 # Verify order of client calls.
56 self.test_storage_client.assert_has_calls(
57 [
58 mock.call.bucket().get_blob(object_id_without_leading_slash),
59 mock.call.bucket().get_blob().delete()
60 ])
61
62 @mock.patch(
63 'google.appengine.api.images.resize', return_value=mock.MagicMock())
64 @mock.patch('uuid.uuid4')
65 def testStoreObjectInGCS_ResizableMimeType(self, mock_uuid4, mock_resize):
Copybara854996b2021-09-07 19:36:02 +000066 guid = 'aaaaa'
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020067 mock_uuid4.return_value = guid
Copybara854996b2021-09-07 19:36:02 +000068 project_id = 100
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020069 blob_name = '%s/attachments/%s' % (project_id, guid)
70 thumb_blob_name = '%s/attachments/%s-thumbnail' % (project_id, guid)
Copybara854996b2021-09-07 19:36:02 +000071 mime_type = 'image/png'
72 content = 'content'
Copybara854996b2021-09-07 19:36:02 +000073
74 ret_id = gcs_helpers.StoreObjectInGCS(
75 content, mime_type, project_id, gcs_helpers.DEFAULT_THUMB_WIDTH,
76 gcs_helpers.DEFAULT_THUMB_HEIGHT)
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020077 self.assertEqual('/%s' % blob_name, ret_id)
78 self.test_storage_client.assert_has_calls(
79 [
80 mock.call.bucket().blob(blob_name),
81 mock.call.bucket().blob().upload_from_string(
82 content, content_type=mime_type),
83 mock.call.bucket().blob(thumb_blob_name),
84 ])
85 mock_resize.assert_called()
Copybara854996b2021-09-07 19:36:02 +000086
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020087 @mock.patch(
88 'google.appengine.api.images.resize', return_value=mock.MagicMock())
89 @mock.patch('uuid.uuid4')
90 def testStoreObjectInGCS_NotResizableMimeType(self, mock_uuid4, mock_resize):
Copybara854996b2021-09-07 19:36:02 +000091 guid = 'aaaaa'
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020092 mock_uuid4.return_value = guid
Copybara854996b2021-09-07 19:36:02 +000093 project_id = 100
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020094 blob_name = '%s/attachments/%s' % (project_id, guid)
Copybara854996b2021-09-07 19:36:02 +000095 mime_type = 'not_resizable_mime_type'
96 content = 'content'
97
Copybara854996b2021-09-07 19:36:02 +000098 ret_id = gcs_helpers.StoreObjectInGCS(
99 content, mime_type, project_id, gcs_helpers.DEFAULT_THUMB_WIDTH,
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200100 gcs_helpers.DEFAULT_THUMB_HEIGHT)
101 self.assertEqual('/%s' % blob_name, ret_id)
102 self.test_storage_client.assert_has_calls(
103 [
104 mock.call.bucket().blob(blob_name),
105 mock.call.bucket().blob().upload_from_string(
106 content, content_type=mime_type),
107 ])
108 mock_resize.assert_not_called()
Copybara854996b2021-09-07 19:36:02 +0000109
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200110 def testCheckMimeTypeResizable(self):
Copybara854996b2021-09-07 19:36:02 +0000111 for resizable_mime_type in gcs_helpers.RESIZABLE_MIME_TYPES:
112 gcs_helpers.CheckMimeTypeResizable(resizable_mime_type)
113
114 with self.assertRaises(gcs_helpers.UnsupportedMimeType):
115 gcs_helpers.CheckMimeTypeResizable('not_resizable_mime_type')
116
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200117 @mock.patch('framework.filecontent.GuessContentTypeFromFilename')
118 @mock.patch('framework.gcs_helpers.StoreObjectInGCS')
119 def testStoreLogoInGCS(self, mock_store_object, mock_guess_content):
120 blob_name = 123
121 mock_store_object.return_value = blob_name
Copybara854996b2021-09-07 19:36:02 +0000122 mime_type = 'image/png'
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200123 mock_guess_content.return_value = mime_type
124 file_name = 'test_file.png'
Copybara854996b2021-09-07 19:36:02 +0000125 content = 'test content'
126 project_id = 100
Copybara854996b2021-09-07 19:36:02 +0000127
128 ret_id = gcs_helpers.StoreLogoInGCS(file_name, content, project_id)
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200129 self.assertEqual(blob_name, ret_id)
Copybara854996b2021-09-07 19:36:02 +0000130
131 @mock.patch('google.appengine.api.urlfetch.fetch')
132 def testFetchSignedURL_Success(self, mock_fetch):
133 mock_fetch.return_value = testing_helpers.Blank(
134 headers={'Location': 'signed url'})
135 actual = gcs_helpers._FetchSignedURL('signing req url')
136 mock_fetch.assert_called_with('signing req url', follow_redirects=False)
137 self.assertEqual('signed url', actual)
138
139 @mock.patch('google.appengine.api.urlfetch.fetch')
140 def testFetchSignedURL_UnderpopulatedResult(self, mock_fetch):
141 mock_fetch.return_value = testing_helpers.Blank(headers={})
142 self.assertRaises(
143 KeyError, gcs_helpers._FetchSignedURL, 'signing req url')
144
145 @mock.patch('google.appengine.api.urlfetch.fetch')
146 def testFetchSignedURL_DownloadError(self, mock_fetch):
147 mock_fetch.side_effect = urlfetch.DownloadError
148 self.assertRaises(
149 urlfetch.DownloadError,
150 gcs_helpers._FetchSignedURL, 'signing req url')
151
152 @mock.patch('framework.gcs_helpers._FetchSignedURL')
153 def testSignUrl_Success(self, mock_FetchSignedURL):
154 with mock.patch(
155 'google.appengine.api.app_identity.get_access_token') as gat:
156 gat.return_value = ['token']
157 mock_FetchSignedURL.return_value = 'signed url'
158 signed_url = gcs_helpers.SignUrl('bucket', '/object')
159 self.assertEqual('signed url', signed_url)
160
161 @mock.patch('framework.gcs_helpers._FetchSignedURL')
162 def testSignUrl_DownloadError(self, mock_FetchSignedURL):
163 mock_FetchSignedURL.side_effect = urlfetch.DownloadError
164 self.assertEqual(
165 '/missing-gcs-url', gcs_helpers.SignUrl('bucket', '/object'))