/*
* Copyright 2017 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.
*/
package com.thoughtworks.go.server.service;
import com.thoughtworks.go.config.*;
import com.thoughtworks.go.config.exceptions.PipelineGroupNotFoundException;
import com.thoughtworks.go.config.registry.ConfigElementImplementationRegistry;
import com.thoughtworks.go.config.validation.GoConfigValidity;
import com.thoughtworks.go.domain.PipelineGroups;
import com.thoughtworks.go.helper.GoConfigMother;
import com.thoughtworks.go.i18n.LocalizedKeyValueMessage;
import com.thoughtworks.go.i18n.LocalizedMessage;
import com.thoughtworks.go.i18n.Localizer;
import com.thoughtworks.go.server.domain.Username;
import com.thoughtworks.go.server.service.responses.GoConfigOperationalResponse;
import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult;
import com.thoughtworks.go.util.ConfigElementImplementationRegistryMother;
import com.thoughtworks.go.util.ReflectionUtil;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.*;
public class PipelineConfigsServiceTest {
private PipelineConfigsService service;
private GoConfigService goConfigService;
private ConfigCache configCache;
private ConfigElementImplementationRegistry registry;
private SecurityService securityService;
private Username validUser;
private HttpLocalizedOperationResult result;
private GoConfigDao dao;
private CruiseConfig cruiseConfig;
@Before
public void setUp() {
goConfigService = mock(GoConfigService.class);
securityService = mock(SecurityService.class);
configCache = new ConfigCache();
registry = ConfigElementImplementationRegistryMother.withNoPlugins();
dao = mock(GoConfigDao.class);
validUser = new Username(new CaseInsensitiveString("validUser"));
service = new PipelineConfigsService(configCache, registry, goConfigService, securityService);
result = new HttpLocalizedOperationResult();
cruiseConfig = new BasicCruiseConfig();
ReflectionUtil.setField(cruiseConfig, "md5", "md5");
}
@Test
public void shouldReturnXmlForGivenGroup_onGetXml() {
CruiseConfig cruiseConfig = new BasicCruiseConfig();
String groupName = "group_name";
new GoConfigMother().addPipelineWithGroup(cruiseConfig, groupName, "pipeline_name", "stage_name", "job_name");
when(goConfigService.getConfigForEditing()).thenReturn(cruiseConfig);
when(securityService.isUserAdminOfGroup(validUser.getUsername(), groupName)).thenReturn(true);
String actualXml = service.getXml(groupName, validUser, result);
String expectedXml = "<pipelines group=\"group_name\">\n <pipeline name=\"pipeline_name\">\n <materials>\n <svn url=\"file:///tmp/foo\" />\n </materials>\n <stage name=\"stage_name\">\n <jobs>\n <job name=\"job_name\" />\n </jobs>\n </stage>\n </pipeline>\n</pipelines>";
assertThat(actualXml, is(expectedXml));
assertThat(result.isSuccessful(), is(true));
verify(goConfigService, times(1)).getConfigForEditing();
}
@Test
public void shouldThrowExceptionWhenTheGroupIsNotFound_onGetXml() {
String groupName = "non-existent-group_name";
Localizer localizer = mock(Localizer.class);
when(localizer.localize("PIPELINE_GROUP_NOT_FOUND", groupName)).thenReturn("Not found");
when(securityService.isUserAdminOfGroup(validUser.getUsername(), groupName)).thenThrow(new PipelineGroupNotFoundException());
service.getXml(groupName, validUser, result);
assertThat(result.httpCode(), is(404));
assertThat(result.isSuccessful(), is(false));
assertThat(result.message(localizer), is("Not found"));
verify(securityService, times(1)).isUserAdminOfGroup(validUser.getUsername(), groupName);
verify(goConfigService, never()).getConfigForEditing();
}
@Test
public void shouldReturnUnauthorizedResultWhenUserIsNotAuthorizedToViewGroup_onGetXml() {
String groupName = "some-secret-group";
Localizer localizer = mock(Localizer.class);
when(localizer.localize("UNAUTHORIZED_TO_EDIT_GROUP", groupName)).thenReturn("Unauthorized!");
Username invalidUser = new Username(new CaseInsensitiveString("invalidUser"));
when(securityService.isUserAdminOfGroup(invalidUser.getUsername(), groupName)).thenReturn(false);
String actual = service.getXml(groupName, invalidUser, result);
assertThat(actual, is(nullValue()));
assertThat(result.httpCode(), is(401));
assertThat(result.isSuccessful(), is(false));
assertThat(result.message(localizer), is("Unauthorized!"));
verify(goConfigService, never()).getConfigForEditing();
verify(securityService, times(1)).isUserAdminOfGroup(invalidUser.getUsername(), groupName);
}
@Test
public void shouldUpdateXmlAndReturnPipelineConfigsIfUserHasEditPermissionsForTheGroupAndUpdateWasSuccessful() throws Exception {
final String groupName = "group_name";
when(securityService.isUserAdminOfGroup(validUser.getUsername(), groupName)).thenReturn(true);
String md5 = "md5";
when(goConfigService.configFileMd5()).thenReturn(md5);
GoConfigService.XmlPartialSaver groupSaver = mock(GoConfigService.XmlPartialSaver.class);
when(goConfigService.groupSaver(groupName)).thenReturn(groupSaver);
String updatedPartial = groupXml();
when(groupSaver.saveXml(updatedPartial, md5)).thenReturn(GoConfigValidity.valid());
GoConfigOperationalResponse<PipelineConfigs> actual = service.updateXml(groupName, updatedPartial, md5, validUser, result);
PipelineConfigs configs = actual.getConfigElement();
GoConfigValidity validity = actual.getValidity();
assertThat(configs, is(not(nullValue())));
assertThat(configs.getGroup(), is("renamed_group_name"));
assertThat(result.httpCode(), is(200));
assertThat(result.isSuccessful(), is(true));
assertThat(validity.isValid(), is(true));
verify(groupSaver).saveXml(updatedPartial, md5);
}
@Test
public void shouldReturnUnsuccessfulResultWhenXmlIsInvalid_onUpdateXml() throws Exception {
String errorMessage = "Can not parse xml";
final String groupName = "group_name";
Localizer localizer = mock(Localizer.class);
when(localizer.localize("UPDATE_GROUP_XML_FAILED", groupName, errorMessage)).thenReturn("Invalid");
when(securityService.isUserAdminOfGroup(validUser.getUsername(), groupName)).thenReturn(true);
String md5 = "md5";
when(goConfigService.configFileMd5()).thenReturn(md5);
GoConfigService.XmlPartialSaver groupSaver = mock(GoConfigService.XmlPartialSaver.class);
when(goConfigService.groupSaver(groupName)).thenReturn(groupSaver);
String updatedPartial = "foobar";
when(groupSaver.saveXml(updatedPartial, md5)).thenReturn(GoConfigValidity.invalid(errorMessage));
GoConfigOperationalResponse<PipelineConfigs> actual = service.updateXml(groupName, updatedPartial, md5, validUser, result);
PipelineConfigs configs = actual.getConfigElement();
GoConfigValidity validity = actual.getValidity();
assertThat(configs, is(nullValue()));
assertThat(result.httpCode(), is(500));
assertThat(result.isSuccessful(), is(false));
assertThat(result.message(localizer), is("Invalid"));
assertThat(validity.isValid(), is(false));
verify(securityService, times(1)).isUserAdminOfGroup(validUser.getUsername(), groupName);
}
@Test
public void shouldReturnUnsuccessfulResultWhenTheGroupIsNotFound_onUpdateXml() throws Exception {
String groupName = "non-existent-group_name";
Localizer localizer = mock(Localizer.class);
when(localizer.localize("PIPELINE_GROUP_NOT_FOUND", groupName)).thenReturn("Not found");
when(securityService.isUserAdminOfGroup(validUser.getUsername(), groupName)).thenThrow(new PipelineGroupNotFoundException());
when(goConfigService.configFileMd5()).thenReturn("md5");
GoConfigOperationalResponse<PipelineConfigs> actual = service.updateXml(groupName, "", "md5", validUser, result);
PipelineConfigs configs = actual.getConfigElement();
GoConfigValidity validity = actual.getValidity();
assertThat(configs, is(nullValue()));
assertThat(result.httpCode(), is(404));
assertThat(result.isSuccessful(), is(false));
assertThat(result.message(localizer), is("Not found"));
assertThat(validity.isValid(), is(true));
verify(securityService, times(1)).isUserAdminOfGroup(validUser.getUsername(), groupName);
}
@Test
public void shouldReturnUnauthorizedResultWhenUserIsNotAuthorizedToViewGroup_onUpdateXml() throws Exception {
String groupName = "some-secret-group";
Localizer localizer = mock(Localizer.class);
when(localizer.localize("UNAUTHORIZED_TO_EDIT_GROUP", groupName)).thenReturn("Unauthorized!");
Username invalidUser = new Username(new CaseInsensitiveString("invalidUser"));
when(securityService.isUserAdminOfGroup(invalidUser.getUsername(), groupName)).thenReturn(false);
when(goConfigService.configFileMd5()).thenReturn("md5");
GoConfigOperationalResponse<PipelineConfigs> actual = service.updateXml(groupName, "", "md5", invalidUser, result);
PipelineConfigs configElement = actual.getConfigElement();
GoConfigValidity validity = actual.getValidity();
assertThat(configElement, is(nullValue()));
assertThat(result.httpCode(), is(401));
assertThat(result.isSuccessful(), is(false));
assertThat(result.message(localizer), is("Unauthorized!"));
assertThat(validity.isValid(), is(true));
verify(securityService, times(1)).isUserAdminOfGroup(invalidUser.getUsername(), groupName);
}
@Test
public void shouldSetSuccessMessageOnSuccessfulUpdate() throws Exception {
String groupName = "renamed_group_name";
String md5 = "md5";
GoConfigService.XmlPartialSaver groupSaver = mock(GoConfigService.XmlPartialSaver.class);
when(securityService.isUserAdminOfGroup(validUser.getUsername(), groupName)).thenReturn(true);
when(goConfigService.configFileMd5()).thenReturn(md5);
when(goConfigService.groupSaver(groupName)).thenReturn(groupSaver);
when(groupSaver.saveXml(groupXml(), md5)).thenReturn(GoConfigValidity.valid(ConfigSaveState.UPDATED));
GoConfigOperationalResponse<PipelineConfigs> actual = service.updateXml(groupName, groupXml(), md5, validUser, result);
GoConfigValidity validity = actual.getValidity();
assertThat(result.localizable(), is(LocalizedMessage.string("SAVED_CONFIGURATION_SUCCESSFULLY")));
assertThat(validity.isValid(), is(true));
}
@Test
public void shouldSetSuccessMessageOnSuccessfulMerge() throws Exception {
String groupName = "renamed_group_name";
GoConfigService.XmlPartialSaver groupSaver = mock(GoConfigService.XmlPartialSaver.class);
when(securityService.isUserAdminOfGroup(validUser.getUsername(), groupName)).thenReturn(true);
when(goConfigService.configFileMd5()).thenReturn("old-md5");
when(goConfigService.groupSaver(groupName)).thenReturn(groupSaver);
when(groupSaver.saveXml(groupXml(), "md5")).thenReturn(GoConfigValidity.valid(ConfigSaveState.MERGED));
GoConfigOperationalResponse<PipelineConfigs> actual = service.updateXml(groupName, groupXml(), "md5", validUser, result);
GoConfigValidity validity = actual.getValidity();
assertThat(result.localizable(), is(LocalizedMessage.composite(LocalizedMessage.string("SAVED_CONFIGURATION_SUCCESSFULLY"), LocalizedMessage.string("CONFIG_MERGED"))));
assertThat(validity.isValid(), is(true));
}
@Test
public void shouldThrowUpWithDifferentMessageForMergeExceptions() throws Exception {
String groupName = "renamed_group_name";
GoConfigService.XmlPartialSaver groupSaver = mock(GoConfigService.XmlPartialSaver.class);
when(securityService.isUserAdminOfGroup(validUser.getUsername(), groupName)).thenReturn(true);
when(goConfigService.configFileMd5()).thenReturn("old-md5");
when(goConfigService.groupSaver(groupName)).thenReturn(groupSaver);
when(groupSaver.saveXml(null, "md5")).thenReturn(GoConfigValidity.invalid("some error").mergeConflict());
GoConfigOperationalResponse<PipelineConfigs> actual = service.updateXml(groupName, null, "md5", validUser, result);
PipelineConfigs configElement = actual.getConfigElement();
GoConfigValidity validity = actual.getValidity();
assertThat(configElement, is(nullValue()));
assertThat(result.isSuccessful(), is(false));
assertThat(validity.isValid(), is(false));
assertThat(validity.isMergeConflict(), is(true));
LocalizedKeyValueMessage message = (LocalizedKeyValueMessage) ReflectionUtil.getField(result, "message");
String key = (String) ReflectionUtil.getField(message, "key");
assertThat(key, is("FLASH_MESSAGE_ON_CONFLICT"));
}
@Test
public void shouldThrowUpWithDifferentMessageForPostMergeValidationExceptions() throws Exception {
String groupName = "renamed_group_name";
GoConfigService.XmlPartialSaver groupSaver = mock(GoConfigService.XmlPartialSaver.class);
when(securityService.isUserAdminOfGroup(validUser.getUsername(), groupName)).thenReturn(true);
when(goConfigService.configFileMd5()).thenReturn("old-md5");
when(goConfigService.groupSaver(groupName)).thenReturn(groupSaver);
when(groupSaver.saveXml(null, "md5")).thenReturn(GoConfigValidity.invalid("some error").mergePostValidationError());
GoConfigOperationalResponse<PipelineConfigs> actual = service.updateXml(groupName, null, "md5", validUser, result);
PipelineConfigs configElement = actual.getConfigElement();
GoConfigValidity validity = actual.getValidity();
assertThat(configElement, is(nullValue()));
assertThat(result.isSuccessful(), is(false));
assertThat(validity.isValid(), is(false));
assertThat(validity.isPostValidationError(), is(true));
LocalizedKeyValueMessage message = (LocalizedKeyValueMessage) ReflectionUtil.getField(result, "message");
String key = (String) ReflectionUtil.getField(message, "key");
assertThat(key, is("FLASH_MESSAGE_ON_CONFLICT"));
}
@Test
public void shouldGetPipelineGroupsForUser() {
PipelineConfig pipelineInGroup1 = new PipelineConfig();
PipelineConfigs group1 = new BasicPipelineConfigs(pipelineInGroup1);
group1.setGroup("group1");
PipelineConfig pipelineInGroup2 = new PipelineConfig();
PipelineConfigs group2 = new BasicPipelineConfigs(pipelineInGroup2);
group2.setGroup("group2");
when(goConfigService.groups()).thenReturn(new PipelineGroups(group1, group2));
String user = "looser";
when(securityService.hasViewPermissionForGroup(user, "group1")).thenReturn(true);
when(securityService.hasViewPermissionForGroup(user, "group2")).thenReturn(false);
List<PipelineConfigs> gotPipelineGroups = service.getGroupsForUser(user);
verify(goConfigService, never()).getAllPipelinesInGroup("group1");
assertThat(gotPipelineGroups, is(Arrays.asList(group1)));
}
private String groupXml() {
return "<pipelines group=\"renamed_group_name\">\n"
+ " <pipeline name=\"new_name\">\n"
+ " <materials>\n"
+ " <svn url=\"file:///tmp/foo\" />\n"
+ " </materials>\n"
+ " <stage name=\"stage_name\">\n"
+ " <jobs>\n"
+ " <job name=\"job_name\" />\n"
+ " </jobs>\n"
+ " </stage>\n"
+ " </pipeline>\n"
+ "</pipelines>";
}
private String parameterizedGroupXml() {
return "<pipelines group=\"group_name\">\n"
+ " <pipeline name=\"new_name\" labeltemplate=\"${COUNT}-#{foo}\">\n"
+ " <materials>\n"
+ " <svn url=\"file:///tmp/foo\" />\n"
+ " </materials>\n"
+ " <params>\n"
+ " <param name=\"foo\">test</param>\n"
+ " </params>"
+ " <stage name=\"stage_name\">\n"
+ " <jobs>\n"
+ " <job name=\"job_name\" />\n"
+ " </jobs>\n"
+ " </stage>\n"
+ " </pipeline>\n"
+ "</pipelines>";
}
}