/* * 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.config; import com.thoughtworks.go.config.exceptions.ConfigMergeException; import com.thoughtworks.go.config.exceptions.ConfigMergePostValidationException; import com.thoughtworks.go.config.exceptions.ConfigMergePreValidationException; import com.thoughtworks.go.config.registry.ConfigElementImplementationRegistry; import com.thoughtworks.go.config.remote.PartialConfig; import com.thoughtworks.go.config.update.FullConfigUpdateCommand; import com.thoughtworks.go.domain.GoConfigRevision; import com.thoughtworks.go.server.util.ServerVersion; import com.thoughtworks.go.service.ConfigRepository; import com.thoughtworks.go.util.SystemEnvironment; import com.thoughtworks.go.util.TimeProvider; import org.jdom2.Document; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import java.util.ArrayList; import java.util.Date; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.mockito.Mockito.*; public class FullConfigSaveMergeFlowTest { private MagicalGoConfigXmlLoader loader; private MagicalGoConfigXmlWriter writer; private ConfigElementImplementationRegistry configElementImplementationRegistry; private FullConfigSaveMergeFlow flow; private FullConfigUpdateCommand updateConfigCommand; private SystemEnvironment systemEnvironment; private CruiseConfig configForEdit; private Document document; private GoConfigFileWriter fileWriter; private ServerVersion serverVersion; private TimeProvider timeProvider; private ConfigRepository configRepository; private CachedGoPartials cachedGoPartials; private List<PartialConfig> partials; @Before public void setup() throws Exception { configForEdit = mock(CruiseConfig.class); updateConfigCommand = new FullConfigUpdateCommand(configForEdit, "md5"); loader = mock(MagicalGoConfigXmlLoader.class); writer = mock(MagicalGoConfigXmlWriter.class); document = mock(Document.class); systemEnvironment = mock(SystemEnvironment.class); fileWriter = mock(GoConfigFileWriter.class); serverVersion = mock(ServerVersion.class); timeProvider = mock(TimeProvider.class); configRepository = mock(ConfigRepository.class); cachedGoPartials = mock(CachedGoPartials.class); configElementImplementationRegistry = mock(ConfigElementImplementationRegistry.class); partials = new ArrayList<>(); flow = new FullConfigSaveMergeFlow(loader, writer, configElementImplementationRegistry, serverVersion, timeProvider, configRepository, cachedGoPartials, fileWriter); } @Test public void shouldUpdateGivenConfigWithPartials() throws Exception { when(writer.documentFrom(configForEdit)).thenReturn(document); when(writer.toString(document)).thenReturn("cruise_config"); when(loader.loadConfigHolder(any(String.class), any(MagicalGoConfigXmlLoader.Callback.class))) .thenReturn(new GoConfigHolder(new BasicCruiseConfig(), new BasicCruiseConfig())); flow.execute(updateConfigCommand, partials, null); verify(configForEdit).setPartials(partials); } @Test public void shouldPreprocessAndValidateTheUpdatedConfig() throws Exception { when(writer.documentFrom(configForEdit)).thenReturn(document); when(writer.toString(document)).thenReturn("cruise_config"); when(loader.loadConfigHolder(any(String.class), any(MagicalGoConfigXmlLoader.Callback.class))) .thenReturn(new GoConfigHolder(new BasicCruiseConfig(), new BasicCruiseConfig())); flow.execute(updateConfigCommand, partials, null); verify(loader).preprocessAndValidate(configForEdit); } @Test public void shouldValidateDomRepresentationOfCruiseConfig() throws Exception { when(writer.documentFrom(configForEdit)).thenReturn(document); when(writer.toString(document)).thenReturn("cruise_config"); when(loader.loadConfigHolder(any(String.class), any(MagicalGoConfigXmlLoader.Callback.class))) .thenReturn(new GoConfigHolder(new BasicCruiseConfig(), new BasicCruiseConfig())); flow.execute(updateConfigCommand, partials, null); verify(writer).verifyXsdValid(document); } @Test(expected = ConfigMergePreValidationException.class) public void shouldErrorOutIfPreprocessOrValidateFails() throws Exception { when(writer.documentFrom(configForEdit)).thenReturn(document); when(loader.preprocessAndValidate(configForEdit)).thenThrow(new Exception()); when(writer.toString(document)).thenReturn("cruise_config"); when(loader.loadConfigHolder(any(String.class), any(MagicalGoConfigXmlLoader.Callback.class))) .thenReturn(new GoConfigHolder(new BasicCruiseConfig(), new BasicCruiseConfig())); flow.execute(updateConfigCommand, partials, null); } @Test(expected = ConfigMergePostValidationException.class) public void shouldErrorOutIfSavingConfigPostValidationFails() throws Exception { when(writer.documentFrom(configForEdit)).thenReturn(document); when(writer.toString(document)).thenReturn("cruise_config"); when(loader.loadConfigHolder(any(String.class), any(MagicalGoConfigXmlLoader.Callback.class))) .thenThrow(new Exception()); when(configRepository.getConfigMergedWithLatestRevision(any(GoConfigRevision.class), anyString())).thenReturn("merged_config"); flow.execute(updateConfigCommand, partials, null); } @Test(expected = ConfigMergeException.class) public void shouldErrorOutIfConfigMergeFails() throws Exception { when(writer.documentFrom(configForEdit)).thenReturn(document); when(writer.toString(document)).thenReturn("cruise_config"); when(configRepository.getConfigMergedWithLatestRevision(any(GoConfigRevision.class), anyString())).thenThrow(new ConfigMergeException("merge fails")); flow.execute(updateConfigCommand, partials, null); } @Test public void shouldMergeConfigWithLatestRevision() throws Exception { when(writer.documentFrom(configForEdit)).thenReturn(document); when(writer.toString(document)).thenReturn("cruise_config"); when(loader.loadConfigHolder(any(String.class), any(MagicalGoConfigXmlLoader.Callback.class))) .thenReturn(new GoConfigHolder(new BasicCruiseConfig(), new BasicCruiseConfig())); flow.execute(updateConfigCommand, partials, null); verify(configRepository).getConfigMergedWithLatestRevision(new GoConfigRevision("config", "temporary-md5-for-branch", "user", "version", new TimeProvider()), "md5"); } @Test public void shouldLoadConfigHolderFromTheMergedConfigXML() throws Exception { String mergedConfig = "merged_config"; when(writer.documentFrom(configForEdit)).thenReturn(document); when(configRepository.getConfigMergedWithLatestRevision(any(GoConfigRevision.class), any(String.class))).thenReturn(mergedConfig); when(writer.toString(document)).thenReturn("cruise_config"); when(loader.loadConfigHolder(any(String.class), any(MagicalGoConfigXmlLoader.Callback.class))) .thenReturn(new GoConfigHolder(new BasicCruiseConfig(), new BasicCruiseConfig())); flow.execute(updateConfigCommand, partials, null); verify(loader).loadConfigHolder(any(String.class), any(MagicalGoConfigXmlLoader.Callback.class)); } @Test public void shouldPersistXmlRepresentationOfMergedCruiseConfig() throws Exception { String mergedConfig = "merged_config"; when(writer.documentFrom(configForEdit)).thenReturn(document); when(configRepository.getConfigMergedWithLatestRevision(any(GoConfigRevision.class), any(String.class))).thenReturn(mergedConfig); when(writer.toString(document)).thenReturn("cruise_config"); when(loader.loadConfigHolder(any(String.class), any(MagicalGoConfigXmlLoader.Callback.class))) .thenReturn(new GoConfigHolder(new BasicCruiseConfig(), new BasicCruiseConfig())); flow.execute(updateConfigCommand, partials, null); verify(fileWriter).writeToConfigXmlFile(mergedConfig); } @Test public void shouldCheckinGeneratedConfigXMLToConfigRepo() throws Exception { String mergedConfig = "merged_config"; Date currentTime = mock(Date.class); ArgumentCaptor<GoConfigRevision> revisionArgumentCaptor = ArgumentCaptor.forClass(GoConfigRevision.class); when(writer.documentFrom(configForEdit)).thenReturn(document); when(configRepository.getConfigMergedWithLatestRevision(any(GoConfigRevision.class), any(String.class))).thenReturn(mergedConfig); when(writer.toString(document)).thenReturn("cruise_config"); when(serverVersion.version()).thenReturn("16.13.0"); when(timeProvider.currentTime()).thenReturn(currentTime); when(loader.loadConfigHolder(any(String.class), any(MagicalGoConfigXmlLoader.Callback.class))) .thenReturn(new GoConfigHolder(new BasicCruiseConfig(), new BasicCruiseConfig())); doNothing().when(configRepository).checkin(revisionArgumentCaptor.capture()); flow.execute(updateConfigCommand, partials, "test_user"); GoConfigRevision goConfigRevision = revisionArgumentCaptor.getValue(); assertThat(goConfigRevision.getContent(), is(mergedConfig)); assertThat(goConfigRevision.getUsername(), is("test_user")); assertThat(goConfigRevision.getMd5(), is(updateConfigCommand.configForEdit().getMd5())); assertThat(goConfigRevision.getGoVersion(), is("16.13.0")); } @Test public void shouldUpdateCachedGoPartialsWithValidPartials() throws Exception { String mergedConfig = "merged_config"; ArrayList<PartialConfig> partials = new ArrayList<>(); when(writer.documentFrom(configForEdit)).thenReturn(document); when(configRepository.getConfigMergedWithLatestRevision(any(GoConfigRevision.class), any(String.class))).thenReturn(mergedConfig); when(writer.toString(document)).thenReturn("cruise_config"); when(loader.loadConfigHolder(any(String.class), any(MagicalGoConfigXmlLoader.Callback.class))) .thenReturn(new GoConfigHolder(new BasicCruiseConfig(), new BasicCruiseConfig())); InOrder inOrder = inOrder(configRepository, fileWriter, cachedGoPartials); flow.execute(updateConfigCommand, partials, null); inOrder.verify(configRepository).checkin(any(GoConfigRevision.class)); inOrder.verify(fileWriter).writeToConfigXmlFile(any(String.class)); inOrder.verify(cachedGoPartials).markAsValid(partials); } }