blob: 3942b0be89313ff028a2926d0393d962bd29800f [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
Jesse Keating64d29012017-09-06 12:27:49 -070020from unittest import skip
Gregory Haynes4fc12542015-04-22 20:38:06 -070021
James E. Blair21037782017-07-19 11:56:55 -070022import git
23
Wayne1a78c612015-06-11 17:14:13 -070024from tests.base import ZuulTestCase, simple_layout, random_sha1
Gregory Haynes4fc12542015-04-22 20:38:06 -070025
Gregory Haynes4fc12542015-04-22 20:38:06 -070026
27class TestGithubDriver(ZuulTestCase):
28 config_file = 'zuul-github-driver.conf'
29
30 @simple_layout('layouts/basic-github.yaml', driver='github')
31 def test_pull_event(self):
32 self.executor_server.hold_jobs_in_build = True
33
Jan Hruban37615e52015-11-19 14:30:49 +010034 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
35 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Gregory Haynes4fc12542015-04-22 20:38:06 -070036 self.waitUntilSettled()
37
Gregory Haynes4fc12542015-04-22 20:38:06 -070038 self.executor_server.hold_jobs_in_build = False
39 self.executor_server.release()
40 self.waitUntilSettled()
41
42 self.assertEqual('SUCCESS',
43 self.getJobFromHistory('project-test1').result)
44 self.assertEqual('SUCCESS',
45 self.getJobFromHistory('project-test2').result)
46
47 job = self.getJobFromHistory('project-test2')
Monty Taylord13bc362017-06-30 13:11:37 -050048 zuulvars = job.parameters['zuul']
James E. Blaire3db2952017-07-21 15:03:36 -070049 self.assertEqual(str(A.number), zuulvars['change'])
50 self.assertEqual(str(A.head_sha), zuulvars['patchset'])
James E. Blair3b222492017-07-21 15:17:37 -070051 self.assertEqual('master', zuulvars['branch'])
Jan Hruban37615e52015-11-19 14:30:49 +010052 self.assertEqual(1, len(A.comments))
liushengd3419e82018-01-06 19:52:03 +080053 self.assertThat(
54 A.comments[0],
55 MatchesRegex('.*\[project-test1 \]\(.*\).*', re.DOTALL))
56 self.assertThat(
57 A.comments[0],
58 MatchesRegex('.*\[project-test2 \]\(.*\).*', re.DOTALL))
Jan Hruband4edee82015-12-16 12:49:51 +010059 self.assertEqual(2, len(self.history))
60
61 # test_pull_unmatched_branch_event(self):
62 self.create_branch('org/project', 'unmatched_branch')
63 B = self.fake_github.openFakePullRequest(
64 'org/project', 'unmatched_branch', 'B')
65 self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
66 self.waitUntilSettled()
67
68 self.assertEqual(2, len(self.history))
Wayne1a78c612015-06-11 17:14:13 -070069
Jan Hruban570d01c2016-03-10 21:51:32 +010070 @simple_layout('layouts/files-github.yaml', driver='github')
71 def test_pull_matched_file_event(self):
72 A = self.fake_github.openFakePullRequest(
73 'org/project', 'master', 'A',
74 files=['random.txt', 'build-requires'])
75 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
76 self.waitUntilSettled()
77 self.assertEqual(1, len(self.history))
78
79 # test_pull_unmatched_file_event
80 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B',
81 files=['random.txt'])
82 self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
83 self.waitUntilSettled()
84 self.assertEqual(1, len(self.history))
85
Jan Hrubanc7ab1602015-10-14 15:29:33 +020086 @simple_layout('layouts/basic-github.yaml', driver='github')
87 def test_comment_event(self):
Jan Hruban37615e52015-11-19 14:30:49 +010088 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
89 self.fake_github.emitEvent(A.getCommentAddedEvent('test me'))
Jan Hrubanc7ab1602015-10-14 15:29:33 +020090 self.waitUntilSettled()
91 self.assertEqual(2, len(self.history))
92
93 # Test an unmatched comment, history should remain the same
Jan Hruban37615e52015-11-19 14:30:49 +010094 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
95 self.fake_github.emitEvent(B.getCommentAddedEvent('casual comment'))
Jan Hrubanc7ab1602015-10-14 15:29:33 +020096 self.waitUntilSettled()
97 self.assertEqual(2, len(self.history))
98
Wayne1a78c612015-06-11 17:14:13 -070099 @simple_layout('layouts/push-tag-github.yaml', driver='github')
100 def test_tag_event(self):
101 self.executor_server.hold_jobs_in_build = True
102
James E. Blair21037782017-07-19 11:56:55 -0700103 self.create_branch('org/project', 'tagbranch')
104 files = {'README.txt': 'test'}
105 self.addCommitToRepo('org/project', 'test tag',
106 files, branch='tagbranch', tag='newtag')
107 path = os.path.join(self.upstream_root, 'org/project')
108 repo = git.Repo(path)
109 tag = repo.tags['newtag']
110 sha = tag.commit.hexsha
111 del repo
112
Wayne1a78c612015-06-11 17:14:13 -0700113 self.fake_github.emitEvent(
114 self.fake_github.getPushEvent('org/project', 'refs/tags/newtag',
115 new_rev=sha))
116 self.waitUntilSettled()
117
118 build_params = self.builds[0].parameters
James E. Blaira438c172017-07-21 14:54:42 -0700119 self.assertEqual('refs/tags/newtag', build_params['zuul']['ref'])
James E. Blairb8203e42017-08-02 17:00:14 -0700120 self.assertFalse('oldrev' in build_params['zuul'])
James E. Blaired8b0012017-07-21 14:49:29 -0700121 self.assertEqual(sha, build_params['zuul']['newrev'])
Wayne1a78c612015-06-11 17:14:13 -0700122 self.executor_server.hold_jobs_in_build = False
123 self.executor_server.release()
124 self.waitUntilSettled()
125
126 self.assertEqual('SUCCESS',
127 self.getJobFromHistory('project-tag').result)
128
129 @simple_layout('layouts/push-tag-github.yaml', driver='github')
130 def test_push_event(self):
131 self.executor_server.hold_jobs_in_build = True
132
James E. Blair289f5932017-07-27 15:02:29 -0700133 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
134 old_sha = '0' * 40
135 new_sha = A.head_sha
136 A.setMerged("merging A")
137 pevent = self.fake_github.getPushEvent(project='org/project',
138 ref='refs/heads/master',
139 old_rev=old_sha,
140 new_rev=new_sha)
141 self.fake_github.emitEvent(pevent)
Wayne1a78c612015-06-11 17:14:13 -0700142 self.waitUntilSettled()
143
144 build_params = self.builds[0].parameters
James E. Blaira438c172017-07-21 14:54:42 -0700145 self.assertEqual('refs/heads/master', build_params['zuul']['ref'])
James E. Blair289f5932017-07-27 15:02:29 -0700146 self.assertFalse('oldrev' in build_params['zuul'])
James E. Blaired8b0012017-07-21 14:49:29 -0700147 self.assertEqual(new_sha, build_params['zuul']['newrev'])
Wayne1a78c612015-06-11 17:14:13 -0700148
149 self.executor_server.hold_jobs_in_build = False
150 self.executor_server.release()
151 self.waitUntilSettled()
152
153 self.assertEqual('SUCCESS',
154 self.getJobFromHistory('project-post').result)
Jan Hruband4edee82015-12-16 12:49:51 +0100155 self.assertEqual(1, len(self.history))
156
157 # test unmatched push event
158 old_sha = random_sha1()
159 new_sha = random_sha1()
160 self.fake_github.emitEvent(
161 self.fake_github.getPushEvent('org/project',
162 'refs/heads/unmatched_branch',
163 old_sha, new_sha))
164 self.waitUntilSettled()
165
166 self.assertEqual(1, len(self.history))
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200167
Jan Hruban16ad31f2015-11-07 14:39:07 +0100168 @simple_layout('layouts/labeling-github.yaml', driver='github')
169 def test_labels(self):
Jan Hruban37615e52015-11-19 14:30:49 +0100170 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100171 self.fake_github.emitEvent(A.addLabel('test'))
172 self.waitUntilSettled()
173 self.assertEqual(1, len(self.history))
174 self.assertEqual('project-labels', self.history[0].name)
175 self.assertEqual(['tests passed'], A.labels)
176
177 # test label removed
Jan Hruban37615e52015-11-19 14:30:49 +0100178 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100179 B.addLabel('do not test')
180 self.fake_github.emitEvent(B.removeLabel('do not test'))
181 self.waitUntilSettled()
182 self.assertEqual(2, len(self.history))
183 self.assertEqual('project-labels', self.history[1].name)
184 self.assertEqual(['tests passed'], B.labels)
185
186 # test unmatched label
Jan Hruban37615e52015-11-19 14:30:49 +0100187 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100188 self.fake_github.emitEvent(C.addLabel('other label'))
189 self.waitUntilSettled()
190 self.assertEqual(2, len(self.history))
191 self.assertEqual(['other label'], C.labels)
192
Jesse Keating5c05a9f2017-01-12 14:44:58 -0800193 @simple_layout('layouts/reviews-github.yaml', driver='github')
194 def test_review_event(self):
195 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
196 self.fake_github.emitEvent(A.getReviewAddedEvent('approve'))
197 self.waitUntilSettled()
198 self.assertEqual(1, len(self.history))
199 self.assertEqual('project-reviews', self.history[0].name)
200 self.assertEqual(['tests passed'], A.labels)
201
202 # test_review_unmatched_event
203 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
204 self.fake_github.emitEvent(B.getReviewAddedEvent('comment'))
205 self.waitUntilSettled()
206 self.assertEqual(1, len(self.history))
207
Jan Hruban324ca5b2015-11-05 19:28:54 +0100208 @simple_layout('layouts/dequeue-github.yaml', driver='github')
209 def test_dequeue_pull_synchronized(self):
210 self.executor_server.hold_jobs_in_build = True
211
Jan Hruban37615e52015-11-19 14:30:49 +0100212 A = self.fake_github.openFakePullRequest(
213 'org/one-job-project', 'master', 'A')
214 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100215 self.waitUntilSettled()
216
217 # event update stamp has resolution one second, wait so the latter
218 # one has newer timestamp
219 time.sleep(1)
Jan Hruban37615e52015-11-19 14:30:49 +0100220 A.addCommit()
221 self.fake_github.emitEvent(A.getPullRequestSynchronizeEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100222 self.waitUntilSettled()
223
224 self.executor_server.hold_jobs_in_build = False
225 self.executor_server.release()
226 self.waitUntilSettled()
227
228 self.assertEqual(2, len(self.history))
229 self.assertEqual(1, self.countJobResults(self.history, 'ABORTED'))
230
231 @simple_layout('layouts/dequeue-github.yaml', driver='github')
232 def test_dequeue_pull_abandoned(self):
233 self.executor_server.hold_jobs_in_build = True
234
Jan Hruban37615e52015-11-19 14:30:49 +0100235 A = self.fake_github.openFakePullRequest(
236 'org/one-job-project', 'master', 'A')
237 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100238 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100239 self.fake_github.emitEvent(A.getPullRequestClosedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100240 self.waitUntilSettled()
241
242 self.executor_server.hold_jobs_in_build = False
243 self.executor_server.release()
244 self.waitUntilSettled()
245
246 self.assertEqual(1, len(self.history))
247 self.assertEqual(1, self.countJobResults(self.history, 'ABORTED'))
248
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200249 @simple_layout('layouts/basic-github.yaml', driver='github')
250 def test_git_https_url(self):
251 """Test that git_ssh option gives git url with ssh"""
Tobias Henkel42f7d6f2017-12-15 16:09:43 +0100252 tenant = self.sched.abide.tenants.get('tenant-one')
253 _, project = tenant.getProject('org/project')
254
255 url = self.fake_github.real_getGitUrl(project)
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200256 self.assertEqual('https://github.com/org/project', url)
257
258 @simple_layout('layouts/basic-github.yaml', driver='github')
259 def test_git_ssh_url(self):
260 """Test that git_ssh option gives git url with ssh"""
Tobias Henkel42f7d6f2017-12-15 16:09:43 +0100261 tenant = self.sched.abide.tenants.get('tenant-one')
262 _, project = tenant.getProject('org/project')
263
264 url = self.fake_github_ssh.real_getGitUrl(project)
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200265 self.assertEqual('ssh://git@github.com/org/project.git', url)
Jan Hrubane252a732017-01-03 15:03:09 +0100266
Jesse Keatingbe4ef8a2016-12-06 11:29:13 -0800267 @simple_layout('layouts/basic-github.yaml', driver='github')
268 def test_git_enterprise_url(self):
269 """Test that git_url option gives git url with proper host"""
Tobias Henkel42f7d6f2017-12-15 16:09:43 +0100270 tenant = self.sched.abide.tenants.get('tenant-one')
271 _, project = tenant.getProject('org/project')
272
273 url = self.fake_github_ent.real_getGitUrl(project)
Jesse Keatingbe4ef8a2016-12-06 11:29:13 -0800274 self.assertEqual('ssh://git@github.enterprise.io/org/project.git', url)
275
Jan Hrubane252a732017-01-03 15:03:09 +0100276 @simple_layout('layouts/reporting-github.yaml', driver='github')
277 def test_reporting(self):
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700278 project = 'org/project'
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200279 github = self.fake_github.github_client
280
Jan Hrubane252a732017-01-03 15:03:09 +0100281 # pipeline reports pull status both on start and success
282 self.executor_server.hold_jobs_in_build = True
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700283 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
Jan Hruban37615e52015-11-19 14:30:49 +0100284 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hrubane252a732017-01-03 15:03:09 +0100285 self.waitUntilSettled()
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200286
Jesse Keatingd96e5882017-01-19 13:55:50 -0800287 # We should have a status container for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200288 self.assertIn(
289 A.head_sha, github.repo_from_project(project)._commits.keys())
290 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
291
Jesse Keatingd96e5882017-01-19 13:55:50 -0800292 # We should only have one status for the head sha
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700293 self.assertEqual(1, len(statuses))
294 check_status = statuses[0]
Jan Hrubanddeb95a2017-01-03 15:12:41 +0100295 check_url = ('http://zuul.example.com/status/#%s,%s' %
296 (A.number, A.head_sha))
Jamie Lennox18bc7ed2017-05-10 10:37:55 +1000297 self.assertEqual('tenant-one/check', check_status['context'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700298 self.assertEqual('check status: pending',
299 check_status['description'])
Jan Hrubane252a732017-01-03 15:03:09 +0100300 self.assertEqual('pending', check_status['state'])
Jan Hrubanddeb95a2017-01-03 15:12:41 +0100301 self.assertEqual(check_url, check_status['url'])
Jan Hruban37615e52015-11-19 14:30:49 +0100302 self.assertEqual(0, len(A.comments))
Jan Hrubane252a732017-01-03 15:03:09 +0100303
304 self.executor_server.hold_jobs_in_build = False
305 self.executor_server.release()
306 self.waitUntilSettled()
Jesse Keatingd96e5882017-01-19 13:55:50 -0800307 # We should only have two statuses for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200308 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700309 self.assertEqual(2, len(statuses))
310 check_status = statuses[0]
Jesse Keatingd96e5882017-01-19 13:55:50 -0800311 check_url = ('http://zuul.example.com/status/#%s,%s' %
312 (A.number, A.head_sha))
Jamie Lennox18bc7ed2017-05-10 10:37:55 +1000313 self.assertEqual('tenant-one/check', check_status['context'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700314 self.assertEqual('check status: success',
315 check_status['description'])
Jan Hrubane252a732017-01-03 15:03:09 +0100316 self.assertEqual('success', check_status['state'])
Jan Hrubanddeb95a2017-01-03 15:12:41 +0100317 self.assertEqual(check_url, check_status['url'])
Jan Hruban37615e52015-11-19 14:30:49 +0100318 self.assertEqual(1, len(A.comments))
319 self.assertThat(A.comments[0],
Jan Hrubane252a732017-01-03 15:03:09 +0100320 MatchesRegex('.*Build succeeded.*', re.DOTALL))
321
322 # pipeline does not report any status but does comment
323 self.executor_server.hold_jobs_in_build = True
324 self.fake_github.emitEvent(
Jan Hruban37615e52015-11-19 14:30:49 +0100325 A.getCommentAddedEvent('reporting check'))
Jan Hrubane252a732017-01-03 15:03:09 +0100326 self.waitUntilSettled()
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200327 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700328 self.assertEqual(2, len(statuses))
Jan Hrubane252a732017-01-03 15:03:09 +0100329 # comments increased by one for the start message
Jan Hruban37615e52015-11-19 14:30:49 +0100330 self.assertEqual(2, len(A.comments))
331 self.assertThat(A.comments[1],
Jan Hrubane252a732017-01-03 15:03:09 +0100332 MatchesRegex('.*Starting reporting jobs.*', re.DOTALL))
333 self.executor_server.hold_jobs_in_build = False
334 self.executor_server.release()
335 self.waitUntilSettled()
Jesse Keatingd96e5882017-01-19 13:55:50 -0800336 # pipeline reports success status
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200337 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700338 self.assertEqual(3, len(statuses))
339 report_status = statuses[0]
Jamie Lennox18bc7ed2017-05-10 10:37:55 +1000340 self.assertEqual('tenant-one/reporting', report_status['context'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700341 self.assertEqual('reporting status: success',
342 report_status['description'])
Jesse Keatingd96e5882017-01-19 13:55:50 -0800343 self.assertEqual('success', report_status['state'])
Jan Hruban37615e52015-11-19 14:30:49 +0100344 self.assertEqual(2, len(A.comments))
Jamie Lennox3f16de52017-05-09 14:24:11 +1000345
346 base = 'http://logs.example.com/tenant-one/reporting/%s/%s/' % (
347 A.project, A.number)
348
349 # Deconstructing the URL because we don't save the BuildSet UUID
350 # anywhere to do a direct comparison and doing regexp matches on a full
351 # URL is painful.
352
353 # The first part of the URL matches the easy base string
354 self.assertThat(report_status['url'], StartsWith(base))
355
356 # The rest of the URL is a UUID and a trailing slash.
357 self.assertThat(report_status['url'][len(base):],
358 MatchesRegex('^[a-fA-F0-9]{32}\/$'))
Jan Hruban49bff072015-11-03 11:45:46 +0100359
Jesse Keating08dab8f2017-06-21 12:59:23 +0100360 @simple_layout('layouts/reporting-github.yaml', driver='github')
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700361 def test_truncated_status_description(self):
362 project = 'org/project'
363 # pipeline reports pull status both on start and success
364 self.executor_server.hold_jobs_in_build = True
365 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
366 self.fake_github.emitEvent(
367 A.getCommentAddedEvent('long pipeline'))
368 self.waitUntilSettled()
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200369 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700370 self.assertEqual(1, len(statuses))
371 check_status = statuses[0]
372 # Status is truncated due to long pipeline name
373 self.assertEqual('status: pending',
374 check_status['description'])
375
376 self.executor_server.hold_jobs_in_build = False
377 self.executor_server.release()
378 self.waitUntilSettled()
379 # We should only have two statuses for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200380 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700381 self.assertEqual(2, len(statuses))
382 check_status = statuses[0]
383 # Status is truncated due to long pipeline name
384 self.assertEqual('status: success',
385 check_status['description'])
386
387 @simple_layout('layouts/reporting-github.yaml', driver='github')
Jesse Keating08dab8f2017-06-21 12:59:23 +0100388 def test_push_reporting(self):
389 project = 'org/project2'
390 # pipeline reports pull status both on start and success
391 self.executor_server.hold_jobs_in_build = True
Jesse Keating08dab8f2017-06-21 12:59:23 +0100392
James E. Blair289f5932017-07-27 15:02:29 -0700393 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
394 old_sha = '0' * 40
395 new_sha = A.head_sha
396 A.setMerged("merging A")
397 pevent = self.fake_github.getPushEvent(project=project,
398 ref='refs/heads/master',
399 old_rev=old_sha,
400 new_rev=new_sha)
Jesse Keating08dab8f2017-06-21 12:59:23 +0100401 self.fake_github.emitEvent(pevent)
402 self.waitUntilSettled()
403
404 # there should only be one report, a status
405 self.assertEqual(1, len(self.fake_github.reports))
406 # Verify the user/context/state of the status
407 status = ('zuul', 'tenant-one/push-reporting', 'pending')
408 self.assertEqual(status, self.fake_github.reports[0][-1])
409
410 # free the executor, allow the build to finish
411 self.executor_server.hold_jobs_in_build = False
412 self.executor_server.release()
413 self.waitUntilSettled()
414
415 # Now there should be a second report, the success of the build
416 self.assertEqual(2, len(self.fake_github.reports))
417 # Verify the user/context/state of the status
418 status = ('zuul', 'tenant-one/push-reporting', 'success')
419 self.assertEqual(status, self.fake_github.reports[-1][-1])
420
421 # now make a PR which should also comment
422 self.executor_server.hold_jobs_in_build = True
423 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
424 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
425 self.waitUntilSettled()
426
427 # Now there should be a four reports, a new comment
428 # and status
429 self.assertEqual(4, len(self.fake_github.reports))
430 self.executor_server.release()
431 self.waitUntilSettled()
432
Jan Hruban49bff072015-11-03 11:45:46 +0100433 @simple_layout('layouts/merging-github.yaml', driver='github')
434 def test_report_pull_merge(self):
435 # pipeline merges the pull request on success
Jan Hruban3b415922016-02-03 13:10:22 +0100436 A = self.fake_github.openFakePullRequest('org/project', 'master',
437 'PR title')
Jan Hruban49bff072015-11-03 11:45:46 +0100438 self.fake_github.emitEvent(A.getCommentAddedEvent('merge me'))
439 self.waitUntilSettled()
440 self.assertTrue(A.is_merged)
Jan Hruban3b415922016-02-03 13:10:22 +0100441 self.assertThat(A.merge_message,
442 MatchesRegex('.*PR title.*Reviewed-by.*', re.DOTALL))
Jan Hruban49bff072015-11-03 11:45:46 +0100443
444 # pipeline merges the pull request on success after failure
445 self.fake_github.merge_failure = True
Jan Hruban37615e52015-11-19 14:30:49 +0100446 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
Jan Hruban49bff072015-11-03 11:45:46 +0100447 self.fake_github.emitEvent(B.getCommentAddedEvent('merge me'))
448 self.waitUntilSettled()
449 self.assertFalse(B.is_merged)
450 self.fake_github.merge_failure = False
451
452 # pipeline merges the pull request on second run of merge
453 # first merge failed on 405 Method Not Allowed error
454 self.fake_github.merge_not_allowed_count = 1
Jan Hruban37615e52015-11-19 14:30:49 +0100455 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
Jan Hruban49bff072015-11-03 11:45:46 +0100456 self.fake_github.emitEvent(C.getCommentAddedEvent('merge me'))
457 self.waitUntilSettled()
458 self.assertTrue(C.is_merged)
459
460 # pipeline does not merge the pull request
461 # merge failed on 405 Method Not Allowed error - twice
462 self.fake_github.merge_not_allowed_count = 2
Jan Hruban37615e52015-11-19 14:30:49 +0100463 D = self.fake_github.openFakePullRequest('org/project', 'master', 'D')
Jan Hruban49bff072015-11-03 11:45:46 +0100464 self.fake_github.emitEvent(D.getCommentAddedEvent('merge me'))
465 self.waitUntilSettled()
466 self.assertFalse(D.is_merged)
Adam Gandelman62198cb2017-02-14 16:11:02 -0800467 self.assertEqual(len(D.comments), 1)
468 self.assertEqual(D.comments[0], 'Merge failed')
Jan Hruban37615e52015-11-19 14:30:49 +0100469
Jesse Keating28434312017-07-31 11:32:48 -0700470 @simple_layout('layouts/reporting-multiple-github.yaml', driver='github')
471 def test_reporting_multiple_github(self):
472 project = 'org/project1'
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200473 github = self.fake_github.github_client
474
Jesse Keating28434312017-07-31 11:32:48 -0700475 # pipeline reports pull status both on start and success
476 self.executor_server.hold_jobs_in_build = True
477 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
478 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
479 # open one on B as well, which should not effect A reporting
480 B = self.fake_github.openFakePullRequest('org/project2', 'master',
481 'B')
482 self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
483 self.waitUntilSettled()
484 # We should have a status container for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200485 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
486 self.assertIn(
487 A.head_sha, github.repo_from_project(project)._commits.keys())
Jesse Keating28434312017-07-31 11:32:48 -0700488 # We should only have one status for the head sha
489 self.assertEqual(1, len(statuses))
490 check_status = statuses[0]
491 check_url = ('http://zuul.example.com/status/#%s,%s' %
492 (A.number, A.head_sha))
493 self.assertEqual('tenant-one/check', check_status['context'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700494 self.assertEqual('check status: pending', check_status['description'])
Jesse Keating28434312017-07-31 11:32:48 -0700495 self.assertEqual('pending', check_status['state'])
496 self.assertEqual(check_url, check_status['url'])
497 self.assertEqual(0, len(A.comments))
498
499 self.executor_server.hold_jobs_in_build = False
500 self.executor_server.release()
501 self.waitUntilSettled()
502 # We should only have two statuses for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200503 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keating28434312017-07-31 11:32:48 -0700504 self.assertEqual(2, len(statuses))
505 check_status = statuses[0]
506 check_url = ('http://zuul.example.com/status/#%s,%s' %
507 (A.number, A.head_sha))
508 self.assertEqual('tenant-one/check', check_status['context'])
509 self.assertEqual('success', check_status['state'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700510 self.assertEqual('check status: success', check_status['description'])
Jesse Keating28434312017-07-31 11:32:48 -0700511 self.assertEqual(check_url, check_status['url'])
512 self.assertEqual(1, len(A.comments))
513 self.assertThat(A.comments[0],
514 MatchesRegex('.*Build succeeded.*', re.DOTALL))
515
Jan Hruban37615e52015-11-19 14:30:49 +0100516 @simple_layout('layouts/dependent-github.yaml', driver='github')
517 def test_parallel_changes(self):
518 "Test that changes are tested in parallel and merged in series"
519
520 self.executor_server.hold_jobs_in_build = True
521 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
522 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
523 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
524
525 self.fake_github.emitEvent(A.addLabel('merge'))
526 self.fake_github.emitEvent(B.addLabel('merge'))
527 self.fake_github.emitEvent(C.addLabel('merge'))
528
529 self.waitUntilSettled()
530 self.assertEqual(len(self.builds), 1)
531 self.assertEqual(self.builds[0].name, 'project-merge')
532 self.assertTrue(self.builds[0].hasChanges(A))
533
534 self.executor_server.release('.*-merge')
535 self.waitUntilSettled()
536 self.assertEqual(len(self.builds), 3)
537 self.assertEqual(self.builds[0].name, 'project-test1')
538 self.assertTrue(self.builds[0].hasChanges(A))
539 self.assertEqual(self.builds[1].name, 'project-test2')
540 self.assertTrue(self.builds[1].hasChanges(A))
541 self.assertEqual(self.builds[2].name, 'project-merge')
542 self.assertTrue(self.builds[2].hasChanges(A, B))
543
544 self.executor_server.release('.*-merge')
545 self.waitUntilSettled()
546 self.assertEqual(len(self.builds), 5)
547 self.assertEqual(self.builds[0].name, 'project-test1')
548 self.assertTrue(self.builds[0].hasChanges(A))
549 self.assertEqual(self.builds[1].name, 'project-test2')
550 self.assertTrue(self.builds[1].hasChanges(A))
551
552 self.assertEqual(self.builds[2].name, 'project-test1')
553 self.assertTrue(self.builds[2].hasChanges(A))
554 self.assertEqual(self.builds[3].name, 'project-test2')
555 self.assertTrue(self.builds[3].hasChanges(A, B))
556
557 self.assertEqual(self.builds[4].name, 'project-merge')
558 self.assertTrue(self.builds[4].hasChanges(A, B, C))
559
560 self.executor_server.release('.*-merge')
561 self.waitUntilSettled()
562 self.assertEqual(len(self.builds), 6)
563 self.assertEqual(self.builds[0].name, 'project-test1')
564 self.assertTrue(self.builds[0].hasChanges(A))
565 self.assertEqual(self.builds[1].name, 'project-test2')
566 self.assertTrue(self.builds[1].hasChanges(A))
567
568 self.assertEqual(self.builds[2].name, 'project-test1')
569 self.assertTrue(self.builds[2].hasChanges(A, B))
570 self.assertEqual(self.builds[3].name, 'project-test2')
571 self.assertTrue(self.builds[3].hasChanges(A, B))
572
573 self.assertEqual(self.builds[4].name, 'project-test1')
574 self.assertTrue(self.builds[4].hasChanges(A, B, C))
575 self.assertEqual(self.builds[5].name, 'project-test2')
576 self.assertTrue(self.builds[5].hasChanges(A, B, C))
577
578 all_builds = self.builds[:]
579 self.release(all_builds[2])
580 self.release(all_builds[3])
581 self.waitUntilSettled()
582 self.assertFalse(A.is_merged)
583 self.assertFalse(B.is_merged)
584 self.assertFalse(C.is_merged)
585
586 self.release(all_builds[0])
587 self.release(all_builds[1])
588 self.waitUntilSettled()
589 self.assertTrue(A.is_merged)
590 self.assertTrue(B.is_merged)
591 self.assertFalse(C.is_merged)
592
593 self.executor_server.hold_jobs_in_build = False
594 self.executor_server.release()
595 self.waitUntilSettled()
596 self.assertEqual(len(self.builds), 0)
597 self.assertEqual(len(self.history), 9)
598 self.assertTrue(C.is_merged)
599
600 self.assertNotIn('merge', A.labels)
601 self.assertNotIn('merge', B.labels)
602 self.assertNotIn('merge', C.labels)
603
604 @simple_layout('layouts/dependent-github.yaml', driver='github')
605 def test_failed_changes(self):
606 "Test that a change behind a failed change is retested"
607 self.executor_server.hold_jobs_in_build = True
608
609 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
610 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
611
612 self.executor_server.failJob('project-test1', A)
613
614 self.fake_github.emitEvent(A.addLabel('merge'))
615 self.fake_github.emitEvent(B.addLabel('merge'))
616 self.waitUntilSettled()
617
618 self.executor_server.release('.*-merge')
619 self.waitUntilSettled()
620
621 self.executor_server.hold_jobs_in_build = False
622 self.executor_server.release()
623
624 self.waitUntilSettled()
625 # It's certain that the merge job for change 2 will run, but
626 # the test1 and test2 jobs may or may not run.
627 self.assertTrue(len(self.history) > 6)
628 self.assertFalse(A.is_merged)
629 self.assertTrue(B.is_merged)
630 self.assertNotIn('merge', A.labels)
631 self.assertNotIn('merge', B.labels)
632
633 @simple_layout('layouts/dependent-github.yaml', driver='github')
634 def test_failed_change_at_head(self):
635 "Test that if a change at the head fails, jobs behind it are canceled"
636
637 self.executor_server.hold_jobs_in_build = True
638 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
639 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
640 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
641
642 self.executor_server.failJob('project-test1', A)
643
644 self.fake_github.emitEvent(A.addLabel('merge'))
645 self.fake_github.emitEvent(B.addLabel('merge'))
646 self.fake_github.emitEvent(C.addLabel('merge'))
647
648 self.waitUntilSettled()
649
650 self.assertEqual(len(self.builds), 1)
651 self.assertEqual(self.builds[0].name, 'project-merge')
652 self.assertTrue(self.builds[0].hasChanges(A))
653
654 self.executor_server.release('.*-merge')
655 self.waitUntilSettled()
656 self.executor_server.release('.*-merge')
657 self.waitUntilSettled()
658 self.executor_server.release('.*-merge')
659 self.waitUntilSettled()
660
661 self.assertEqual(len(self.builds), 6)
662 self.assertEqual(self.builds[0].name, 'project-test1')
663 self.assertEqual(self.builds[1].name, 'project-test2')
664 self.assertEqual(self.builds[2].name, 'project-test1')
665 self.assertEqual(self.builds[3].name, 'project-test2')
666 self.assertEqual(self.builds[4].name, 'project-test1')
667 self.assertEqual(self.builds[5].name, 'project-test2')
668
669 self.release(self.builds[0])
670 self.waitUntilSettled()
671
672 # project-test2, project-merge for B
673 self.assertEqual(len(self.builds), 2)
674 self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 4)
675
676 self.executor_server.hold_jobs_in_build = False
677 self.executor_server.release()
678 self.waitUntilSettled()
679
680 self.assertEqual(len(self.builds), 0)
681 self.assertEqual(len(self.history), 15)
682 self.assertFalse(A.is_merged)
683 self.assertTrue(B.is_merged)
684 self.assertTrue(C.is_merged)
685 self.assertNotIn('merge', A.labels)
686 self.assertNotIn('merge', B.labels)
687 self.assertNotIn('merge', C.labels)
Jesse Keating71a47ff2017-06-06 11:36:43 -0700688
689 @simple_layout('layouts/basic-github.yaml', driver='github')
690 def test_push_event_reconfigure(self):
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200691 pevent = self.fake_github.getPushEvent(project='org/common-config',
Jesse Keating71a47ff2017-06-06 11:36:43 -0700692 ref='refs/heads/master',
693 modified_files=['zuul.yaml'])
694
695 # record previous tenant reconfiguration time, which may not be set
696 old = self.sched.tenant_last_reconfigured.get('tenant-one', 0)
697 time.sleep(1)
698 self.fake_github.emitEvent(pevent)
699 self.waitUntilSettled()
700 new = self.sched.tenant_last_reconfigured.get('tenant-one', 0)
701 # New timestamp should be greater than the old timestamp
702 self.assertLess(old, new)
Tristan Cacqueray2bafb1f2017-06-12 07:10:26 +0000703
Jesse Keating64d29012017-09-06 12:27:49 -0700704 # TODO(jlk): Make this a more generic test for unknown project
705 @skip("Skipped for rewrite of webhook handler")
Tristan Cacqueray2bafb1f2017-06-12 07:10:26 +0000706 @simple_layout('layouts/basic-github.yaml', driver='github')
707 def test_ping_event(self):
708 # Test valid ping
709 pevent = {'repository': {'full_name': 'org/project'}}
710 req = self.fake_github.emitEvent(('ping', pevent))
711 self.assertEqual(req.status, 200, "Ping event didn't succeed")
712
713 # Test invalid ping
714 pevent = {'repository': {'full_name': 'unknown-project'}}
715 self.assertRaises(
716 urllib.error.HTTPError,
717 self.fake_github.emitEvent,
718 ('ping', pevent),
719 )
Tobias Henkeleca46202017-08-02 20:27:10 +0200720
721
722class TestGithubUnprotectedBranches(ZuulTestCase):
723 config_file = 'zuul-github-driver.conf'
724 tenant_config_file = 'config/unprotected-branches/main.yaml'
725
726 def test_unprotected_branches(self):
727 tenant = self.sched.abide.tenants.get('tenant-one')
728
729 project1 = tenant.untrusted_projects[0]
730 project2 = tenant.untrusted_projects[1]
731
732 # project1 should have parsed master
733 self.assertIn('master', project1.unparsed_branch_config.keys())
734
735 # project2 should have no parsed branch
736 self.assertEqual(0, len(project2.unparsed_branch_config.keys()))