/*
* The MIT License
*
* Copyright 2012 Sony Mobile Communications Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.sonyericsson.jenkins.plugins.bfa.model;
import com.sonyericsson.jenkins.plugins.bfa.CauseManagement;
import com.sonyericsson.jenkins.plugins.bfa.PluginImpl;
import com.sonyericsson.jenkins.plugins.bfa.db.KnowledgeBase;
import com.sonyericsson.jenkins.plugins.bfa.db.LocalFileKnowledgeBase;
import com.sonyericsson.jenkins.plugins.bfa.model.indication.BuildLogIndication;
import com.sonyericsson.jenkins.plugins.bfa.model.indication.Indication;
import hudson.model.AutoCompletionCandidates;
import hudson.model.Failure;
import hudson.util.FormValidation;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.mockito.Matchers;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import javax.servlet.ServletException;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.hasItems;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
/**
* Tests for {@link FailureCause}.
*
* @author Robert Sandell <robert.sandell@sonyericsson.com>
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({Jenkins.class, PluginImpl.class })
public class FailureCauseTest {
private PluginImpl pluginMock;
private KnowledgeBase baseMock;
/**
* Runs before every test.
* Mocks {@link com.sonyericsson.jenkins.plugins.bfa.PluginImpl#getInstance()} to avoid NPE's
* when checking permissions in the code under test.
*/
@Before
public void setUp() {
pluginMock = PowerMockito.mock(PluginImpl.class);
mockStatic(PluginImpl.class);
when(PluginImpl.getInstance()).thenReturn(pluginMock);
}
/**
* Puts a simple plain mock to be returned from
* {@link com.sonyericsson.jenkins.plugins.bfa.PluginImpl#getKnowledgeBase()}.
* All tests doesn't need this or needs a more complicated mock.
*/
private void mockEmptyKnowledgeBase() {
baseMock = mock(KnowledgeBase.class);
when(pluginMock.getKnowledgeBase()).thenReturn(baseMock);
}
/**
* Tests that the auto completion can find the correct canditates.
* @throws Exception if so.
*/
@Test
public void testAutoCompletionHappy() throws Exception {
mockEmptyKnowledgeBase();
List<String> categories = new LinkedList<String>();
String compFail = "compilationFailure";
String compCrashed = "computerCrashed";
String otherError = "otherError";
categories.add(compFail);
categories.add(compCrashed);
categories.add(otherError);
when(baseMock.getCategories()).thenReturn(categories);
FailureCause.FailureCauseDescriptor descriptor = new FailureCause.FailureCauseDescriptor();
AutoCompletionCandidates candidates = descriptor.doAutoCompleteCategories("comp");
List<String> values = candidates.getValues();
assertEquals("Two autocompletion candidates should have been found", 2, values.size());
assertThat(values, hasItems(compFail, compCrashed));
}
/**
* Test for {@link FailureCause#validate(String, String, java.util.List)}.
* With missing name.
*
* @throws Exception if so.
*/
@Test
public void testValidateBadName() throws Exception {
FailureCause cause = new FailureCause();
FormValidation validate = cause.validate("", "description", Collections.EMPTY_LIST);
assertSame(FormValidation.Kind.ERROR, validate.kind);
}
/**
* Test for {@link FailureCause#validate(String, String, java.util.List)}.
* With missing description.
*
* @throws Exception if so.
*/
@Test
public void testValidateBadDescription() throws Exception {
mockEmptyKnowledgeBase();
FailureCause cause = new FailureCause();
FormValidation validate = cause.validate("Some Name", "", Collections.EMPTY_LIST);
assertSame(FormValidation.Kind.ERROR, validate.kind);
}
/**
* Test for {@link FailureCause#validate(String, String, java.util.List)}.
* With missing indications.
*
* @throws Exception if so.
*/
@Test
public void testValidateNoIndications() throws Exception {
mockEmptyKnowledgeBase();
FailureCause cause = new FailureCause();
FormValidation validate = cause.validate("Some Name", "The Description", Collections.EMPTY_LIST);
assertSame(FormValidation.Kind.ERROR, validate.kind);
}
/**
* Test for {@link FailureCause#validate(String, String, java.util.List)}.
* With an indication that won't validate but the rest is ok.
*
* @throws Exception if so.
*/
@Test
public void testValidateBadIndication() throws Exception {
mockEmptyKnowledgeBase();
FailureCause cause = new FailureCause();
Indication indication = new BuildLogIndication("some(thing");
FormValidation validate =
cause.validate("Some Name", "The Description", Collections.singletonList(indication));
assertSame(FormValidation.Kind.ERROR, validate.kind);
}
/**
* Happy test for {@link FailureCause#validate(String, String, java.util.List)}.
*
* @throws Exception if so.
*/
@Test
public void testValidate() throws Exception {
mockEmptyKnowledgeBase();
FailureCause cause = new FailureCause();
Indication indication = new BuildLogIndication(".*");
FormValidation validate =
cause.validate("Some Name", "The Description", Collections.singletonList(indication));
assertSame(FormValidation.Kind.OK, validate.kind);
}
/**
* Test for {@link FailureCause#doCheckDescription(String)} with an empty description.
*
* @throws Exception if so.
*/
@Test
public void testDoCheckDescriptionEmpty() throws Exception {
FailureCause cause = new FailureCause();
FormValidation validation = cause.doCheckDescription("");
assertSame(FormValidation.Kind.ERROR, validation.kind);
}
/**
* Test for {@link FailureCause#doCheckDescription(String)}. With the reserved "new" description ({@link
* CauseManagement#NEW_CAUSE_DESCRIPTION}).
*
* @throws Exception if so.
*/
@Test
public void testDoCheckDescriptionReserved() throws Exception {
FailureCause cause = new FailureCause();
FormValidation validation = cause.doCheckDescription(CauseManagement.NEW_CAUSE_DESCRIPTION);
assertSame(FormValidation.Kind.ERROR, validation.kind);
}
/**
* Happy test for {@link FailureCause#doCheckDescription(String)}.
*
* @throws Exception if so.
*/
@Test
public void testDoCheckDescription() throws Exception {
FailureCause cause = new FailureCause();
FormValidation validation = cause.doCheckDescription("My <b>Description</b>");
assertSame(FormValidation.Kind.OK, validation.kind);
}
/**
* Test for {@link FailureCause#doCheckName(String)} with an empty name.
*
* @throws Exception if so.
*/
@Test
public void testDoCheckNameEmpty() throws Exception {
FailureCause cause = new FailureCause();
FormValidation validation = cause.doCheckName("");
assertSame(FormValidation.Kind.ERROR, validation.kind);
}
/**
* Test for {@link FailureCause#doCheckName(String)} with the reserved "new" name ({@link
* CauseManagement#NEW_CAUSE_NAME}).
*
* @throws Exception if so.
*/
@Test
public void testDoCheckNameReserved() throws Exception {
FailureCause cause = new FailureCause();
FormValidation validation = cause.doCheckName(CauseManagement.NEW_CAUSE_NAME);
assertSame(FormValidation.Kind.ERROR, validation.kind);
}
/**
* Test for {@link FailureCause#doCheckName(String)} with bad characters in the name.
*
* @throws Exception if so.
*/
@Test
public void testDoCheckNameNoneGood() throws Exception {
FailureCause cause = new FailureCause();
FormValidation validation = cause.doCheckName("[Name]");
assertSame(FormValidation.Kind.ERROR, validation.kind);
}
/**
* Test for {@link FailureCause#doCheckName(String)} when there already exists a cause with the same name.
*
* @throws Exception if so.
*/
@Test
public void testDoCheckNameExisting() throws Exception {
List<FailureCause> initial = new LinkedList<FailureCause>();
FailureCause other =
new FailureCause("abc", "AName", "description", "comment", null, "", Collections.EMPTY_LIST, null);
initial.add(other);
other = new FailureCause("cde", "BName", "description", "comment", null, "", Collections.EMPTY_LIST, null);
initial.add(other);
LocalFileKnowledgeBase base = new LocalFileKnowledgeBase(initial);
when(pluginMock.getKnowledgeBase()).thenReturn(base);
FailureCause cause = new FailureCause();
FormValidation validation = cause.doCheckName("BName");
assertSame(FormValidation.Kind.ERROR, validation.kind);
}
/**
* Happy test for {@link FailureCause#doCheckName(String)}.
*
* @throws Exception if so.
*/
@Test
public void testDoCheckName() throws Exception {
LocalFileKnowledgeBase base = new LocalFileKnowledgeBase();
when(pluginMock.getKnowledgeBase()).thenReturn(base);
FailureCause cause = new FailureCause();
FormValidation validation = cause.doCheckName("Some name");
assertSame(FormValidation.Kind.OK, validation.kind);
}
/**
* Happy test for {@link FailureCause#doConfigSubmit(org.kohsuke.stapler.StaplerRequest,
* org.kohsuke.stapler.StaplerResponse)}. Where the intention is to save a new cause.
*
* @throws Exception if so.
*/
@Test
public void testDoConfigSubmitNewOk() throws Exception {
Jenkins jenkinsMock = mock(Jenkins.class);
mockStatic(Jenkins.class);
when(Jenkins.getInstance()).thenReturn(jenkinsMock);
mockEmptyKnowledgeBase();
String name = "AName";
String description = "The Description";
String comment = "Comment";
String category = "category";
String pattern = ".*";
StaplerRequest request = mockRequest("", name, description, comment, null, category,
Collections.singletonList(new BuildLogIndication(pattern)), null);
StaplerResponse response = mock(StaplerResponse.class);
FailureCause cause = new FailureCause(null, CauseManagement.NEW_CAUSE_NAME,
CauseManagement.NEW_CAUSE_DESCRIPTION, null, null, category, null, null);
cause.doConfigSubmit(request, response);
verify(baseMock).addCause(same(cause));
assertEquals(name, cause.getName());
assertEquals(description, cause.getDescription());
assertEquals(category, cause.getCategoriesAsString());
assertEquals(1, cause.getIndications().size());
assertEquals(pattern, cause.getIndications().get(0).getPattern().pattern());
verify(response).sendRedirect2(any(String.class));
}
/**
* Happy test for {@link FailureCause#doConfigSubmit(org.kohsuke.stapler.StaplerRequest,
* org.kohsuke.stapler.StaplerResponse)}. Where the intention is to save changes to an existing cause.
*
* @throws Exception if so.
*/
@Test
public void testDoConfigSubmitSaveOk() throws Exception {
Jenkins jenkinsMock = mock(Jenkins.class);
mockStatic(Jenkins.class);
when(Jenkins.getInstance()).thenReturn(jenkinsMock);
mockEmptyKnowledgeBase();
String id = "abc";
String name = "AName";
String description = "The Description";
String comment = "New Comment";
String pattern = ".*";
StaplerRequest request = mockRequest(id, name, description, comment, null, "",
Collections.singletonList(new BuildLogIndication(pattern)), null);
StaplerResponse response = mock(StaplerResponse.class);
FailureCause cause = new FailureCause("Old Name", "Old Description");
cause.setId(id);
cause.doConfigSubmit(request, response);
verify(baseMock).saveCause(same(cause));
assertEquals(id, cause.getId());
assertEquals(name, cause.getName());
assertEquals(description, cause.getDescription());
assertEquals(1, cause.getIndications().size());
assertEquals(pattern, cause.getIndications().get(0).getPattern().pattern());
verify(response).sendRedirect2(any(String.class));
}
/**
* Tests {@link FailureCause#doConfigSubmit(org.kohsuke.stapler.StaplerRequest,
* org.kohsuke.stapler.StaplerResponse)} with a different id in the form than what the stapler binding says it is.
*
* @throws Exception if so
*/
@Test(expected = Failure.class)
public void testDoConfigSubmitSaveWrongId() throws Exception {
Jenkins jenkinsMock = mock(Jenkins.class);
mockStatic(Jenkins.class);
when(Jenkins.getInstance()).thenReturn(jenkinsMock);
mockEmptyKnowledgeBase();
String origId = "abc";
String newId = "cde";
String name = "AName";
String description = "The Description";
String comment = "New Comment";
String pattern = ".*";
StaplerRequest request = mockRequest(newId, name, description, comment, null, "",
Collections.singletonList(new BuildLogIndication(pattern)), null);
StaplerResponse response = mock(StaplerResponse.class);
FailureCause cause = new FailureCause("Old Name", "Old Description");
cause.setId(origId);
cause.doConfigSubmit(request, response);
}
/**
* Tests {@link FailureCause#doConfigSubmit(org.kohsuke.stapler.StaplerRequest,
* org.kohsuke.stapler.StaplerResponse)} where the form data for id is set when it should not be since it is for a
* new cause.
*
* @throws Exception if so
*/
@Test(expected = Failure.class)
public void testDoConfigSubmitNewWithId() throws Exception {
Jenkins jenkinsMock = mock(Jenkins.class);
mockStatic(Jenkins.class);
when(Jenkins.getInstance()).thenReturn(jenkinsMock);
mockEmptyKnowledgeBase();
String origId = "";
String newId = "cde";
String name = "AName";
String description = "The Description";
String comment = "New Comment";
String pattern = ".*";
StaplerRequest request = mockRequest(newId, name, description, comment, null, "",
Collections.singletonList(new BuildLogIndication(pattern)), null);
StaplerResponse response = mock(StaplerResponse.class);
FailureCause cause = new FailureCause("Old Name", "Old Description");
cause.setId(origId);
cause.doConfigSubmit(request, response);
}
/**
* Tests {@link FailureCause#doConfigSubmit(org.kohsuke.stapler.StaplerRequest,
* org.kohsuke.stapler.StaplerResponse)} where the form data for id is null when it should be for an existing
* cause.
*
* @throws Exception if so
*/
@Test(expected = Failure.class)
public void testDoConfigSubmitNewCloneAttempt() throws Exception {
Jenkins jenkinsMock = mock(Jenkins.class);
mockStatic(Jenkins.class);
when(Jenkins.getInstance()).thenReturn(jenkinsMock);
mockEmptyKnowledgeBase();
String origId = "abc";
String newId = "";
String name = "New Name";
String description = "New Description";
String comment = "New Comment";
String pattern = ".*";
StaplerRequest request = mockRequest(newId, name, description, comment, null, "",
Collections.singletonList(new BuildLogIndication(pattern)), null);
StaplerResponse response = mock(StaplerResponse.class);
FailureCause cause = new FailureCause("A Name", "The Description");
cause.setId(origId);
cause.doConfigSubmit(request, response);
}
/**
* Mocks a request to contain the form data for a {@code FailureCause} with the provided content.
*
* @param id the id of the cause
* @param name the name
* @param description the description
* @param comment the comment
* @param occurred the time of last occurrence
* @param category the category
* @param indications the list of indications as they should be returned from
* {@link StaplerRequest#bindJSONToList(Class, Object)}
* @param modifications the modification history of this FailureCause
* @return a mocked request object.
*
* @throws ServletException if so, but probably not.
*/
private StaplerRequest mockRequest(String id, String name, String description, String comment, Date occurred,
String category, List<? extends Indication> indications,
List<FailureCauseModification> modifications) throws ServletException {
JSONObject form = new JSONObject();
form.put("id", id);
form.put("name", name);
form.put("description", description);
form.put("comment", comment);
if (occurred != null) {
form.put("occurred", occurred);
}
form.put("categories", category);
if (indications != null) {
form.put("indications", "");
}
if (modifications != null) {
form.put("modifications", modifications);
}
StaplerRequest request = mock(StaplerRequest.class);
when(request.getSubmittedForm()).thenReturn(form);
when(request.bindJSONToList(same(Indication.class), Matchers.<Object>anyObject()))
.thenReturn((List<Indication>)indications);
return request;
}
}