package org.jenkinsci.plugins.ghprb;
import com.coravy.hudson.plugins.github.GithubProjectProperty;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.*;
import hudson.triggers.Trigger;
import hudson.triggers.TriggerDescriptor;
import org.jenkinsci.plugins.ghprb.GhprbTrigger.DescriptorImpl;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.jvnet.hudson.test.JenkinsRule;
import org.kohsuke.github.*;
import org.kohsuke.github.GHPullRequestCommitDetail.Commit;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class GhprbPullRequestMergeTest {
@Rule
public JenkinsRule jenkinsRule = new JenkinsRule();
private FreeStyleProject project = mock(FreeStyleProject.class);
private Run<?, ?> build = mock(Run.class);
@Mock
private GhprbPullRequest pullRequest;
@Mock
private GHPullRequest pr;
@Mock
private GitUser committer;
@Mock
private GHUser triggerSender;
@Mock
private GHUser prCreator;
@Mock
private GhprbCause cause;
@Mock
private Ghprb helper;
@Mock
private GhprbRepository repo;
@Mock
private GHRepository ghRepo;
@Mock
private TaskListener listener;
@Mock
private ItemGroup<?> parent;
@Mock
private Launcher launcher;
private FilePath mockFilePath;
private final String triggerPhrase = "ok to merge";
private final String nonTriggerPhrase = "This phrase is not the trigger phrase";
private final String adminList = "admin";
private final String adminLogin = "admin";
private final String nonAdminLogin = "nonadmin";
private final String committerName = "committer";
private final String nonCommitterName = "noncommitter";
private final String committerEmail = "committer@mail.com";
private final String nonCommitterEmail = "noncommitter@mail.com";
private final String mergeComment = "merge";
private final Integer pullId = 1;
private Map<String, Object> triggerValues;
@Before
public void beforeTest() throws Exception {
launcher = mock(Launcher.class);
mockFilePath = new FilePath(new File(""));
triggerValues = new HashMap<String, Object>(10);
triggerValues.put("adminlist", adminList);
triggerValues.put("triggerPhrase", triggerPhrase);
final GhprbTrigger trigger = GhprbTestUtil.getTrigger(triggerValues);
Mockito.doReturn(repo).when(trigger).getRepository();
ConcurrentMap<Integer, GhprbPullRequest> pulls = new ConcurrentHashMap<Integer, GhprbPullRequest>(1);
pulls.put(pullId, pullRequest);
Map<String, ConcurrentMap<Integer, GhprbPullRequest>> jobs = new HashMap<String, ConcurrentMap<Integer, GhprbPullRequest>>(1);
jobs.put("project", pulls);
Mockito.doReturn(project).when(trigger).getActualProject();
Mockito.doReturn(repo).when(trigger).getRepository();
repo.addPullRequests(pulls);
Mockito.doReturn(pullRequest).when(repo).getPullRequest(pullId);
Mockito.doReturn(pr).when(repo).getActualPullRequest(pullId);
GithubProjectProperty projectProperty = new GithubProjectProperty("https://github.com/jenkinsci/ghprb-plugin");
DescriptorImpl descriptor = trigger.getDescriptor();
PrintStream logger = mock(PrintStream.class);
given(parent.getFullName()).willReturn("");
Map<TriggerDescriptor, Trigger<?>> map = new HashMap<TriggerDescriptor, Trigger<?>>();
map.put(descriptor,trigger);
given(project.getParent()).willReturn(parent);
given(project.getTriggers()).willReturn(map);
given(project.getName()).willReturn("project");
given(project.getProperty(GithubProjectProperty.class)).willReturn(projectProperty);
given(project.isBuildable()).willReturn(true);
given(build.getCause(GhprbCause.class)).willReturn(cause);
given(build.getResult()).willReturn(Result.SUCCESS);
given(build.getParent()).willCallRealMethod();
given(pullRequest.getPullRequest()).willReturn(pr);
given(cause.getPullID()).willReturn(pullId);
given(cause.isMerged()).willReturn(true);
given(cause.getTriggerSender()).willReturn(triggerSender);
given(cause.getCommitAuthor()).willReturn(committer);
given(listener.getLogger()).willReturn(logger);
doNothing().when(repo).addComment(anyInt(), anyString());
doNothing().when(logger).println();
Field parentField = Run.class.getDeclaredField("project");
parentField.setAccessible(true);
parentField.set(build, project);
Field jobsField = descriptor.getClass().getDeclaredField("jobs");
jobsField.setAccessible(true);
jobsField.set(descriptor, jobs);
helper = spy(new Ghprb(trigger));
trigger.setHelper(helper);
given(helper.isBotUser(any(GHUser.class))).willReturn(false);
}
@After
public void afterClass() {
}
@SuppressWarnings("unchecked")
private void setupConditions(String prUserLogin, String triggerLogin, String committerName, String committerEmail, String comment) throws IOException {
given(triggerSender.getLogin()).willReturn(triggerLogin);
given(triggerSender.getName()).willReturn(committerName);
given(triggerSender.getEmail()).willReturn(committerEmail);
given(committer.getName()).willReturn(this.committerName);
given(prCreator.getLogin()).willReturn(prUserLogin);
given(pr.getUser()).willReturn(prCreator);
PagedIterator<GHPullRequestCommitDetail> itr = Mockito.mock(PagedIterator.class);
PagedIterable<GHPullRequestCommitDetail> pagedItr = Mockito.mock(PagedIterable.class);
Commit commit = mock(Commit.class);
GHPullRequestCommitDetail commitDetail = mock(GHPullRequestCommitDetail.class);
given(pr.listCommits()).willReturn(pagedItr);
given(pagedItr.iterator()).willReturn(itr);
given(itr.hasNext()).willReturn(true, false);
given(itr.next()).willReturn(commitDetail);
given(commitDetail.getCommit()).willReturn(commit);
given(commit.getCommitter()).willReturn(committer);
given(cause.getCommentBody()).willReturn(comment);
}
private void setupConditions(String triggerLogin, String committerName, String committerEmail, String comment) throws IOException {
setupConditions(nonCommitterName, triggerLogin, committerName, committerEmail, comment);
}
private GhprbPullRequestMerge setupMerger(
boolean onlyAdminsMerge,
boolean disallowOwnCode,
boolean failOnNonMerge,
boolean deleteOnMerge,
boolean allowMergeWithoutTriggerPhrase
) {
GhprbPullRequestMerge merger = spy(new GhprbPullRequestMerge(
mergeComment,
onlyAdminsMerge,
disallowOwnCode,
failOnNonMerge,
deleteOnMerge,
allowMergeWithoutTriggerPhrase));
merger.setHelper(helper);
Mockito.reset(pr);
return merger;
}
private GhprbPullRequestMerge setupMerger(
boolean onlyAdminsMerge,
boolean disallowOwnCode) {
return setupMerger(onlyAdminsMerge, disallowOwnCode, false, false, false);
}
@Test
public void testApproveMerge() throws Exception {
boolean onlyAdminsMerge = false;
boolean disallowOwnCode = false;
GhprbPullRequestMerge merger = setupMerger(onlyAdminsMerge, disallowOwnCode);
setupConditions(nonAdminLogin, committerName, committerEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(1)).merge(mergeComment);
setupConditions(adminLogin, nonCommitterName, nonCommitterEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(2)).merge(mergeComment);
setupConditions(adminLogin, committerName, committerEmail, nonTriggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(2)).merge(mergeComment);
setupConditions(nonAdminLogin, nonCommitterName, nonCommitterEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(3)).merge(mergeComment);
setupConditions(nonAdminLogin, nonCommitterName, nonCommitterEmail, nonTriggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(3)).merge(mergeComment);
setupConditions(adminLogin, nonCommitterName, nonCommitterEmail, nonTriggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(3)).merge(mergeComment);
setupConditions(nonAdminLogin, nonCommitterName, nonCommitterEmail, nonTriggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(3)).merge(mergeComment);
setupConditions(adminLogin, committerName, committerEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(4)).merge(mergeComment);
}
@Test
public void testAdminMerge() throws Exception {
boolean onlyAdminsMerge = true;
boolean disallowOwnCode = false;
GhprbPullRequestMerge merger = setupMerger(onlyAdminsMerge, disallowOwnCode);
setupConditions(adminLogin, committerName, committerEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(1)).merge(mergeComment);
setupConditions(nonAdminLogin, committerName, committerEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(1)).merge(mergeComment);
}
@Test
public void testTriggerMerge() throws Exception {
boolean onlyAdminsMerge = false;
boolean disallowOwnCode = false;
GhprbPullRequestMerge merger = setupMerger(onlyAdminsMerge, disallowOwnCode);
setupConditions(adminLogin, committerName, committerEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(1)).merge(mergeComment);
setupConditions(adminLogin, committerName, committerEmail, nonTriggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(1)).merge(mergeComment);
}
@Test
public void testOwnCodeMerge() throws Exception {
boolean onlyAdminsMerge = false;
boolean disallowOwnCode = true;
GhprbPullRequestMerge merger = setupMerger(onlyAdminsMerge, disallowOwnCode);
setupConditions(adminLogin, nonCommitterName, nonCommitterEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(1)).merge(mergeComment);
setupConditions(adminLogin, committerName, committerEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(1)).merge(mergeComment);
}
@Test
public void testDenyMerge() throws Exception {
boolean onlyAdminsMerge = true;
boolean disallowOwnCode = true;
GhprbPullRequestMerge merger = setupMerger(onlyAdminsMerge, disallowOwnCode);
setupConditions(nonAdminLogin, nonAdminLogin, nonCommitterName, nonCommitterEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(0)).merge(mergeComment);
setupConditions(nonAdminLogin, adminLogin, committerName, committerEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(0)).merge(mergeComment);
setupConditions(nonAdminLogin, adminLogin, nonCommitterName, nonCommitterEmail, nonTriggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(0)).merge(mergeComment);
setupConditions(nonAdminLogin, nonAdminLogin, nonCommitterName, nonCommitterEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(0)).merge(mergeComment);
setupConditions(nonAdminLogin, nonAdminLogin, nonCommitterName, nonCommitterEmail, nonTriggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(0)).merge(mergeComment);
setupConditions(nonAdminLogin, adminLogin, committerName, committerEmail, nonTriggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(0)).merge(mergeComment);
setupConditions(nonAdminLogin, nonAdminLogin, committerName, committerEmail, nonTriggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(0)).merge(mergeComment);
setupConditions(adminLogin, adminLogin, nonCommitterName, nonCommitterEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(0)).merge(mergeComment);
setupConditions(nonAdminLogin, adminLogin, nonCommitterName, nonCommitterEmail, triggerPhrase);
merger.perform(build, mockFilePath, launcher, listener);
verify(pr, times(1)).merge(mergeComment);
}
}