/*
* 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.tasks;
import com.thoughtworks.go.config.AntTask;
import com.thoughtworks.go.config.ExecTask;
import com.thoughtworks.go.config.FetchTask;
import com.thoughtworks.go.config.pluggabletask.PluggableTask;
import com.thoughtworks.go.config.registry.ConfigElementImplementationRegistry;
import com.thoughtworks.go.domain.Task;
import com.thoughtworks.go.domain.config.Configuration;
import com.thoughtworks.go.domain.config.PluginConfiguration;
import com.thoughtworks.go.plugin.access.pluggabletask.PluggableTaskConfigStore;
import com.thoughtworks.go.plugin.access.pluggabletask.TaskPreference;
import com.thoughtworks.go.plugin.api.config.Property;
import com.thoughtworks.go.plugin.api.response.validation.ValidationResult;
import com.thoughtworks.go.plugin.api.task.TaskConfig;
import com.thoughtworks.go.plugin.api.task.TaskConfigProperty;
import com.thoughtworks.go.plugin.api.task.TaskExecutor;
import com.thoughtworks.go.plugin.api.task.TaskView;
import com.thoughtworks.go.plugin.infra.PluginManager;
import com.thoughtworks.go.plugin.infra.plugininfo.GoPluginDescriptor;
import com.thoughtworks.go.plugins.presentation.PluggableViewModel;
import com.thoughtworks.go.presentation.TaskViewModel;
import com.thoughtworks.go.util.ReflectionUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
import static com.thoughtworks.go.domain.packagerepository.ConfigurationPropertyMother.create;
import static com.thoughtworks.go.util.DataStructureUtils.s;
import static java.util.Arrays.asList;
import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;
public class TaskViewServiceTest {
private ConfigElementImplementationRegistry registry;
private TaskViewService taskViewService;
private PluginManager pluginManager;
@Before
public void setUp() {
cleanupTaskPreferences();
registry = mock(ConfigElementImplementationRegistry.class);
pluginManager = mock(PluginManager.class);
taskViewService = new TaskViewService(registry, pluginManager);
}
@After
public void tearDown() throws Exception {
cleanupTaskPreferences();
}
private void cleanupTaskPreferences() {
Set<String> plugins = PluggableTaskConfigStore.store().pluginIds();
for (String pluginId : plugins) {
PluggableTaskConfigStore.store().removePreferenceFor(pluginId);
}
}
@Test
public void shouldGetViewModelsForBuiltinTasks() {
List<Class<? extends Task>> taskClasses = taskImplementations();
when(registry.implementersOf(Task.class)).thenReturn(taskClasses);
when(registry.getViewModelFor(new AntTask(), "new")).thenReturn(viewModel(new AntTask()));
when(registry.getViewModelFor(new ExecTask(), "new")).thenReturn(new TaskViewModel(new ExecTask(), ""));
List<PluggableViewModel> taskViewModels = taskViewService.getTaskViewModels();
assertThat(taskViewModels.size(), is(3));
assertThat(taskViewModels, hasItem((PluggableViewModel) viewModel(new AntTask())));
assertThat(taskViewModels, hasItem((PluggableViewModel) new TaskViewModel(new ExecTask(), "")));
}
private void storeTaskPreferences(String pluginId, String... properties) {
FakeTask task = new FakeTask(properties);
PluggableTaskConfigStore.store().setPreferenceFor(pluginId, new TaskPreference(task));
}
private void storeTaskPreferences(String pluginId, Property... properties) {
FakeTask task = new FakeTask(properties);
PluggableTaskConfigStore.store().setPreferenceFor(pluginId, new TaskPreference(task));
}
@Test
public void shouldGetViewModelsForPluggedInTasks_ButOnlyForExistingPlugins() throws Exception {
String plugin1 = "task-plugin-1";
String plugin2 = "task-plugin-2";
when(pluginManager.getPluginDescriptorFor(plugin1)).thenReturn(new GoPluginDescriptor(plugin1, "1", null, null, null, false));
when(pluginManager.getPluginDescriptorFor(plugin2)).thenReturn(new GoPluginDescriptor(plugin2, "1", null, null, null, false));
storeTaskPreferences(plugin1, "key_1", "key_2");
storeTaskPreferences(plugin2, "key_3", "key_4");
when(registry.implementersOf(Task.class)).thenReturn(Arrays.<Class<? extends Task>>asList(ExecTask.class, PluggableTask.class));
PluggableTask expectedPluggableTaskForPlugin1 = new PluggableTask(new PluginConfiguration(plugin1, "1"), new Configuration(create("key_1"), create("key_2")));
PluggableTask expectedPluggableTaskForPlugin2 = new PluggableTask(new PluginConfiguration(plugin2, "1"), new Configuration(create("key_3"), create("key_4")));
when(registry.getViewModelFor(new ExecTask(), "new")).thenReturn(viewModel(new ExecTask()));
when(registry.getViewModelFor(expectedPluggableTaskForPlugin1, "new")).thenReturn(viewModel(expectedPluggableTaskForPlugin1));
when(registry.getViewModelFor(expectedPluggableTaskForPlugin2, "new")).thenReturn(viewModel(expectedPluggableTaskForPlugin2));
List<PluggableViewModel> viewModels = taskViewService.getTaskViewModels();
assertThat(viewModels.size(), is(3));
assertThat(viewModels.get(0).getModel(), instanceOf(ExecTask.class));
assertThat(viewModels.get(1).getModel(), instanceOf(PluggableTask.class));
assertThat(viewModels.get(2).getModel(), instanceOf(PluggableTask.class));
assertThat(viewModels.get(1).getModel(), is(expectedPluggableTaskForPlugin1));
assertThat(viewModels.get(2).getModel(), is(expectedPluggableTaskForPlugin2));
verify(registry).getViewModelFor(expectedPluggableTaskForPlugin1, "new");
verify(registry).getViewModelFor(expectedPluggableTaskForPlugin2, "new");
}
@Test
public void shouldStoreDefaultValuesGivenForPropertiesInAPluginWhenInitializingANewTaskPlugin() throws Exception {
String plugin = "task-plugin";
when(pluginManager.getPluginDescriptorFor(plugin)).thenReturn(new GoPluginDescriptor(plugin, "1", null, null, null, false));
Property taskConfigProperty1 = new TaskConfigProperty("key1", null);
Property taskConfigProperty2 = new TaskConfigProperty("key2", null);
Property taskConfigProperty3 = new TaskConfigProperty("key3", null);
storeTaskPreferences(plugin, taskConfigProperty1.withDefault("default1"), taskConfigProperty2.withDefault("default2"), taskConfigProperty3);
when(registry.implementersOf(Task.class)).thenReturn(Arrays.<Class<? extends Task>>asList(PluggableTask.class));
PluggableTask pluggableTask = (PluggableTask) taskViewService.taskInstanceFor(new PluggableTask(new PluginConfiguration(plugin, "1"), new Configuration()).getTaskType());
assertThat(pluggableTask.getConfiguration().getProperty("key1").getValue(), is("default1"));
assertThat(pluggableTask.getConfiguration().getProperty("key2").getValue(), is("default2"));
assertNull(pluggableTask.getConfiguration().getProperty("key3").getValue());
}
@Test
public void shouldFetchPluggableTasksWithSecureConfigurations() throws Exception {
String plugin = "task-plugin";
when(pluginManager.getPluginDescriptorFor(plugin)).thenReturn(new GoPluginDescriptor(plugin, "1", null, null, null, false));
Property taskConfigProperty = new TaskConfigProperty("key1", null).with(Property.SECURE, true);
storeTaskPreferences(plugin, taskConfigProperty);
when(registry.implementersOf(Task.class)).thenReturn(Arrays.<Class<? extends Task>>asList(PluggableTask.class));
PluggableTask pluggableTask = (PluggableTask) taskViewService.taskInstanceFor(new PluggableTask(new PluginConfiguration(plugin, "1"), null).getTaskType());
assertTrue(pluggableTask.getConfiguration().first().isSecure());
}
@Test
public void shouldGetViewModelsOnlyForBuiltInTasks_WhenThereAreNoExistingPlugins() throws Exception {
List<Class<? extends Task>> taskClasses = taskImplementations();
taskClasses.add(PluggableTask.class);
when(registry.implementersOf(Task.class)).thenReturn(taskClasses);
when(registry.getViewModelFor(new AntTask(), "new")).thenReturn(viewModel(new AntTask()));
when(registry.getViewModelFor(new ExecTask(), "new")).thenReturn(new TaskViewModel(new ExecTask(), ""));
List<PluggableViewModel> taskViewModels = taskViewService.getTaskViewModels();
assertThat(taskViewModels.size(), is(3));
assertThat(taskViewModels, hasItem((PluggableViewModel) viewModel(new AntTask())));
assertThat(taskViewModels, hasItem((PluggableViewModel) new TaskViewModel(new ExecTask(), "")));
}
@Test
public void shouldThrowAnExceptionIfTheTaskOfGivenTypeIsNotFound() {
ConfigElementImplementationRegistry registry = this.registry;
List<Class<? extends Task>> taskClasses = new ArrayList<>();
taskClasses.add(AntTask.class);
when(registry.implementersOf(Task.class)).thenReturn(taskClasses);
TaskViewService service = new TaskViewService(registry, mock(PluginManager.class));
try {
service.taskInstanceFor("Unknown");
fail("Should have failed since the given task is not available in the registry");
} catch (RuntimeException e) {
assertThat(e.getMessage(), is("Could not find any task of type: Unknown"));
}
}
@Test
public void shouldGetTaskInstanceForAType() throws Exception {
ConfigElementImplementationRegistry registry = this.registry;
when(registry.implementersOf(Task.class)).thenReturn(taskImplementations());
TaskViewService taskViewService = new TaskViewService(registry, mock(PluginManager.class));
assertThat(taskViewService.taskInstanceFor(new AntTask().getTaskType()), is(new AntTask()));
}
@Test
public void shouldGetListOfOnCancelTaskViewModels() {
ConfigElementImplementationRegistry registry = this.registry;
when(registry.implementersOf(Task.class)).thenReturn(taskImplementations());
AntTask ant = new AntTask();
FetchTask fetch = new FetchTask();
ExecTask exec = new ExecTask();
when(registry.getViewModelFor(ant, "new")).thenReturn(viewModel(ant));
when(registry.getViewModelFor(fetch, "new")).thenReturn(viewModel(fetch));
when(registry.getViewModelFor(exec, "new")).thenReturn(viewModel(exec));
TaskViewService taskViewService = new TaskViewService(registry, mock(PluginManager.class));
List<PluggableViewModel<Task>> onCancelTaskViewModels = taskViewService.getOnCancelTaskViewModels(new AntTask());
assertThat(onCancelTaskViewModels, is(asList(viewModel(ant), viewModel(exec), viewModel(fetch))));
}
@Test
public void getOnCancelTaskViewModels_shouldUsePassedInTaskOnCancelIfAvailable() {
when(registry.implementersOf(Task.class)).thenReturn(taskImplementations());
AntTask ant = new AntTask();
FetchTask fetch = new FetchTask();
AntTask given = new AntTask();
ExecTask cancelExec = new ExecTask("ls", "-la", ".");
given.setCancelTask(cancelExec);
when(registry.getViewModelFor(ant, "new")).thenReturn(viewModel(ant));
when(registry.getViewModelFor(fetch, "new")).thenReturn(viewModel(fetch));
when(registry.getViewModelFor(cancelExec, "edit")).thenReturn(viewModel(cancelExec));
List<PluggableViewModel<Task>> onCancelTaskViewModels = new TaskViewService(registry, mock(PluginManager.class)).getOnCancelTaskViewModels(given);
assertThat(onCancelTaskViewModels, is(asList(viewModel(ant), viewModel(cancelExec), viewModel(fetch))));
}
@Test
public void shouldCreateTaskViewModelsGivenATask() {
when(registry.implementersOf(Task.class)).thenReturn(taskImplementations());
ExecTask given = new ExecTask("mkdir", "foo", "work");
AntTask ant = new AntTask();
FetchTask fetch = new FetchTask();
when(registry.getViewModelFor(ant, "new")).thenReturn(viewModel(ant));
when(registry.getViewModelFor(fetch, "new")).thenReturn(viewModel(fetch));
when(registry.getViewModelFor(given, "new")).thenReturn(viewModel(given));
List<PluggableViewModel> viewModels = taskViewService.getTaskViewModelsWith(given);
assertThat(viewModels.size(), is(3));
assertThat(viewModels.contains(viewModel(ant)), is(true));
assertThat(viewModels.contains(viewModel(fetch)), is(true));
assertThat(viewModels.contains(viewModel(given)), is(true));
}
@Test
public void shouldNotThrowNullPointerExceptionWhenPluginDescriptorOrTaskPreferenceBecomesNullDueToRaceCondition() throws Exception {
PluggableTaskConfigStore pluggableTaskConfigStore = mock(PluggableTaskConfigStore.class);
Object original = ReflectionUtil.getStaticField(PluggableTaskConfigStore.class, "PLUGGABLE_TASK_CONFIG_STORE");
try {
ReflectionUtil.setStaticField(PluggableTaskConfigStore.class, "PLUGGABLE_TASK_CONFIG_STORE", pluggableTaskConfigStore);
String pluginId = "some.plugin.id";
when(pluggableTaskConfigStore.pluginIds()).thenReturn(s(pluginId));
when(pluginManager.getPluginDescriptorFor(pluginId)).thenReturn(null);
TaskViewService taskViewService = new TaskViewService(registry, pluginManager);
taskViewService.getTaskViewModelsWith(mock(Task.class));
} finally {
ReflectionUtil.setStaticField(PluggableTaskConfigStore.class, "PLUGGABLE_TASK_CONFIG_STORE", original);
}
}
private List<Class<? extends Task>> taskImplementations() {
List<Class<? extends Task>> taskClasses = new ArrayList<>();
taskClasses.add(AntTask.class);
taskClasses.add(ExecTask.class);
taskClasses.add(FetchTask.class);
return taskClasses;
}
private PluggableViewModel<Task> viewModel(final Task task) {
return new TaskViewModel(task, "");
}
private class FakeTask implements com.thoughtworks.go.plugin.api.task.Task {
final Property[] properties;
private FakeTask(String... properties) {
this.properties = convertToProperties(properties).toArray(new Property[]{});
}
private FakeTask(Property... properties) {
this.properties = properties;
}
private List<TaskConfigProperty> convertToProperties(String[] properties) {
ArrayList<TaskConfigProperty> taskConfigProperties = new ArrayList<>();
for (String property : properties) {
taskConfigProperties.add(new TaskConfigProperty(property, null));
}
return taskConfigProperties;
}
@Override
public TaskConfig config() {
TaskConfig taskConfig = new TaskConfig();
for (Property property : properties) {
taskConfig.add(property);
}
return taskConfig;
}
@Override
public TaskExecutor executor() {
return null;
}
@Override
public TaskView view() {
return null;
}
@Override
public ValidationResult validate(TaskConfig configuration) {
return null;
}
}
}