zuul/tests/unit/test_github_requirements.py
James E. Blair 1a4ec7e926 Add GitHub pipeline trigger requirements
This mimics a useful feature of the Gerrit driver and allows users
to configure pipelines that trigger on events but only if certain
conditions of the PR are met.

Unlike the Gerrit driver, this embeds the entire require/reject
filter within the trigger filter (the trigger filter has-a require
or reject filter).  This makes the code simpler and is easier for
users to configure.  If we like this approach, we should migrate the
gerrit driver as well, and perhaps the other drivers.

The "require-status" attribute already existed, but was undocumented.
This documents it, adds backwards-compat handling for it, and
deprecates it.

Some documentation typos are also corrected.

Change-Id: I4b6dd8c970691b1e74ffd5a96c2be4b8075f1a87
2023-04-28 11:46:33 -07:00

859 lines
35 KiB
Python

# Copyright (c) 2017 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import time
from tests.base import ZuulGithubAppTestCase, ZuulTestCase, simple_layout
class TestGithubRequirements(ZuulTestCase):
"""Test pipeline and trigger requirements"""
config_file = 'zuul-github-driver.conf'
scheduler_count = 1
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_pipeline_require_status(self):
"Test pipeline requirement: status"
project = 'org/project1'
A = self.fake_github.openFakePullRequest(project, 'master', 'A')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No status from zuul so should not be enqueued
self.assertEqual(len(self.history), 0)
# An error status should not cause it to be enqueued
self.fake_github.setCommitStatus(project, A.head_sha, 'error',
context='tenant-one/check')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A success status goes in
self.fake_github.setCommitStatus(project, A.head_sha, 'success',
context='tenant-one/check')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project1-pipeline')
# Trigger regex matched status
self.fake_github.emitEvent(A.getCommentAddedEvent('test regex'))
self.waitUntilSettled()
self.assertEqual(len(self.history), 2)
self.assertEqual(self.history[1].name, 'project1-pipeline')
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_trigger_require_status(self):
"Test trigger requirement: status"
project = 'org/project1'
A = self.fake_github.openFakePullRequest(project, 'master', 'A')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('trigger me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No status from zuul so should not be enqueued
self.assertEqual(len(self.history), 0)
# An error status should not cause it to be enqueued
self.fake_github.setCommitStatus(project, A.head_sha, 'error',
context='tenant-one/check')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A success status goes in
self.fake_github.setCommitStatus(project, A.head_sha, 'success',
context='tenant-one/check')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project1-pipeline')
self.fake_github.emitEvent(A.getCommentAddedEvent('trigger regex'))
self.waitUntilSettled()
self.assertEqual(len(self.history), 2)
self.assertEqual(self.history[1].name, 'project1-pipeline')
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_trigger_on_status(self):
"Test trigger on: status"
project = 'org/project2'
A = self.fake_github.openFakePullRequest(project, 'master', 'A')
# Create second PR which contains the head of A in its history. Zuul
# should not get disturbed by the existence of this one.
self.fake_github.openFakePullRequest(
project, 'master', 'A', base_sha=A.head_sha)
# An error status should not cause it to be enqueued
self.fake_github.setCommitStatus(project, A.head_sha, 'error',
context='tenant-one/check')
self.fake_github.emitEvent(A.getCommitStatusEvent('tenant-one/check',
state='error'))
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A success status from unknown user should not cause it to be
# enqueued
self.fake_github.setCommitStatus(project, A.head_sha, 'success',
context='tenant-one/check',
user='foo')
self.fake_github.emitEvent(A.getCommitStatusEvent('tenant-one/check',
state='success',
user='foo'))
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A success status from zuul goes in
self.fake_github.setCommitStatus(project, A.head_sha, 'success',
context='tenant-one/check')
self.fake_github.emitEvent(A.getCommitStatusEvent('tenant-one/check'))
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project2-trigger')
# An error status for a different context should not cause it to be
# enqueued
self.fake_github.setCommitStatus(project, A.head_sha, 'error',
context='tenant-one/gate')
self.fake_github.emitEvent(A.getCommitStatusEvent('tenant-one/gate',
state='error'))
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
# A success status with a regex match goes in
self.fake_github.emitEvent(A.getCommitStatusEvent('cooltest',
user='other-ci'))
self.waitUntilSettled()
self.assertEqual(len(self.history), 2)
self.assertEqual(self.history[1].name, 'project2-trigger')
@simple_layout("layouts/requirements-github.yaml", driver="github")
def test_trigger_on_check_run(self):
"""Test trigger on: check_run"""
project = "org/project15"
A = self.fake_github.openFakePullRequest(project, "master", "A")
# A check_run request with a different name should not cause it to be
# enqueued.
self.fake_github.emitEvent(
A.getCheckRunRequestedEvent("tenant-one/different-check")
)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A check_run request with the correct name, but for a different app
# should not cause it to be enqueued.
self.fake_github.emitEvent(
A.getCheckRunRequestedEvent("tenant-one/check", app="other-ci")
)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A check_run request with the correct name for the correct app should
# cause it to be enqueued.
self.fake_github.emitEvent(
A.getCheckRunRequestedEvent("tenant-one/check"))
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, "project15-check-run")
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_pipeline_require_review_username(self):
"Test pipeline requirement: review username"
A = self.fake_github.openFakePullRequest('org/project3', 'master', 'A')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No approval from derp so should not be enqueued
self.assertEqual(len(self.history), 0)
# Add an approved review from derp
A.addReview('derp', 'APPROVED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project3-reviewusername')
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_pipeline_require_review_state(self):
"Test pipeline requirement: review state"
A = self.fake_github.openFakePullRequest('org/project4', 'master', 'A')
# Add derp to writers
A.writers.extend(('derp', 'werp'))
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No positive review from derp so should not be enqueued
self.assertEqual(len(self.history), 0)
# A negative review from derp should not cause it to be enqueued
A.addReview('derp', 'CHANGES_REQUESTED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A negative review from werp should not cause it to be enqueued
A.addReview('werp', 'CHANGES_REQUESTED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A positive from nobody should not cause it to be enqueued
A.addReview('nobody', 'APPROVED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A positive review from derp should still be blocked by the
# negative review from werp
A.addReview('derp', 'APPROVED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A positive review from werp should cause it to be enqueued
A.addReview('werp', 'APPROVED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project4-reviewreq')
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_pipeline_require_review_user_state(self):
"Test pipeline requirement: review state from user"
A = self.fake_github.openFakePullRequest('org/project5', 'master', 'A')
# Add derp and herp to writers
A.writers.extend(('derp', 'herp'))
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No positive review from derp so should not be enqueued
self.assertEqual(len(self.history), 0)
# A negative review from derp should not cause it to be enqueued
A.addReview('derp', 'CHANGES_REQUESTED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A positive from nobody should not cause it to be enqueued
A.addReview('nobody', 'APPROVED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A positive review from herp (a writer) should not cause it to be
# enqueued
A.addReview('herp', 'APPROVED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A positive review from derp should cause it to be enqueued
A.addReview('derp', 'APPROVED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project5-reviewuserstate')
# TODO: Implement reject on approval username/state
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_pipeline_require_review_latest_user_state(self):
"Test pipeline requirement: review state from user"
A = self.fake_github.openFakePullRequest('org/project5', 'master', 'A')
# Add derp and herp to writers
A.writers.extend(('derp', 'herp'))
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No positive review from derp so should not be enqueued
self.assertEqual(len(self.history), 0)
# The first negative review from derp should not cause it to be
# enqueued
A.addReview('derp', 'CHANGES_REQUESTED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A positive review from derp should cause it to be enqueued
A.addReview('derp', 'APPROVED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project5-reviewuserstate')
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_pipeline_require_review_write_perms(self):
"Test pipeline requirement: review from user with write"
A = self.fake_github.openFakePullRequest('org/project4', 'master', 'A')
# Add herp to admins
A.admins.append('herp')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No positive review from derp so should not be enqueued
self.assertEqual(len(self.history), 0)
# The first review is from a reader, and thus should not be enqueued
A.addReview('derp', 'APPROVED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A positive review from herp should cause it to be enqueued
A.addReview('herp', 'APPROVED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project4-reviewreq')
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_pipeline_require_review_comment_masked(self):
"Test pipeline requirement: review comments on top of votes"
A = self.fake_github.openFakePullRequest('org/project5', 'master', 'A')
# Add derp to writers
A.writers.append('derp')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No positive review from derp so should not be enqueued
self.assertEqual(len(self.history), 0)
# The first negative review from derp should not cause it to be
# enqueued
A.addReview('derp', 'CHANGES_REQUESTED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A positive review is required, so provide it
A.addReview('derp', 'APPROVED')
# Add a comment review on top to make sure we can still enqueue
A.addReview('derp', 'COMMENTED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project5-reviewuserstate')
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_require_review_newer_than(self):
A = self.fake_github.openFakePullRequest('org/project6', 'master', 'A')
# Add derp and herp to writers
A.writers.extend(('derp', 'herp'))
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No positive review from derp so should not be enqueued
self.assertEqual(len(self.history), 0)
# Add a too-old positive review, should not be enqueued
submitted_at = time.time() - 72 * 60 * 60
A.addReview('derp', 'APPROVED',
submitted_at)
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# Add a recent positive review
submitted_at = time.time() - 12 * 60 * 60
A.addReview('derp', 'APPROVED', submitted_at)
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project6-newerthan')
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_require_review_older_than(self):
A = self.fake_github.openFakePullRequest('org/project7', 'master', 'A')
# Add derp and herp to writers
A.writers.extend(('derp', 'herp'))
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No positive review from derp so should not be enqueued
self.assertEqual(len(self.history), 0)
# Add a too-new positive, should not be enqueued
submitted_at = time.time() - 12 * 60 * 60
A.addReview('derp', 'APPROVED',
submitted_at)
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# Add an old enough positive, should enqueue
submitted_at = time.time() - 72 * 60 * 60
A.addReview('herp', 'APPROVED', submitted_at)
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project7-olderthan')
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_require_open(self):
A = self.fake_github.openFakePullRequest('org/project8', 'master', 'A')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# PR is open, we should have enqueued
self.assertEqual(len(self.history), 1)
# close the PR and try again
A.state = 'closed'
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# PR is closed, should not trigger
self.assertEqual(len(self.history), 1)
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_reject_open(self):
A = self.fake_github.openFakePullRequest('org/project13', 'master',
'A')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# PR is open, we should not have enqueued
self.assertEqual(len(self.history), 0)
# close the PR and try again
A.state = 'closed'
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# PR is closed, should trigger
self.assertEqual(len(self.history), 1)
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_require_current(self):
A = self.fake_github.openFakePullRequest('org/project9', 'master',
'A')
# A sync event that we will keep submitting to trigger
sync = A.getPullRequestSynchronizeEvent()
self.fake_github.emitEvent(sync)
self.waitUntilSettled()
# PR head is current should enqueue
self.assertEqual(len(self.history), 1)
# Add a commit to the PR, re-issue the original comment event
A.addCommit()
self.fake_github.emitEvent(sync)
self.waitUntilSettled()
# Event hash is not current, should not trigger
self.assertEqual(len(self.history), 1)
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_reject_current(self):
A = self.fake_github.openFakePullRequest('org/project14', 'master',
'A')
# A sync event that we will keep submitting to trigger
sync = A.getPullRequestSynchronizeEvent()
self.fake_github.emitEvent(sync)
self.waitUntilSettled()
# PR head is current, should not enqueue
self.assertEqual(len(self.history), 0)
# Add a commit to the PR, re-issue the original comment event
A.addCommit()
self.fake_github.emitEvent(sync)
self.waitUntilSettled()
# Event hash is not current, should trigger
self.assertEqual(len(self.history), 1)
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_require_draft(self):
A = self.fake_github.openFakePullRequest('org/project17', 'master',
'A', draft=True)
# A sync event that we will keep submitting to trigger
sync = A.getPullRequestSynchronizeEvent()
self.fake_github.emitEvent(sync)
self.waitUntilSettled()
# PR is a draft, should enqueue
self.assertEqual(len(self.history), 1)
# Make the PR not a draft
A.draft = False
self.fake_github.emitEvent(sync)
self.waitUntilSettled()
# PR is not a draft, should not enqueue
self.assertEqual(len(self.history), 1)
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_reject_draft(self):
A = self.fake_github.openFakePullRequest('org/project18', 'master',
'A', draft=True)
# A sync event that we will keep submitting to trigger
sync = A.getPullRequestSynchronizeEvent()
self.fake_github.emitEvent(sync)
self.waitUntilSettled()
# PR is a draft, should not enqueue
self.assertEqual(len(self.history), 0)
# Make the PR not a draft
A.draft = False
self.fake_github.emitEvent(sync)
self.waitUntilSettled()
# PR is not a draft, should enqueue
self.assertEqual(len(self.history), 1)
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_pipeline_require_label(self):
"Test pipeline requirement: label"
A = self.fake_github.openFakePullRequest('org/project10', 'master',
'A')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No label so should not be enqueued
self.assertEqual(len(self.history), 0)
# A derp label should not cause it to be enqueued
A.addLabel('derp')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# An approved label goes in
A.addLabel('approved')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project10-label')
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_pipeline_reject_label(self):
"Test pipeline reject: label"
A = self.fake_github.openFakePullRequest('org/project11', 'master',
'A')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No label so should not be enqueued
self.assertEqual(len(self.history), 0)
# A do-not-merge label should not cause it to be enqueued
A.addLabel('do-not-merge')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# An approved label should still not enqueue due to d-n-m
A.addLabel('approved')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# Remove do-not-merge should enqueue
A.removeLabel('do-not-merge')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project11-label')
@simple_layout('layouts/requirements-github.yaml', driver='github')
def test_pipeline_reject_status(self):
"Test pipeline reject: status"
project = 'org/project12'
A = self.fake_github.openFakePullRequest(project, 'master', 'A')
# Set rejected error status
self.fake_github.setCommitStatus(project, A.head_sha, 'error',
context='tenant-one/check')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent('test me')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# Status should cause it to be rejected
self.assertEqual(len(self.history), 0)
# Test that also the regex matched pipeline doesn't trigger
self.fake_github.emitEvent(A.getCommentAddedEvent('test regex'))
self.waitUntilSettled()
# Status should cause it to be rejected
self.assertEqual(len(self.history), 0)
self.fake_github.setCommitStatus(project, A.head_sha, 'success',
context='tenant-one/check')
# Now that status is not error, it should be enqueued
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, 'project12-status')
# Test that also the regex matched pipeline triggers now
self.fake_github.emitEvent(A.getCommentAddedEvent('test regex'))
self.waitUntilSettled()
self.assertEqual(len(self.history), 2)
self.assertEqual(self.history[1].name, 'project12-status')
class TestGithubAppRequirements(ZuulGithubAppTestCase):
"""Test pipeline and trigger requirements with app authentication"""
config_file = 'zuul-github-driver.conf'
scheduler_count = 1
@simple_layout("layouts/requirements-github.yaml", driver="github")
def test_pipeline_require_check_run(self):
"Test pipeline requirement: status (reported via a check run)"
project = "org/project16"
github = self.fake_github.getGithubClient()
repo = github.repo_from_project(project)
A = self.fake_github.openFakePullRequest(project, "master", "A")
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent("trigger me")
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
# No status from zuul, so nothing should be enqueued
self.assertEqual(len(self.history), 0)
# An error check run should also not cause it to be enqueued
repo.create_check_run(
A.head_sha,
"tenant-one/check",
conclusion="failure",
app="check-run",
)
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A success check run goes in, ready to be enqueued
repo.create_check_run(
A.head_sha,
"tenant-one/check",
conclusion="success",
app="check-run",
)
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
class TestGithubTriggerRequirements(ZuulTestCase):
"""Test pipeline and trigger requirements"""
config_file = 'zuul-github-driver.conf'
scheduler_count = 1
@simple_layout('layouts/github-trigger-requirements.yaml', driver='github')
def test_require_status(self):
# Test trigger require-status
jobname = 'require-status'
project = 'org/project'
A = self.fake_github.openFakePullRequest(project, 'master', 'A')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent(f'test {jobname}')
# No status from zuul so should not be enqueued
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# An error status should not cause it to be enqueued
self.fake_github.setCommitStatus(project, A.head_sha, 'error',
context='tenant-one/check')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A success status goes in
self.fake_github.setCommitStatus(project, A.head_sha, 'success',
context='tenant-one/check')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, jobname)
@simple_layout('layouts/github-trigger-requirements.yaml', driver='github')
def test_reject_status(self):
# Test trigger reject-status
jobname = 'reject-status'
project = 'org/project'
A = self.fake_github.openFakePullRequest(project, 'master', 'A')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent(f'test {jobname}')
# No status from zuul so should be enqueued
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, jobname)
# A failure status should not cause it to be enqueued
self.fake_github.setCommitStatus(project, A.head_sha, 'failure',
context='tenant-one/check')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
# A success status goes in
self.fake_github.setCommitStatus(project, A.head_sha, 'success',
context='tenant-one/check')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 2)
self.assertEqual(self.history[1].name, jobname)
@simple_layout('layouts/github-trigger-requirements.yaml', driver='github')
def test_require_review(self):
# Test trigger require-review
jobname = 'require-review'
project = 'org/project'
A = self.fake_github.openFakePullRequest(project, 'master', 'A')
A.writers.extend(('maintainer',))
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent(f'test {jobname}')
# No review so should not be enqueued
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# An changes requested review should not cause it to be enqueued
A.addReview('maintainer', 'CHANGES_REQUESTED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A positive review goes in
A.addReview('maintainer', 'APPROVED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, jobname)
@simple_layout('layouts/github-trigger-requirements.yaml', driver='github')
def test_reject_review(self):
# Test trigger reject-review
jobname = 'reject-review'
project = 'org/project'
A = self.fake_github.openFakePullRequest(project, 'master', 'A')
A.writers.extend(('maintainer',))
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent(f'test {jobname}')
# No review so should be enqueued
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, jobname)
# An changes requested review should not cause it to be enqueued
A.addReview('maintainer', 'CHANGES_REQUESTED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
# A positive review goes in
A.addReview('maintainer', 'APPROVED')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 2)
self.assertEqual(self.history[1].name, jobname)
@simple_layout('layouts/github-trigger-requirements.yaml', driver='github')
def test_require_label(self):
# Test trigger require-label
jobname = 'require-label'
project = 'org/project'
A = self.fake_github.openFakePullRequest(project, 'master', 'A')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent(f'test {jobname}')
# No label so should not be enqueued
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# A random should not cause it to be enqueued
A.addLabel('foobar')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# An approved label goes in
A.addLabel('approved')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, jobname)
@simple_layout('layouts/github-trigger-requirements.yaml', driver='github')
def test_reject_label(self):
# Test trigger reject-label
jobname = 'reject-label'
project = 'org/project'
A = self.fake_github.openFakePullRequest(project, 'master', 'A')
# A comment event that we will keep submitting to trigger
comment = A.getCommentAddedEvent(f'test {jobname}')
# No label so should be enqueued
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
self.assertEqual(self.history[0].name, jobname)
# A rejected label should not cause it to be enqueued
A.addLabel('rejected')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 1)
# Any other label, it goes in
A.removeLabel('rejected')
A.addLabel('okay')
self.fake_github.emitEvent(comment)
self.waitUntilSettled()
self.assertEqual(len(self.history), 2)
self.assertEqual(self.history[1].name, jobname)