• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2020 Google Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15################################################################################
16"""Unit tests for Cloud Function update builds status."""
17import os
18import sys
19import unittest
20from unittest import mock
21from unittest.mock import MagicMock
22
23from google.cloud import ndb
24
25sys.path.append(os.path.dirname(__file__))
26# pylint: disable=wrong-import-position
27
28from datastore_entities import BuildsHistory
29from datastore_entities import LastSuccessfulBuild
30import test_utils
31import update_build_status
32
33# pylint: disable=no-member
34
35
36# pylint: disable=too-few-public-methods
37class MockGetBuild:
38  """Spoofing get_builds function."""
39
40  def __init__(self, builds):
41    self.builds = builds
42
43  def get_build(self, cloudbuild, image_project, build_id):
44    """Mimic build object retrieval."""
45    del cloudbuild, image_project
46    for build in self.builds:
47      if build['build_id'] == build_id:
48        return build
49
50    return None
51
52
53@mock.patch('google.auth.default', return_value=['temp', 'temp'])
54@mock.patch('update_build_status.build', return_value='cloudbuild')
55@mock.patch('update_build_status.upload_log')
56class TestGetBuildHistory(unittest.TestCase):
57  """Unit tests for get_build_history."""
58
59  def test_get_build_history(self, mock_upload_log, mock_cloud_build,
60                             mock_google_auth):
61    """Test for get_build_steps."""
62    del mock_cloud_build, mock_google_auth
63    mock_upload_log.return_value = True
64    builds = [{'build_id': '1', 'finishTime': 'test_time', 'status': 'SUCCESS'}]
65    mock_get_build = MockGetBuild(builds)
66    update_build_status.get_build = mock_get_build.get_build
67
68    expected_projects = {
69        'history': [{
70            'build_id': '1',
71            'finish_time': 'test_time',
72            'success': True
73        }],
74        'last_successful_build': {
75            'build_id': '1',
76            'finish_time': 'test_time'
77        }
78    }
79    self.assertDictEqual(update_build_status.get_build_history(['1']),
80                         expected_projects)
81
82  def test_get_build_history_missing_log(self, mock_upload_log,
83                                         mock_cloud_build, mock_google_auth):
84    """Test for missing build log file."""
85    del mock_cloud_build, mock_google_auth
86    builds = [{'build_id': '1', 'finishTime': 'test_time', 'status': 'SUCCESS'}]
87    mock_get_build = MockGetBuild(builds)
88    update_build_status.get_build = mock_get_build.get_build
89    mock_upload_log.return_value = False
90    self.assertRaises(update_build_status.MissingBuildLogError,
91                      update_build_status.get_build_history, ['1'])
92
93  def test_get_build_history_no_last_success(self, mock_upload_log,
94                                             mock_cloud_build,
95                                             mock_google_auth):
96    """Test when there is no last successful build."""
97    del mock_cloud_build, mock_google_auth
98    builds = [{'build_id': '1', 'finishTime': 'test_time', 'status': 'FAILURE'}]
99    mock_get_build = MockGetBuild(builds)
100    update_build_status.get_build = mock_get_build.get_build
101    mock_upload_log.return_value = True
102
103    expected_projects = {
104        'history': [{
105            'build_id': '1',
106            'finish_time': 'test_time',
107            'success': False
108        }]
109    }
110    self.assertDictEqual(update_build_status.get_build_history(['1']),
111                         expected_projects)
112
113
114class TestSortProjects(unittest.TestCase):
115  """Unit tests for testing sorting functionality."""
116
117  def test_sort_projects(self):
118    """Test sorting functionality."""
119    projects = [{
120        'name': '1',
121        'history': []
122    }, {
123        'name': '2',
124        'history': [{
125            'success': True
126        }]
127    }, {
128        'name': '3',
129        'history': [{
130            'success': False
131        }]
132    }]
133    expected_order = ['3', '2', '1']
134    update_build_status.sort_projects(projects)
135    self.assertEqual(expected_order, [project['name'] for project in projects])
136
137
138class TestUpdateLastSuccessfulBuild(unittest.TestCase):
139  """Unit tests for updating last successful build."""
140
141  @classmethod
142  def setUpClass(cls):
143    cls.ds_emulator = test_utils.start_datastore_emulator()
144    test_utils.wait_for_emulator_ready(cls.ds_emulator, 'datastore',
145                                       test_utils.DATASTORE_READY_INDICATOR)
146    test_utils.set_gcp_environment()
147
148  def setUp(self):
149    test_utils.reset_ds_emulator()
150
151  def test_update_last_successful_build_new(self):
152    """When last successful build isn't available in datastore."""
153    with ndb.Client().context():
154      project = {
155          'name': 'test-project',
156          'last_successful_build': {
157              'build_id': '1',
158              'finish_time': 'test_time'
159          }
160      }
161      update_build_status.update_last_successful_build(project, 'fuzzing')
162      expected_build_id = '1'
163      self.assertEqual(
164          expected_build_id,
165          ndb.Key(LastSuccessfulBuild, 'test-project-fuzzing').get().build_id)
166
167  def test_update_last_successful_build_datastore(self):
168    """When last successful build is only available in datastore."""
169    with ndb.Client().context():
170      project = {'name': 'test-project'}
171      LastSuccessfulBuild(id='test-project-fuzzing',
172                          build_tag='fuzzing',
173                          project='test-project',
174                          build_id='1',
175                          finish_time='test_time').put()
176
177      update_build_status.update_last_successful_build(project, 'fuzzing')
178      expected_project = {
179          'name': 'test-project',
180          'last_successful_build': {
181              'build_id': '1',
182              'finish_time': 'test_time'
183          }
184      }
185      self.assertDictEqual(project, expected_project)
186
187  def test_update_last_successful_build(self):
188    """When last successful build is available at both places."""
189    with ndb.Client().context():
190      project = {
191          'name': 'test-project',
192          'last_successful_build': {
193              'build_id': '2',
194              'finish_time': 'test_time'
195          }
196      }
197      LastSuccessfulBuild(id='test-project-fuzzing',
198                          build_tag='fuzzing',
199                          project='test-project',
200                          build_id='1',
201                          finish_time='test_time').put()
202
203      update_build_status.update_last_successful_build(project, 'fuzzing')
204      expected_build_id = '2'
205      self.assertEqual(
206          expected_build_id,
207          ndb.Key(LastSuccessfulBuild, 'test-project-fuzzing').get().build_id)
208
209  @classmethod
210  def tearDownClass(cls):
211    test_utils.cleanup_emulator(cls.ds_emulator)
212
213
214class TestUpdateBuildStatus(unittest.TestCase):
215  """Unit test for update build status."""
216
217  @classmethod
218  def setUpClass(cls):
219    cls.ds_emulator = test_utils.start_datastore_emulator()
220    test_utils.wait_for_emulator_ready(cls.ds_emulator, 'datastore',
221                                       test_utils.DATASTORE_READY_INDICATOR)
222    test_utils.set_gcp_environment()
223
224  def setUp(self):
225    test_utils.reset_ds_emulator()
226
227  # pylint: disable=no-self-use
228  @mock.patch('google.auth.default', return_value=['temp', 'temp'])
229  @mock.patch('update_build_status.build', return_value='cloudbuild')
230  @mock.patch('update_build_status.upload_log')
231  def test_update_build_status(self, mock_upload_log, mock_cloud_build,
232                               mock_google_auth):
233    """Testing update build status as a whole."""
234    del self, mock_cloud_build, mock_google_auth
235    update_build_status.upload_status = MagicMock()
236    mock_upload_log.return_value = True
237    status_filename = 'status.json'
238    with ndb.Client().context():
239      BuildsHistory(id='test-project-1-fuzzing',
240                    build_tag='fuzzing',
241                    project='test-project-1',
242                    build_ids=['1']).put()
243
244      BuildsHistory(id='test-project-2-fuzzing',
245                    build_tag='fuzzing',
246                    project='test-project-2',
247                    build_ids=['2']).put()
248
249      BuildsHistory(id='test-project-3-fuzzing',
250                    build_tag='fuzzing',
251                    project='test-project-3',
252                    build_ids=['3']).put()
253
254      builds = [{
255          'build_id': '1',
256          'finishTime': 'test_time',
257          'status': 'SUCCESS'
258      }, {
259          'build_id': '2',
260          'finishTime': 'test_time',
261          'status': 'FAILURE'
262      }, {
263          'build_id': '3',
264          'status': 'WORKING'
265      }]
266      mock_get_build = MockGetBuild(builds)
267      update_build_status.get_build = mock_get_build.get_build
268
269      expected_data = {
270          'projects': [{
271              'history': [{
272                  'build_id': '2',
273                  'finish_time': 'test_time',
274                  'success': False
275              }],
276              'name': 'test-project-2'
277          }, {
278              'history': [{
279                  'build_id': '1',
280                  'finish_time': 'test_time',
281                  'success': True
282              }],
283              'last_successful_build': {
284                  'build_id': '1',
285                  'finish_time': 'test_time'
286              },
287              'name': 'test-project-1'
288          }, {
289              'history': [],
290              'name': 'test-project-3'
291          }]
292      }
293
294      update_build_status.update_build_status('fuzzing', 'status.json')
295      update_build_status.upload_status.assert_called_with(
296          expected_data, status_filename)
297
298  @classmethod
299  def tearDownClass(cls):
300    test_utils.cleanup_emulator(cls.ds_emulator)
301
302
303if __name__ == '__main__':
304  unittest.main(exit=False)
305