package org.jvnet.hudson.plugins.jira.issueversioning.plugin.hudson.rest;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.IOException;
import java.io.PrintStream;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import com.atlassian.plugins.rest.common.security.AnonymousAllowed;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Result;
import hudson.model.Run.Artifact;
import hudson.plugins.jira.JiraBuildAction;
import hudson.plugins.jira.JiraCarryOverAction;
import hudson.plugins.jira.JiraIssue;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.test.EmbeddedContainer;
import org.jboss.resteasy.test.TestPortProvider;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.jvnet.hudson.plugins.jira.issueversioning.domain.api.model.rest.Build;
import org.jvnet.hudson.plugins.jira.issueversioning.domain.api.model.rest.Project;
import org.jvnet.hudson.plugins.jira.issueversioning.plugin.hudson.JiraProjectKeyJobProperty;
import org.mockito.ArgumentCaptor;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Unit test for {@link IssuesToJiraPoster}
*
* @author Stig Kleppe-Jorgensen, 2010.01.05
*/
public class IssuesToJiraPosterTest {
private static final Result[] results =
new Result[]{Result.UNSTABLE, Result.FAILURE, Result.NOT_BUILT, Result.ABORTED};
private static final ArgumentCaptor<Project> argument = ArgumentCaptor.forClass(Project.class);
private final PrintStream printStream = mockPrintStream();
@Test
public void should_marshall() throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(new Class<?>[]{Project.class, Build.class});
final Marshaller marshaller = jc.createMarshaller();
Project p = createProject();
final StringWriter writer = new StringWriter();
marshaller.marshal(p, writer);
System.out.println(writer.toString());
}
private Project createProject() {
final Project project = new Project("name", "1.2", new Build(2, Sets.newHashSet("DEV-123", "DEV-234")));
project.addFailedBuild(new Build(3, Sets.newHashSet("DEV-345", "DEV-456")));
return project;
}
@Test
public void should_post_whole_build_issue_structure_to_given_url() throws MalformedURLException {
postToUrlForBuild("", mockedBuildWithPreviousBuilds());
final Project project = argument.getValue();
assertThat(project.getName(), is("project-name"));
assertThat(project.getVersionForOkBuild(), is("1.2.5"));
assertThat(project.getOkBuild(), is(notNullValue()));
assertThat(project.getFailedBuilds().size(), is(3));
assertThat(project.getAllIssues().size(), is(2));
assertThat(project.getOkBuild().getNumber(), is(5));
assertThat(project.getOkBuild().getIssues().size(), is(2));
assertThat(Iterables.get(project.getFailedBuilds(), 0).getNumber(), is(2));
assertThat(Iterables.get(project.getFailedBuilds(), 0).getIssues().size(), is(2));
assertThat(Iterables.get(project.getFailedBuilds(), 1).getNumber(), is(4));
assertThat(Iterables.get(project.getFailedBuilds(), 1).getIssues().size(), is(2));
assertThat(Iterables.get(project.getFailedBuilds(), 2).getNumber(), is(3));
assertThat(Iterables.get(project.getFailedBuilds(), 2).getIssues().size(), is(2));
verify(printStream).println(
"Posted the following 8 issues to Jira that is added to version 1.2.5 if status is fixed:");
verify(printStream).println(Sets.newHashSet("DEV-1234", "DEV-4312"));
}
@Test
public void should_return_false_for_wrong_url() throws MalformedURLException {
boolean correctUrl = postToUrlForBuild("/should_return_404", mockedBuildWithPreviousBuilds());
assertThat(correctUrl, is(false));
verify(printStream).println("ERROR: posting to http://localhost:8081/should_return_404 failed. " +
"Make sure the Jira URL is setup correctly");
}
@Test
public void should_stop_searching_when_no_more_previous_builds() throws MalformedURLException {
final AbstractBuild build = mockedBuildWith(mockProject());
when(build.getPreviousBuild()).thenReturn(null);
postToUrlForBuild("", build);
final Project project = argument.getValue();
assertThat(project.getOkBuild(), is(notNullValue()));
assertThat(project.getFailedBuilds().size(), is(0));
}
private boolean postToUrlForBuild(final String path, final AbstractBuild build) throws MalformedURLException {
final String baseUrl = TestPortProvider.generateURL(path);
final IssuesToJiraPoster poster = new IssuesToJiraPoster(build, baseUrl, printStream);
try {
return poster.post();
} catch (JAXBException e) {
throw new IllegalStateException(e);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private PrintStream mockPrintStream() {
return mock(PrintStream.class);
}
private AbstractBuild mockedBuildWithPreviousBuilds() {
final AbstractProject project = mockProject();
final AbstractBuild build = mockedBuildWith(project);
mockPreviousBuild(build, project);
return build;
}
private AbstractBuild mockedBuildWith(AbstractProject project) {
final AbstractBuild build = mock(AbstractBuild.class);
when(build.getProject()).thenReturn(project);
when(build.getNumber()).thenReturn(5);
when(build.getAction(JiraBuildAction.class)).thenReturn(createJiraBuildAction());
final List<Artifact> mockedArtifacts = mockedArtifacts();
when(build.getArtifacts()).thenReturn(mockedArtifacts);
return build;
}
private JiraBuildAction createJiraBuildAction() {
return new JiraBuildAction(null, createIssues());
}
private Collection<JiraIssue> createIssues() {
final List<JiraIssue> issues = new ArrayList<JiraIssue>();
issues.add(new JiraIssue("DEV-1234", "test issue DEV-1234"));
issues.add(new JiraIssue("DEV-4312", "test issue DEV-4312"));
return issues;
}
private List<Artifact> mockedArtifacts() {
final Artifact artifact = mock(Artifact.class);
when(artifact.getFileName()).thenReturn("test-andre-1.2.5.jar");
return Collections.singletonList(artifact);
}
private void mockPreviousBuild(AbstractBuild build, AbstractProject project) {
AbstractBuild previousBuild = mockGetPrevious(build);
when(previousBuild.getProject()).thenReturn(project);
when(previousBuild.getNumber()).thenReturn(4);
when(previousBuild.getAction(JiraCarryOverAction.class)).thenReturn(createJiraCarryOverAction());
}
private JiraCarryOverAction createJiraCarryOverAction() {
return new JiraCarryOverAction(createIssueIds());
}
private Collection<String> createIssueIds() {
final List<String> issueIds = new ArrayList<String>();
issueIds.add("DEV-1234");
issueIds.add("DEV-5432");
return issueIds;
}
private AbstractBuild mockGetPrevious(AbstractBuild build) {
AbstractBuild previousBuild = mock(AbstractBuild.class);
when(build.getPreviousBuild()).thenReturn(previousBuild);
return previousBuild;
}
private AbstractProject mockProject() {
final AbstractProject project = mock(AbstractProject.class);
when(project.getName()).thenReturn("project-name");
final JiraProjectKeyJobProperty property = mockJiraProjectKeyJobProperty();
when(project.getProperty(JiraProjectKeyJobProperty.class)).thenReturn(property);
return project;
}
private JiraProjectKeyJobProperty mockJiraProjectKeyJobProperty() {
final JiraProjectKeyJobProperty property = mock(JiraProjectKeyJobProperty.class);
when(property.getJiraBaseUrl()).thenReturn("test-url");
return property;
}
@BeforeClass
public static void startupServer() throws Exception {
Dispatcher dispatcher = EmbeddedContainer.start().getDispatcher();
dispatcher.getRegistry().addSingletonResource(mockedVersionResource());
}
@AfterClass
public static void stopContainer() throws Exception {
EmbeddedContainer.stop();
}
private static VersionResource mockedVersionResource() {
final VersionResource versionResource = mock(VersionResource.class);
when(versionResource.associateWithIssues(argument.capture())).thenReturn(Response.ok().build());
return versionResource;
}
/**
* REST resource that associates the given issues for a release build in Hudson with a JIRA version. The version name
* is taken from the Maven version of the release.
*
* @author Stig Kleppe-Jorgensen, 2009.12.29
*/
@Path("/version")
public static interface VersionResource {
@POST
@Path("/associate")
@AnonymousAllowed
@Consumes(MediaType.APPLICATION_XML)
Response associateWithIssues(Project project);
}
}