/*************************GO-LICENSE-START*********************************
* Copyright 2014 ThoughtWorks, Inc.
*
* 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.
*************************GO-LICENSE-END***********************************/
package com.thoughtworks.go.server.controller;
import com.thoughtworks.go.config.GoConfigDao;
import com.thoughtworks.go.domain.Pipeline;
import com.thoughtworks.go.domain.Stage;
import com.thoughtworks.go.server.dao.DatabaseAccessHelper;
import com.thoughtworks.go.server.dao.PipelineDao;
import com.thoughtworks.go.server.functional.helpers.CSVResponse;
import com.thoughtworks.go.server.service.PipelineService;
import com.thoughtworks.go.server.service.PropertiesService;
import com.thoughtworks.go.server.service.RestfulService;
import com.thoughtworks.go.util.GoConfigFileHelper;
import com.thoughtworks.go.util.SystemEnvironment;
import org.hamcrest.core.Is;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.servlet.ModelAndView;
import javax.sql.DataSource;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.Map;
import static com.thoughtworks.go.server.controller.RestfulActionTestHelper.assertContentStatusWithTextPlain;
import static javax.servlet.http.HttpServletResponse.*;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertThat;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"classpath:WEB-INF/spring-tabs-servlet.xml",
"classpath:WEB-INF/applicationContext-global.xml",
"classpath:WEB-INF/applicationContext-dataLocalAccess.xml",
"classpath:WEB-INF/applicationContext-acegi-security.xml"
})
public class RestfulPropertiesControllerTest {
@Autowired private DataSource dataSource;
@Autowired private PipelineDao pipelineDao;
@Autowired private PropertiesService propertiesService;
@Autowired private RestfulService restfulService;
@Autowired private GoConfigDao goConfigDao;
@Autowired private PipelineService pipelineService;
@Autowired private SystemEnvironment systemEnvironment;
private MockHttpServletResponse response;
private MockHttpServletRequest request;
@Autowired private DatabaseAccessHelper dbHelper;
private Pipeline oldPipeline;
private Pipeline newPipeline;
private PropertiesController propertiesController;
private Stage oldStage;
private Stage newStage;
private static GoConfigFileHelper configHelper = new GoConfigFileHelper();
@Before public void setup() throws Exception {
configHelper.onSetUp();
configHelper.usingCruiseConfigDao(goConfigDao);
response = new MockHttpServletResponse();
request = new MockHttpServletRequest();
dbHelper.onSetUp();
oldPipeline = dbHelper.saveTestPipeline("pipeline", "stage", "build");
oldStage = oldPipeline.getStages().byName("stage");
newPipeline = dbHelper.saveTestPipeline("pipeline", "stage", "build");
newStage = newPipeline.getStages().byName("stage");
configHelper.addPipeline("pipeline", "stage", "build");
propertiesController = new PropertiesController(propertiesService, restfulService, pipelineService, systemEnvironment);
request.addHeader("Confirm", "True");
}
@After public void teardown() throws Exception {
dbHelper.onTearDown();
configHelper.onTearDown();
}
@Test public void shouldGetPropertyRestfully() throws Exception {
setProperty("foo", "bar");
ModelAndView modelAndView = getProperty("foo", "json");
Map map = modelAndView.getModel();
String content = map.get("json").toString();
assertThat(content, containsString("bar"));
assertThat(response.getStatus(), is(SC_OK));
}
@Test public void shouldReturn404WhenPropertyNotSet() throws Exception {
getProperty("foo", "json");
assertValidJsonContentAndStatus(SC_NOT_FOUND, "Property 'foo' not found.");
}
@Test public void shouldReturn404WhenUnknownBuildOnGettingProperty() throws Exception {
String counter = String.valueOf(newStage.getCounter());
propertiesController.jobSearch("unknown", "latest", "stage", counter,
"build", "json", "foo", response);
assertValidJsonContentAndStatus(SC_NOT_FOUND, "Job unknown/latest/stage/" + counter + "/build not found.");
}
@Test public void shouldReturnCreatedWhenCreatingNewProperty() throws Exception {
setProperty("a", "b");
assertValidJsonContentAndStatus(SC_CREATED, "Property 'a' created with value 'b'");
ModelAndView modelAndView = getProperty("a", "json");
Map map = modelAndView.getModel();
String content = map.get("json").toString();
assertThat(content, containsString("b"));
assertThat(response.getStatus(), is(SC_OK));
}
@Test public void shouldNotCreatePropertyTwice() throws Exception {
setProperty("a", "b");
assertValidJsonContentAndStatus(SC_CREATED, "Property 'a' created with value 'b'");
setProperty("a", "c");
assertValidJsonContentAndStatus(SC_CONFLICT, "Property 'a' is already set.");
}
@Test public void shouldNotAllowCreatingPropertyWithKeyOrValueLargerThat255Characters() throws Exception {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 200; i++) {
sb.append("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
}
String manyChars = sb.toString();
setProperty(manyChars, "a");
assertValidJsonContentAndStatus(SC_FORBIDDEN, "Unable to set property with key larger than 255 characters.");
setProperty("a", manyChars);
assertValidJsonContentAndStatus(SC_FORBIDDEN, "Unable to set property with value larger than 255 characters.");
}
@Test public void shouldAllowCreatingAPropertyContainingAURI() throws Exception {
URI uri = new URI("https", "user:password", "10.18.32.41", 986, "/foo/bar/baz", "a=b&c=d", "some_thing");
setProperty("uri1", uri.toString());
assertValidJsonContentAndStatus(SC_CREATED,
"Property 'uri1' created with value 'https://user:password@10.18.32.41:986/foo/bar/baz?a=b&c=d#some_thing'");
setProperty("uri2", uri.toString());
assertValidJsonContentAndStatus(SC_CREATED,
"Property 'uri2' created with value 'https://user:password@10.18.32.41:986/foo/bar/baz?a=b&c=d#some_thing'");
}
@Test public void shouldNotAllowCreatingPropertyWithKeyWithInvalidChars() throws Exception {
String valid = "4aZ_-./";
String invalid = "*aZ_-./,";
setProperty(invalid, valid);
assertValidJsonContentAndStatus(SC_FORBIDDEN, PropertiesController.INVALID_VALUE);
}
@Test public void shouldReturn404WhenUnknownBuildOnSettingProperty() throws Exception {
propertiesController.setProperty("unknown", "latest", "stage", "1", "build", "foo", "bar", response, request);
assertValidJsonContentAndStatus(SC_NOT_FOUND, "Job unknown/latest/stage/1/build not found.");
}
@Test public void shouldReturnOkListingAllPropertiesHistoryInCsvFormatBySearch() throws Exception {
setProperty(oldPipeline, "a/2", "200");
setProperty(oldPipeline, "a/1", "100");
setProperty(newPipeline, "a/2", "400");
setProperty(newPipeline, "a/1", "300");
getAllPropertyHistoryListAsCsvBySearch();
CSVResponse csvResponse = new CSVResponse(response);
assertThat(csvResponse.isCSV(), is(true));
assertThat(csvResponse.statusEquals(SC_OK), is(true));
assertThat(csvResponse.containsRow("a/1", "a/2"), is(true));
assertThat(csvResponse.containsColumn("a/1", "100", "300"), is(true));
assertThat(csvResponse.containsColumn("a/2", "200", "400"), is(true));
}
@Test public void shouldSupportLimitingHistoryBySearch() throws Exception {
setProperty(oldPipeline, "a", "100");
setProperty(newPipeline, "a", "300");
getAllPropertyHistoryListAsCsvBySearch(null, 1);
CSVResponse csvResponse = new CSVResponse(response);
assertThat(csvResponse.isCSV(), is(true));
assertThat(csvResponse.statusEquals(SC_OK), is(true));
assertThat(csvResponse.containsColumn("a", "300"), is(true));
}
@Test public void shouldSupportLimitingHistoryBasedOnPipelineLabelBySearch() throws Exception {
setProperty(oldPipeline, "a", "100");
setProperty(newPipeline, "a", "300");
getAllPropertyHistoryListAsCsvBySearch(oldPipeline, 1);
CSVResponse csvResponse = new CSVResponse(response);
assertThat(csvResponse.statusEquals(SC_OK), is(true));
assertThat(csvResponse.containsColumn("a", "100"), is(true));
}
@Test public void shouldReturnOkListingAllPropertiesInCsvFormatBySearch() throws Exception {
setProperty(oldPipeline, "a/2", "200");
setProperty(oldPipeline, "a/1", "100");
getPropertyHistoryListBySearch(oldStage.getCounter(), oldPipeline.getLabel(), "csv", null);
CSVResponse csvResponse = new CSVResponse(response);
assertThat(csvResponse.isCSV(), is(true));
assertThat(csvResponse.statusEquals(SC_OK), is(true));
assertThat(csvResponse.containsRow("a/1", "a/2"), is(true));
assertThat(csvResponse.containsColumn("a/1", "100"), is(true));
assertThat(csvResponse.containsColumn("a/2", "200"), is(true));
}
@Test public void shouldReturnOkListingAllPropertiesInCsvFormatAsDefaultBySearch() throws Exception {
setProperty(oldPipeline, "a/2", "200");
setProperty(oldPipeline, "a/1", "100");
getPropertyHistoryListBySearch(oldStage.getCounter(), oldPipeline.getLabel(), null, null);
CSVResponse csvResponse = new CSVResponse(response);
assertThat(csvResponse.isCSV(), is(true));
assertThat(csvResponse.statusEquals(SC_OK), is(true));
assertThat(csvResponse.containsRow("a/1", "a/2"), is(true));
assertThat(csvResponse.containsColumn("a/1", "100"), is(true));
assertThat(csvResponse.containsColumn("a/2", "200"), is(true));
}
@Test public void shouldReturnOkSpecificPropertyInCsvFormatBySearch() throws Exception {
setProperty(oldPipeline, "a/2", "200");
setProperty(oldPipeline, "a/1", "100");
getPropertyHistoryListBySearch(oldStage.getCounter(), oldPipeline.getLabel(), null, "a/2");
CSVResponse csvResponse = new CSVResponse(response);
assertThat(csvResponse.isCSV(), is(true));
assertThat(csvResponse.statusEquals(SC_OK), is(true));
assertThat(csvResponse.containsRow("a/2"), is(true));
assertThat(csvResponse.containsColumn("a/2", "200"), is(true));
assertThat(csvResponse.containsColumn("a/1", "100"), is(false));
}
@Test public void shouldReturnOkSpecificPropertyInJSONFormatBySearch() throws Exception {
setProperty(oldPipeline, "a/2", "200");
setProperty(oldPipeline, "a/1", "100");
response = new MockHttpServletResponse();
ModelAndView modelAndView = propertiesController.jobSearch("pipeline", oldPipeline.getLabel(), "stage",
String.valueOf(oldStage.getCounter()), "build",
"json", "a/2", response);
Map map = modelAndView.getModel();
String content = map.get("json").toString();
assertThat(content, containsString("a/2"));
assertThat(content, containsString("200"));
assertThat(content, not(containsString("a/1")));
assertThat(content, not(containsString("100")));
}
@Test public void shouldReturnOkListingAllPropertiesInJsonFormatBySearch() throws Exception {
setProperty(oldPipeline, "a/2", "200");
setProperty(oldPipeline, "a/1", "100");
response = new MockHttpServletResponse();
ModelAndView modelAndView = propertiesController.jobSearch("pipeline", oldPipeline.getLabel(), "stage",
String.valueOf(oldStage.getCounter()), "build",
"json", null, response);
Map map = modelAndView.getModel();
String content = map.get("json").toString();
assertThat(content, containsString("a/2"));
assertThat(content, containsString("a/1"));
assertThat(content, containsString("200"));
assertThat(content, containsString("100"));
}
@Test public void shouldReturn404WhenUnknownBuildOnList() throws Exception {
setProperty(oldPipeline, "a/2", "200");
setProperty(oldPipeline, "a/1", "100");
response = new MockHttpServletResponse();
propertiesController.jobSearch("unknown", oldPipeline.getLabel(), "stage",
String.valueOf(oldStage.getCounter()), "build",
"json", null, response);
assertThat(response.getStatus(), Is.is(SC_NOT_FOUND));
}
private void getAllPropertyHistoryListAsCsvBySearch() throws Exception {
getAllPropertyHistoryListAsCsvBySearch(null, null);
}
private ModelAndView getPropertyHistoryListBySearch(Integer counter, String label, String type, String propertyKey)
throws Exception {
response = new MockHttpServletResponse();
return propertiesController.jobSearch("pipeline", label, "stage", String.valueOf(counter), "build", type,
propertyKey, response);
}
private void getAllPropertyHistoryListAsCsvBySearch(Pipeline startFrom, Integer count) throws Exception {
response = new MockHttpServletResponse();
String limitLabel = startFrom == null ? null : startFrom.getLabel();
propertiesController.jobsSearch("pipeline", "stage", "build", limitLabel, count, response);
}
private ModelAndView getProperty(String property, String type) throws Exception {
response = new MockHttpServletResponse();
return propertiesController.jobSearch("pipeline", "latest", "stage", String.valueOf(newStage.getCounter()),
"build", type, property, response);
}
private void setProperty(String property, String value) throws Exception {
response = new MockHttpServletResponse();
propertiesController.setProperty("pipeline", "latest", "stage", null, "build", property, value, response, request);
}
private void setProperty(Pipeline pipeline, String property, String value) throws Exception {
response = new MockHttpServletResponse();
propertiesController.setProperty("pipeline", pipeline.getLabel(), "stage", null, "build",
property, value, response, request);
assertThat(response.getContentAsString(), response.getStatus(), is(SC_CREATED));
}
private void assertValidJsonContentAndStatus(int status, String content) throws UnsupportedEncodingException {
assertContentStatusWithTextPlain(response, status, content);
}
}