blob: 5eab073b9c70f7fa9e83814cf50f618fab9f8de0 [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001# Copyright 2016 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Copybara854996b2021-09-07 19:36:02 +00004
5"""Unit tests for the framework_helpers module."""
6from __future__ import print_function
7from __future__ import division
8from __future__ import absolute_import
9
10import mock
11import unittest
Copybara854996b2021-09-07 19:36:02 +000012
Copybara854996b2021-09-07 19:36:02 +000013from google.appengine.api import urlfetch
14from google.appengine.ext import testbed
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020015from google.cloud import storage
Copybara854996b2021-09-07 19:36:02 +000016
Copybara854996b2021-09-07 19:36:02 +000017from framework import gcs_helpers
Copybara854996b2021-09-07 19:36:02 +000018from testing import testing_helpers
19
20
21class GcsHelpersTest(unittest.TestCase):
22
23 def setUp(self):
Copybara854996b2021-09-07 19:36:02 +000024 self.testbed = testbed.Testbed()
25 self.testbed.activate()
26 self.testbed.init_memcache_stub()
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020027 self.testbed.init_app_identity_stub()
28
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010029 self.test_storage_client = mock.MagicMock()
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020030 mock.patch.object(
31 storage, 'Client', return_value=self.test_storage_client).start()
Copybara854996b2021-09-07 19:36:02 +000032
33 def tearDown(self):
Copybara854996b2021-09-07 19:36:02 +000034 self.testbed.deactivate()
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020035 self.test_storage_client = None
36 mock.patch.stopall()
Copybara854996b2021-09-07 19:36:02 +000037
38 def testDeleteObjectFromGCS(self):
39 object_id = 'aaaaa'
Copybara854996b2021-09-07 19:36:02 +000040 gcs_helpers.DeleteObjectFromGCS(object_id)
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020041 # Verify order of client calls.
42 self.test_storage_client.assert_has_calls(
43 [
44 mock.call.bucket().get_blob(object_id),
45 mock.call.bucket().get_blob().delete()
46 ])
Copybara854996b2021-09-07 19:36:02 +000047
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020048 def testDeleteLegacyObjectFromGCS(self):
49 # A previous python module expected object ids with leading '/'
50 object_id = '/aaaaa'
51 object_id_without_leading_slash = 'aaaaa'
52 gcs_helpers.DeleteObjectFromGCS(object_id)
53 # Verify order of client calls.
54 self.test_storage_client.assert_has_calls(
55 [
56 mock.call.bucket().get_blob(object_id_without_leading_slash),
57 mock.call.bucket().get_blob().delete()
58 ])
59
60 @mock.patch(
61 'google.appengine.api.images.resize', return_value=mock.MagicMock())
62 @mock.patch('uuid.uuid4')
63 def testStoreObjectInGCS_ResizableMimeType(self, mock_uuid4, mock_resize):
Copybara854996b2021-09-07 19:36:02 +000064 guid = 'aaaaa'
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020065 mock_uuid4.return_value = guid
Copybara854996b2021-09-07 19:36:02 +000066 project_id = 100
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020067 blob_name = '%s/attachments/%s' % (project_id, guid)
68 thumb_blob_name = '%s/attachments/%s-thumbnail' % (project_id, guid)
Copybara854996b2021-09-07 19:36:02 +000069 mime_type = 'image/png'
70 content = 'content'
Copybara854996b2021-09-07 19:36:02 +000071
72 ret_id = gcs_helpers.StoreObjectInGCS(
73 content, mime_type, project_id, gcs_helpers.DEFAULT_THUMB_WIDTH,
74 gcs_helpers.DEFAULT_THUMB_HEIGHT)
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020075 self.assertEqual('/%s' % blob_name, ret_id)
76 self.test_storage_client.assert_has_calls(
77 [
78 mock.call.bucket().blob(blob_name),
79 mock.call.bucket().blob().upload_from_string(
80 content, content_type=mime_type),
81 mock.call.bucket().blob(thumb_blob_name),
82 ])
83 mock_resize.assert_called()
Copybara854996b2021-09-07 19:36:02 +000084
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020085 @mock.patch(
86 'google.appengine.api.images.resize', return_value=mock.MagicMock())
87 @mock.patch('uuid.uuid4')
88 def testStoreObjectInGCS_NotResizableMimeType(self, mock_uuid4, mock_resize):
Copybara854996b2021-09-07 19:36:02 +000089 guid = 'aaaaa'
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020090 mock_uuid4.return_value = guid
Copybara854996b2021-09-07 19:36:02 +000091 project_id = 100
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020092 blob_name = '%s/attachments/%s' % (project_id, guid)
Copybara854996b2021-09-07 19:36:02 +000093 mime_type = 'not_resizable_mime_type'
94 content = 'content'
95
Copybara854996b2021-09-07 19:36:02 +000096 ret_id = gcs_helpers.StoreObjectInGCS(
97 content, mime_type, project_id, gcs_helpers.DEFAULT_THUMB_WIDTH,
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +020098 gcs_helpers.DEFAULT_THUMB_HEIGHT)
99 self.assertEqual('/%s' % blob_name, ret_id)
100 self.test_storage_client.assert_has_calls(
101 [
102 mock.call.bucket().blob(blob_name),
103 mock.call.bucket().blob().upload_from_string(
104 content, content_type=mime_type),
105 ])
106 mock_resize.assert_not_called()
Copybara854996b2021-09-07 19:36:02 +0000107
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200108 def testCheckMimeTypeResizable(self):
Copybara854996b2021-09-07 19:36:02 +0000109 for resizable_mime_type in gcs_helpers.RESIZABLE_MIME_TYPES:
110 gcs_helpers.CheckMimeTypeResizable(resizable_mime_type)
111
112 with self.assertRaises(gcs_helpers.UnsupportedMimeType):
113 gcs_helpers.CheckMimeTypeResizable('not_resizable_mime_type')
114
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200115 @mock.patch('framework.filecontent.GuessContentTypeFromFilename')
116 @mock.patch('framework.gcs_helpers.StoreObjectInGCS')
117 def testStoreLogoInGCS(self, mock_store_object, mock_guess_content):
118 blob_name = 123
119 mock_store_object.return_value = blob_name
Copybara854996b2021-09-07 19:36:02 +0000120 mime_type = 'image/png'
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200121 mock_guess_content.return_value = mime_type
122 file_name = 'test_file.png'
Copybara854996b2021-09-07 19:36:02 +0000123 content = 'test content'
124 project_id = 100
Copybara854996b2021-09-07 19:36:02 +0000125
126 ret_id = gcs_helpers.StoreLogoInGCS(file_name, content, project_id)
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200127 self.assertEqual(blob_name, ret_id)
Copybara854996b2021-09-07 19:36:02 +0000128
129 @mock.patch('google.appengine.api.urlfetch.fetch')
130 def testFetchSignedURL_Success(self, mock_fetch):
131 mock_fetch.return_value = testing_helpers.Blank(
132 headers={'Location': 'signed url'})
133 actual = gcs_helpers._FetchSignedURL('signing req url')
134 mock_fetch.assert_called_with('signing req url', follow_redirects=False)
135 self.assertEqual('signed url', actual)
136
137 @mock.patch('google.appengine.api.urlfetch.fetch')
138 def testFetchSignedURL_UnderpopulatedResult(self, mock_fetch):
139 mock_fetch.return_value = testing_helpers.Blank(headers={})
140 self.assertRaises(
141 KeyError, gcs_helpers._FetchSignedURL, 'signing req url')
142
143 @mock.patch('google.appengine.api.urlfetch.fetch')
144 def testFetchSignedURL_DownloadError(self, mock_fetch):
145 mock_fetch.side_effect = urlfetch.DownloadError
146 self.assertRaises(
147 urlfetch.DownloadError,
148 gcs_helpers._FetchSignedURL, 'signing req url')
149
150 @mock.patch('framework.gcs_helpers._FetchSignedURL')
151 def testSignUrl_Success(self, mock_FetchSignedURL):
152 with mock.patch(
153 'google.appengine.api.app_identity.get_access_token') as gat:
154 gat.return_value = ['token']
155 mock_FetchSignedURL.return_value = 'signed url'
156 signed_url = gcs_helpers.SignUrl('bucket', '/object')
157 self.assertEqual('signed url', signed_url)
158
159 @mock.patch('framework.gcs_helpers._FetchSignedURL')
160 def testSignUrl_DownloadError(self, mock_FetchSignedURL):
161 mock_FetchSignedURL.side_effect = urlfetch.DownloadError
162 self.assertEqual(
163 '/missing-gcs-url', gcs_helpers.SignUrl('bucket', '/object'))