package hudson.plugins.jira; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.IOException; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Set; import hudson.plugins.jira.model.JiraIssue; import org.hamcrest.Matchers; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.jvnet.hudson.test.Bug; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.WithoutJenkins; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import com.atlassian.jira.rest.client.api.RestClientException; import com.atlassian.jira.rest.client.api.domain.Comment; import com.atlassian.jira.rest.client.api.domain.Issue; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; import hudson.model.Job; import hudson.model.Result; import hudson.model.Run; import hudson.model.TopLevelItem; import hudson.model.User; import hudson.scm.ChangeLogSet; import hudson.scm.EditType; import hudson.scm.SCM; import hudson.scm.ChangeLogSet.Entry; import jenkins.model.Jenkins; /** * Test case for the JIRA {@link Updater}. * * @author kutzi */ @SuppressWarnings("unchecked") public class UpdaterTest { @Rule public JenkinsRule rule = new JenkinsRule(); private Updater updater; private static class MockEntry extends Entry { private final String msg; public MockEntry(String msg) { this.msg = msg; } @Override public Collection<String> getAffectedPaths() { return null; } @Override public User getAuthor() { return null; } @Override public String getMsg() { return this.msg; } } @Before public void prepare() { SCM scm = mock(SCM.class); this.updater = new Updater(scm); } @Test @WithoutJenkins public void testGetScmCommentsFromPreviousBuilds() throws Exception { final FreeStyleProject project = mock(FreeStyleProject.class); final FreeStyleBuild build1 = mock(FreeStyleBuild.class); final MockEntry entry1 = new MockEntry("FOOBAR-1: The first build"); { ChangeLogSet changeLogSet = mock(ChangeLogSet.class); when(build1.getChangeSet()).thenReturn(changeLogSet); List<ChangeLogSet<? extends ChangeLogSet.Entry>> changeSets = new ArrayList<ChangeLogSet<? extends Entry>>(); changeSets.add(changeLogSet); when(build1.getChangeSets()).thenReturn(changeSets); when(build1.getResult()).thenReturn(Result.FAILURE); doReturn(project).when(build1).getProject(); doReturn(new JiraCarryOverAction(Sets.newHashSet(new JiraIssue("FOOBAR-1", null)))) .when(build1).getAction(JiraCarryOverAction.class); final Set<? extends Entry> entries = Sets.newHashSet(entry1); when(changeLogSet.iterator()).thenAnswer(new Answer<Object>() { public Object answer(final InvocationOnMock invocation) throws Throwable { return entries.iterator(); } }); } final FreeStyleBuild build2 = mock(FreeStyleBuild.class); final MockEntry entry2 = new MockEntry("FOOBAR-2: The next build"); { ChangeLogSet changeLogSet = mock(ChangeLogSet.class); when(build2.getChangeSet()).thenReturn(changeLogSet); List<ChangeLogSet<? extends ChangeLogSet.Entry>> changeSets = new ArrayList<ChangeLogSet<? extends Entry>>(); changeSets.add(changeLogSet); when(build2.getChangeSets()).thenReturn(changeSets); when(build2.getPreviousBuild()).thenReturn(build1); when(build2.getResult()).thenReturn(Result.SUCCESS); doReturn(project).when(build2).getProject(); final Set<? extends Entry> entries = Sets.newHashSet(entry2); when(changeLogSet.iterator()).thenAnswer(new Answer<Object>() { public Object answer(final InvocationOnMock invocation) throws Throwable { return entries.iterator(); } }); } final List<Comment> comments = Lists.newArrayList(); final JiraSession session = mock(JiraSession.class); doAnswer(new Answer<Object>() { public Object answer(final InvocationOnMock invocation) throws Throwable { Comment rc = Comment.createWithGroupLevel((String) invocation.getArguments()[1], (String) invocation.getArguments()[2]); comments.add(rc); return null; } }).when(session).addComment(anyString(), anyString(), anyString(), anyString()); this.updater = new Updater(build2.getProject().getScm()); final Set<JiraIssue> ids = Sets.newHashSet(new JiraIssue("FOOBAR-1", null), new JiraIssue("FOOBAR-2", null)); updater.submitComments(build2, System.out, "http://jenkins", ids, session, false, false, "", ""); Assert.assertEquals(2, comments.size()); Assert.assertThat(comments.get(0).getBody(), Matchers.containsString(entry1.getMsg())); Assert.assertThat(comments.get(1).getBody(), Matchers.containsString(entry2.getMsg())); } /** * Tests that the generated comment matches the expectations - * especially that the JIRA id is not stripped from the comment. */ @Test @Bug(4572) @WithoutJenkins public void testComment() throws Exception { // mock JIRA session: JiraSession session = mock(JiraSession.class); when(session.existsIssue(Mockito.anyString())).thenReturn(Boolean.TRUE); final Issue mockIssue = Mockito.mock(Issue.class); when(session.getIssue(Mockito.anyString())).thenReturn(mockIssue); final List<String> comments = new ArrayList<String>(); Answer answer = new Answer<Object>() { public Object answer(InvocationOnMock invocation) throws Throwable { comments.add((String) invocation.getArguments()[1]); return null; } }; doAnswer(answer).when(session).addComment(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); // mock build: FreeStyleBuild build = mock(FreeStyleBuild.class); FreeStyleProject project = mock(FreeStyleProject.class); when(build.getProject()).thenReturn(project); ChangeLogSet changeLogSet = mock(ChangeLogSet.class); when(build.getChangeSet()).thenReturn(changeLogSet); when(build.getResult()).thenReturn(Result.SUCCESS); Set<? extends Entry> entries = Sets.newHashSet(new MockEntry("Fixed FOOBAR-4711")); when(changeLogSet.iterator()).thenReturn(entries.iterator()); List<ChangeLogSet<? extends ChangeLogSet.Entry>> changeSets = new ArrayList<ChangeLogSet<? extends Entry>>(); changeSets.add(changeLogSet); when(build.getChangeSets()).thenReturn(changeSets); // test: Set<JiraIssue> ids = Sets.newHashSet(new JiraIssue("FOOBAR-4711", "Title")); Updater updaterCurrent = new Updater(build.getParent().getScm()); updaterCurrent.submitComments(build, System.out, "http://jenkins", ids, session, false, false, "", ""); Assert.assertEquals(1, comments.size()); String comment = comments.get(0); Assert.assertTrue(comment.contains("FOOBAR-4711")); // must also work case-insensitively (JENKINS-4132) comments.clear(); entries = Sets.newHashSet(new MockEntry("Fixed Foobar-4711")); when(changeLogSet.iterator()).thenReturn(entries.iterator()); ids = Sets.newHashSet(new JiraIssue("FOOBAR-4711", "Title")); updaterCurrent.submitComments(build, System.out, "http://jenkins", ids, session, false, false, "", ""); Assert.assertEquals(1, comments.size()); comment = comments.get(0); Assert.assertTrue(comment.contains("Foobar-4711")); } /** /** * Checks if issues are correctly removed from the carry over list. * @throws RemoteException */ @Test @Bug(17156) @WithoutJenkins public void testIssueIsRemovedFromCarryOverListAfterSubmission() throws RestClientException { // mock build: FreeStyleBuild build = mock(FreeStyleBuild.class); FreeStyleProject project = mock(FreeStyleProject.class); when(build.getProject()).thenReturn(project); ChangeLogSet changeLogSet = ChangeLogSet.createEmpty(build); when(build.getChangeSet()).thenReturn(changeLogSet); when(build.getResult()).thenReturn(Result.SUCCESS); final JiraIssue firstIssue = new JiraIssue("FOOBAR-1", "Title"); final JiraIssue secondIssue = new JiraIssue("ALIBA-1", "Title"); final JiraIssue thirdIssue = new JiraIssue("MOONA-1", "Title"); final JiraIssue deletedIssue = new JiraIssue("FOOBAR-2", "Title"); final JiraIssue forbiddenIssue = new JiraIssue("LASSO-17", "Title"); // assume that there is a following list of jira issues from scm commit messages out of hudson.plugins.jira.JiraCarryOverAction Set<JiraIssue> issues = Sets.newHashSet(firstIssue, secondIssue, forbiddenIssue, thirdIssue); // mock JIRA session: JiraSession session = mock(JiraSession.class); when(session.existsIssue(Mockito.anyString())).thenReturn(Boolean.TRUE); // when(session.getIssue(Mockito.anyString())).thenReturn( new Issue()); // when(session.getGroup(Mockito.anyString())).thenReturn(new Group("Software Development", null)); final List<Comment> comments = new ArrayList<Comment>(); Answer answer = new Answer<Object>() { public Object answer(InvocationOnMock invocation) throws Throwable { Comment c = Comment.createWithGroupLevel((String) invocation.getArguments()[0], (String) invocation.getArguments()[1]); comments.add(c); return null; } }; doAnswer(answer).when(session).addComment(eq(firstIssue.getKey()), Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); doAnswer(answer).when(session).addComment(eq(secondIssue.getKey()), Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); doAnswer(answer).when(session).addComment(eq(thirdIssue.getKey()), Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); // issue for the caught exception doThrow(new RestClientException(new Throwable(), 404)).when(session).addComment(eq(deletedIssue.getKey()), Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); doThrow(new RestClientException(new Throwable(), 403)).when(session).addComment(eq(forbiddenIssue.getKey()), Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); final String groupVisibility = ""; final String roleVisibility = ""; Updater updaterCurrent = new Updater(build.getParent().getScm()); updaterCurrent.submitComments( build, System.out, "http://jenkins", issues, session, false, false, groupVisibility, roleVisibility ); // expected issue list final Set<JiraIssue> expectedIssuesToCarryOver = Sets.newLinkedHashSet(); expectedIssuesToCarryOver.add(forbiddenIssue); Assert.assertThat(issues, is(expectedIssuesToCarryOver)); } @Rule public TemporaryFolder folder = new TemporaryFolder(); /** * Test that workflow job - run instance of type WorkflowJob - can * return changeSets using java reflection api * @throws IOException * */ @Test @WithoutJenkins public void testGetChangesUsingReflectionForWorkflowJob() throws IOException { Jenkins jenkins = mock(Jenkins.class); when(jenkins.getRootDirFor(Mockito.<TopLevelItem>anyObject())).thenReturn(folder.getRoot()); WorkflowJob workflowJob = new WorkflowJob(jenkins, "job"); WorkflowRun workflowRun = new WorkflowRun(workflowJob); ChangeLogSet changeLogSet = ChangeLogSet.createEmpty(workflowRun); List<ChangeLogSet<? extends Entry>> changesUsingReflection = RunScmChangeExtractor.getChangesUsingReflection(workflowRun); Assert.assertNotNull(changesUsingReflection); Assert.assertTrue(changesUsingReflection.isEmpty()); } @WithoutJenkins @Test(expected=IllegalArgumentException.class) public void testGetChangesUsingReflectionForunknownJob() throws IOException { Run run = mock(Run.class); List<ChangeLogSet<? extends Entry>> changesUsingReflection = RunScmChangeExtractor.getChangesUsingReflection(run); } /** * Test formatting of scm entry change time. * */ @Test @WithoutJenkins public void testAppendChangeTimestampToDescription() { Updater updater = new Updater(null); StringBuilder description = new StringBuilder(); Calendar calendar = Calendar.getInstance(); calendar.set(2016, 0, 1, 0, 0, 0); JiraSite site = mock(JiraSite.class); when(site.getDateTimePattern()).thenReturn("yyyy-MM-dd HH:mm:ss"); updater.appendChangeTimestampToDescription(description, site, calendar.getTimeInMillis()); System.out.println(description.toString()); Assert.assertThat(description.toString(), equalTo("2016-01-01 00:00:00")); } /** * Test formatting of scm entry change description. * */ @Test public void testDateTimeInChangeDescription() { rule.getInstance(); Updater updater = new Updater(null); Calendar calendar = Calendar.getInstance(); calendar.set(2016, 0, 1, 0, 0, 0); JiraSite site = mock(JiraSite.class); when(site.isAppendChangeTimestamp()).thenReturn(true); when(site.getDateTimePattern()).thenReturn("yyyy-MM-dd HH:mm:ss"); Run r = mock(Run.class); Job j = mock(Job.class); when(r.getParent()).thenReturn(j); JiraProjectProperty jiraProjectProperty = mock(JiraProjectProperty.class); when(j.getProperty(JiraProjectProperty.class)).thenReturn(jiraProjectProperty); when(jiraProjectProperty.getSite()).thenReturn(site); ChangeLogSet.Entry entry = mock(ChangeLogSet.Entry.class); when(entry.getTimestamp()).thenReturn(calendar.getTimeInMillis()); when(entry.getCommitId()).thenReturn("dsgsvds2re3dsv"); User mockAuthor = mock(User.class); when(mockAuthor.getId()).thenReturn("jenkins-user"); when(entry.getAuthor()).thenReturn(mockAuthor); String description = updater.createScmChangeEntryDescription(r, entry, false, false); System.out.println(description); Assert.assertThat(description, containsString("2016-01-01 00:00:00")); Assert.assertThat(description, containsString("jenkins-user")); Assert.assertThat(description, containsString("dsgsvds2re3dsv")); } /** * Test formatting of scm entry change description * when no format is provided (e.g. when null). * */ @Test @WithoutJenkins public void testAppendChangeTimestampToDescriptionNullFormat() { //set default locale -> predictable test without explicit format Locale.setDefault(Locale.ENGLISH); Updater updater = new Updater(null); JiraSite site = mock(JiraSite.class); when(site.isAppendChangeTimestamp()).thenReturn(true); when(site.getDateTimePattern()).thenReturn(null); Calendar calendar = Calendar.getInstance(); calendar.set(2016, 0, 1, 0, 0, 0); StringBuilder builder = new StringBuilder(); updater.appendChangeTimestampToDescription(builder, site, calendar.getTimeInMillis()); Assert.assertThat(builder.toString(), equalTo("1/1/16 12:00 AM")); } /** * Test formatting of scm entry change description * when no format is provided (e.g. when empty string). * */ @Test @WithoutJenkins public void testAppendChangeTimestampToDescriptionNoFormat() { //set default locale -> predictable test without explicit format Locale.setDefault(Locale.ENGLISH); Updater updater = new Updater(null); JiraSite site = mock(JiraSite.class); when(site.isAppendChangeTimestamp()).thenReturn(true); when(site.getDateTimePattern()).thenReturn(""); Calendar calendar = Calendar.getInstance(); calendar.set(2016, 0, 1, 0, 0, 0); StringBuilder builder = new StringBuilder(); updater.appendChangeTimestampToDescription(builder, site, calendar.getTimeInMillis()); Assert.assertThat(builder.toString(), equalTo("1/1/16 12:00 AM")); } /** * Test formatting of scm entry change description coverage primary wiki * style appendRevisionToDescription and appendAffectedFilesToDescription * */ @Test public void tesDescriptionWithAffectedFiles() { rule.getInstance(); Updater updater = new Updater(null); Calendar calendar = Calendar.getInstance(); calendar.set(2016, 0, 1, 0, 0, 0); JiraSite site = mock(JiraSite.class); when(site.isAppendChangeTimestamp()).thenReturn(false); Run r = mock(Run.class); Job j = mock(Job.class); when(r.getParent()).thenReturn(j); JiraProjectProperty jiraProjectProperty = mock(JiraProjectProperty.class); when(j.getProperty(JiraProjectProperty.class)).thenReturn(jiraProjectProperty); when(jiraProjectProperty.getSite()).thenReturn(site); ChangeLogSet.Entry entry = mock(ChangeLogSet.Entry.class); when(entry.getTimestamp()).thenReturn(calendar.getTimeInMillis()); when(entry.getCommitId()).thenReturn("dsgsvds2re3dsv"); User mockAuthor = mock(User.class); when(mockAuthor.getId()).thenReturn("jenkins-user"); when(entry.getAuthor()).thenReturn(mockAuthor); Collection<MockAffectedFile> affectedFiles = Lists.newArrayList(); MockAffectedFile affectedFile1 = mock(MockAffectedFile.class); when(affectedFile1.getEditType()).thenReturn(EditType.ADD); when(affectedFile1.getPath()).thenReturn("hudson/plugins/jira/File1"); affectedFiles.add(affectedFile1); MockAffectedFile corruptedFile = mock(MockAffectedFile.class); when(corruptedFile.getEditType()).thenReturn(null); when(corruptedFile.getPath()).thenReturn(null); affectedFiles.add(corruptedFile); MockAffectedFile affectedFile2 = mock(MockAffectedFile.class); when(affectedFile2.getEditType()).thenReturn(EditType.DELETE); when(affectedFile2.getPath()).thenReturn("hudson/plugins/jira/File2"); affectedFiles.add(affectedFile2); MockAffectedFile affectedFile3 = mock(MockAffectedFile.class); when(affectedFile3.getEditType()).thenReturn(EditType.EDIT); when(affectedFile3.getPath()).thenReturn("hudson/plugins/jira/File3"); affectedFiles.add(affectedFile3); doReturn(affectedFiles).when(entry).getAffectedFiles(); String description = updater.createScmChangeEntryDescription(r, entry, true, true); System.out.println(description); Assert.assertThat(description, equalTo(" (jenkins-user: rev dsgsvds2re3dsv)\n" + "* (add) hudson/plugins/jira/File1\n" + "* \n" + "* (delete) hudson/plugins/jira/File2\n" + "* (edit) hudson/plugins/jira/File3\n")); } }