/*
* Copyright 2016 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.config;
import com.thoughtworks.go.config.materials.ScmMaterialConfig;
import com.thoughtworks.go.config.materials.git.GitMaterialConfig;
import com.thoughtworks.go.config.remote.*;
import com.thoughtworks.go.domain.config.Configuration;
import com.thoughtworks.go.helper.PartialConfigMother;
import com.thoughtworks.go.serverhealth.*;
import org.hamcrest.core.Is;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
public class GoRepoConfigDataSourceTest {
private GoConfigPluginService configPluginService;
private GoConfigWatchList configWatchList;
private PartialConfigProvider plugin;
private GoRepoConfigDataSource repoConfigDataSource;
private BasicCruiseConfig cruiseConfig ;
private ServerHealthService serverHealthService;
File folder = new File("dir");
@Before
public void setUp()
{
serverHealthService = new ServerHealthService();
configPluginService = mock(GoConfigPluginService.class);
plugin = mock(PartialConfigProvider.class);
when(configPluginService.partialConfigProviderFor(any(ConfigRepoConfig.class))).thenReturn(plugin);
cruiseConfig = new BasicCruiseConfig();
CachedGoConfig cachedGoConfig = mock(CachedGoConfig.class);
when(cachedGoConfig.currentConfig()).thenReturn(cruiseConfig);
configWatchList = new GoConfigWatchList(cachedGoConfig);
repoConfigDataSource = new GoRepoConfigDataSource(configWatchList,configPluginService,serverHealthService);
}
@Test
public void shouldCallPluginLoadOnCheckout_WhenMaterialInWatchList()
{
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
cruiseConfig.setConfigRepos(new ConfigReposConfig(new ConfigRepoConfig(material,"myplugin")));
configWatchList.onConfigChange(cruiseConfig);
repoConfigDataSource.onCheckoutComplete(material,folder,"7a8f");
verify(plugin,times(1)).load(eq(folder), any(PartialConfigLoadContext.class));
}
@Test
public void shouldAssignConfigOrigin() throws Exception
{
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
ConfigRepoConfig configRepo = new ConfigRepoConfig(material, "myplugin");
cruiseConfig.setConfigRepos(new ConfigReposConfig(configRepo));
configWatchList.onConfigChange(cruiseConfig);
repoConfigDataSource.onCheckoutComplete(material, folder, "7a8f");
PartialConfig partialConfig = repoConfigDataSource.latestPartialConfigForMaterial(material);
assertNotNull(partialConfig.getOrigin());
RepoConfigOrigin repoConfigOrigin = new RepoConfigOrigin(configRepo,"7a8f");
assertThat(partialConfig.getOrigin(), Is.<ConfigOrigin>is(repoConfigOrigin));
}
@Test
public void shouldAssignConfigOriginInPipelines() throws Exception
{
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
ConfigRepoConfig configRepo = new ConfigRepoConfig(material, "myplugin");
cruiseConfig.setConfigRepos(new ConfigReposConfig(configRepo));
configWatchList.onConfigChange(cruiseConfig);
when(plugin.load(any(File.class), any(PartialConfigLoadContext.class)))
.thenReturn(PartialConfigMother.withPipeline("pipe1"));
repoConfigDataSource.onCheckoutComplete(material, folder, "7a8f");
PartialConfig partialConfig = repoConfigDataSource.latestPartialConfigForMaterial(material);
RepoConfigOrigin repoConfigOrigin = new RepoConfigOrigin(configRepo,"7a8f");
assertNotNull(partialConfig.getOrigin());
assertThat(partialConfig.getOrigin(), Is.<ConfigOrigin>is(repoConfigOrigin));
PipelineConfig pipe = partialConfig.getGroups().get(0).get(0);
assertThat(pipe.getOrigin(), Is.<ConfigOrigin>is(repoConfigOrigin));
}
@Test
public void shouldAssignConfigOriginInEnvironments() throws Exception
{
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
ConfigRepoConfig configRepo = new ConfigRepoConfig(material, "myplugin");
cruiseConfig.setConfigRepos(new ConfigReposConfig(configRepo));
configWatchList.onConfigChange(cruiseConfig);
when(plugin.load(any(File.class), any(PartialConfigLoadContext.class)))
.thenReturn(PartialConfigMother.withEnvironment("UAT"));
repoConfigDataSource.onCheckoutComplete(material, folder, "7a8f");
PartialConfig partialConfig = repoConfigDataSource.latestPartialConfigForMaterial(material);
RepoConfigOrigin repoConfigOrigin = new RepoConfigOrigin(configRepo,"7a8f");
assertNotNull(partialConfig.getOrigin());
assertThat(partialConfig.getOrigin(), Is.<ConfigOrigin>is(repoConfigOrigin));
EnvironmentConfig environmentConfig = partialConfig.getEnvironments().get(0);
assertThat(environmentConfig.getOrigin(), Is.<ConfigOrigin>is(repoConfigOrigin));
}
@Test
public void shouldProvideParseContextWhenCallingPlugin() throws Exception
{
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
ConfigRepoConfig repoConfig = new ConfigRepoConfig(material, "myplugin");
cruiseConfig.setConfigRepos(new ConfigReposConfig(repoConfig));
configWatchList.onConfigChange(cruiseConfig);
when(configPluginService.partialConfigProviderFor(any(ConfigRepoConfig.class))).thenReturn(plugin);
repoConfigDataSource.onCheckoutComplete(material,folder,"7a8f");
verify(plugin,times(1)).load(eq(folder), notNull(PartialConfigLoadContext.class));
}
@Test
public void shouldProvideConfigurationInParseContextWhenCallingPlugin() throws Exception
{
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
ConfigRepoConfig repoConfig = new ConfigRepoConfig(material, "myplugin");
cruiseConfig.setConfigRepos(new ConfigReposConfig(repoConfig));
configWatchList.onConfigChange(cruiseConfig);
repoConfig.getConfiguration().addNewConfigurationWithValue("key","value",false);
plugin = new AssertingConfigPlugin(repoConfig.getConfiguration());
when(configPluginService.partialConfigProviderFor(any(ConfigRepoConfig.class))).thenReturn(plugin);
repoConfigDataSource.onCheckoutComplete(material,folder,"7a8f");
}
private class AssertingConfigPlugin implements PartialConfigProvider
{
private Configuration configuration;
public AssertingConfigPlugin(Configuration configuration) {
this.configuration = configuration;
}
@Override
public PartialConfig load(File configRepoCheckoutDirectory, PartialConfigLoadContext context) {
Assert.assertThat(context.configuration(),is(configuration));
Assert.assertThat(context.configuration().getProperty("key").getValue(),is("value"));
return mock(PartialConfig.class);
}
@Override
public String displayName() {
return "Assert config provider";
}
}
@Test
public void shouldNotCallPluginLoadOnCheckout_WhenMaterialNotInWatchList() throws Exception
{
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
repoConfigDataSource.onCheckoutComplete(material,folder,"7a8f");
verify(plugin,times(0)).load(eq(folder), any(PartialConfigLoadContext.class));
}
@Test
public void shouldReturnLatestPartialConfigForMaterial_WhenPartialExists() throws Exception
{
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
cruiseConfig.setConfigRepos(new ConfigReposConfig(new ConfigRepoConfig(material,"myplugin")));
configWatchList.onConfigChange(cruiseConfig);
repoConfigDataSource.onCheckoutComplete(material,folder,"7a8f");
assertNotNull(repoConfigDataSource.latestPartialConfigForMaterial(material));
}
@Test
public void shouldThrowWhenGettingLatestPartialConfig_WhenPluginHasFailed() throws Exception
{
// use broken plugin now
when(configPluginService.partialConfigProviderFor(any(ConfigRepoConfig.class)))
.thenReturn(new BrokenConfigPlugin());
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
cruiseConfig.setConfigRepos(new ConfigReposConfig(new ConfigRepoConfig(material,"myplugin")));
configWatchList.onConfigChange(cruiseConfig);
repoConfigDataSource.onCheckoutComplete(material,folder,"7a8f");
assertTrue(repoConfigDataSource.latestParseHasFailedForMaterial(material));
try
{
repoConfigDataSource.latestPartialConfigForMaterial(material);
}
catch (BrokenConfigPluginException ex)
{
return;
}
fail("should have thrown BrokenConfigPluginException");
}
@Test
public void shouldSetErrorHealthState_AtConfigRepoScope_WhenPluginHasThrown()
{
// use broken plugin now
when(configPluginService.partialConfigProviderFor(any(ConfigRepoConfig.class)))
.thenReturn(new BrokenConfigPlugin());
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
ConfigRepoConfig configRepoConfig = new ConfigRepoConfig(material, "myplugin");
cruiseConfig.setConfigRepos(new ConfigReposConfig(configRepoConfig));
configWatchList.onConfigChange(cruiseConfig);
repoConfigDataSource.onCheckoutComplete(material,folder,"7a8f");
assertTrue(repoConfigDataSource.latestParseHasFailedForMaterial(material));
assertFalse(serverHealthService.filterByScope(HealthStateScope.forPartialConfigRepo(configRepoConfig)).isEmpty());
}
@Test
public void shouldSetOKHealthState_AtConfigRepoScope_WhenPluginHasParsed()
{
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
ConfigRepoConfig configRepoConfig = new ConfigRepoConfig(material, "myplugin");
cruiseConfig.setConfigRepos(new ConfigReposConfig(configRepoConfig));
configWatchList.onConfigChange(cruiseConfig);
// set error state to simulate previously failed parse
HealthStateScope scope = HealthStateScope.forPartialConfigRepo(configRepoConfig);
serverHealthService.update(ServerHealthState.error("Parse failed", "Bad config format", HealthStateType.general(scope)));
// verify error health
assertFalse(serverHealthService.filterByScope(HealthStateScope.forPartialConfigRepo(configRepoConfig)).isEmpty());
// now this should fix health
repoConfigDataSource.onCheckoutComplete(material,folder,"7a8f");
assertTrue(serverHealthService.filterByScope(HealthStateScope.forPartialConfigRepo(configRepoConfig)).isEmpty());
}
private class BrokenConfigPlugin implements PartialConfigProvider
{
@Override
public PartialConfig load(File configRepoCheckoutDirectory, PartialConfigLoadContext context) {
throw new BrokenConfigPluginException();
}
@Override
public String displayName() {
return "Broken Test Provider";
}
}
private class BrokenConfigPluginException extends RuntimeException {
}
@Test
public void shouldThrowWhenGettingLatestPartialConfig_WhenInitializingPluginHasFailed() throws Exception
{
when(configPluginService.partialConfigProviderFor(any(ConfigRepoConfig.class)))
.thenThrow(new RuntimeException("Failed to initialize plugin"));
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
cruiseConfig.setConfigRepos(new ConfigReposConfig(new ConfigRepoConfig(material,"myplugin")));
configWatchList.onConfigChange(cruiseConfig);
repoConfigDataSource.onCheckoutComplete(material,folder,"7a8f");
assertTrue(repoConfigDataSource.latestParseHasFailedForMaterial(material));
try
{
repoConfigDataSource.latestPartialConfigForMaterial(material);
}
catch (RuntimeException ex)
{
assertThat(ex.getMessage(),is("Failed to initialize plugin"));
}
}
@Test
public void shouldRemovePartialsWhenRemovedFromWatchList() throws Exception
{
ScmMaterialConfig material = new GitMaterialConfig("http://my.git");
cruiseConfig.setConfigRepos(new ConfigReposConfig(new ConfigRepoConfig(material,"myplugin")));
configWatchList.onConfigChange(cruiseConfig);
repoConfigDataSource.onCheckoutComplete(material,folder,"7a8f");
assertNotNull(repoConfigDataSource.latestPartialConfigForMaterial(material));
// we change current configuration
ScmMaterialConfig othermaterial = new GitMaterialConfig("http://myother.git");
cruiseConfig.setConfigRepos(new ConfigReposConfig(new ConfigRepoConfig(othermaterial,"myplugin")));
configWatchList.onConfigChange(cruiseConfig);
assertNull(repoConfigDataSource.latestPartialConfigForMaterial(material));
}
@Test
public void shouldListenForConfigRepoListChanged()
{
assertTrue(configWatchList.hasListener(repoConfigDataSource));
}
}