/* * 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.server.service; import com.thoughtworks.go.config.*; import com.thoughtworks.go.config.materials.Filter; import com.thoughtworks.go.config.materials.IgnoredFiles; import com.thoughtworks.go.config.materials.dependency.DependencyMaterialConfig; 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.domain.MaterialRevision; import com.thoughtworks.go.domain.MaterialRevisions; import com.thoughtworks.go.domain.buildcause.BuildCause; import com.thoughtworks.go.domain.materials.MaterialConfig; import com.thoughtworks.go.domain.materials.Modification; import com.thoughtworks.go.helper.GoConfigMother; import com.thoughtworks.go.helper.MaterialConfigsMother; import com.thoughtworks.go.helper.MaterialsMother; import com.thoughtworks.go.helper.PipelineConfigMother; import com.thoughtworks.go.server.domain.PipelineConfigDependencyGraph; import com.thoughtworks.go.server.materials.MaterialChecker; 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.GoConstants; import com.thoughtworks.go.util.SystemEnvironment; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import java.util.Date; import static com.thoughtworks.go.helper.ModificationsMother.*; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; public class AutoBuildCauseTest { @Mock private GoConfigService goConfigService; @Mock private PipelineService pipelineService; @Mock private MaterialChecker materialChecker; @Mock private SystemEnvironment systemEnvironment; private CruiseConfig cruiseConfig; private ServerHealthService serverHealthService; @Before public void setUp() throws Exception { initMocks(this); cruiseConfig = new BasicCruiseConfig(); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); when(materialChecker.hasPipelineEverRunWith(any(String.class), any(MaterialRevisions.class))).thenReturn(false); serverHealthService = new ServerHealthService(); } @Test public void shouldThrowExceptionIfNoChanges() throws Exception { MaterialRevisions modifications = new MaterialRevisions(); try { new AutoBuild(goConfigService, pipelineService, "foo", new SystemEnvironment(), materialChecker, serverHealthService).onModifications(modifications, false, null); Assert.fail("Should throw Exception"); } catch (Exception e) { } } @Test public void shouldSetApproverToCruiseForTheProducedBuildCause() throws Exception { SvnMaterial material = new SvnMaterial("http://foo.bar/baz", "user", "pass", false); MaterialRevisions materialRevisions = new MaterialRevisions(new MaterialRevision(material, new Modification(new Date(), "1234", "MOCK_LABEL-12", null))); when(goConfigService.upstreamDependencyGraphOf("foo", cruiseConfig)).thenReturn(new PipelineConfigDependencyGraph(null)); when(pipelineService.getRevisionsBasedOnDependencies(materialRevisions, cruiseConfig, new CaseInsensitiveString("foo"))).thenReturn(materialRevisions); BuildCause buildCause = new AutoBuild(goConfigService, pipelineService, "foo", new SystemEnvironment(), materialChecker, serverHealthService).onModifications(materialRevisions, false, null); assertThat(buildCause.getApprover(), is(GoConstants.DEFAULT_APPROVED_BY)); } @Test public void shouldReturnNullIfUpstreamMaterialHasNotChanged_WithFaninOff() throws Exception { PipelineConfigDependencyGraph dependencyGraph = dependencyGraphOfDepthOne(MaterialConfigsMother.hgMaterialConfig()); MaterialRevisions revisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("2")); MaterialRevision dependencyRevision = dependencyMaterialRevision("up1", 1, "label", "first", 1, new Date()); dependencyRevision.markAsNotChanged(); revisions.addRevision(dependencyRevision); when(goConfigService.upstreamDependencyGraphOf("current", cruiseConfig)).thenReturn(dependencyGraph); when(systemEnvironment.enforceRevisionCompatibilityWithUpstream()).thenReturn(false); AutoBuild autoBuild = new AutoBuild(goConfigService, pipelineService, "current", systemEnvironment, materialChecker, serverHealthService); BuildCause current = autoBuild.onModifications(revisions, false, null); assertThat(current, is(nullValue())); } @Test public void shouldReturnNullIfUpstreamMaterialAndFirstOrderMaterialHaveNotChanged_WithFaninOff() throws Exception { PipelineConfigDependencyGraph dependencyGraph = dependencyGraphOfDepthOne(MaterialConfigsMother.hgMaterialConfig(), MaterialConfigsMother.svnMaterialConfig()); MaterialRevisions revisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("2")); MaterialRevisions firstOrderRevision = createSvnMaterialWithMultipleRevisions(1, oneModifiedFile("2")); MaterialRevision dependencyRevision = dependencyMaterialRevision("up1", 1, "label", "first", 1, new Date()); revisions.addAll(firstOrderRevision); revisions.addRevision(dependencyRevision); dependencyRevision.markAsNotChanged(); firstOrderRevision.getMaterialRevision(0).markAsNotChanged(); revisions.getMaterialRevision(0).markAsChanged(); when(goConfigService.upstreamDependencyGraphOf("downstream", cruiseConfig)).thenReturn(dependencyGraph); when(systemEnvironment.enforceRevisionCompatibilityWithUpstream()).thenReturn(false); AutoBuild build = new AutoBuild(goConfigService, pipelineService, "downstream", systemEnvironment, materialChecker, serverHealthService); BuildCause cause = build.onModifications(revisions, false, null); assertThat(cause, is(nullValue())); } @Test public void shouldReturnCorrectRevisionsIfFirstOrderMaterialIsChanged() throws Exception { PipelineConfigDependencyGraph dependencyGraph = dependencyGraphOfDepthOne(MaterialConfigsMother.hgMaterialConfig(), MaterialConfigsMother.svnMaterialConfig()); String targetPipeline = dependencyGraph.getCurrent().name().toLower(); MaterialRevisions revisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("2")); MaterialRevisions firstOrderRevision = createSvnMaterialWithMultipleRevisions(1, oneModifiedFile("2")); MaterialRevision dependencyRevision = dependencyMaterialRevision("upstream", 1, "label", "first", 1, new Date()); revisions.addAll(firstOrderRevision); revisions.addRevision(dependencyRevision); dependencyRevision.markAsNotChanged(); firstOrderRevision.getMaterialRevision(0).markAsChanged(); revisions.getMaterialRevision(0).markAsChanged(); MaterialRevisions expectedRevisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("1")); expectedRevisions.addRevision(dependencyRevision); when(goConfigService.upstreamDependencyGraphOf(targetPipeline, cruiseConfig)).thenReturn(dependencyGraph); when(pipelineService.getRevisionsBasedOnDependencies(eq(revisions), eq(cruiseConfig), eq(dependencyGraph.getCurrent().name()))).thenReturn(expectedRevisions); assertThat(new AutoBuild(goConfigService, pipelineService, targetPipeline, new SystemEnvironment(), materialChecker, serverHealthService).onModifications(revisions, false, null).getMaterialRevisions(), is(expectedRevisions)); } @Test public void shouldReturnCorrectRevisionsIfUpstreamIgnoresAllTheModificationsAndFirstOrderMaterialNotChanged() throws Exception { PipelineConfigDependencyGraph dependencyGraph = dependencyGraphOfDepthOne(MaterialConfigsMother.hgMaterialConfig(), MaterialConfigsMother.svnMaterialConfig()); String targetPipeline = dependencyGraph.getCurrent().name().toLower(); firstHgMaterial(dependencyGraph).setFilter(new Filter(new IgnoredFiles("**/*.xml"))); MaterialRevisions revisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("2")); MaterialRevisions firstOrderRevision = createSvnMaterialWithMultipleRevisions(1, oneModifiedFile("2")); MaterialRevision dependencyRevision = dependencyMaterialRevision("up1", 1, "label", "first", 1, new Date()); revisions.addAll(firstOrderRevision); revisions.addRevision(dependencyRevision); dependencyRevision.markAsNotChanged(); firstOrderRevision.getMaterialRevision(0).markAsNotChanged(); revisions.getMaterialRevision(0).markAsChanged(); MaterialRevisions expectedRevisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("2")); expectedRevisions.addRevision(dependencyRevision); when(goConfigService.upstreamDependencyGraphOf(targetPipeline, cruiseConfig)).thenReturn(dependencyGraph); when(pipelineService.getRevisionsBasedOnDependencies(eq(revisions), eq(cruiseConfig), eq(dependencyGraph.getCurrent().name()))).thenReturn(expectedRevisions); assertThat(new AutoBuild(goConfigService, pipelineService, targetPipeline, new SystemEnvironment(), materialChecker, serverHealthService).onModifications(revisions, false, null).getMaterialRevisions(), is(expectedRevisions)); } @Test public void shouldReturnNullIfUpstreamMaterialHasChangedButNoFirstOrderMaterialHas_WithFaninOff() throws Exception { HgMaterialConfig hg = MaterialConfigsMother.hgMaterialConfig(); PipelineConfig third = PipelineConfigMother.pipelineConfig("third", MaterialConfigsMother.dependencyMaterialConfig("second", "mingle"), new JobConfigs()); PipelineConfig second = PipelineConfigMother.pipelineConfig("second", MaterialConfigsMother.dependencyMaterialConfig("first", "mingle"), new JobConfigs()); PipelineConfig first = PipelineConfigMother.pipelineConfig("first", hg, new JobConfigs()); PipelineConfigDependencyGraph dependencyGraph = new PipelineConfigDependencyGraph(third, new PipelineConfigDependencyGraph(second, new PipelineConfigDependencyGraph(first)), new PipelineConfigDependencyGraph(first) ); MaterialRevisions revisions = new MaterialRevisions(); MaterialRevision firstRev = dependencyMaterialRevision("first", 1, "label", "mingle", 1, new Date()); firstRev.markAsChanged(); MaterialRevision secondRev = dependencyMaterialRevision("second", 1, "label", "mingle", 1, new Date()); secondRev.markAsNotChanged(); revisions.addRevision(secondRev); revisions.addRevision(firstRev); when(goConfigService.upstreamDependencyGraphOf("third", cruiseConfig)).thenReturn(dependencyGraph); when(systemEnvironment.enforceRevisionCompatibilityWithUpstream()).thenReturn(false); AutoBuild build = new AutoBuild(goConfigService, pipelineService, "third", systemEnvironment, materialChecker, serverHealthService); BuildCause cause = build.onModifications(revisions, false, null); assertThat(cause, is(nullValue())); } @Test public void shouldReturnPeggedRevisionsForUpstreamMaterialWhenFirstOrderDependencyMaterialIsChanged() throws Exception { HgMaterialConfig hg = MaterialConfigsMother.hgMaterialConfig(); PipelineConfig third = PipelineConfigMother.pipelineConfig("third", MaterialConfigsMother.dependencyMaterialConfig("second", "mingle"), new JobConfigs()); PipelineConfig second = PipelineConfigMother.pipelineConfig("second", MaterialConfigsMother.dependencyMaterialConfig("first", "mingle"), new JobConfigs()); PipelineConfig first = PipelineConfigMother.pipelineConfig("first", hg, new JobConfigs()); PipelineConfigDependencyGraph dependencyGraph = new PipelineConfigDependencyGraph(third, new PipelineConfigDependencyGraph(second, new PipelineConfigDependencyGraph(first)), new PipelineConfigDependencyGraph(first) ); MaterialRevisions revisions = new MaterialRevisions(); MaterialRevision firstRev = dependencyMaterialRevision("first", 10, "label", "mingle", 1, new Date()); firstRev.markAsNotChanged(); MaterialRevision secondRev = dependencyMaterialRevision("second", 1, "label", "mingle", 1, new Date()); secondRev.markAsChanged(); revisions.addRevision(secondRev); revisions.addRevision(firstRev); when(goConfigService.upstreamDependencyGraphOf("third", cruiseConfig)).thenReturn(dependencyGraph); MaterialRevisions expectedRevisions = new MaterialRevisions(); when(pipelineService.getRevisionsBasedOnDependencies(eq(revisions), eq(cruiseConfig), eq(dependencyGraph.getCurrent().name()))).thenReturn(expectedRevisions); assertThat(new AutoBuild(goConfigService, pipelineService, "third", new SystemEnvironment(), materialChecker, serverHealthService).onModifications(revisions, false, null).getMaterialRevisions(), sameInstance(expectedRevisions)); } @Test public void shouldUseTheMaterialRevisionsAfterGettingTheRightVersionsBasedOnDependency() throws Exception { PipelineConfigDependencyGraph dependencyGraph = dependencyGraphOfDepthOne(MaterialConfigsMother.hgMaterialConfig()); String targetPipeline = dependencyGraph.getCurrent().name().toLower(); MaterialRevisions revisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("2")); MaterialRevision dependencyRevision = dependencyMaterialRevision("up1", 1, "label", "first", 1, new Date()); dependencyRevision.markAsChanged(); revisions.addRevision(dependencyRevision); MaterialRevisions expectedRevisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("1")); expectedRevisions.addRevision(dependencyRevision); when(goConfigService.upstreamDependencyGraphOf(targetPipeline, cruiseConfig)).thenReturn(dependencyGraph); when(pipelineService.getRevisionsBasedOnDependencies(eq(revisions), eq(cruiseConfig), eq(dependencyGraph.getCurrent().name()))).thenReturn(expectedRevisions); BuildCause buildCause = new AutoBuild(goConfigService, pipelineService, targetPipeline, new SystemEnvironment(), materialChecker, serverHealthService).onModifications(revisions, false, null); MaterialRevision expected = expectedRevisions.getMaterialRevision(0); assertThat(buildCause.getMaterialRevisions().getMaterialRevision(0), is(expected)); } @Test public void shouldUpdateRecomputedMaterialRevisionsChangedStatus() throws Exception { PipelineConfigDependencyGraph dependencyGraph = dependencyGraphOfDepthOne(MaterialConfigsMother.hgMaterialConfig(), MaterialConfigsMother.svnMaterialConfig()); String targetPipeline = dependencyGraph.getCurrent().name().toLower(); MaterialRevisions revisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("2"), oneModifiedFile("1")); MaterialRevisions revisionsForMaterial2 = multipleRevisions(MaterialsMother.svnMaterial(), 10, oneModifiedFile("svn2")); MaterialRevision dependencyRevision = dependencyMaterialRevision("upstream", 1, "label", "first", 1, new Date()); revisions.addRevision(dependencyRevision); revisions.addAll(revisionsForMaterial2); for (MaterialRevision revision : revisions) { revision.markAsChanged(); } MaterialRevisions expectedRevisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("1")); expectedRevisions.getMaterialRevision(0).markAsChanged(); MaterialRevisions expectedForMaterial2 = multipleRevisions(MaterialsMother.svnMaterial(), 10, oneModifiedFile("svn1")); expectedForMaterial2.getMaterialRevision(0).markAsNotChanged(); expectedRevisions.addRevision(dependencyRevision); expectedRevisions.addAll(expectedForMaterial2); when(goConfigService.upstreamDependencyGraphOf(targetPipeline, cruiseConfig)).thenReturn(dependencyGraph); when(pipelineService.getRevisionsBasedOnDependencies(eq(revisions), eq(cruiseConfig), eq(dependencyGraph.getCurrent().name()))).thenReturn(expectedRevisions); BuildCause buildCause = new AutoBuild(goConfigService, pipelineService, targetPipeline, new SystemEnvironment(), materialChecker, serverHealthService).onModifications(revisions, false, null); MaterialRevisions finalRevisions = buildCause.getMaterialRevisions(); assertThat(finalRevisions.numberOfRevisions(), is(expectedRevisions.numberOfRevisions())); for (int i = 0; i < expectedRevisions.numberOfRevisions(); i++) { MaterialRevision finalRev = finalRevisions.getMaterialRevision(i); MaterialRevision expectedRev = expectedRevisions.getMaterialRevision(i); assertThat(finalRev, is(expectedRev)); assertThat(finalRev.isChanged(), is(expectedRev.isChanged())); } } @Test public void shouldFallbackToFanInOffTriangleDependencyBehaviourOnExceptionInFanInOn() throws Exception { PipelineConfigDependencyGraph dependencyGraph = dependencyGraphOfDepthOne(MaterialConfigsMother.hgMaterialConfig()); String targetPipeline = dependencyGraph.getCurrent().name().toLower(); MaterialRevisions revisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("2")); MaterialRevision dependencyRevision = dependencyMaterialRevision("up1", 1, "label", "first", 1, new Date()); dependencyRevision.markAsChanged(); revisions.addRevision(dependencyRevision); when(goConfigService.upstreamDependencyGraphOf(targetPipeline, cruiseConfig)).thenReturn(dependencyGraph); // first time throw exception to check fanin off behavior and server logs. second time return null (no exception) to check that the server health logs are cleared when(pipelineService.getRevisionsBasedOnDependencies(eq(revisions), eq(cruiseConfig), eq(dependencyGraph.getCurrent().name()))).thenThrow(new RuntimeException("failed")).thenReturn(null); when(systemEnvironment.enforceRevisionCompatibilityWithUpstream()).thenReturn(true); when(systemEnvironment.enforceFanInFallbackBehaviour()).thenReturn(true); new AutoBuild(goConfigService, pipelineService, targetPipeline, systemEnvironment, materialChecker, serverHealthService).onModifications(revisions, false, null); verify(pipelineService, times(1)).getRevisionsBasedOnDependencies(dependencyGraph, revisions); assertThat(serverHealthService.getAllLogs().size(), is(1)); assertThat(serverHealthService.getAllLogs(), hasItem((ServerHealthState.warning("Turning off Fan-In for pipeline: 'current'", "Error occurred during Fan-In resolution for the pipeline.", HealthStateType.general(HealthStateScope.forFanin(targetPipeline)))))); new AutoBuild(goConfigService, pipelineService, targetPipeline, systemEnvironment, materialChecker, serverHealthService).onModifications(revisions, false, null); verify(pipelineService, times(1)).getRevisionsBasedOnDependencies(dependencyGraph, revisions); assertThat(serverHealthService.getAllLogs().size(), is(0)); } @Test public void shouldNotFallbackToFanInOffTriangleDependencyBehaviourOnNoCompatibleUpstreamRevisionsException() throws Exception { PipelineConfigDependencyGraph dependencyGraph = dependencyGraphOfDepthOne(MaterialConfigsMother.hgMaterialConfig()); String targetPipeline = dependencyGraph.getCurrent().name().toLower(); MaterialRevisions revisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("2")); MaterialRevision dependencyRevision = dependencyMaterialRevision("up1", 1, "label", "first", 1, new Date()); dependencyRevision.markAsChanged(); revisions.addRevision(dependencyRevision); NoCompatibleUpstreamRevisionsException expectedException = NoCompatibleUpstreamRevisionsException.failedToFindCompatibleRevision(new CaseInsensitiveString("downstream"), null); when(goConfigService.upstreamDependencyGraphOf(targetPipeline, cruiseConfig)).thenReturn(dependencyGraph); when(pipelineService.getRevisionsBasedOnDependencies(eq(revisions), eq(cruiseConfig), eq(dependencyGraph.getCurrent().name()))).thenThrow(expectedException); when(systemEnvironment.enforceRevisionCompatibilityWithUpstream()).thenReturn(true); when(systemEnvironment.enforceFanInFallbackBehaviour()).thenReturn(false); try { new AutoBuild(goConfigService, pipelineService, targetPipeline, systemEnvironment, materialChecker, serverHealthService).onModifications(revisions, false, null); fail("should have thrown exception"); } catch (NoCompatibleUpstreamRevisionsException e) { assertThat(e, is(expectedException)); } } @Test public void shouldTurnOffFanInFallbackBehaviourWhenSystemEnvironmentVariableIsOff() throws Exception { PipelineConfigDependencyGraph dependencyGraph = dependencyGraphOfDepthOne(MaterialConfigsMother.hgMaterialConfig()); String targetPipeline = dependencyGraph.getCurrent().name().toLower(); MaterialRevisions revisions = createHgMaterialWithMultipleRevisions(1, oneModifiedFile("2")); MaterialRevision dependencyRevision = dependencyMaterialRevision("up1", 1, "label", "first", 1, new Date()); dependencyRevision.markAsChanged(); revisions.addRevision(dependencyRevision); RuntimeException expectedException = new RuntimeException("failed"); when(goConfigService.upstreamDependencyGraphOf(targetPipeline, cruiseConfig)).thenReturn(dependencyGraph); when(pipelineService.getRevisionsBasedOnDependencies(eq(revisions), eq(cruiseConfig), eq(dependencyGraph.getCurrent().name()))).thenThrow(expectedException); when(systemEnvironment.enforceRevisionCompatibilityWithUpstream()).thenReturn(true); when(systemEnvironment.enforceFanInFallbackBehaviour()).thenReturn(false); try { new AutoBuild(goConfigService, pipelineService, targetPipeline, systemEnvironment, materialChecker, serverHealthService).onModifications(revisions, false, null); fail("should have thrown exception"); } catch (RuntimeException e) { assertThat(e, is(expectedException)); } } private PipelineConfigDependencyGraph dependencyGraphOfDepthOne(MaterialConfig sharedMaterial, MaterialConfig firstOrderMaterial) { PipelineConfig current; if (firstOrderMaterial != null) { current = GoConfigMother.createPipelineConfigWithMaterialConfig("current", sharedMaterial, firstOrderMaterial, new DependencyMaterialConfig(new CaseInsensitiveString("up1"), new CaseInsensitiveString("first"))); } else { current = GoConfigMother.createPipelineConfigWithMaterialConfig("current", sharedMaterial, new DependencyMaterialConfig(new CaseInsensitiveString("up1"), new CaseInsensitiveString("first"))); } PipelineConfig upStream = GoConfigMother.createPipelineConfigWithMaterialConfig("up1", sharedMaterial); return new PipelineConfigDependencyGraph(current, new PipelineConfigDependencyGraph(upStream)); } private PipelineConfigDependencyGraph dependencyGraphOfDepthOne(MaterialConfig sharedMaterial) { return dependencyGraphOfDepthOne(sharedMaterial, null); } private HgMaterial firstHgMaterial(PipelineConfigDependencyGraph dependencyGraph) { return ((HgMaterial) new MaterialConfigConverter().toMaterial(daddy(dependencyGraph).materialConfigs().first())); } private PipelineConfig daddy(PipelineConfigDependencyGraph dependencyGraph) { return dependencyGraph.getUpstreamDependencies().get(0).getCurrent(); } }