/*
* 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.builder.pluggableTask;
import com.thoughtworks.go.config.pluggabletask.PluggableTask;
import com.thoughtworks.go.domain.BuildLogElement;
import com.thoughtworks.go.domain.RunIfConfigs;
import com.thoughtworks.go.domain.builder.Builder;
import com.thoughtworks.go.domain.config.PluginConfiguration;
import com.thoughtworks.go.plugin.access.pluggabletask.JobConsoleLoggerInternal;
import com.thoughtworks.go.plugin.access.pluggabletask.TaskExtension;
import com.thoughtworks.go.plugin.api.response.execution.ExecutionResult;
import com.thoughtworks.go.plugin.api.task.*;
import com.thoughtworks.go.plugin.infra.ActionWithReturn;
import com.thoughtworks.go.plugin.infra.PluginManager;
import com.thoughtworks.go.plugin.infra.PluginManagerReference;
import com.thoughtworks.go.plugin.infra.plugininfo.GoPluginDescriptor;
import com.thoughtworks.go.util.ReflectionUtil;
import com.thoughtworks.go.util.command.CruiseControlException;
import com.thoughtworks.go.util.command.EnvironmentVariableContext;
import com.thoughtworks.go.work.DefaultGoPublisher;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
public class PluggableTaskBuilderTest {
public static final String TEST_PLUGIN_ID = "test-plugin-id";
@Mock private RunIfConfigs runIfConfigs;
@Mock private Builder cancelBuilder;
@Mock private PluggableTask pluggableTask;
@Mock private PluginManager pluginManager;
@Mock private EnvironmentVariableContext variableContext;
@Mock private DefaultGoPublisher goPublisher;
@Mock private BuildLogElement buildLogElement;
@Mock private GoPluginDescriptor pluginDescriptor;
private TaskExtension taskExtension;
@Before
public void setUp() {
initMocks(this);
PluginManagerReference.reference().setPluginManager(pluginManager);
when(pluggableTask.getPluginConfiguration()).thenReturn(new PluginConfiguration(TEST_PLUGIN_ID, "1.0"));
HashMap<String, Map<String, String>> pluginConfig = new HashMap<>();
when(pluggableTask.configAsMap()).thenReturn(pluginConfig);
taskExtension = new TaskExtension(pluginManager);
when(pluginManager.hasReferenceFor(Task.class, TEST_PLUGIN_ID)).thenReturn(true);
when(pluginManager.getPluginDescriptorFor(TEST_PLUGIN_ID)).thenReturn(mock(GoPluginDescriptor.class));
}
@After
public void teardown(){
JobConsoleLoggerInternal.unsetContext();
}
@Test
public void shouldInvokeTheTaskExecutorOfThePlugin() throws Exception {
final int[] executeTaskCalled = new int[1];
PluggableTaskBuilder taskBuilder = new PluggableTaskBuilder(runIfConfigs, cancelBuilder, pluggableTask, TEST_PLUGIN_ID, "test-directory") {
@Override
protected ExecutionResult executeTask(Task task, BuildLogElement buildLogElement, DefaultGoPublisher publisher, EnvironmentVariableContext environmentVariableContext) {
executeTaskCalled[0]++;
return ExecutionResult.success("Test succeeded");
}
};
taskBuilder.build(buildLogElement, goPublisher, variableContext, taskExtension);
assertThat(executeTaskCalled[0], is(1));
}
@Test
public void shouldBuildExecutorConfigPlusExecutionContextAndInvokeTheTaskExecutorWithIt() throws Exception {
Task task = mock(Task.class);
TaskConfig defaultTaskConfig = mock(TaskConfig.class);
when(task.config()).thenReturn(defaultTaskConfig);
final TaskConfig executorTaskConfig = mock(TaskConfig.class);
final TaskExecutionContext taskExecutionContext = mock(TaskExecutionContext.class);
PluggableTaskBuilder taskBuilder = new PluggableTaskBuilder(runIfConfigs, cancelBuilder, pluggableTask, TEST_PLUGIN_ID, "test-directory") {
@Override
protected TaskConfig buildTaskConfig(TaskConfig config) {
return executorTaskConfig;
}
@Override
protected TaskExecutionContext buildTaskContext(BuildLogElement buildLogElement, DefaultGoPublisher publisher,
EnvironmentVariableContext environmentVariableContext) {
return taskExecutionContext;
}
};
TaskExecutor taskExecutor = mock(TaskExecutor.class);
when(taskExecutor.execute(executorTaskConfig, taskExecutionContext)).thenReturn(new ExecutionResult());
when(task.executor()).thenReturn(taskExecutor);
taskBuilder.executeTask(task, null, null, null);
verify(task).config();
verify(task).executor();
verify(taskExecutor).execute(executorTaskConfig, taskExecutionContext);
assertThat(ReflectionUtil.getStaticField(JobConsoleLogger.class, "context"), is(not(nullValue())));
}
@Test
public void shouldReturnDefaultValueInExecConfigWhenNoConfigValueIsProvided() throws Exception {
Map<String, Map<String, String>> configMap = new HashMap<>();
PluggableTask task = mock(PluggableTask.class);
when(task.getPluginConfiguration()).thenReturn(new PluginConfiguration());
when(task.configAsMap()).thenReturn(configMap);
PluggableTaskBuilder taskBuilder = new PluggableTaskBuilder(runIfConfigs, cancelBuilder, task, TEST_PLUGIN_ID, "test-directory");
TaskConfig defaultTaskConfig = new TaskConfig();
String propertyName = "URL";
String defaultValue = "ABC.TXT";
defaultTaskConfig.addProperty(propertyName).withDefault(defaultValue);
TaskConfig config = taskBuilder.buildTaskConfig(defaultTaskConfig);
assertThat(config.getValue(propertyName), is(defaultValue));
}
@Test
public void shouldReturnDefaultValueInExecConfigWhenConfigValueIsNull() throws Exception {
TaskConfig defaultTaskConfig = new TaskConfig();
String propertyName = "URL";
String defaultValue = "ABC.TXT";
Map<String, Map<String, String>> configMap = new HashMap<>();
configMap.put(propertyName, null);
PluggableTask task = mock(PluggableTask.class);
when(task.getPluginConfiguration()).thenReturn(new PluginConfiguration());
when(task.configAsMap()).thenReturn(configMap);
PluggableTaskBuilder taskBuilder = new PluggableTaskBuilder(runIfConfigs, cancelBuilder, task, TEST_PLUGIN_ID, "test-directory");
defaultTaskConfig.addProperty(propertyName).withDefault(defaultValue);
TaskConfig config = taskBuilder.buildTaskConfig(defaultTaskConfig);
assertThat(config.getValue(propertyName), is(defaultValue));
}
@Test
public void shouldReturnDefaultValueInExecConfigWhenConfigValueIsEmptyString() throws Exception {
TaskConfig defaultTaskConfig = new TaskConfig();
String propertyName = "URL";
String defaultValue = "ABC.TXT";
Map<String, Map<String, String>> configMap = new HashMap<>();
HashMap<String, String> configValue = new HashMap<>();
configValue.put("value", "");
configMap.put(propertyName, configValue);
PluggableTask task = mock(PluggableTask.class);
when(task.getPluginConfiguration()).thenReturn(new PluginConfiguration());
when(task.configAsMap()).thenReturn(configMap);
PluggableTaskBuilder taskBuilder = new PluggableTaskBuilder(runIfConfigs, cancelBuilder, task, TEST_PLUGIN_ID, "test-directory");
defaultTaskConfig.addProperty(propertyName).withDefault(defaultValue);
TaskConfig config = taskBuilder.buildTaskConfig(defaultTaskConfig);
assertThat(config.getValue(propertyName), is(defaultValue));
}
@Test
public void shouldReturnConfigValueInExecConfig() throws Exception {
TaskConfig defaultTaskConfig = new TaskConfig();
String propertyName = "URL";
String defaultValue = "ABC.TXT";
HashMap<String, String> configValue = new HashMap<>();
configValue.put("value", "XYZ.TXT");
Map<String, Map<String, String>> configMap = new HashMap<>();
configMap.put(propertyName, configValue);
PluggableTask task = mock(PluggableTask.class);
when(task.getPluginConfiguration()).thenReturn(new PluginConfiguration());
when(task.configAsMap()).thenReturn(configMap);
PluggableTaskBuilder taskBuilder = new PluggableTaskBuilder(runIfConfigs, cancelBuilder, task, TEST_PLUGIN_ID, "test-directory");
defaultTaskConfig.addProperty(propertyName).withDefault(defaultValue);
TaskConfig config = taskBuilder.buildTaskConfig(defaultTaskConfig);
assertThat(config.getValue(propertyName), is(configValue.get("value")));
}
@Test
public void shouldReturnPluggableTaskContext() throws Exception {
PluggableTask task = mock(PluggableTask.class);
when(task.getPluginConfiguration()).thenReturn(new PluginConfiguration());
String workingDir = "test-directory";
PluggableTaskBuilder taskBuilder = new PluggableTaskBuilder(runIfConfigs, cancelBuilder, task, TEST_PLUGIN_ID, workingDir);
TaskExecutionContext taskExecutionContext = taskBuilder.buildTaskContext(buildLogElement, goPublisher, variableContext);
assertThat(taskExecutionContext instanceof PluggableTaskContext, is(true));
assertThat(taskExecutionContext.workingDir(), is(workingDir));
}
@Test
public void shouldPublishErrorMessageIfPluginThrowsAnException() throws CruiseControlException {
PluggableTask task = mock(PluggableTask.class);
when(task.getPluginConfiguration()).thenReturn(new PluginConfiguration());
PluggableTaskBuilder taskBuilder = new PluggableTaskBuilder(runIfConfigs, cancelBuilder, pluggableTask, TEST_PLUGIN_ID, "test-directory") {
@Override
protected ExecutionResult executeTask(Task task, BuildLogElement buildLogElement, DefaultGoPublisher publisher, EnvironmentVariableContext environmentVariableContext) {
throw new RuntimeException("err");
}
};
when(pluginManager.doOn(eq(Task.class), eq(TEST_PLUGIN_ID), any(ActionWithReturn.class))).thenAnswer(new Answer<ExecutionResult>() {
@Override
public ExecutionResult answer(InvocationOnMock invocationOnMock) throws Throwable {
ActionWithReturn<Task, ExecutionResult> actionWithReturn = (ActionWithReturn<Task, ExecutionResult>) invocationOnMock.getArguments()[2];
return actionWithReturn.execute(mock(Task.class), pluginDescriptor);
}
});
try {
taskBuilder.build(buildLogElement, goPublisher, variableContext, taskExtension);
fail("expected exception to be thrown");
} catch (Exception e) {
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(goPublisher).taggedConsumeLine(eq(DefaultGoPublisher.ERR), captor.capture());
String error = "Error: err";
assertThat(captor.getValue(), is(error));
assertThat(e.getMessage(), is(new RuntimeException("err").toString()));
}
}
@Test
public void shouldPublishErrorMessageIfPluginReturnsAFailureResponse() throws CruiseControlException {
PluggableTask task = mock(PluggableTask.class);
when(task.getPluginConfiguration()).thenReturn(new PluginConfiguration());
PluggableTaskBuilder taskBuilder = new PluggableTaskBuilder(runIfConfigs, cancelBuilder, pluggableTask, TEST_PLUGIN_ID, "test-directory") {
@Override
protected ExecutionResult executeTask(Task task, BuildLogElement buildLogElement, DefaultGoPublisher publisher, EnvironmentVariableContext environmentVariableContext) {
return ExecutionResult.failure("err");
}
};
when(pluginManager.doOn(eq(Task.class), eq(TEST_PLUGIN_ID), any(ActionWithReturn.class))).thenAnswer(new Answer<ExecutionResult>() {
@Override
public ExecutionResult answer(InvocationOnMock invocationOnMock) throws Throwable {
ActionWithReturn<Task, ExecutionResult> actionWithReturn = (ActionWithReturn<Task, ExecutionResult>) invocationOnMock.getArguments()[2];
return actionWithReturn.execute(mock(Task.class), pluginDescriptor);
}
});
try {
taskBuilder.build(buildLogElement, goPublisher, variableContext, taskExtension);
fail("expected exception to be thrown");
} catch (Exception e) {
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(goPublisher).taggedConsumeLine(eq(DefaultGoPublisher.ERR), captor.capture());
assertThat(captor.getValue(), is("err"));
assertThat(e.getMessage(), is("err"));
}
}
@Test
public void shouldRegisterTaskConfigDuringExecutionAndUnregisterOnSuccessfulCompletion() throws CruiseControlException {
final PluggableTaskBuilder builder = spy(new PluggableTaskBuilder(runIfConfigs, cancelBuilder, pluggableTask, "", ""));
taskExtension = mock(TaskExtension.class);
when(taskExtension.execute(eq(TEST_PLUGIN_ID), any(ActionWithReturn.class))).thenReturn(ExecutionResult.success("yay"));
builder.build(buildLogElement, goPublisher, variableContext, taskExtension);
assertThat(ReflectionUtil.getStaticField(JobConsoleLogger.class, "context"), is(nullValue()));
}
@Test
public void shouldUnsetTaskExecutionContextFromJobConsoleLoggerWhenTaskExecutionFails() throws CruiseControlException {
final PluggableTaskBuilder builder = spy(new PluggableTaskBuilder(runIfConfigs, cancelBuilder, pluggableTask, "", ""));
taskExtension = mock(TaskExtension.class);
when(taskExtension.execute(eq(TEST_PLUGIN_ID), any(ActionWithReturn.class))).thenReturn(ExecutionResult.failure("oh no"));
try {
builder.build(buildLogElement, goPublisher, variableContext, taskExtension);
fail("should throw exception");
} catch (Exception e) {
assertThat(ReflectionUtil.getStaticField(JobConsoleLogger.class, "context"), is(nullValue()));
}
}
@Test
public void shouldUnsetTaskExecutionContextFromJobConsoleLoggerWhenTaskExecutionThrowsException() throws CruiseControlException {
final PluggableTaskBuilder builder = spy(new PluggableTaskBuilder(runIfConfigs, cancelBuilder, pluggableTask, "", ""));
taskExtension = mock(TaskExtension.class);
when(taskExtension.execute(eq(TEST_PLUGIN_ID), any(ActionWithReturn.class))).thenThrow(new RuntimeException("something"));
try {
builder.build(buildLogElement, goPublisher, variableContext, taskExtension);
fail("should throw exception");
} catch (Exception e) {
assertThat(ReflectionUtil.getStaticField(JobConsoleLogger.class, "context"), is(nullValue()));
}
}
}