/* * 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.scheduling; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import com.thoughtworks.go.config.BasicCruiseConfig; import com.thoughtworks.go.config.CaseInsensitiveString; import com.thoughtworks.go.config.PipelineConfig; import com.thoughtworks.go.config.materials.MaterialConfigs; import com.thoughtworks.go.config.materials.Materials; import com.thoughtworks.go.config.materials.dependency.DependencyMaterial; import com.thoughtworks.go.config.materials.mercurial.HgMaterial; import com.thoughtworks.go.config.materials.mercurial.HgMaterialConfig; import com.thoughtworks.go.config.materials.svn.SvnMaterial; import com.thoughtworks.go.config.materials.svn.SvnMaterialConfig; import com.thoughtworks.go.config.remote.ConfigRepoConfig; import com.thoughtworks.go.config.remote.RepoConfigOrigin; import com.thoughtworks.go.domain.MaterialRevision; import com.thoughtworks.go.domain.MaterialRevisions; import com.thoughtworks.go.domain.buildcause.BuildCause; import com.thoughtworks.go.domain.materials.Material; import com.thoughtworks.go.domain.materials.Modification; import com.thoughtworks.go.helper.MaterialsMother; import com.thoughtworks.go.helper.ModificationsMother; import com.thoughtworks.go.helper.PipelineConfigMother; import com.thoughtworks.go.server.domain.PipelineConfigDependencyGraph; import com.thoughtworks.go.server.domain.PipelineTimeline; import com.thoughtworks.go.server.domain.Username; import com.thoughtworks.go.server.materials.MaterialUpdateCompletedTopic; import com.thoughtworks.go.server.materials.MaterialUpdateFailedMessage; import com.thoughtworks.go.server.materials.MaterialUpdateService; import com.thoughtworks.go.server.materials.MaterialUpdateStatusListener; import com.thoughtworks.go.server.materials.MaterialUpdateStatusNotifier; import com.thoughtworks.go.server.materials.MaterialUpdateSuccessfulMessage; import com.thoughtworks.go.server.materials.SpecificMaterialRevisionFactory; import com.thoughtworks.go.server.perf.SchedulingPerformanceLogger; import com.thoughtworks.go.server.persistence.MaterialRepository; import com.thoughtworks.go.server.service.AutoBuild; import com.thoughtworks.go.server.service.GoConfigService; import com.thoughtworks.go.server.service.ManualBuild; import com.thoughtworks.go.server.service.MaterialConfigConverter; import com.thoughtworks.go.server.service.MaterialExpansionService; import com.thoughtworks.go.server.service.NoModificationsPresentForDependentMaterialException; import com.thoughtworks.go.server.service.PipelineScheduleQueue; import com.thoughtworks.go.server.service.PipelineService; import com.thoughtworks.go.server.service.SchedulingCheckerService; import com.thoughtworks.go.server.service.result.HttpOperationResult; import com.thoughtworks.go.server.service.result.OperationResult; import com.thoughtworks.go.server.service.result.ServerHealthStateOperationResult; import com.thoughtworks.go.serverhealth.HealthStateScope; import com.thoughtworks.go.serverhealth.HealthStateType; import com.thoughtworks.go.serverhealth.ServerHealthService; import com.thoughtworks.go.serverhealth.ServerHealthState; import com.thoughtworks.go.util.SystemEnvironment; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import org.junit.Before; import org.junit.Test; import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import static com.thoughtworks.go.serverhealth.HealthStateScope.GLOBAL; import static com.thoughtworks.go.serverhealth.ServerHealthState.error; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; public class BuildCauseProducerServiceTest { private static final ServerHealthState SERVER_ERROR = error("something", "else", HealthStateType.general(GLOBAL)); private PipelineConfig pipelineConfig = new PipelineConfig(new CaseInsensitiveString("pipeline"), new MaterialConfigs()); @Mock private ServerHealthService mockServerHealthService; @Mock private SchedulingCheckerService mockSchedulingCheckerService; @Mock private MaterialUpdateStatusNotifier mockMaterialUpdateStatusNotifier; @Mock private MaterialUpdateService mockMaterialUpdateService; @Mock private OperationResult operationResult; @Mock private PipelineScheduleQueue pipelineScheduleQueue; @Mock private MaterialRepository materialRepository; @Mock private SpecificMaterialRevisionFactory specificMaterialRevisionFactory; @Mock private PipelineService pipelineService; @Mock private PipelineTimeline pipelineTimeline; @Mock private GoConfigService goConfigService; @Mock private MaterialConfigConverter materialConfigConverter; @Mock private MaterialExpansionService materialExpansionService; @Mock private SchedulingPerformanceLogger schedulingPerformanceLogger; private final Map<String, String> EMPTY_REVISIONS = new HashMap<>(); private HealthStateType healthStateType; private TriggerMonitor triggerMonitor; private BuildCauseProducerService buildCauseProducerService; @Before public void setUp() throws Exception { initMocks(this); triggerMonitor = new TriggerMonitor(); healthStateType = HealthStateType.general(HealthStateScope.forPipeline(CaseInsensitiveString.str(pipelineConfig.name()))); when(goConfigService.pipelineConfigNamed(pipelineConfig.name())).thenReturn(pipelineConfig); buildCauseProducerService = createBuildCauseProducerService(mockMaterialUpdateStatusNotifier); } private BuildCauseProducerService createBuildCauseProducerService(MaterialUpdateStatusNotifier materialUpdateStatusNotifier) { return new BuildCauseProducerService(mockSchedulingCheckerService, mockServerHealthService, pipelineScheduleQueue, goConfigService, materialRepository, materialUpdateStatusNotifier, mockMaterialUpdateService, specificMaterialRevisionFactory, triggerMonitor, pipelineService, new SystemEnvironment(), materialConfigConverter, materialExpansionService, schedulingPerformanceLogger); } @Test public void onErrorShouldUpdateServerHealthWhenUpdateServerHealthStatusByDefault() throws Exception { buildCauseProducerService.manualSchedulePipeline(Username.CRUISE_TIMER, pipelineConfig.name(), new ScheduleOptions(), errorResult()); verify(mockServerHealthService).update(SERVER_ERROR); } @Test public void shouldAllowRetriggeringIfThePreviousTriggerFailed() throws Exception { buildCauseProducerService.manualSchedulePipeline(Username.CRUISE_TIMER, pipelineConfig.name(), new ScheduleOptions(), errorResult()); HttpOperationResult result = new HttpOperationResult(); buildCauseProducerService.manualSchedulePipeline(Username.CRUISE_TIMER, pipelineConfig.name(), new ScheduleOptions(), result); assertThat(result.httpCode(), is(202)); } @Test public void shouldCheckForModificationsWhenManuallyScheduling() throws Exception { HgMaterialConfig hgMaterialConfig = new HgMaterialConfig("url", null); HgMaterial hgMaterial = new HgMaterial("url", null); SvnMaterial svnMaterial = new SvnMaterial("url", null, null, false); SvnMaterialConfig svnMaterialConfig = new SvnMaterialConfig("url", null, null, false); pipelineConfig.addMaterialConfig(hgMaterialConfig); pipelineConfig.addMaterialConfig(svnMaterialConfig); when(materialConfigConverter.toMaterial(hgMaterialConfig)).thenReturn(hgMaterial); when(materialConfigConverter.toMaterial(svnMaterialConfig)).thenReturn(svnMaterial); ServerHealthStateOperationResult result = new ServerHealthStateOperationResult(); buildCauseProducerService.manualSchedulePipeline(Username.ANONYMOUS, pipelineConfig.name(), new ScheduleOptions(), result); assertThat(result.getServerHealthState().isSuccess(), is(true)); verify(mockMaterialUpdateService, times(2)).updateMaterial(any(Material.class)); verify(mockMaterialUpdateStatusNotifier).registerListenerFor(eq(pipelineConfig), any(MaterialUpdateStatusListener.class)); } @Test public void shouldNotCheckForModificationsIfAlreadyChecking() throws Exception { Username user = Username.ANONYMOUS; final HttpOperationResult result = new HttpOperationResult(); when(mockSchedulingCheckerService.canTriggerManualPipeline(pipelineConfig, CaseInsensitiveString.str(user.getUsername()), operationResult)).thenAnswer(new Answer<Boolean>() { public Boolean answer(InvocationOnMock invocationOnMock) throws Throwable { result.accepted("junk", "junk", healthStateType); return true; } }); buildCauseProducerService.markPipelineAsAlreadyTriggered(pipelineConfig); buildCauseProducerService.manualSchedulePipeline(user, pipelineConfig.name(), new ScheduleOptions(), result); assertThat(result.canContinue(), is(false)); assertThat(result.message(), is("Failed to force pipeline: pipeline")); verify(mockMaterialUpdateService, never()).updateMaterial(any(Material.class)); verify(mockMaterialUpdateStatusNotifier, never()).registerListenerFor(eq(pipelineConfig), any(MaterialUpdateStatusListener.class)); } @Test public void shouldAllowTriggeringOfPipelineWhenThereIsAnErrorAfterPipelineIsMarkedAsTriggeredAndBeforeTheMaterialUpdateIsScheduled() throws Exception { try { when(operationResult.canContinue()).thenThrow(new RuntimeException("force a failure")); buildCauseProducerService.manualSchedulePipeline(Username.ANONYMOUS, pipelineConfig.name(), new ScheduleOptions(), operationResult); fail("expected exception, got none"); } catch (Exception e) { assertThat(triggerMonitor.isAlreadyTriggered(CaseInsensitiveString.str(pipelineConfig.name())), is(false)); } } @Test public void shouldAllowTriggeringOfPipelineAfterMaterialUpdate() throws Exception { HgMaterial hgMaterial = new HgMaterial("url", null); HgMaterialConfig hgMaterialConfig = new HgMaterialConfig("url", null); pipelineConfig.addMaterialConfig(hgMaterialConfig); when(materialConfigConverter.toMaterial(hgMaterialConfig)).thenReturn(hgMaterial); when(specificMaterialRevisionFactory.create("pipeline", new HashMap<>())).thenReturn(new MaterialRevisions()); when(pipelineScheduleQueue.mostRecentScheduled(CaseInsensitiveString.str(pipelineConfig.name()))).thenReturn(BuildCause.createNeverRun()); when(materialRepository.findLatestModification(hgMaterial)).thenReturn(new MaterialRevisions(new MaterialRevision(hgMaterial, new ArrayList<>()))); buildCauseProducerService.manualSchedulePipeline(Username.ANONYMOUS, pipelineConfig.name(), new ScheduleOptions(), new ServerHealthStateOperationResult()); assertThat(triggerMonitor.isAlreadyTriggered(CaseInsensitiveString.str(pipelineConfig.name())), is(true)); sendMaterialUpdateCompleteMessage(extractMaterialListenerInstanceFromRegisterCall(), hgMaterial); assertThat(triggerMonitor.isAlreadyTriggered(CaseInsensitiveString.str(pipelineConfig.name())), is(false)); } @Test public void manualTriggerShouldNotTriggerThePipelineIfMaterialUpdateFailed() throws Exception { HgMaterialConfig hgMaterialConfig = new HgMaterialConfig("url", null); HgMaterial hgMaterial = new HgMaterial("url", null); pipelineConfig.addMaterialConfig(hgMaterialConfig); when(materialConfigConverter.toMaterial(hgMaterialConfig)).thenReturn(hgMaterial); buildCauseProducerService.manualSchedulePipeline(Username.ANONYMOUS, pipelineConfig.name(), new ScheduleOptions(), new ServerHealthStateOperationResult()); sendMaterialUpdateFailedMessage(extractMaterialListenerInstanceFromRegisterCall(), hgMaterial); assertThat(triggerMonitor.isAlreadyTriggered(CaseInsensitiveString.str(pipelineConfig.name())), is(false)); } private void sendMaterialUpdateCompleteMessage(MaterialUpdateStatusListener materialUpdateStatusListener, HgMaterial material) { materialUpdateStatusListener.onMaterialUpdate(new MaterialUpdateSuccessfulMessage(material, 0)); } private void sendMaterialUpdateFailedMessage(MaterialUpdateStatusListener materialUpdateStatusListener, HgMaterial material) { materialUpdateStatusListener.onMaterialUpdate(new MaterialUpdateFailedMessage(material, 0, new RuntimeException("Seven Nation Army"))); } private MaterialUpdateStatusListener extractMaterialListenerInstanceFromRegisterCall() { final MaterialUpdateStatusListener[] listener = new MaterialUpdateStatusListener[1]; verify(mockMaterialUpdateStatusNotifier).registerListenerFor(any(PipelineConfig.class), argThat(new BaseMatcher<MaterialUpdateStatusListener>() { public boolean matches(Object o) { listener[0] = (MaterialUpdateStatusListener) o; return true; } public void describeTo(Description description) { } })); return listener[0]; } @Test public void shouldNotCheckForModificationsUnableToTriggerManualPipeline() throws Exception { buildCauseProducerService.manualSchedulePipeline(Username.ANONYMOUS, pipelineConfig.name(), new ScheduleOptions(), errorResult()); verify(mockMaterialUpdateService, never()).updateMaterial(any(Material.class)); verify(mockMaterialUpdateStatusNotifier, never()).registerListenerFor(eq(pipelineConfig), any(MaterialUpdateStatusListener.class)); } @Test public void shouldScheduleAfterAllMaterialsAreUpdated() throws Exception { HgMaterial hgMaterial = new HgMaterial("url", null); HgMaterialConfig hgMaterialConfig = new HgMaterialConfig("url", null); SvnMaterial svnMaterial = new SvnMaterial("url", null, null, false); SvnMaterialConfig svnMaterialConfig = new SvnMaterialConfig("url", null, null, false); pipelineConfig.addMaterialConfig(hgMaterialConfig); pipelineConfig.addMaterialConfig(svnMaterialConfig); GoConfigService service = mock(GoConfigService.class); when(service.pipelineConfigNamed(pipelineConfig.name())).thenReturn(pipelineConfig); when(materialConfigConverter.toMaterial(hgMaterialConfig)).thenReturn(hgMaterial); when(materialConfigConverter.toMaterial(svnMaterialConfig)).thenReturn(svnMaterial); MaterialUpdateStatusNotifier notifier = new MaterialUpdateStatusNotifier(mock(MaterialUpdateCompletedTopic.class)); buildCauseProducerService = spy(createBuildCauseProducerService(notifier)); buildCauseProducerService.manualSchedulePipeline(Username.ANONYMOUS, pipelineConfig.name(), new ScheduleOptions(), new ServerHealthStateOperationResult()); final HashMap<String, String> stringStringHashMap = new HashMap<>(); doReturn(ServerHealthState.success(healthStateType)).when(buildCauseProducerService).newProduceBuildCause( eq(pipelineConfig), any(ManualBuild.class), new ScheduleOptions(eq(EMPTY_REVISIONS), stringStringHashMap, new HashMap<>()), any(ServerHealthStateOperationResult.class), eq(12345L)); assertThat(notifier.hasListenerFor(pipelineConfig), is(true)); notifier.onMessage(new MaterialUpdateSuccessfulMessage(hgMaterial, 1111L)); assertThat(notifier.hasListenerFor(pipelineConfig), is(true)); notifier.onMessage(new MaterialUpdateSuccessfulMessage(svnMaterial, 2222L)); assertThat(notifier.hasListenerFor(pipelineConfig), is(false)); verify(buildCauseProducerService).newProduceBuildCause(eq(pipelineConfig), any(ManualBuild.class), eq(new ScheduleOptions()), any(ServerHealthStateOperationResult.class), eq(2222L)); } @Test public void shouldUpdateResultAsAcceptedOnSuccess() throws Exception { when(mockMaterialUpdateStatusNotifier.hasListenerFor(pipelineConfig)).thenReturn(false); when(operationResult.canContinue()).thenReturn(true); buildCauseProducerService.manualSchedulePipeline(Username.BLANK, pipelineConfig.name(), new ScheduleOptions(), operationResult); verify(operationResult).accepted(eq("Request to schedule pipeline pipeline accepted"), any(String.class), any(HealthStateType.class)); } @Test public void shouldBeAbleToPassInSpecificRevisionForMaterialsAndScheduleABuild() throws Exception { DependencyMaterial dependencyMaterial = new DependencyMaterial(new CaseInsensitiveString("upstream-pipeline"), new CaseInsensitiveString("stage")); SvnMaterial svnMaterial = new SvnMaterial("url", null, null, false); pipelineConfig.addMaterialConfig(dependencyMaterial.config()); pipelineConfig.addMaterialConfig(svnMaterial.config()); List<Modification> svnModifications = ModificationsMother.multipleModificationList(); MaterialConfigs knownMaterialConfigs = new MaterialConfigs(pipelineConfig.materialConfigs()); MaterialRevision specificMaterialRevision = new MaterialRevision(dependencyMaterial, new Modification(new Date(), "upstream-pipeline/2/stage/1", "MOCK_LABEL-12", null)); when(specificMaterialRevisionFactory.create(eq("pipeline"), eq(Collections.singletonMap(dependencyMaterial.getPipelineUniqueFingerprint(), "upstream-pipeline/2/stage/1")))) .thenReturn(new MaterialRevisions(specificMaterialRevision)); when(pipelineScheduleQueue.mostRecentScheduled("pipeline")).thenReturn(BuildCause.createNeverRun()); when(materialRepository.findLatestModification(svnMaterial)).thenReturn(new MaterialRevisions(new MaterialRevision(svnMaterial, svnModifications))); when(materialConfigConverter.toMaterials(pipelineConfig.materialConfigs())).thenReturn(new Materials(dependencyMaterial, svnMaterial)); when(materialExpansionService.expandMaterialConfigsForScheduling(pipelineConfig.materialConfigs())).thenReturn(knownMaterialConfigs); when(materialConfigConverter.toMaterials(knownMaterialConfigs)).thenReturn(new Materials(dependencyMaterial, svnMaterial)); ManualBuild buildType = new ManualBuild(Username.ANONYMOUS); final HashMap<String, String> stringStringHashMap = new HashMap<>(); buildCauseProducerService.newProduceBuildCause(pipelineConfig, buildType, new ScheduleOptions(Collections.singletonMap(dependencyMaterial.getPipelineUniqueFingerprint(), "upstream-pipeline/2/stage/1"), stringStringHashMap, new HashMap<>()), new ServerHealthStateOperationResult(), 12345); verify(pipelineScheduleQueue).schedule(eq("pipeline"), argThat(containsRevisions(new MaterialRevision(svnMaterial, svnModifications), specificMaterialRevision))); } @Test public void shouldHandleCaseWhereSpecifiedRevisionDoesNotExist() throws Exception { DependencyMaterial dependencyMaterial = new DependencyMaterial(new CaseInsensitiveString("upstream-pipeline"), new CaseInsensitiveString("stage")); when(specificMaterialRevisionFactory.create(eq("pipeline"), eq(Collections.singletonMap(dependencyMaterial.getPipelineUniqueFingerprint(), "upstream-pipeline/200/stage/1")))) .thenThrow(new RuntimeException("Invalid specified revision")); ManualBuild buildType = new ManualBuild(Username.ANONYMOUS); final HashMap<String, String> stringStringHashMap = new HashMap<>(); buildCauseProducerService.newProduceBuildCause(pipelineConfig, buildType, new ScheduleOptions(Collections.singletonMap(dependencyMaterial.getPipelineUniqueFingerprint(), "upstream-pipeline/200/stage/1"), stringStringHashMap, new HashMap<>()), new ServerHealthStateOperationResult(), 12345); verify(mockServerHealthService).update(argThat(hasErrorHealthState("Error while scheduling pipeline: pipeline", "Invalid specified revision"))); } @Test public void shouldHandleCaseWhenExceptionWithoutMessageIsRaised() throws Exception { DependencyMaterial dependencyMaterial = new DependencyMaterial(new CaseInsensitiveString("upstream-pipeline"), new CaseInsensitiveString("stage")); when(specificMaterialRevisionFactory.create(eq("pipeline"), eq(Collections.singletonMap(dependencyMaterial.getPipelineUniqueFingerprint(), "upstream-pipeline/200/stage/1")))) .thenThrow(new NullPointerException()); ManualBuild buildType = new ManualBuild(Username.ANONYMOUS); final HashMap<String, String> stringStringHashMap = new HashMap<>(); buildCauseProducerService.newProduceBuildCause(pipelineConfig, buildType, new ScheduleOptions(Collections.singletonMap(dependencyMaterial.getPipelineUniqueFingerprint(), "upstream-pipeline/200/stage/1"), stringStringHashMap, new HashMap<>()), new ServerHealthStateOperationResult(), 12345); verify(mockServerHealthService).update(argThat(hasErrorHealthState("Error while scheduling pipeline: pipeline", "Details not available, please check server logs."))); } @Test public void shouldUpdateOnlyOnceIfThereAreTwoMaterialsWithSameFingerPrintButDifferentDest() { HgMaterial material1 = new HgMaterial("url", null); HgMaterial material2 = new HgMaterial("url", null); HgMaterialConfig materialConfig1 = new HgMaterialConfig("url", null); HgMaterialConfig materialConfig2 = new HgMaterialConfig("url", null); material1.setFolder("folder1"); material2.setFolder("folder2"); assertThat(material1.getFingerprint(), is(material2.getFingerprint())); pipelineConfig.addMaterialConfig(materialConfig1); pipelineConfig.addMaterialConfig(materialConfig2); Material[] materials = new Material[]{material1, material2}; when(materialConfigConverter.toMaterial(materialConfig1)).thenReturn(material1); when(materialConfigConverter.toMaterial(materialConfig2)).thenReturn(material2); buildCauseProducerService.manualSchedulePipeline(Username.ANONYMOUS, pipelineConfig.name(), new ScheduleOptions(new HashMap<>(), new HashMap<>(), new HashMap<>()), new ServerHealthStateOperationResult()); verify(mockMaterialUpdateService, times(1)).updateMaterial(any(Material.class)); MaterialUpdateStatusListener statusListener = extractMaterialListenerInstanceFromRegisterCall(); statusListener.onMaterialUpdate(new MaterialUpdateFailedMessage(materials[0], 0, new Exception("Cannot connect to repo"))); verify(mockMaterialUpdateStatusNotifier).removeListenerFor(pipelineConfig); } @Test public void manualTrigger_shouldUpdatePipelineConfigWhenMaterialIsConfigRepo() { HgMaterial material1 = new HgMaterial("url", null); HgMaterialConfig materialConfig1 = new HgMaterialConfig("url", null); pipelineConfig.addMaterialConfig(materialConfig1); pipelineConfig.setOrigin(new RepoConfigOrigin(new ConfigRepoConfig(materialConfig1,"plug"),"revision1")); when(materialConfigConverter.toMaterial(materialConfig1)).thenReturn(material1); when(goConfigService.hasPipelineNamed(pipelineConfig.name())).thenReturn(true); buildCauseProducerService.manualSchedulePipeline(Username.ANONYMOUS, pipelineConfig.name(), new ScheduleOptions(new HashMap<>(), new HashMap<>(), new HashMap<>()), new ServerHealthStateOperationResult()); verify(goConfigService, times(1)).pipelineConfigNamed(pipelineConfig.name()); MaterialUpdateStatusListener statusListener = extractMaterialListenerInstanceFromRegisterCall(); statusListener.onMaterialUpdate(new MaterialUpdateSuccessfulMessage(material1, 0)); verify(mockMaterialUpdateStatusNotifier).removeListenerFor(pipelineConfig); verify(goConfigService, times(2)).pipelineConfigNamed(pipelineConfig.name()); } @Test public void manualTrigger_shouldNotUpdatePipelineConfigWhenConfigRepoIsNotInMaterials() { HgMaterial material1 = new HgMaterial("url", null); HgMaterialConfig materialConfig1 = new HgMaterialConfig("url", null); HgMaterialConfig materialConfig2 = new HgMaterialConfig("url2", null); pipelineConfig.addMaterialConfig(materialConfig1); pipelineConfig.setOrigin(new RepoConfigOrigin(new ConfigRepoConfig(materialConfig2,"plug"),"revision1")); when(materialConfigConverter.toMaterial(materialConfig1)).thenReturn(material1); buildCauseProducerService.manualSchedulePipeline(Username.ANONYMOUS, pipelineConfig.name(), new ScheduleOptions(new HashMap<>(), new HashMap<>(), new HashMap<>()), new ServerHealthStateOperationResult()); verify(goConfigService, times(1)).pipelineConfigNamed(pipelineConfig.name()); MaterialUpdateStatusListener statusListener = extractMaterialListenerInstanceFromRegisterCall(); statusListener.onMaterialUpdate(new MaterialUpdateSuccessfulMessage(material1, 0)); verify(mockMaterialUpdateStatusNotifier).removeListenerFor(pipelineConfig); verify(goConfigService, times(1)).pipelineConfigNamed(pipelineConfig.name()); } @Test public void manualTrigger_shouldRequestUpdateOfNewMaterials_WhenPipelineConfigInConfigRepo() { HgMaterial material1 = new HgMaterial("url", null); HgMaterial material2 = new HgMaterial("url2", null); HgMaterialConfig materialConfig1 = new HgMaterialConfig("url", null); HgMaterialConfig materialConfig2 = new HgMaterialConfig("url2", null); pipelineConfig.addMaterialConfig(materialConfig1); pipelineConfig.setOrigin(new RepoConfigOrigin(new ConfigRepoConfig(materialConfig1,"plug"),"revision1")); when(materialConfigConverter.toMaterial(materialConfig1)).thenReturn(material1); when(materialConfigConverter.toMaterial(materialConfig2)).thenReturn(material2); buildCauseProducerService.manualSchedulePipeline(Username.ANONYMOUS, pipelineConfig.name(), new ScheduleOptions(new HashMap<>(), new HashMap<>(), new HashMap<>()), new ServerHealthStateOperationResult()); verify(goConfigService, times(1)).pipelineConfigNamed(pipelineConfig.name()); // updated pipeline config PipelineConfig pipelineConfig1 = new PipelineConfig(new CaseInsensitiveString("pipeline"), new MaterialConfigs()); pipelineConfig1.addMaterialConfig(materialConfig1); pipelineConfig1.addMaterialConfig(materialConfig2); when(goConfigService.pipelineConfigNamed(pipelineConfig.name())).thenReturn(pipelineConfig1); when(goConfigService.hasPipelineNamed(pipelineConfig.name())).thenReturn(true); MaterialUpdateStatusListener statusListener = extractMaterialListenerInstanceFromRegisterCall(); statusListener.onMaterialUpdate(new MaterialUpdateSuccessfulMessage(material1, 0)); verify(goConfigService, times(2)).pipelineConfigNamed(pipelineConfig.name()); verify(mockMaterialUpdateService,times(1)).updateMaterial(material1); verify(mockMaterialUpdateService,times(1)).updateMaterial(material2); statusListener.onMaterialUpdate(new MaterialUpdateSuccessfulMessage(material2, 0)); verify(mockMaterialUpdateStatusNotifier).removeListenerFor(pipelineConfig1); } @Test public void shouldHandleNoModificationExceptionThrownByAutoBuild() { String pipelineName = "pipeline"; ServerHealthStateOperationResult result = new ServerHealthStateOperationResult(); PipelineConfig config = PipelineConfigMother.pipelineConfig(pipelineName); Material svnMaterial = MaterialsMother.defaultMaterials().get(0); DependencyMaterial dependencyMaterial = new DependencyMaterial(new CaseInsensitiveString("up"), new CaseInsensitiveString("s1")); config.materialConfigs().clear(); config.addMaterialConfig(svnMaterial.config()); config.addMaterialConfig(dependencyMaterial.config()); when(pipelineService.getRevisionsBasedOnDependencies(Matchers.<MaterialRevisions>any(), Matchers.<BasicCruiseConfig>any(), Matchers.<CaseInsensitiveString>any())).thenThrow( new NoModificationsPresentForDependentMaterialException("P/1/S/1")); when(pipelineScheduleQueue.mostRecentScheduled(pipelineName)).thenReturn(BuildCause.createNeverRun()); Modification modification = ModificationsMother.checkinWithComment("r", "c", new Date(), "f1"); when(materialRepository.findLatestModification(svnMaterial)).thenReturn(ModificationsMother.createSvnMaterialWithMultipleRevisions(1, modification)); when(materialRepository.findLatestModification(dependencyMaterial)).thenReturn(new MaterialRevisions(ModificationsMother.changedDependencyMaterialRevision("up", 1, "1", "s", 1, new Date()))); when(specificMaterialRevisionFactory.create(Matchers.<String>any(), Matchers.<Map<String, String>>any())).thenReturn(MaterialRevisions.EMPTY); when(goConfigService.upstreamDependencyGraphOf(Matchers.<String>any(), Matchers.<BasicCruiseConfig>any())).thenReturn(new PipelineConfigDependencyGraph(config)); MaterialConfigs knownMaterialConfigs = new MaterialConfigs(svnMaterial.config(), dependencyMaterial.config()); Materials materials = new Materials(svnMaterial, dependencyMaterial); when(materialConfigConverter.toMaterials(config.materialConfigs())).thenReturn(materials); when(materialExpansionService.expandMaterialConfigsForScheduling(config.materialConfigs())).thenReturn(knownMaterialConfigs); when(materialConfigConverter.toMaterials(knownMaterialConfigs)).thenReturn(materials); AutoBuild autoBuild = new AutoBuild(goConfigService, pipelineService, pipelineName, new SystemEnvironment(), null, mockServerHealthService); ServerHealthState serverHealthState = buildCauseProducerService.newProduceBuildCause(config, autoBuild, result, 12345); assertThat(serverHealthState.isSuccess(), is(true)); } private TypeSafeMatcher<ServerHealthState> hasErrorHealthState(final String message, final String description) { return new TypeSafeMatcher<ServerHealthState>() { @Override public boolean matchesSafely(ServerHealthState item) { assertThat("isSuccess", item.isSuccess(), is(false)); assertThat("message", item.getMessage(), is(message)); assertThat("description", item.getDescription(), is(description)); return true; } public void describeTo(Description description) { description.appendText("message = [" + message + "] and description = [" + description + "]"); } }; } private TypeSafeMatcher<BuildCause> containsRevisions(final MaterialRevision... revisions) { return new TypeSafeMatcher<BuildCause>() { @Override public boolean matchesSafely(BuildCause item) { for (MaterialRevision revision : revisions) { if (!item.getMaterialRevisions().getRevisions().contains(revision)) { return false; } } return true; } public void describeTo(Description description) { description.appendText("should contain revisions " + Arrays.toString(revisions)); } }; } private OperationResult errorResult() { HttpOperationResult operationResult1 = new HttpOperationResult(); operationResult1.error("something", "else", HealthStateType.general(GLOBAL)); return operationResult1; } }