/* * 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.ConfigRepoConfig; import com.thoughtworks.go.config.remote.ConfigReposConfig; import com.thoughtworks.go.config.remote.PartialConfig; import com.thoughtworks.go.helper.PartialConfigMother; import com.thoughtworks.go.server.service.GoConfigService; import com.thoughtworks.go.serverhealth.ServerHealthService; import org.junit.Before; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.io.File; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; public class GoPartialConfigTest { private GoConfigPluginService configPluginService; private GoConfigWatchList configWatchList; private PartialConfigProvider plugin; private GoRepoConfigDataSource repoConfigDataSource; private BasicCruiseConfig cruiseConfig; private GoPartialConfig partialConfig; File folder = new File("dir"); private CachedGoConfig cachedGoConfig; private ConfigRepoConfig configRepoConfig; private CachedGoPartials cachedGoPartials; private GoConfigService goConfigService; private ServerHealthService serverHealthService; @Before public void setUp() { serverHealthService = mock(ServerHealthService.class); configPluginService = mock(GoConfigPluginService.class); plugin = mock(PartialConfigProvider.class); when(configPluginService.partialConfigProviderFor(any(ConfigRepoConfig.class))).thenReturn(plugin); cruiseConfig = new BasicCruiseConfig(); configRepoConfig = new ConfigRepoConfig(new GitMaterialConfig("url"), "plugin"); cruiseConfig.setConfigRepos(new ConfigReposConfig(configRepoConfig)); cachedGoConfig = mock(CachedGoConfig.class); when(cachedGoConfig.currentConfig()).thenReturn(cruiseConfig); configWatchList = new GoConfigWatchList(cachedGoConfig); repoConfigDataSource = new GoRepoConfigDataSource(configWatchList, configPluginService, serverHealthService); cachedGoPartials = new CachedGoPartials(serverHealthService); goConfigService = mock(GoConfigService.class); serverHealthService = mock(ServerHealthService.class); partialConfig = new GoPartialConfig(repoConfigDataSource, configWatchList, goConfigService, cachedGoPartials, serverHealthService); } @Test public void shouldReturnEmptyListWhenWatchListEmpty() { assertThat(partialConfig.lastPartials().isEmpty(), is(true)); } @Test public void shouldReturnEmptyListWhenNothingParsedYet_AndWatchListNotEmpty() { setOneConfigRepo(); assertThat(partialConfig.lastPartials().isEmpty(), is(true)); } private ScmMaterialConfig setOneConfigRepo() { ScmMaterialConfig material = new GitMaterialConfig("http://my.git"); cruiseConfig.setConfigRepos(new ConfigReposConfig(new ConfigRepoConfig(material, "myplugin"))); configWatchList.onConfigChange(cruiseConfig); return material; } @Test public void shouldReturnLatestPartialAfterCheckout_AndWatchListNotEmpty() throws Exception { ScmMaterialConfig material = setOneConfigRepo(); PartialConfig part = new PartialConfig(); when(plugin.load(any(File.class), any(PartialConfigLoadContext.class))).thenReturn(part); repoConfigDataSource.onCheckoutComplete(material, folder, "7a8f"); assertThat(partialConfig.lastPartials().size(), is(1)); assertThat(partialConfig.lastPartials().get(0), is(part)); } @Test public void shouldReturnEmptyList_WhenFirstParsingFailed() { ScmMaterialConfig material = setOneConfigRepo(); PartialConfig part = new PartialConfig(); when(plugin.load(any(File.class), any(PartialConfigLoadContext.class))) .thenThrow(new RuntimeException("Failed parsing")); repoConfigDataSource.onCheckoutComplete(material, folder, "7a8f"); assertThat(repoConfigDataSource.latestParseHasFailedForMaterial(material), is(true)); assertThat(partialConfig.lastPartials().size(), is(0)); } @Test public void shouldReturnFirstPartial_WhenFirstParsedSucceed_ButSecondFailed() throws Exception { ScmMaterialConfig material = setOneConfigRepo(); PartialConfig part = new PartialConfig(); when(plugin.load(any(File.class), any(PartialConfigLoadContext.class))).thenReturn(part); repoConfigDataSource.onCheckoutComplete(material, folder, "7a8f"); when(plugin.load(any(File.class), any(PartialConfigLoadContext.class))) .thenThrow(new RuntimeException("Failed parsing")); repoConfigDataSource.onCheckoutComplete(material, folder, "6354"); assertThat(repoConfigDataSource.latestParseHasFailedForMaterial(material), is(true)); assertThat(partialConfig.lastPartials().size(), is(1)); assertThat(partialConfig.lastPartials().get(0), is(part)); } @Test public void shouldRemovePartialWhenNoLongerInWatchList() throws Exception { ScmMaterialConfig material = setOneConfigRepo(); PartialConfig part = new PartialConfig(); when(plugin.load(any(File.class), any(PartialConfigLoadContext.class))).thenReturn(part); repoConfigDataSource.onCheckoutComplete(material, folder, "7a8f"); assertThat(partialConfig.lastPartials().size(), is(1)); assertThat(partialConfig.lastPartials().get(0), is(part)); // we change current configuration ScmMaterialConfig othermaterial = new GitMaterialConfig("http://myother.git"); cruiseConfig.setConfigRepos(new ConfigReposConfig(new ConfigRepoConfig(othermaterial, "myplugin"))); configWatchList.onConfigChange(cruiseConfig); assertThat(partialConfig.lastPartials().size(), is(0)); } @Test public void shouldListenForConfigRepoListChanges() { assertTrue(repoConfigDataSource.hasListener(partialConfig)); } @Test public void shouldListenForCompletedParsing() { assertTrue(configWatchList.hasListener(partialConfig)); } @Test public void shouldNotifyListenersAfterUpdatingMapOfLatestValidConfig() { ScmMaterialConfig material = setOneConfigRepo(); PartialConfig part = new PartialConfig(); when(plugin.load(any(File.class), any(PartialConfigLoadContext.class))).thenReturn(part); PartialConfigUpdateCompletedListener listener = mock(PartialConfigUpdateCompletedListener.class); repoConfigDataSource.registerListener(listener); repoConfigDataSource.onCheckoutComplete(material, folder, "7a8f"); verify(listener, times(1)).onSuccessPartialConfig(any(ConfigRepoConfig.class), any(PartialConfig.class)); } @Test public void shouldMergeRemoteGroupToMain() { when(goConfigService.updateConfig(any(UpdateConfigCommand.class))).thenAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocationOnMock) throws Throwable { UpdateConfigCommand command = (UpdateConfigCommand) invocationOnMock.getArguments()[0]; command.update(cruiseConfig); return cruiseConfig; } }); partialConfig.onSuccessPartialConfig(configRepoConfig, PartialConfigMother.withPipeline("p1")); assertThat(cruiseConfig.getPartials().size(), is(1)); assertThat(cruiseConfig.getPartials().get(0).getGroups().first().getGroup(), is("group")); } @Test public void shouldMergeRemoteEnvironmentToMain() { when(goConfigService.updateConfig(any(UpdateConfigCommand.class))).thenAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocationOnMock) throws Throwable { UpdateConfigCommand command = (UpdateConfigCommand) invocationOnMock.getArguments()[0]; command.update(cruiseConfig); return cruiseConfig; } }); partialConfig.onSuccessPartialConfig(configRepoConfig, PartialConfigMother.withEnvironment("env1")); assertThat(cruiseConfig.getPartials().size(), is(1)); assertThat(cruiseConfig.getPartials().get(0).getEnvironments().first().name(), is(new CaseInsensitiveString("env1"))); } }