blob: d52e38e7c592ad83f387f80e24bcf745a5fea11c [file] [log] [blame]
Gregory Haynes4fc12542015-04-22 20:38:06 -07001# Copyright 2015 GoodData
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# 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, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
James E. Blair21037782017-07-19 11:56:55 -070015import os
Jan Hrubane252a732017-01-03 15:03:09 +010016import re
Jamie Lennox3f16de52017-05-09 14:24:11 +100017from testtools.matchers import MatchesRegex, StartsWith
Tristan Cacqueray2bafb1f2017-06-12 07:10:26 +000018import urllib
Jan Hruban324ca5b2015-11-05 19:28:54 +010019import time
Gregory Haynes4fc12542015-04-22 20:38:06 -070020
James E. Blair21037782017-07-19 11:56:55 -070021import git
22
Wayne1a78c612015-06-11 17:14:13 -070023from tests.base import ZuulTestCase, simple_layout, random_sha1
Gregory Haynes4fc12542015-04-22 20:38:06 -070024
Gregory Haynes4fc12542015-04-22 20:38:06 -070025
26class TestGithubDriver(ZuulTestCase):
27 config_file = 'zuul-github-driver.conf'
28
29 @simple_layout('layouts/basic-github.yaml', driver='github')
30 def test_pull_event(self):
31 self.executor_server.hold_jobs_in_build = True
32
Jan Hruban37615e52015-11-19 14:30:49 +010033 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
34 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Gregory Haynes4fc12542015-04-22 20:38:06 -070035 self.waitUntilSettled()
36
Gregory Haynes4fc12542015-04-22 20:38:06 -070037 self.executor_server.hold_jobs_in_build = False
38 self.executor_server.release()
39 self.waitUntilSettled()
40
41 self.assertEqual('SUCCESS',
42 self.getJobFromHistory('project-test1').result)
43 self.assertEqual('SUCCESS',
44 self.getJobFromHistory('project-test2').result)
45
46 job = self.getJobFromHistory('project-test2')
Monty Taylord13bc362017-06-30 13:11:37 -050047 zuulvars = job.parameters['zuul']
James E. Blaire3db2952017-07-21 15:03:36 -070048 self.assertEqual(str(A.number), zuulvars['change'])
49 self.assertEqual(str(A.head_sha), zuulvars['patchset'])
James E. Blair3b222492017-07-21 15:17:37 -070050 self.assertEqual('master', zuulvars['branch'])
Jan Hruban37615e52015-11-19 14:30:49 +010051 self.assertEqual(1, len(A.comments))
Jan Hruband4edee82015-12-16 12:49:51 +010052 self.assertEqual(2, len(self.history))
53
54 # test_pull_unmatched_branch_event(self):
55 self.create_branch('org/project', 'unmatched_branch')
56 B = self.fake_github.openFakePullRequest(
57 'org/project', 'unmatched_branch', 'B')
58 self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
59 self.waitUntilSettled()
60
61 self.assertEqual(2, len(self.history))
Wayne1a78c612015-06-11 17:14:13 -070062
Jan Hruban570d01c2016-03-10 21:51:32 +010063 @simple_layout('layouts/files-github.yaml', driver='github')
64 def test_pull_matched_file_event(self):
65 A = self.fake_github.openFakePullRequest(
66 'org/project', 'master', 'A',
67 files=['random.txt', 'build-requires'])
68 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
69 self.waitUntilSettled()
70 self.assertEqual(1, len(self.history))
71
72 # test_pull_unmatched_file_event
73 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B',
74 files=['random.txt'])
75 self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
76 self.waitUntilSettled()
77 self.assertEqual(1, len(self.history))
78
Jan Hrubanc7ab1602015-10-14 15:29:33 +020079 @simple_layout('layouts/basic-github.yaml', driver='github')
80 def test_comment_event(self):
Jan Hruban37615e52015-11-19 14:30:49 +010081 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
82 self.fake_github.emitEvent(A.getCommentAddedEvent('test me'))
Jan Hrubanc7ab1602015-10-14 15:29:33 +020083 self.waitUntilSettled()
84 self.assertEqual(2, len(self.history))
85
86 # Test an unmatched comment, history should remain the same
Jan Hruban37615e52015-11-19 14:30:49 +010087 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
88 self.fake_github.emitEvent(B.getCommentAddedEvent('casual comment'))
Jan Hrubanc7ab1602015-10-14 15:29:33 +020089 self.waitUntilSettled()
90 self.assertEqual(2, len(self.history))
91
Wayne1a78c612015-06-11 17:14:13 -070092 @simple_layout('layouts/push-tag-github.yaml', driver='github')
93 def test_tag_event(self):
94 self.executor_server.hold_jobs_in_build = True
95
James E. Blair21037782017-07-19 11:56:55 -070096 self.create_branch('org/project', 'tagbranch')
97 files = {'README.txt': 'test'}
98 self.addCommitToRepo('org/project', 'test tag',
99 files, branch='tagbranch', tag='newtag')
100 path = os.path.join(self.upstream_root, 'org/project')
101 repo = git.Repo(path)
102 tag = repo.tags['newtag']
103 sha = tag.commit.hexsha
104 del repo
105
Wayne1a78c612015-06-11 17:14:13 -0700106 self.fake_github.emitEvent(
107 self.fake_github.getPushEvent('org/project', 'refs/tags/newtag',
108 new_rev=sha))
109 self.waitUntilSettled()
110
111 build_params = self.builds[0].parameters
James E. Blaira438c172017-07-21 14:54:42 -0700112 self.assertEqual('refs/tags/newtag', build_params['zuul']['ref'])
Wayne1a78c612015-06-11 17:14:13 -0700113 self.assertEqual('00000000000000000000000000000000',
James E. Blaired8b0012017-07-21 14:49:29 -0700114 build_params['zuul']['oldrev'])
115 self.assertEqual(sha, build_params['zuul']['newrev'])
Wayne1a78c612015-06-11 17:14:13 -0700116
117 self.executor_server.hold_jobs_in_build = False
118 self.executor_server.release()
119 self.waitUntilSettled()
120
121 self.assertEqual('SUCCESS',
122 self.getJobFromHistory('project-tag').result)
123
124 @simple_layout('layouts/push-tag-github.yaml', driver='github')
125 def test_push_event(self):
126 self.executor_server.hold_jobs_in_build = True
127
128 old_sha = random_sha1()
129 new_sha = random_sha1()
130 self.fake_github.emitEvent(
131 self.fake_github.getPushEvent('org/project', 'refs/heads/master',
132 old_sha, new_sha))
133 self.waitUntilSettled()
134
135 build_params = self.builds[0].parameters
James E. Blaira438c172017-07-21 14:54:42 -0700136 self.assertEqual('refs/heads/master', build_params['zuul']['ref'])
James E. Blaired8b0012017-07-21 14:49:29 -0700137 self.assertEqual(old_sha, build_params['zuul']['oldrev'])
138 self.assertEqual(new_sha, build_params['zuul']['newrev'])
Wayne1a78c612015-06-11 17:14:13 -0700139
140 self.executor_server.hold_jobs_in_build = False
141 self.executor_server.release()
142 self.waitUntilSettled()
143
144 self.assertEqual('SUCCESS',
145 self.getJobFromHistory('project-post').result)
Jan Hruband4edee82015-12-16 12:49:51 +0100146 self.assertEqual(1, len(self.history))
147
148 # test unmatched push event
149 old_sha = random_sha1()
150 new_sha = random_sha1()
151 self.fake_github.emitEvent(
152 self.fake_github.getPushEvent('org/project',
153 'refs/heads/unmatched_branch',
154 old_sha, new_sha))
155 self.waitUntilSettled()
156
157 self.assertEqual(1, len(self.history))
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200158
Jan Hruban16ad31f2015-11-07 14:39:07 +0100159 @simple_layout('layouts/labeling-github.yaml', driver='github')
160 def test_labels(self):
Jan Hruban37615e52015-11-19 14:30:49 +0100161 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100162 self.fake_github.emitEvent(A.addLabel('test'))
163 self.waitUntilSettled()
164 self.assertEqual(1, len(self.history))
165 self.assertEqual('project-labels', self.history[0].name)
166 self.assertEqual(['tests passed'], A.labels)
167
168 # test label removed
Jan Hruban37615e52015-11-19 14:30:49 +0100169 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100170 B.addLabel('do not test')
171 self.fake_github.emitEvent(B.removeLabel('do not test'))
172 self.waitUntilSettled()
173 self.assertEqual(2, len(self.history))
174 self.assertEqual('project-labels', self.history[1].name)
175 self.assertEqual(['tests passed'], B.labels)
176
177 # test unmatched label
Jan Hruban37615e52015-11-19 14:30:49 +0100178 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100179 self.fake_github.emitEvent(C.addLabel('other label'))
180 self.waitUntilSettled()
181 self.assertEqual(2, len(self.history))
182 self.assertEqual(['other label'], C.labels)
183
Jesse Keating5c05a9f2017-01-12 14:44:58 -0800184 @simple_layout('layouts/reviews-github.yaml', driver='github')
185 def test_review_event(self):
186 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
187 self.fake_github.emitEvent(A.getReviewAddedEvent('approve'))
188 self.waitUntilSettled()
189 self.assertEqual(1, len(self.history))
190 self.assertEqual('project-reviews', self.history[0].name)
191 self.assertEqual(['tests passed'], A.labels)
192
193 # test_review_unmatched_event
194 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
195 self.fake_github.emitEvent(B.getReviewAddedEvent('comment'))
196 self.waitUntilSettled()
197 self.assertEqual(1, len(self.history))
198
Jan Hruban324ca5b2015-11-05 19:28:54 +0100199 @simple_layout('layouts/dequeue-github.yaml', driver='github')
200 def test_dequeue_pull_synchronized(self):
201 self.executor_server.hold_jobs_in_build = True
202
Jan Hruban37615e52015-11-19 14:30:49 +0100203 A = self.fake_github.openFakePullRequest(
204 'org/one-job-project', 'master', 'A')
205 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100206 self.waitUntilSettled()
207
208 # event update stamp has resolution one second, wait so the latter
209 # one has newer timestamp
210 time.sleep(1)
Jan Hruban37615e52015-11-19 14:30:49 +0100211 A.addCommit()
212 self.fake_github.emitEvent(A.getPullRequestSynchronizeEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100213 self.waitUntilSettled()
214
215 self.executor_server.hold_jobs_in_build = False
216 self.executor_server.release()
217 self.waitUntilSettled()
218
219 self.assertEqual(2, len(self.history))
220 self.assertEqual(1, self.countJobResults(self.history, 'ABORTED'))
221
222 @simple_layout('layouts/dequeue-github.yaml', driver='github')
223 def test_dequeue_pull_abandoned(self):
224 self.executor_server.hold_jobs_in_build = True
225
Jan Hruban37615e52015-11-19 14:30:49 +0100226 A = self.fake_github.openFakePullRequest(
227 'org/one-job-project', 'master', 'A')
228 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100229 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100230 self.fake_github.emitEvent(A.getPullRequestClosedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100231 self.waitUntilSettled()
232
233 self.executor_server.hold_jobs_in_build = False
234 self.executor_server.release()
235 self.waitUntilSettled()
236
237 self.assertEqual(1, len(self.history))
238 self.assertEqual(1, self.countJobResults(self.history, 'ABORTED'))
239
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200240 @simple_layout('layouts/basic-github.yaml', driver='github')
241 def test_git_https_url(self):
242 """Test that git_ssh option gives git url with ssh"""
243 url = self.fake_github.real_getGitUrl('org/project')
244 self.assertEqual('https://github.com/org/project', url)
245
246 @simple_layout('layouts/basic-github.yaml', driver='github')
247 def test_git_ssh_url(self):
248 """Test that git_ssh option gives git url with ssh"""
249 url = self.fake_github_ssh.real_getGitUrl('org/project')
250 self.assertEqual('ssh://git@github.com/org/project.git', url)
Jan Hrubane252a732017-01-03 15:03:09 +0100251
Jesse Keatingbe4ef8a2016-12-06 11:29:13 -0800252 @simple_layout('layouts/basic-github.yaml', driver='github')
253 def test_git_enterprise_url(self):
254 """Test that git_url option gives git url with proper host"""
255 url = self.fake_github_ent.real_getGitUrl('org/project')
256 self.assertEqual('ssh://git@github.enterprise.io/org/project.git', url)
257
Jan Hrubane252a732017-01-03 15:03:09 +0100258 @simple_layout('layouts/reporting-github.yaml', driver='github')
259 def test_reporting(self):
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700260 project = 'org/project'
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200261 github = self.fake_github.github_client
262
Jan Hrubane252a732017-01-03 15:03:09 +0100263 # pipeline reports pull status both on start and success
264 self.executor_server.hold_jobs_in_build = True
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700265 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
Jan Hruban37615e52015-11-19 14:30:49 +0100266 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hrubane252a732017-01-03 15:03:09 +0100267 self.waitUntilSettled()
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200268
Jesse Keatingd96e5882017-01-19 13:55:50 -0800269 # We should have a status container for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200270 self.assertIn(
271 A.head_sha, github.repo_from_project(project)._commits.keys())
272 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
273
Jesse Keatingd96e5882017-01-19 13:55:50 -0800274 # We should only have one status for the head sha
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700275 self.assertEqual(1, len(statuses))
276 check_status = statuses[0]
Jan Hrubanddeb95a2017-01-03 15:12:41 +0100277 check_url = ('http://zuul.example.com/status/#%s,%s' %
278 (A.number, A.head_sha))
Jamie Lennox18bc7ed2017-05-10 10:37:55 +1000279 self.assertEqual('tenant-one/check', check_status['context'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700280 self.assertEqual('check status: pending',
281 check_status['description'])
Jan Hrubane252a732017-01-03 15:03:09 +0100282 self.assertEqual('pending', check_status['state'])
Jan Hrubanddeb95a2017-01-03 15:12:41 +0100283 self.assertEqual(check_url, check_status['url'])
Jan Hruban37615e52015-11-19 14:30:49 +0100284 self.assertEqual(0, len(A.comments))
Jan Hrubane252a732017-01-03 15:03:09 +0100285
286 self.executor_server.hold_jobs_in_build = False
287 self.executor_server.release()
288 self.waitUntilSettled()
Jesse Keatingd96e5882017-01-19 13:55:50 -0800289 # We should only have two statuses for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200290 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700291 self.assertEqual(2, len(statuses))
292 check_status = statuses[0]
Jesse Keatingd96e5882017-01-19 13:55:50 -0800293 check_url = ('http://zuul.example.com/status/#%s,%s' %
294 (A.number, A.head_sha))
Jamie Lennox18bc7ed2017-05-10 10:37:55 +1000295 self.assertEqual('tenant-one/check', check_status['context'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700296 self.assertEqual('check status: success',
297 check_status['description'])
Jan Hrubane252a732017-01-03 15:03:09 +0100298 self.assertEqual('success', check_status['state'])
Jan Hrubanddeb95a2017-01-03 15:12:41 +0100299 self.assertEqual(check_url, check_status['url'])
Jan Hruban37615e52015-11-19 14:30:49 +0100300 self.assertEqual(1, len(A.comments))
301 self.assertThat(A.comments[0],
Jan Hrubane252a732017-01-03 15:03:09 +0100302 MatchesRegex('.*Build succeeded.*', re.DOTALL))
303
304 # pipeline does not report any status but does comment
305 self.executor_server.hold_jobs_in_build = True
306 self.fake_github.emitEvent(
Jan Hruban37615e52015-11-19 14:30:49 +0100307 A.getCommentAddedEvent('reporting check'))
Jan Hrubane252a732017-01-03 15:03:09 +0100308 self.waitUntilSettled()
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200309 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700310 self.assertEqual(2, len(statuses))
Jan Hrubane252a732017-01-03 15:03:09 +0100311 # comments increased by one for the start message
Jan Hruban37615e52015-11-19 14:30:49 +0100312 self.assertEqual(2, len(A.comments))
313 self.assertThat(A.comments[1],
Jan Hrubane252a732017-01-03 15:03:09 +0100314 MatchesRegex('.*Starting reporting jobs.*', re.DOTALL))
315 self.executor_server.hold_jobs_in_build = False
316 self.executor_server.release()
317 self.waitUntilSettled()
Jesse Keatingd96e5882017-01-19 13:55:50 -0800318 # pipeline reports success status
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200319 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700320 self.assertEqual(3, len(statuses))
321 report_status = statuses[0]
Jamie Lennox18bc7ed2017-05-10 10:37:55 +1000322 self.assertEqual('tenant-one/reporting', report_status['context'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700323 self.assertEqual('reporting status: success',
324 report_status['description'])
Jesse Keatingd96e5882017-01-19 13:55:50 -0800325 self.assertEqual('success', report_status['state'])
Jan Hruban37615e52015-11-19 14:30:49 +0100326 self.assertEqual(2, len(A.comments))
Jamie Lennox3f16de52017-05-09 14:24:11 +1000327
328 base = 'http://logs.example.com/tenant-one/reporting/%s/%s/' % (
329 A.project, A.number)
330
331 # Deconstructing the URL because we don't save the BuildSet UUID
332 # anywhere to do a direct comparison and doing regexp matches on a full
333 # URL is painful.
334
335 # The first part of the URL matches the easy base string
336 self.assertThat(report_status['url'], StartsWith(base))
337
338 # The rest of the URL is a UUID and a trailing slash.
339 self.assertThat(report_status['url'][len(base):],
340 MatchesRegex('^[a-fA-F0-9]{32}\/$'))
Jan Hruban49bff072015-11-03 11:45:46 +0100341
Jesse Keating08dab8f2017-06-21 12:59:23 +0100342 @simple_layout('layouts/reporting-github.yaml', driver='github')
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700343 def test_truncated_status_description(self):
344 project = 'org/project'
345 # pipeline reports pull status both on start and success
346 self.executor_server.hold_jobs_in_build = True
347 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
348 self.fake_github.emitEvent(
349 A.getCommentAddedEvent('long pipeline'))
350 self.waitUntilSettled()
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200351 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700352 self.assertEqual(1, len(statuses))
353 check_status = statuses[0]
354 # Status is truncated due to long pipeline name
355 self.assertEqual('status: pending',
356 check_status['description'])
357
358 self.executor_server.hold_jobs_in_build = False
359 self.executor_server.release()
360 self.waitUntilSettled()
361 # We should only have two statuses for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200362 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700363 self.assertEqual(2, len(statuses))
364 check_status = statuses[0]
365 # Status is truncated due to long pipeline name
366 self.assertEqual('status: success',
367 check_status['description'])
368
369 @simple_layout('layouts/reporting-github.yaml', driver='github')
Jesse Keating08dab8f2017-06-21 12:59:23 +0100370 def test_push_reporting(self):
371 project = 'org/project2'
372 # pipeline reports pull status both on start and success
373 self.executor_server.hold_jobs_in_build = True
374 pevent = self.fake_github.getPushEvent(project=project,
375 ref='refs/heads/master')
376
377 self.fake_github.emitEvent(pevent)
378 self.waitUntilSettled()
379
380 # there should only be one report, a status
381 self.assertEqual(1, len(self.fake_github.reports))
382 # Verify the user/context/state of the status
383 status = ('zuul', 'tenant-one/push-reporting', 'pending')
384 self.assertEqual(status, self.fake_github.reports[0][-1])
385
386 # free the executor, allow the build to finish
387 self.executor_server.hold_jobs_in_build = False
388 self.executor_server.release()
389 self.waitUntilSettled()
390
391 # Now there should be a second report, the success of the build
392 self.assertEqual(2, len(self.fake_github.reports))
393 # Verify the user/context/state of the status
394 status = ('zuul', 'tenant-one/push-reporting', 'success')
395 self.assertEqual(status, self.fake_github.reports[-1][-1])
396
397 # now make a PR which should also comment
398 self.executor_server.hold_jobs_in_build = True
399 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
400 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
401 self.waitUntilSettled()
402
403 # Now there should be a four reports, a new comment
404 # and status
405 self.assertEqual(4, len(self.fake_github.reports))
406 self.executor_server.release()
407 self.waitUntilSettled()
408
Jan Hruban49bff072015-11-03 11:45:46 +0100409 @simple_layout('layouts/merging-github.yaml', driver='github')
410 def test_report_pull_merge(self):
411 # pipeline merges the pull request on success
Jan Hruban3b415922016-02-03 13:10:22 +0100412 A = self.fake_github.openFakePullRequest('org/project', 'master',
413 'PR title')
Jan Hruban49bff072015-11-03 11:45:46 +0100414 self.fake_github.emitEvent(A.getCommentAddedEvent('merge me'))
415 self.waitUntilSettled()
416 self.assertTrue(A.is_merged)
Jan Hruban3b415922016-02-03 13:10:22 +0100417 self.assertThat(A.merge_message,
418 MatchesRegex('.*PR title.*Reviewed-by.*', re.DOTALL))
Jan Hruban49bff072015-11-03 11:45:46 +0100419
420 # pipeline merges the pull request on success after failure
421 self.fake_github.merge_failure = True
Jan Hruban37615e52015-11-19 14:30:49 +0100422 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
Jan Hruban49bff072015-11-03 11:45:46 +0100423 self.fake_github.emitEvent(B.getCommentAddedEvent('merge me'))
424 self.waitUntilSettled()
425 self.assertFalse(B.is_merged)
426 self.fake_github.merge_failure = False
427
428 # pipeline merges the pull request on second run of merge
429 # first merge failed on 405 Method Not Allowed error
430 self.fake_github.merge_not_allowed_count = 1
Jan Hruban37615e52015-11-19 14:30:49 +0100431 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
Jan Hruban49bff072015-11-03 11:45:46 +0100432 self.fake_github.emitEvent(C.getCommentAddedEvent('merge me'))
433 self.waitUntilSettled()
434 self.assertTrue(C.is_merged)
435
436 # pipeline does not merge the pull request
437 # merge failed on 405 Method Not Allowed error - twice
438 self.fake_github.merge_not_allowed_count = 2
Jan Hruban37615e52015-11-19 14:30:49 +0100439 D = self.fake_github.openFakePullRequest('org/project', 'master', 'D')
Jan Hruban49bff072015-11-03 11:45:46 +0100440 self.fake_github.emitEvent(D.getCommentAddedEvent('merge me'))
441 self.waitUntilSettled()
442 self.assertFalse(D.is_merged)
Adam Gandelman62198cb2017-02-14 16:11:02 -0800443 self.assertEqual(len(D.comments), 1)
444 self.assertEqual(D.comments[0], 'Merge failed')
Jan Hruban37615e52015-11-19 14:30:49 +0100445
Jesse Keating28434312017-07-31 11:32:48 -0700446 @simple_layout('layouts/reporting-multiple-github.yaml', driver='github')
447 def test_reporting_multiple_github(self):
448 project = 'org/project1'
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200449 github = self.fake_github.github_client
450
Jesse Keating28434312017-07-31 11:32:48 -0700451 # pipeline reports pull status both on start and success
452 self.executor_server.hold_jobs_in_build = True
453 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
454 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
455 # open one on B as well, which should not effect A reporting
456 B = self.fake_github.openFakePullRequest('org/project2', 'master',
457 'B')
458 self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
459 self.waitUntilSettled()
460 # We should have a status container for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200461 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
462 self.assertIn(
463 A.head_sha, github.repo_from_project(project)._commits.keys())
Jesse Keating28434312017-07-31 11:32:48 -0700464 # We should only have one status for the head sha
465 self.assertEqual(1, len(statuses))
466 check_status = statuses[0]
467 check_url = ('http://zuul.example.com/status/#%s,%s' %
468 (A.number, A.head_sha))
469 self.assertEqual('tenant-one/check', check_status['context'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700470 self.assertEqual('check status: pending', check_status['description'])
Jesse Keating28434312017-07-31 11:32:48 -0700471 self.assertEqual('pending', check_status['state'])
472 self.assertEqual(check_url, check_status['url'])
473 self.assertEqual(0, len(A.comments))
474
475 self.executor_server.hold_jobs_in_build = False
476 self.executor_server.release()
477 self.waitUntilSettled()
478 # We should only have two statuses for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200479 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keating28434312017-07-31 11:32:48 -0700480 self.assertEqual(2, len(statuses))
481 check_status = statuses[0]
482 check_url = ('http://zuul.example.com/status/#%s,%s' %
483 (A.number, A.head_sha))
484 self.assertEqual('tenant-one/check', check_status['context'])
485 self.assertEqual('success', check_status['state'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700486 self.assertEqual('check status: success', check_status['description'])
Jesse Keating28434312017-07-31 11:32:48 -0700487 self.assertEqual(check_url, check_status['url'])
488 self.assertEqual(1, len(A.comments))
489 self.assertThat(A.comments[0],
490 MatchesRegex('.*Build succeeded.*', re.DOTALL))
491
Jan Hruban37615e52015-11-19 14:30:49 +0100492 @simple_layout('layouts/dependent-github.yaml', driver='github')
493 def test_parallel_changes(self):
494 "Test that changes are tested in parallel and merged in series"
495
496 self.executor_server.hold_jobs_in_build = True
497 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
498 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
499 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
500
501 self.fake_github.emitEvent(A.addLabel('merge'))
502 self.fake_github.emitEvent(B.addLabel('merge'))
503 self.fake_github.emitEvent(C.addLabel('merge'))
504
505 self.waitUntilSettled()
506 self.assertEqual(len(self.builds), 1)
507 self.assertEqual(self.builds[0].name, 'project-merge')
508 self.assertTrue(self.builds[0].hasChanges(A))
509
510 self.executor_server.release('.*-merge')
511 self.waitUntilSettled()
512 self.assertEqual(len(self.builds), 3)
513 self.assertEqual(self.builds[0].name, 'project-test1')
514 self.assertTrue(self.builds[0].hasChanges(A))
515 self.assertEqual(self.builds[1].name, 'project-test2')
516 self.assertTrue(self.builds[1].hasChanges(A))
517 self.assertEqual(self.builds[2].name, 'project-merge')
518 self.assertTrue(self.builds[2].hasChanges(A, B))
519
520 self.executor_server.release('.*-merge')
521 self.waitUntilSettled()
522 self.assertEqual(len(self.builds), 5)
523 self.assertEqual(self.builds[0].name, 'project-test1')
524 self.assertTrue(self.builds[0].hasChanges(A))
525 self.assertEqual(self.builds[1].name, 'project-test2')
526 self.assertTrue(self.builds[1].hasChanges(A))
527
528 self.assertEqual(self.builds[2].name, 'project-test1')
529 self.assertTrue(self.builds[2].hasChanges(A))
530 self.assertEqual(self.builds[3].name, 'project-test2')
531 self.assertTrue(self.builds[3].hasChanges(A, B))
532
533 self.assertEqual(self.builds[4].name, 'project-merge')
534 self.assertTrue(self.builds[4].hasChanges(A, B, C))
535
536 self.executor_server.release('.*-merge')
537 self.waitUntilSettled()
538 self.assertEqual(len(self.builds), 6)
539 self.assertEqual(self.builds[0].name, 'project-test1')
540 self.assertTrue(self.builds[0].hasChanges(A))
541 self.assertEqual(self.builds[1].name, 'project-test2')
542 self.assertTrue(self.builds[1].hasChanges(A))
543
544 self.assertEqual(self.builds[2].name, 'project-test1')
545 self.assertTrue(self.builds[2].hasChanges(A, B))
546 self.assertEqual(self.builds[3].name, 'project-test2')
547 self.assertTrue(self.builds[3].hasChanges(A, B))
548
549 self.assertEqual(self.builds[4].name, 'project-test1')
550 self.assertTrue(self.builds[4].hasChanges(A, B, C))
551 self.assertEqual(self.builds[5].name, 'project-test2')
552 self.assertTrue(self.builds[5].hasChanges(A, B, C))
553
554 all_builds = self.builds[:]
555 self.release(all_builds[2])
556 self.release(all_builds[3])
557 self.waitUntilSettled()
558 self.assertFalse(A.is_merged)
559 self.assertFalse(B.is_merged)
560 self.assertFalse(C.is_merged)
561
562 self.release(all_builds[0])
563 self.release(all_builds[1])
564 self.waitUntilSettled()
565 self.assertTrue(A.is_merged)
566 self.assertTrue(B.is_merged)
567 self.assertFalse(C.is_merged)
568
569 self.executor_server.hold_jobs_in_build = False
570 self.executor_server.release()
571 self.waitUntilSettled()
572 self.assertEqual(len(self.builds), 0)
573 self.assertEqual(len(self.history), 9)
574 self.assertTrue(C.is_merged)
575
576 self.assertNotIn('merge', A.labels)
577 self.assertNotIn('merge', B.labels)
578 self.assertNotIn('merge', C.labels)
579
580 @simple_layout('layouts/dependent-github.yaml', driver='github')
581 def test_failed_changes(self):
582 "Test that a change behind a failed change is retested"
583 self.executor_server.hold_jobs_in_build = True
584
585 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
586 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
587
588 self.executor_server.failJob('project-test1', A)
589
590 self.fake_github.emitEvent(A.addLabel('merge'))
591 self.fake_github.emitEvent(B.addLabel('merge'))
592 self.waitUntilSettled()
593
594 self.executor_server.release('.*-merge')
595 self.waitUntilSettled()
596
597 self.executor_server.hold_jobs_in_build = False
598 self.executor_server.release()
599
600 self.waitUntilSettled()
601 # It's certain that the merge job for change 2 will run, but
602 # the test1 and test2 jobs may or may not run.
603 self.assertTrue(len(self.history) > 6)
604 self.assertFalse(A.is_merged)
605 self.assertTrue(B.is_merged)
606 self.assertNotIn('merge', A.labels)
607 self.assertNotIn('merge', B.labels)
608
609 @simple_layout('layouts/dependent-github.yaml', driver='github')
610 def test_failed_change_at_head(self):
611 "Test that if a change at the head fails, jobs behind it are canceled"
612
613 self.executor_server.hold_jobs_in_build = True
614 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
615 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
616 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
617
618 self.executor_server.failJob('project-test1', A)
619
620 self.fake_github.emitEvent(A.addLabel('merge'))
621 self.fake_github.emitEvent(B.addLabel('merge'))
622 self.fake_github.emitEvent(C.addLabel('merge'))
623
624 self.waitUntilSettled()
625
626 self.assertEqual(len(self.builds), 1)
627 self.assertEqual(self.builds[0].name, 'project-merge')
628 self.assertTrue(self.builds[0].hasChanges(A))
629
630 self.executor_server.release('.*-merge')
631 self.waitUntilSettled()
632 self.executor_server.release('.*-merge')
633 self.waitUntilSettled()
634 self.executor_server.release('.*-merge')
635 self.waitUntilSettled()
636
637 self.assertEqual(len(self.builds), 6)
638 self.assertEqual(self.builds[0].name, 'project-test1')
639 self.assertEqual(self.builds[1].name, 'project-test2')
640 self.assertEqual(self.builds[2].name, 'project-test1')
641 self.assertEqual(self.builds[3].name, 'project-test2')
642 self.assertEqual(self.builds[4].name, 'project-test1')
643 self.assertEqual(self.builds[5].name, 'project-test2')
644
645 self.release(self.builds[0])
646 self.waitUntilSettled()
647
648 # project-test2, project-merge for B
649 self.assertEqual(len(self.builds), 2)
650 self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 4)
651
652 self.executor_server.hold_jobs_in_build = False
653 self.executor_server.release()
654 self.waitUntilSettled()
655
656 self.assertEqual(len(self.builds), 0)
657 self.assertEqual(len(self.history), 15)
658 self.assertFalse(A.is_merged)
659 self.assertTrue(B.is_merged)
660 self.assertTrue(C.is_merged)
661 self.assertNotIn('merge', A.labels)
662 self.assertNotIn('merge', B.labels)
663 self.assertNotIn('merge', C.labels)
Jesse Keating71a47ff2017-06-06 11:36:43 -0700664
665 @simple_layout('layouts/basic-github.yaml', driver='github')
666 def test_push_event_reconfigure(self):
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200667 pevent = self.fake_github.getPushEvent(project='org/common-config',
Jesse Keating71a47ff2017-06-06 11:36:43 -0700668 ref='refs/heads/master',
669 modified_files=['zuul.yaml'])
670
671 # record previous tenant reconfiguration time, which may not be set
672 old = self.sched.tenant_last_reconfigured.get('tenant-one', 0)
673 time.sleep(1)
674 self.fake_github.emitEvent(pevent)
675 self.waitUntilSettled()
676 new = self.sched.tenant_last_reconfigured.get('tenant-one', 0)
677 # New timestamp should be greater than the old timestamp
678 self.assertLess(old, new)
Tristan Cacqueray2bafb1f2017-06-12 07:10:26 +0000679
680 @simple_layout('layouts/basic-github.yaml', driver='github')
681 def test_ping_event(self):
682 # Test valid ping
683 pevent = {'repository': {'full_name': 'org/project'}}
684 req = self.fake_github.emitEvent(('ping', pevent))
685 self.assertEqual(req.status, 200, "Ping event didn't succeed")
686
687 # Test invalid ping
688 pevent = {'repository': {'full_name': 'unknown-project'}}
689 self.assertRaises(
690 urllib.error.HTTPError,
691 self.fake_github.emitEvent,
692 ('ping', pevent),
693 )
Tobias Henkeleca46202017-08-02 20:27:10 +0200694
695
696class TestGithubUnprotectedBranches(ZuulTestCase):
697 config_file = 'zuul-github-driver.conf'
698 tenant_config_file = 'config/unprotected-branches/main.yaml'
699
700 def test_unprotected_branches(self):
701 tenant = self.sched.abide.tenants.get('tenant-one')
702
703 project1 = tenant.untrusted_projects[0]
704 project2 = tenant.untrusted_projects[1]
705
706 # project1 should have parsed master
707 self.assertIn('master', project1.unparsed_branch_config.keys())
708
709 # project2 should have no parsed branch
710 self.assertEqual(0, len(project2.unparsed_branch_config.keys()))