/*
* 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.domain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.thoughtworks.go.config.*;
import com.thoughtworks.go.helper.PipelineConfigMother;
import com.thoughtworks.go.helper.StageConfigMother;
import org.apache.commons.collections.map.SingletonMap;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Matchers;
import static com.thoughtworks.go.util.DataStructureUtils.a;
import static com.thoughtworks.go.util.DataStructureUtils.m;
import static com.thoughtworks.go.util.TestUtils.contains;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.Matchers.hasItem;
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;
public class StageConfigTest {
private String md5 = "md5-test";
@Test
public void shouldSetPrimitiveAttributes() throws Exception{
StageConfig config = new StageConfig();
config.setConfigAttributes(new SingletonMap(StageConfig.NAME, "foo_bar"));
config.setConfigAttributes(new SingletonMap(StageConfig.FETCH_MATERIALS, "0"));
config.setConfigAttributes(new SingletonMap(StageConfig.CLEAN_WORKING_DIR, "1"));
assertThat(config.name(), is(new CaseInsensitiveString("foo_bar")));
assertThat(config.isFetchMaterials(), is(false));
assertThat(config.isCleanWorkingDir(), is(true));
}
@Test
public void shouldSetArtifactCleanupOptOutAttribute() throws Exception{
StageConfig config = new StageConfig();
assertThat(config.isArtifactCleanupProhibited(), is(false));
config.setConfigAttributes(new SingletonMap(StageConfig.ARTIFACT_CLEANUP_PROHIBITED, "1"));
assertThat(config.isArtifactCleanupProhibited(), is(true));
config.setConfigAttributes(new HashMap());
assertThat(config.isArtifactCleanupProhibited(), is(true));
config.setConfigAttributes(new SingletonMap(StageConfig.ARTIFACT_CLEANUP_PROHIBITED, "0"));
assertThat(config.isArtifactCleanupProhibited(), is(false));
}
@Test
public void shouldRemoveStageLevelAuthorizationWhenInheritingPermissionsFromGroup() {
StageConfig config = new StageConfig();
StageConfigMother.addApprovalWithRoles(config, "role1");
StageConfigMother.addApprovalWithUsers(config, "user1");
HashMap map = new HashMap();
List operateUsers = new ArrayList();
operateUsers.add(nameMap("user1"));
map.put(StageConfig.OPERATE_USERS, operateUsers);
List operateRoles = new ArrayList();
operateRoles.add(nameMap("role1"));
map.put(StageConfig.OPERATE_ROLES, operateRoles);
map.put(StageConfig.SECURITY_MODE, "inherit");
config.setConfigAttributes(map);
assertThat(config.getApproval().getAuthConfig().isEmpty(), is(true));
}
@Test
public void shouldSetOperateUsers() {
StageConfig config = new StageConfig();
HashMap map = new HashMap();
List operateUsers = new ArrayList();
operateUsers.add(nameMap("user1"));
operateUsers.add(nameMap("user1"));
operateUsers.add(nameMap("user2"));
map.put(StageConfig.OPERATE_USERS, operateUsers);
map.put(StageConfig.OPERATE_ROLES, new ArrayList());
map.put(StageConfig.SECURITY_MODE, "define");
config.setConfigAttributes(map);
assertThat(config.getOperateUsers().size(), is(2));
assertThat(config.getOperateUsers(), hasItem(new AdminUser(new CaseInsensitiveString("user1"))));
assertThat(config.getOperateUsers(), hasItem(new AdminUser(new CaseInsensitiveString("user2"))));
}
@Test
public void shouldSetOperateRoles() {
StageConfig config = new StageConfig();
HashMap map = new HashMap();
List operateRoles = new ArrayList();
operateRoles.add(nameMap("role1"));
operateRoles.add(nameMap("role1"));
operateRoles.add(nameMap("role2"));
map.put(StageConfig.OPERATE_ROLES, operateRoles);
map.put(StageConfig.OPERATE_USERS, new ArrayList());
map.put(StageConfig.SECURITY_MODE, "define");
config.setConfigAttributes(map);
assertThat(config.getOperateRoles().size(), is(2));
assertThat(config.getOperateRoles(), hasItem(new AdminRole(new CaseInsensitiveString("role1"))));
assertThat(config.getOperateRoles(), hasItem(new AdminRole(new CaseInsensitiveString("role2"))));
}
private Map nameMap(final String name) {
Map valueHashMap = new HashMap();
valueHashMap.put("name", name);
return valueHashMap;
}
@Test
public void shouldPopulateEnvironmentVariablesFromAttributeMap() {
StageConfig stageConfig = new StageConfig();
HashMap map = new HashMap();
HashMap valueHashMap = new HashMap();
valueHashMap.put("name", "FOO");
valueHashMap.put("value", "BAR");
map.put(StageConfig.ENVIRONMENT_VARIABLES, valueHashMap);
EnvironmentVariablesConfig mockEnvironmentVariablesConfig = mock(EnvironmentVariablesConfig.class);
stageConfig.setVariables(mockEnvironmentVariablesConfig);
stageConfig.setConfigAttributes(map);
verify(mockEnvironmentVariablesConfig).setConfigAttributes(valueHashMap);
}
@Test
public void shouldSetApprovalFromConfigAttrs() throws Exception{
StageConfig config = new StageConfig();
config.setConfigAttributes(new SingletonMap(StageConfig.APPROVAL, new SingletonMap(Approval.TYPE, Approval.MANUAL)));
assertThat(config.getApproval().getType(), is(Approval.MANUAL));
config.setConfigAttributes(new HashMap());
assertThat(config.getApproval().getType(), is(Approval.MANUAL));
config.setConfigAttributes(new SingletonMap(StageConfig.APPROVAL, new SingletonMap(Approval.TYPE, Approval.SUCCESS)));
assertThat(config.getApproval().getType(), is(Approval.SUCCESS));
config.setConfigAttributes(new HashMap());
assertThat(config.getApproval().getType(), is(Approval.SUCCESS));
}
@Test
public void shouldPickupJobConfigDetailsFromAttributeMap() throws Exception{
StageConfig config = new StageConfig();
Map stageAttrs = m(StageConfig.JOBS, a(m(JobConfig.NAME, "con-job"), m(JobConfig.NAME, "boring-job")));
config.setConfigAttributes(stageAttrs);
assertThat(config.getJobs().get(0).name(), is(new CaseInsensitiveString("con-job")));
assertThat(config.getJobs().get(1).name(), is(new CaseInsensitiveString("boring-job")));
}
@Test public void shouldFindCorrectJobIfJobIsOnAllAgents() throws Exception {
JobConfig allAgentsJob = new JobConfig("job-for-all-agents");
allAgentsJob.setRunOnAllAgents(true);
JobConfigs jobs = new JobConfigs();
jobs.add(allAgentsJob);
jobs.add(new JobConfig("job"));
StageConfig stage = new StageConfig(new CaseInsensitiveString("stage-name"), jobs);
JobConfig found = stage.jobConfigByInstanceName("job-for-all-agents-" + RunOnAllAgentsJobTypeConfig.MARKER + "-1", true);
assertThat(found, is(allAgentsJob));
}
@Test public void shouldFindCorrectJobIfJobIsOnAllAgentsAndAmbiguousName() throws Exception {
JobConfig allAgentsJob = new JobConfig("job-for-all-agents");
JobConfig ambiguousJob = new JobConfig("job-for-all");
allAgentsJob.setRunOnAllAgents(true);
ambiguousJob.setRunOnAllAgents(true);
JobConfigs jobs = new JobConfigs();
jobs.add(ambiguousJob);
jobs.add(allAgentsJob);
StageConfig stage = new StageConfig(new CaseInsensitiveString("stage-name"), jobs);
JobConfig found = stage.jobConfigByInstanceName(RunOnAllAgents.CounterBasedJobNameGenerator.appendMarker("job-for-all-agents", 1), true);
assertThat(found, is(allAgentsJob));
}
@Test
public void shouldReturnTrueIfStageHasTests() {
StageConfig stageWithTests = StageConfigMother.stageConfigWithArtifact("stage1", "job1", ArtifactType.unit);
StageConfig stageWithoutTests = StageConfigMother.stageConfigWithArtifact("stage2", "job2", ArtifactType.file);
assertThat(stageWithTests.hasTests(), is(true));
assertThat(stageWithoutTests.hasTests(), is(false));
}
@Test
public void shouldPopulateErrorMessagesWhenHasJobNamesRepeated() {
CruiseConfig config = new BasicCruiseConfig();
PipelineConfig pipelineConfig = PipelineConfigMother.createPipelineConfig("pipeline", "stage-1", "con-job");
config.addPipeline("group-foo", pipelineConfig);
StageConfig stageConfig = pipelineConfig.get(0);
JobConfig newJob = new JobConfig("foo!");
StageConfig newlyAddedStage = new StageConfig(new CaseInsensitiveString("."), new JobConfigs(newJob));
pipelineConfig.addStageWithoutValidityAssertion(newlyAddedStage);
stageConfig.getJobs().addJobWithoutValidityAssertion(new JobConfig(new CaseInsensitiveString("con-job"), new Resources(), new ArtifactPlans(), new Tasks(new ExecTask("ls", "-la", "foo"))));
List<ConfigErrors> allErrors = config.validateAfterPreprocess();
assertThat(allErrors.size(), is(4));
assertThat(allErrors.get(0).on(JobConfig.NAME), is("You have defined multiple jobs called 'con-job'. Job names are case-insensitive and must be unique."));
assertThat(allErrors.get(1).on(JobConfig.NAME), is("You have defined multiple jobs called 'con-job'. Job names are case-insensitive and must be unique."));
assertThat(allErrors.get(2).on(StageConfig.NAME), is("Invalid stage name '.'. This must be alphanumeric and can contain underscores and periods (however, it cannot start with a period). The maximum allowed length is 255 characters."));
assertThat(allErrors.get(3).on(JobConfig.NAME), is("Invalid job name 'foo!'. This must be alphanumeric and may contain underscores and periods. The maximum allowed length is 255 characters."));
assertThat(stageConfig.getJobs().get(0).errors().on(JobConfig.NAME), is("You have defined multiple jobs called 'con-job'. Job names are case-insensitive and must be unique."));
assertThat(stageConfig.getJobs().get(1).errors().on(JobConfig.NAME), is("You have defined multiple jobs called 'con-job'. Job names are case-insensitive and must be unique."));
assertThat(newlyAddedStage.errors().on(StageConfig.NAME), is("Invalid stage name '.'. This must be alphanumeric and can contain underscores and periods (however, it cannot start with a period). The maximum allowed length is 255 characters."));
assertThat(newJob.errors().on(JobConfig.NAME), is("Invalid job name 'foo!'. This must be alphanumeric and may contain underscores and periods. The maximum allowed length is 255 characters."));
}
@Test
public void shouldReturnAllTheUsersAndRoleThatCanOperateThisStage() {
StageConfig stage = StageConfigMother.stageConfig("stage");
StageConfigMother.addApprovalWithUsers(stage, "user1", "user2");
StageConfigMother.addApprovalWithRoles(stage, "role1", "role2");
assertThat(stage.getOperateUsers(), is(Arrays.asList(new AdminUser(new CaseInsensitiveString("user1")), new AdminUser(new CaseInsensitiveString("user2")))));
assertThat(stage.getOperateRoles(), is(Arrays.asList(new AdminRole(new CaseInsensitiveString("role1")), new AdminRole(new CaseInsensitiveString("role2")))));
}
@Test
public void shouldFailValidationWhenNameIsBlank(){
StageConfig stageConfig = new StageConfig();
stageConfig.validate(null);
assertThat(stageConfig.errors().on(StageConfig.NAME), contains("Invalid stage name 'null'"));
stageConfig.setName(null);
stageConfig.validate(null);
assertThat(stageConfig.errors().on(StageConfig.NAME), contains("Invalid stage name 'null'"));
stageConfig.setName(new CaseInsensitiveString(""));
stageConfig.validate(null);
assertThat(stageConfig.errors().on(StageConfig.NAME), contains("Invalid stage name 'null'"));
}
@Test
public void shouldValidateTree(){
EnvironmentVariablesConfig variables = mock(EnvironmentVariablesConfig.class);
JobConfigs jobConfigs = mock(JobConfigs.class);
Approval approval = mock(Approval.class);
StageConfig stageConfig = new StageConfig(new CaseInsensitiveString("stage$"), jobConfigs, approval);
stageConfig.setVariables(variables);
stageConfig.validateTree(PipelineConfigSaveValidationContext.forChain(true, "group", new PipelineConfig(), stageConfig));
assertThat(stageConfig.errors().on(StageConfig.NAME), contains("Invalid stage name 'stage$'"));
ArgumentCaptor<PipelineConfigSaveValidationContext> captor = ArgumentCaptor.forClass(PipelineConfigSaveValidationContext.class);
verify(jobConfigs).validateTree(captor.capture());
PipelineConfigSaveValidationContext childContext = captor.getValue();
assertThat(childContext.getParent(), is(stageConfig));
verify(approval).validateTree(childContext);
verify(variables).validateTree(childContext);
}
@Test
public void shouldAddValidateTreeErrorsOnStageConfigIfPipelineIsAssociatedToATemplate(){
Approval approval = mock(Approval.class);
JobConfigs jobConfigs = mock(JobConfigs.class);
ConfigErrors jobErrors = new ConfigErrors();
jobErrors.add("KEY", "ERROR");
when(jobConfigs.errors()).thenReturn(jobErrors);
StageConfig stageConfig = new StageConfig(new CaseInsensitiveString("stage$"), jobConfigs, approval);
PipelineConfig pipelineConfig = new PipelineConfig();
pipelineConfig.setTemplateName(new CaseInsensitiveString("template"));
stageConfig.validateTree(PipelineConfigSaveValidationContext.forChain(true, "group", pipelineConfig, stageConfig));
assertThat(stageConfig.errors().on(StageConfig.NAME), contains("Invalid stage name 'stage$'"));
}
@Test
public void shouldReturnTrueIfAllDescendentsAreValid(){
EnvironmentVariablesConfig variables = mock(EnvironmentVariablesConfig.class);
JobConfigs jobConfigs = mock(JobConfigs.class);
Approval approval = mock(Approval.class);
when(variables.validateTree(Matchers.<PipelineConfigSaveValidationContext>any())).thenReturn(true);
when(jobConfigs.validateTree(Matchers.<PipelineConfigSaveValidationContext>any())).thenReturn(true);
when(approval.validateTree(Matchers.<PipelineConfigSaveValidationContext>any())).thenReturn(true);
StageConfig stageConfig = new StageConfig(new CaseInsensitiveString("p1"), jobConfigs);
stageConfig.setVariables(variables);
stageConfig.setApproval(approval);
boolean isValid = stageConfig.validateTree(PipelineConfigSaveValidationContext.forChain(true, "group", new PipelineConfig(), stageConfig));
assertTrue(isValid);
verify(jobConfigs).validateTree(Matchers.<PipelineConfigSaveValidationContext>any());
verify(variables).validateTree(Matchers.<PipelineConfigSaveValidationContext>any());
verify(approval).validateTree(Matchers.<PipelineConfigSaveValidationContext>any());
}
@Test
public void shouldReturnFalseIfAnyDescendentIsInValid(){
EnvironmentVariablesConfig variables = mock(EnvironmentVariablesConfig.class);
JobConfigs jobConfigs = mock(JobConfigs.class);
Approval approval = mock(Approval.class);
when(variables.validateTree(Matchers.<PipelineConfigSaveValidationContext>any())).thenReturn(false);
when(jobConfigs.validateTree(Matchers.<PipelineConfigSaveValidationContext>any())).thenReturn(false);
when(approval.validateTree(Matchers.<PipelineConfigSaveValidationContext>any())).thenReturn(false);
StageConfig stageConfig = new StageConfig(new CaseInsensitiveString("p1"), jobConfigs);
stageConfig.setVariables(variables);
stageConfig.setApproval(approval);
boolean isValid = stageConfig.validateTree(PipelineConfigSaveValidationContext.forChain(true, "group", new PipelineConfig(), stageConfig));
assertFalse(isValid);
verify(jobConfigs).validateTree(Matchers.<PipelineConfigSaveValidationContext>any());
verify(variables).validateTree(Matchers.<PipelineConfigSaveValidationContext>any());
verify(approval).validateTree(Matchers.<PipelineConfigSaveValidationContext>any());
}
}