/* * 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.service; import com.thoughtworks.go.config.*; import com.thoughtworks.go.config.materials.MaterialConfigs; import com.thoughtworks.go.config.materials.dependency.DependencyMaterial; import com.thoughtworks.go.config.materials.dependency.DependencyMaterialConfig; import com.thoughtworks.go.config.materials.git.GitMaterial; import com.thoughtworks.go.config.materials.git.GitMaterialConfig; import com.thoughtworks.go.domain.*; 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.domain.materials.Modifications; import com.thoughtworks.go.domain.materials.git.GitMaterialInstance; import com.thoughtworks.go.domain.valuestreammap.*; import com.thoughtworks.go.helper.GoConfigMother; import com.thoughtworks.go.helper.ModificationsMother; import com.thoughtworks.go.helper.PipelineConfigMother; import com.thoughtworks.go.helper.PipelineMother; import com.thoughtworks.go.server.domain.Username; import com.thoughtworks.go.server.persistence.MaterialRepository; import com.thoughtworks.go.server.presentation.models.ValueStreamMapPresentationModel; import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult; import com.thoughtworks.go.server.valuestreammap.DownstreamInstancePopulator; import com.thoughtworks.go.server.valuestreammap.RunStagesPopulator; import com.thoughtworks.go.server.valuestreammap.UnrunStagesPopulator; import com.thoughtworks.go.util.ReflectionUtil; import org.hamcrest.CoreMatchers; import org.hamcrest.core.IsNull; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import java.util.*; import static com.thoughtworks.go.domain.valuestreammap.VSMTestHelper.assertDepth; import static com.thoughtworks.go.helper.ModificationsMother.checkinWithComment; import static java.util.Arrays.asList; import static javax.servlet.http.HttpServletResponse.*; import static junit.framework.TestCase.assertNull; import static org.hamcrest.Matchers.nullValue; 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 ValueStreamMapServiceTest { @Mock private PipelineService pipelineService; @Mock private MaterialRepository materialRepository; @Mock private GoConfigService goConfigService; @Mock private RunStagesPopulator runStagesPopulator; @Mock private UnrunStagesPopulator unrunStagesPopulator; @Mock private DownstreamInstancePopulator downstreaminstancepopulator; @Mock private SecurityService securityService; private Username user; private ValueStreamMapService valueStreamMapService; private HttpLocalizedOperationResult result; @Before public void setUp() throws Exception { initMocks(this); user = new Username(new CaseInsensitiveString("poovan")); setupExistenceOfPipelines("p1", "p2", "p3", "MYPIPELINE"); setupViewPermissionForPipelines("C", "A", "B", "P1", "P2", "P3", "p1", "p2", "p3", "MYPIPELINE"); setupViewPermissionForGroups("g1"); valueStreamMapService = new ValueStreamMapService(pipelineService, materialRepository, goConfigService, downstreaminstancepopulator, runStagesPopulator, unrunStagesPopulator, securityService); result = new HttpLocalizedOperationResult(); } private void setupExistenceOfPipelines(String... pipelineNames) { for (String pipelineName : pipelineNames) { when(goConfigService.hasPipelineNamed(new CaseInsensitiveString(pipelineName))).thenReturn(true); } } private void setupViewPermissionForPipelines(String... pipelineNames) { for (String pipelineName : pipelineNames) { when(securityService.hasViewPermissionForPipeline(user, pipelineName)).thenReturn(true); } } private void setupViewPermissionForGroups(String... groups) { for (String group : groups) { when(securityService.hasViewPermissionForGroup(CaseInsensitiveString.str(user.getUsername()), group)).thenReturn(true); } } @Test public void shouldBeCaseInsensitiveWhenGettingPipelineDependencyGraphForAPipeline() { /* * svn => P1 * */ String pipelineName = "myPipeline"; int counter = 1; BuildCause buildCause = PipelineMother.pipeline(pipelineName, new Stage()).getBuildCause(); MaterialConfig materialConfig = buildCause.getMaterialRevisions().getMaterialRevision(0).getMaterial().config(); PipelineConfig p1Config = PipelineConfigMother.pipelineConfig(pipelineName, new MaterialConfigs(materialConfig)); when(pipelineService.buildCauseFor(pipelineName, counter)).thenReturn(buildCause); when(goConfigService.currentCruiseConfig()).thenReturn(new BasicCruiseConfig(new BasicPipelineConfigs(p1Config))); when(pipelineService.findPipelineByCounterOrLabel(pipelineName, "1")).thenReturn(new Pipeline("MYPIPELINE", "p1-label", buildCause)); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap("MYPIPELINE", counter, user, result); assertThat(graph.getCurrentPipeline().getName(), is(pipelineName)); List<List<Node>> nodesAtEachLevel = graph.getNodesAtEachLevel(); assertThat(nodesAtEachLevel.size(), is(2)); List<Node> firstLevel = nodesAtEachLevel.get(0); assertThat(firstLevel.size(), is(1)); assertNode(-1, firstLevel.get(0), materialConfig.getDisplayName(), materialConfig.getFingerprint(), 0, pipelineName); List<Node> secondLevel = nodesAtEachLevel.get(1); assertThat(secondLevel.size(), is(1)); assertNode(0, secondLevel.get(0), pipelineName, pipelineName, 0); } @Test public void shouldGetPipelineDependencyGraphForAPipelineWithNoCrossLevelDependencies() { /* * svn => P1 * */ String pipeline = "P1"; int counter = 1; BuildCause buildCause = PipelineMother.pipeline(pipeline, new Stage()).getBuildCause(); MaterialConfig material = buildCause.getMaterialRevisions().getMaterialRevision(0).getMaterial().config(); when(pipelineService.buildCauseFor(pipeline, counter)).thenReturn(buildCause); PipelineConfig p1Config = PipelineConfigMother.pipelineConfig(pipeline, new MaterialConfigs(material)); when(goConfigService.currentCruiseConfig()).thenReturn(new BasicCruiseConfig(new BasicPipelineConfigs(p1Config))); when(pipelineService.findPipelineByCounterOrLabel(pipeline, "1")).thenReturn(new Pipeline(pipeline, "p1-label", buildCause)); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap(pipeline, counter, user, result); List<List<Node>> nodesAtEachLevel = graph.getNodesAtEachLevel(); assertThat(graph.getCurrentPipeline().getName(), is(pipeline)); assertThat(nodesAtEachLevel.size(), is(2)); List<Node> firstLevel = nodesAtEachLevel.get(0); assertThat(firstLevel.size(), is(1)); assertNode(-1, firstLevel.get(0), material.getDisplayName(), material.getFingerprint(), 0, pipeline); List<Node> secondLevel = nodesAtEachLevel.get(1); assertThat(secondLevel.size(), is(1)); assertNode(0, secondLevel.get(0), pipeline, pipeline, 0); } @Test public void shouldGetPipelineDependencyGraphForAPipelineWithDiamondDependency() { /* * |----> P1-----> * g |_> p3 * | | * ---- > P2-----> * * */ GitMaterial git = new GitMaterial("git"); BuildCause p3buildCause = createBuildCause(asList("p1", "p2"), new ArrayList<>()); BuildCause p2buildCause = createBuildCause(new ArrayList<>(), asList(git)); BuildCause p1buildCause = createBuildCause(new ArrayList<>(), asList(git)); when(pipelineService.buildCauseFor("p3", 1)).thenReturn(p3buildCause); when(pipelineService.buildCauseFor("p2", 1)).thenReturn(p2buildCause); when(pipelineService.buildCauseFor("p1", 1)).thenReturn(p1buildCause); PipelineConfig p1Config = PipelineConfigMother.pipelineConfig("p1", new MaterialConfigs(git.config())); PipelineConfig p2Config = PipelineConfigMother.pipelineConfig("p2", new MaterialConfigs(git.config())); PipelineConfig p3Config = PipelineConfigMother.pipelineConfig("p3", new MaterialConfigs(new DependencyMaterialConfig(p1Config.name(), p1Config.getFirstStageConfig().name()), new DependencyMaterialConfig(p2Config.name(), p2Config.getFirstStageConfig().name()))); CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(p1Config, p2Config, p3Config)); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); when(pipelineService.findPipelineByCounterOrLabel("p3", "1")).thenReturn(new Pipeline("p3", "p3-label", p3buildCause)); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap("p3", 1, user, result); List<List<Node>> nodesAtEachLevel = graph.getNodesAtEachLevel(); assertThat(graph.getCurrentPipeline().getName(), is("p3")); assertThat(nodesAtEachLevel.size(), is(3)); List<Node> firstLevel = nodesAtEachLevel.get(0); assertThat(firstLevel.size(), is(1)); assertNode(-2, firstLevel.get(0), git.getDisplayName(), git.getFingerprint(), 0, "p1", "p2"); List<Node> secondLevel = nodesAtEachLevel.get(1); assertThat(secondLevel.size(), is(2)); assertNode(-1, secondLevel.get(0), "p1", "p1", 0, "p3"); assertNode(-1, secondLevel.get(1), "p2", "p2", 0, "p3"); List<Node> thirdLevel = nodesAtEachLevel.get(2); assertThat(thirdLevel.size(), is(1)); assertNode(0, thirdLevel.get(0), "p3", "p3", 0); } @Test public void shouldGetPipelineDependencyGraphForAPipelineWithDiamondDependency_VSMForMaterial() { /* * |----> P1-----> * g |_> p3 * | | * ---- > P2-----> * * */ GitMaterial gitMaterial = new GitMaterial("git"); MaterialConfig gitConfig = gitMaterial.config(); GitMaterialInstance gitMaterialInstance = new GitMaterialInstance("git", "master", "submodule", "flyweight"); BuildCause p3buildCause = createBuildCause(asList("p1", "p2"), new ArrayList<>()); BuildCause p2buildCause = createBuildCause(new ArrayList<>(), asList(gitMaterial)); Modification gitModification = p2buildCause.getMaterialRevisions().getRevisions().get(0).getModifications().get(0); String gitRevision = gitModification.getRevision(); BuildCause p1buildCause = createBuildCause(new ArrayList<>(), asList(gitMaterial)); when(pipelineService.buildCauseFor("p3", 1)).thenReturn(p3buildCause); when(pipelineService.buildCauseFor("p2", 1)).thenReturn(p2buildCause); when(pipelineService.buildCauseFor("p1", 1)).thenReturn(p1buildCause); PipelineConfig p1Config = PipelineConfigMother.pipelineConfig("p1", new MaterialConfigs(gitConfig)); PipelineConfig p2Config = PipelineConfigMother.pipelineConfig("p2", new MaterialConfigs(gitConfig)); PipelineConfig p3Config = PipelineConfigMother.pipelineConfig("p3", new MaterialConfigs(new DependencyMaterialConfig(p1Config.name(), p1Config.getFirstStageConfig().name()), new DependencyMaterialConfig(p2Config.name(), p2Config.getFirstStageConfig().name()))); PipelineConfigs pipelineConfigs = new BasicPipelineConfigs("g1", new Authorization(), p1Config, p2Config, p3Config); CruiseConfig cruiseConfig = new BasicCruiseConfig(pipelineConfigs); when(goConfigService.groups()).thenReturn(new PipelineGroups(pipelineConfigs)); when(materialRepository.findMaterialInstance(gitConfig)).thenReturn(gitMaterialInstance); when(materialRepository.findModificationWithRevision(gitMaterial, gitRevision)).thenReturn(gitModification); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap(gitMaterial.getFingerprint(), gitRevision, user, result); List<List<Node>> nodesAtEachLevel = graph.getNodesAtEachLevel(); assertThat(graph.getCurrentPipeline(), is(nullValue())); assertThat(graph.getCurrentMaterial().getId(), is(gitMaterial.getFingerprint())); assertThat(nodesAtEachLevel.size(), is(3)); List<Node> firstLevel = nodesAtEachLevel.get(0); assertThat(firstLevel.size(), is(1)); assertNode(0, firstLevel.get(0), gitMaterial.getDisplayName(), gitMaterial.getFingerprint(), 0, "p1", "p2"); assertDepth(graph, firstLevel.get(0).getId(), 1); List<Node> secondLevel = nodesAtEachLevel.get(1); assertThat(secondLevel.size(), is(2)); assertNode(1, secondLevel.get(0), "p1", "p1", 0, "p3"); assertDepth(graph, secondLevel.get(0).getId(), 1); assertNode(1, secondLevel.get(1), "p2", "p2", 0, "p3"); assertDepth(graph, secondLevel.get(1).getId(), 2); List<Node> thirdLevel = nodesAtEachLevel.get(2); assertThat(thirdLevel.size(), is(1)); assertNode(2, thirdLevel.get(0), "p3", "p3", 0); assertDepth(graph, thirdLevel.get(0).getId(), 1); } @Test public void shouldMoveNodeAndIntroduceDummyNodesWhenCurrentLevelIsDeeperThanExistingNodeLevel() throws Exception { /* * +-------------+ * | v * g---->p1---->p2 ---> p3 * | ^ * -------------+ * * */ GitMaterial git = new GitMaterial("git"); MaterialConfig gitConfig = git.config(); String p1 = "p1"; String p2 = "p2"; String p3 = "p3"; BuildCause p3buildCause = createBuildCause(asList(p1, p2), new ArrayList<>()); BuildCause p2buildCause = createBuildCause(asList(p1), asList(git)); BuildCause p1buildCause = createBuildCause(new ArrayList<>(), asList(git)); when(pipelineService.buildCauseFor(p3, 1)).thenReturn(p3buildCause); when(pipelineService.buildCauseFor(p2, 1)).thenReturn(p2buildCause); when(pipelineService.buildCauseFor(p1, 1)).thenReturn(p1buildCause); PipelineConfig p1Config = PipelineConfigMother.pipelineConfig(p1, new MaterialConfigs(gitConfig)); PipelineConfig p2Config = PipelineConfigMother.pipelineConfig(p2, new MaterialConfigs(gitConfig, new DependencyMaterialConfig(p1Config.name(), p1Config.getFirstStageConfig().name()))); PipelineConfig p3Config = PipelineConfigMother.pipelineConfig(p3, new MaterialConfigs(new DependencyMaterialConfig(p1Config.name(), p1Config.getFirstStageConfig().name()), new DependencyMaterialConfig(p2Config.name(), p2Config.getFirstStageConfig().name()))); CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(p1Config, p2Config, p3Config)); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); when(pipelineService.findPipelineByCounterOrLabel("p3", "1")).thenReturn(new Pipeline("p3", "p3-label", p3buildCause)); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap(p3, 1, user, result); List<List<Node>> nodesAtEachLevel = graph.getNodesAtEachLevel(); assertThat(graph.getCurrentPipeline().getName(), is(p3)); assertThat(nodesAtEachLevel.size(), is(4)); List<Node> firstLevel = nodesAtEachLevel.get(0); assertThat(firstLevel.size(), is(1)); assertLayerHasNode(firstLevel, git.getDisplayName(), git.getFingerprint(), p1); List<Node> secondLevel = nodesAtEachLevel.get(1); assertThat(secondLevel.size(), is(2)); assertLayerHasNode(secondLevel, p1, p1, p2); assertLayerHasDummyNodeWithDependents(secondLevel, p2); List<Node> thirdLevel = nodesAtEachLevel.get(2); assertThat(thirdLevel.size(), is(2)); assertLayerHasNode(thirdLevel, p2, p2, p3); assertLayerHasDummyNodeWithDependents(thirdLevel, p3); List<Node> fourthLevel = nodesAtEachLevel.get(3); assertThat(fourthLevel.size(), is(1)); assertLayerHasNode(fourthLevel, p3, p3); } @Test public void shouldDrawDependenciesIncludingDownstreamBasedOnConfig() { /* * These are all the pipelines in the config. * * |----> p1-----> * g |_> p3 * | | * ---- > p2-----> * * We are drawing a graph for 'p1' : g -> p1 -> p3 * */ CruiseConfig cruiseConfig = GoConfigMother.simpleDiamond(); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); String currentPipeline = "p1"; String p3 = "p3"; GitMaterial git = new GitMaterial("git"); BuildCause p1buildCause = createBuildCause(new ArrayList<>(), asList(git)); when(pipelineService.buildCauseFor(currentPipeline, 1)).thenReturn(p1buildCause); when(pipelineService.findPipelineByCounterOrLabel(currentPipeline, "1")).thenReturn(new Pipeline(currentPipeline, "p1-label", p1buildCause)); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap(currentPipeline, 1, user, result); List<List<Node>> nodesAtEachLevel = graph.getNodesAtEachLevel(); assertThat(graph.getCurrentPipeline().getName(), is(currentPipeline)); assertThat(nodesAtEachLevel.size(), is(3)); assertLayerHasNode(nodesAtEachLevel.get(0), git.getDisplayName(), git.getFingerprint(), currentPipeline); assertLayerHasNode(nodesAtEachLevel.get(1), currentPipeline, currentPipeline, p3); assertLayerHasNode(nodesAtEachLevel.get(2), p3, p3); } @Test public void shouldAddDummyNodesUpstreamAndDownstreamInDependencyGraph() { /* * +------------+ * | v * g---->p1---->p2 ---> p3 * | ^ * --------------+ * * Drawing graph for p2, expected graph: * * +------X1------+ * | v * g---->p1---->p2 ---> p3 * */ String p1 = "p1"; String p2 = "p2"; String p3 = "p3"; GitMaterial git = new GitMaterial("git"); PipelineConfig p1Config = PipelineConfigMother.pipelineConfig(p1, new MaterialConfigs(git.config())); DependencyMaterial dependencyMaterialP1 = new DependencyMaterial(new CaseInsensitiveString(p1), p1Config.getFirstStageConfig().name()); PipelineConfig p2Config = PipelineConfigMother.pipelineConfig(p2, new MaterialConfigs(git.config(), dependencyMaterialP1.config())); DependencyMaterial dependencyMaterialP2 = new DependencyMaterial(new CaseInsensitiveString(p2), p2Config.getFirstStageConfig().name()); PipelineConfig p3Config = PipelineConfigMother.pipelineConfig(p3, new MaterialConfigs(dependencyMaterialP1.config(), dependencyMaterialP2.config())); CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(p1Config, p2Config, p3Config)); BuildCause p1buildCause = createBuildCause(new ArrayList<>(), asList(git)); BuildCause p2buildCause = createBuildCause(asList(p1), asList(git)); when(pipelineService.buildCauseFor(p1, 1)).thenReturn(p1buildCause); when(pipelineService.buildCauseFor(p2, 1)).thenReturn(p2buildCause); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); when(pipelineService.findPipelineByCounterOrLabel(p2, "1")).thenReturn(new Pipeline(p2, "label-p2", p2buildCause)); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap(p2, 1, user, result); List<List<Node>> nodesAtEachLevel = graph.getNodesAtEachLevel(); assertThat(graph.getCurrentPipeline().getName(), is(p2)); assertThat(nodesAtEachLevel.size(), is(4)); assertThatLevelHasNodes(nodesAtEachLevel.get(0), 0, git.getFingerprint()); Node gitNode = nodesAtEachLevel.get(0).get(0); assertNode(-2, gitNode, git.getDisplayName(), git.getFingerprint(), 1, p1); VSMTestHelper.assertThatNodeHasParents(gitNode, 0); assertThatLevelHasNodes(nodesAtEachLevel.get(1), 1, p1); assertLayerHasDummyNodeWithDependents(nodesAtEachLevel.get(1), p2); Node p1Node = nodesAtEachLevel.get(1).get(0); assertNode(-1, p1Node, p1, p1, 0, p2); VSMTestHelper.assertThatNodeHasParents(p1Node, 0, git.getFingerprint()); assertThatLevelHasNodes(nodesAtEachLevel.get(2), 0, p2); Node p2Node = nodesAtEachLevel.get(2).get(0); assertNode(0, p2Node, p2, p2, 0, p3); VSMTestHelper.assertThatNodeHasParents(p2Node, 1, p1); assertThatLevelHasNodes(nodesAtEachLevel.get(3), 0, p3); Node p3Node = nodesAtEachLevel.get(3).get(0); assertNode(1, p3Node, p3, p3, 0); VSMTestHelper.assertThatNodeHasParents(p3Node, 0, p2); } @Test public void shouldPushAllAncestorsLeftByOneWhenMovingImmediateParentToLeftOfANode() { /* * g1 -> P1--->X------>P2 * \ ^ * \ | * V | * g2-->P3-------+ * */ String p1 = "p1"; String p2 = "p2"; String p3 = "p3"; GitMaterial g1 = new GitMaterial("g1"); GitMaterial g2 = new GitMaterial("g2"); BuildCause p1buildCause = createBuildCause(new ArrayList<>(), asList(g1)); BuildCause p3buildCause = createBuildCause(asList(p1), asList(g2)); BuildCause p2buildCause = createBuildCause(asList(p1, p3), Arrays.<GitMaterial>asList()); when(pipelineService.buildCauseFor(p1, 1)).thenReturn(p1buildCause); when(pipelineService.buildCauseFor(p2, 1)).thenReturn(p2buildCause); when(pipelineService.buildCauseFor(p3, 1)).thenReturn(p3buildCause); PipelineConfig p1Config = PipelineConfigMother.pipelineConfig(p1, new MaterialConfigs(g1.config())); PipelineConfig p3Config = PipelineConfigMother.pipelineConfig(p3, new MaterialConfigs(g2.config(), new DependencyMaterialConfig(p1Config.name(), p1Config.getFirstStageConfig().name()))); PipelineConfig p2Config = PipelineConfigMother.pipelineConfig(p2, new MaterialConfigs(new DependencyMaterialConfig(p1Config.name(), p1Config.getFirstStageConfig().name()), new DependencyMaterialConfig(p3Config.name(), p3Config.getFirstStageConfig().name()))); CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(p1Config, p2Config, p3Config)); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); when(pipelineService.findPipelineByCounterOrLabel(p2, "1")).thenReturn(new Pipeline(p2, "p2-label", p2buildCause)); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap(p2, 1, user, result); List<List<Node>> nodesAtEachLevel = graph.getNodesAtEachLevel(); assertThat(nodesAtEachLevel.size(), is(4)); VSMTestHelper.assertThatLevelHasNodes(nodesAtEachLevel.get(0), 0, g1.getFingerprint()); VSMTestHelper.assertThatLevelHasNodes(nodesAtEachLevel.get(1), 0, p1, g2.getFingerprint()); VSMTestHelper.assertThatLevelHasNodes(nodesAtEachLevel.get(2), 1, p3); VSMTestHelper.assertThatLevelHasNodes(nodesAtEachLevel.get(3), 0, p2); } @Test public void shouldPopulateRevisionsForUpstreamPipelines() { /* * git---> p1 ---> p3 * | v ^ * +---> p2 -----+ * **/ GitMaterial git = new GitMaterial("git"); MaterialConfig gitConfig = git.config(); BuildCause p3buildCause = createBuildCause(asList("p1", "p2"), new ArrayList<>()); BuildCause p2buildCause = createBuildCauseForRevisions(asList(dependencyMaterial("p1", 2)), asList(git), ModificationsMother.multipleModificationList(0)); BuildCause p1buildCause = createBuildCause(new ArrayList<>(), asList(git)); Modifications modifications = p1buildCause.getMaterialRevisions().getMaterialRevision(0).getModifications(); when(pipelineService.buildCauseFor("p3", 1)).thenReturn(p3buildCause); when(pipelineService.buildCauseFor("p2", 1)).thenReturn(p2buildCause); when(pipelineService.buildCauseFor("p1", 1)).thenReturn(p1buildCause); when(pipelineService.buildCauseFor("p1", 2)).thenReturn(p1buildCause); PipelineConfig p1Config = PipelineConfigMother.pipelineConfig("p1", new MaterialConfigs(gitConfig)); PipelineConfig p2Config = PipelineConfigMother.pipelineConfig("p2", new MaterialConfigs(gitConfig)); PipelineConfig p3Config = PipelineConfigMother.pipelineConfig("p3", new MaterialConfigs(new DependencyMaterialConfig(p1Config.name(), p1Config.getFirstStageConfig().name()), new DependencyMaterialConfig(p2Config.name(), p2Config.getFirstStageConfig().name()))); CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(p1Config, p2Config, p3Config)); when(pipelineService.findPipelineByCounterOrLabel("p3", "1")).thenReturn(new Pipeline("p3", "LABEL-P3", p3buildCause)); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap("p3", 1, user, result); VSMTestHelper.assertNodeHasRevisions(graph, "p1", new PipelineRevision("p1", 1, "LABEL-p1-1"), new PipelineRevision("p1", 2, "LABEL-p1-2")); VSMTestHelper.assertNodeHasRevisions(graph, "p2", new PipelineRevision("p2", 1, "LABEL-p2-1")); VSMTestHelper.assertNodeHasRevisions(graph, "p3", new PipelineRevision("p3", 1, "LABEL-P3")); VSMTestHelper.assertSCMNodeHasMaterialRevisions(graph, git.getFingerprint(), new MaterialRevision(git, false, modifications)); verify(runStagesPopulator).apply(any(ValueStreamMap.class)); } @Test public void shouldPopulateAllMaterialRevisionsThatCausedPipelineRun() { /* * git---> p1 --->p2 * | ^ * +-------------+ * **/ GitMaterial git = new GitMaterial("git"); MaterialConfig gitConfig = git.config(); BuildCause p2buildCause = createBuildCauseForRevisions(asList(dependencyMaterial("p1", 1)), asList(git), ModificationsMother.multipleModificationList(0)); BuildCause p1buildCause = createBuildCause(new ArrayList<>(), asList(git)); Modifications gitModifications = p1buildCause.getMaterialRevisions().getMaterialRevision(0).getModifications(); when(pipelineService.buildCauseFor("p2", 1)).thenReturn(p2buildCause); when(pipelineService.buildCauseFor("p1", 1)).thenReturn(p1buildCause); PipelineConfig p1Config = PipelineConfigMother.pipelineConfig("p1", new MaterialConfigs(gitConfig)); PipelineConfig p2Config = PipelineConfigMother.pipelineConfig("p2", new MaterialConfigs(gitConfig)); CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(p1Config, p2Config)); when(pipelineService.findPipelineByCounterOrLabel("p2", "1")).thenReturn(new Pipeline("p2", "LABEL-P2", p2buildCause)); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap("p2", 1, user, result); VSMTestHelper.assertNodeHasRevisions(graph, "p1", new PipelineRevision("p1", 1, "LABEL-p1-1")); VSMTestHelper.assertNodeHasRevisions(graph, "p2", new PipelineRevision("p2", 1, "LABEL-P2")); VSMTestHelper.assertSCMNodeHasMaterialRevisions(graph, git.getFingerprint(), new MaterialRevision(git, false, gitModifications)); verify(runStagesPopulator).apply(any(ValueStreamMap.class)); } @Test public void shouldPopulateAllSCMMaterialRevisionsThatCausedPipelineRun_WhenFaninIsNotObeyed() { /* * git---> p1 --->p2 * | ^ * +-------------+ * **/ GitMaterial git = new GitMaterial("git"); MaterialConfig gitConfig = git.config(); Modification modification1 = checkinWithComment("rev1", "comment1", new Date()); Modification modification2 = checkinWithComment("rev2", "comment2", new Date()); Modification modification3 = checkinWithComment("rev3", "comment3", new Date()); BuildCause p1buildCause = createBuildCauseForRevisions(new ArrayList<>(), asList(git), new Modifications( modification1, modification2)); BuildCause p2buildCause = createBuildCauseForRevisions(asList(dependencyMaterial("p1", 1)),asList(git) , new Modifications(modification3)); when(pipelineService.buildCauseFor("p2", 1)).thenReturn(p2buildCause); when(pipelineService.buildCauseFor("p1", 1)).thenReturn(p1buildCause); PipelineConfig p1Config = PipelineConfigMother.pipelineConfig("p1", new MaterialConfigs(gitConfig)); PipelineConfig p2Config = PipelineConfigMother.pipelineConfig("p2", new MaterialConfigs(gitConfig)); CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(p1Config, p2Config)); when(pipelineService.findPipelineByCounterOrLabel("p2", "1")).thenReturn(new Pipeline("p2", "LABEL-P2", p2buildCause)); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap("p2", 1, user, result); VSMTestHelper.assertNodeHasRevisions(graph, "p1", new PipelineRevision("p1", 1, "LABEL-p1-1")); VSMTestHelper.assertNodeHasRevisions(graph, "p2", new PipelineRevision("p2", 1, "LABEL-P2")); VSMTestHelper.assertSCMNodeHasMaterialRevisions(graph, git.getFingerprint(), new MaterialRevision(git, false, modification1, modification2), new MaterialRevision(git, false, modification3)); verify(runStagesPopulator).apply(any(ValueStreamMap.class)); } @Test public void currentPipelineShouldHaveWarningsIfBuiltFromIncompatibleRevisions() { /* /-> P1 -- \ git -> p3 \-> P2 -- / */ GitMaterial git = new GitMaterial("git"); MaterialConfig gitConfig = git.config(); BuildCause p1buildCause = createBuildCauseForRevisions(new ArrayList<>(), asList(git), Arrays.asList(ModificationsMother.oneModifiedFile("rev1"))); BuildCause p2buildCause = createBuildCauseForRevisions(new ArrayList<>(), asList(git), Arrays.asList(ModificationsMother.oneModifiedFile("rev2"))); BuildCause p3buildCause = createBuildCauseForRevisions(asList(dependencyMaterial("p1", 1), dependencyMaterial("p2", 1)), new ArrayList<>(), new ArrayList<>()); when(pipelineService.buildCauseFor("p3", 1)).thenReturn(p3buildCause); when(pipelineService.buildCauseFor("p1", 1)).thenReturn(p1buildCause); when(pipelineService.buildCauseFor("p2", 1)).thenReturn(p2buildCause); PipelineConfig p1Config = PipelineConfigMother.pipelineConfig("p1", new MaterialConfigs(gitConfig)); PipelineConfig p2Config = PipelineConfigMother.pipelineConfig("p2", new MaterialConfigs(gitConfig)); PipelineConfig p3Config = PipelineConfigMother.pipelineConfig("p3", new MaterialConfigs(new DependencyMaterialConfig(p1Config.name(), p1Config.getFirstStageConfig().name()), new DependencyMaterialConfig(p2Config.name(), p2Config.getFirstStageConfig().name()))); CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(p1Config, p2Config, p3Config)); when(pipelineService.findPipelineByCounterOrLabel("p3", "1")).thenReturn(new Pipeline("p3", "LABEL-P3", p3buildCause)); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap("p3", 1, user, result); assertThat(graph.getCurrentPipeline().getViewType(), is(VSMViewType.WARNING)); } @Test public void currentPipelineShouldNotHaveWarningsIfBuiltFromMultipleRevisionsWithSameLatestRevision() { /* /-> P1 -- \ git -> p3 \-> P2 -- / */ GitMaterial git = new GitMaterial("git"); MaterialConfig gitConfig = git.config(); Modification rev1 = ModificationsMother.oneModifiedFile("rev1"); Modification rev2 = ModificationsMother.oneModifiedFile("rev2"); Modification rev3 = ModificationsMother.oneModifiedFile("rev3"); BuildCause p1buildCause = createBuildCauseForRevisions(new ArrayList<>(), asList(git), Arrays.asList(rev3, rev2, rev1)); BuildCause p2buildCause = createBuildCauseForRevisions(new ArrayList<>(), asList(git), Arrays.asList(rev3)); BuildCause p3buildCause = createBuildCauseForRevisions(asList(dependencyMaterial("p1", 1), dependencyMaterial("p2", 1)), new ArrayList<>(), new ArrayList<>()); when(pipelineService.buildCauseFor("p3", 1)).thenReturn(p3buildCause); when(pipelineService.buildCauseFor("p1", 1)).thenReturn(p1buildCause); when(pipelineService.buildCauseFor("p2", 1)).thenReturn(p2buildCause); PipelineConfig p1Config = PipelineConfigMother.pipelineConfig("p1", new MaterialConfigs(gitConfig)); PipelineConfig p2Config = PipelineConfigMother.pipelineConfig("p2", new MaterialConfigs(gitConfig)); PipelineConfig p3Config = PipelineConfigMother.pipelineConfig("p3", new MaterialConfigs(new DependencyMaterialConfig(p1Config.name(), p1Config.getFirstStageConfig().name()), new DependencyMaterialConfig(p2Config.name(), p2Config.getFirstStageConfig().name()))); CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(p1Config, p2Config, p3Config)); when(pipelineService.findPipelineByCounterOrLabel("p3", "1")).thenReturn(new Pipeline("p3", "LABEL-P3", p3buildCause)); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap("p3", 1, user, result); assertNull(graph.getCurrentPipeline().getViewType()); } @Test public void shouldPopulateLabelForCurrentPipeline() throws Exception { /* git --> p1 */ GitMaterial git = new GitMaterial("git"); String pipelineName = "p1"; CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(PipelineConfigMother.pipelineConfig("p1", new MaterialConfigs(git.config())))); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); BuildCause p1buildCause = createBuildCause(new ArrayList<>(), asList(git)); when(pipelineService.buildCauseFor("p1", 1)).thenReturn(p1buildCause); when(pipelineService.findPipelineByCounterOrLabel(pipelineName, "1")).thenReturn(new Pipeline("p1", "label-1", p1buildCause)); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap(pipelineName, 1, user, result); PipelineRevision revision = (PipelineRevision) graph.getCurrentPipeline().revisions().get(0); assertThat(revision.getLabel(), is("label-1")); } @Test public void shouldPopulateErrorWhenUserDoesNotHaveViewPermissionForCurrentPipeline() throws Exception { /* git --> p1 */ GitMaterial git = new GitMaterial("git"); String pipelineName = "p1"; CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(PipelineConfigMother.pipelineConfig("p1", new MaterialConfigs(git.config())))); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); BuildCause p1buildCause = createBuildCause(new ArrayList<>(), asList(git)); when(pipelineService.buildCauseFor("p1", 1)).thenReturn(p1buildCause); when(pipelineService.findPipelineByCounterOrLabel(pipelineName, "1")).thenReturn(new Pipeline("p1", "label-1", p1buildCause)); Username newUser = new Username(new CaseInsensitiveString("looser")); when(securityService.hasViewPermissionForPipeline(newUser, pipelineName)).thenReturn(false); valueStreamMapService.getValueStreamMap(pipelineName, 1, newUser, result); assertResult(SC_UNAUTHORIZED, "PIPELINE_CANNOT_VIEW"); } @Test public void shouldPopulateErrorWhenUpstreamPipelineDoesNotExistInCurrentConfig() throws Exception { /* * g --> p1 --> p3 */ GitMaterial git = new GitMaterial("git"); BuildCause p3buildCause = createBuildCause(asList("p1"), new ArrayList<>()); BuildCause p1buildCause = createBuildCause(new ArrayList<>(), asList(git)); when(pipelineService.buildCauseFor("p3", 1)).thenReturn(p3buildCause); when(pipelineService.buildCauseFor("p1", 1)).thenReturn(p1buildCause); PipelineConfig p3Config = PipelineConfigMother.pipelineConfig("p3", new MaterialConfigs(new GitMaterialConfig("test"))); CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(p3Config)); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); when(goConfigService.hasPipelineNamed(new CaseInsensitiveString("p1"))).thenReturn(false); when(pipelineService.findPipelineByCounterOrLabel("p3", "1")).thenReturn(new Pipeline("p3", "p3-label", p3buildCause)); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap("p3", 1, user, result); PipelineDependencyNode node = (PipelineDependencyNode) graph.getNodesAtEachLevel().get(1).get(0); assertThat(node.revisions().toString(), node.revisions().isEmpty(), is(true)); assertThat(node.getViewType(), is(VSMViewType.DELETED)); assertThat(ReflectionUtil.getField((node.getMessage()), "key"), is("VSM_PIPELINE_DELETED")); } @Test public void shouldPopulateErrorWhenPipelineNameAndCounterAreMultiple() { PipelineConfig pipelineConfig = PipelineConfigMother.pipelineConfig("MYPIPELINE", new MaterialConfigs(new GitMaterialConfig("sampleGit"))); CruiseConfig cruiseConfig = new BasicCruiseConfig(new BasicPipelineConfigs(pipelineConfig)); when(pipelineService.findPipelineByCounterOrLabel("MYPIPELINE", "1")).thenThrow(Exception.class); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); ValueStreamMapPresentationModel graph = valueStreamMapService.getValueStreamMap("MYPIPELINE", 1, user, result); assertThat(graph, is(IsNull.nullValue())); assertThat(result.isSuccessful(), is(false)); assertThat(ReflectionUtil.getField(result.localizable(), "key"), is("VSM_INTERNAL_SERVER_ERROR")); } @Test public void shouldPopulateErrorCorrectly_VSMForMaterial() throws Exception { /* git --> p1 */ String groupName = "g1"; String pipelineName = "p1"; String userName = "looser"; GitMaterial gitMaterial = new GitMaterial("git"); MaterialConfig gitConfig = gitMaterial.config(); GitMaterialInstance gitMaterialInstance = new GitMaterialInstance("url", "branch", "submodule", "flyweight"); PipelineConfigs groups = new BasicPipelineConfigs(groupName, new Authorization(), PipelineConfigMother.pipelineConfig(pipelineName, new MaterialConfigs(gitConfig))); CruiseConfig cruiseConfig = new BasicCruiseConfig(groups); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); when(goConfigService.groups()).thenReturn(new PipelineGroups(groups)); when(securityService.hasViewPermissionForGroup(userName, groupName)).thenReturn(false); // unknown material valueStreamMapService.getValueStreamMap("unknown-material", "r1", new Username(new CaseInsensitiveString(userName)), result); assertResult(SC_NOT_FOUND, "MATERIAL_CONFIG_WITH_FINGERPRINT_NOT_FOUND"); // unauthorized valueStreamMapService.getValueStreamMap(gitMaterial.getFingerprint(), "r1", new Username(new CaseInsensitiveString(userName)), result); assertResult(SC_UNAUTHORIZED, "MATERIAL_CANNOT_VIEW"); // material config exists but no material instance when(securityService.hasViewPermissionForGroup(userName, groupName)).thenReturn(true); when(materialRepository.findMaterialInstance(gitConfig)).thenReturn(null); valueStreamMapService.getValueStreamMap(gitMaterial.getFingerprint(), "r1", new Username(new CaseInsensitiveString(userName)), result); assertResult(SC_NOT_FOUND, "MATERIAL_INSTANCE_WITH_FINGERPRINT_NOT_FOUND"); // modification (revision) doesn't exist when(materialRepository.findMaterialInstance(gitConfig)).thenReturn(gitMaterialInstance); when(materialRepository.findModificationWithRevision(gitMaterial, "r1")).thenReturn(null); valueStreamMapService.getValueStreamMap(gitMaterial.getFingerprint(), "r1", new Username(new CaseInsensitiveString(userName)), result); assertResult(SC_NOT_FOUND, "MATERIAL_MODIFICATION_NOT_FOUND"); // internal error when(goConfigService.groups()).thenThrow(new RuntimeException("just for fun")); valueStreamMapService.getValueStreamMap(gitMaterial.getFingerprint(), "r1", new Username(new CaseInsensitiveString(userName)), result); assertResult(SC_INTERNAL_SERVER_ERROR, "VSM_INTERNAL_SERVER_ERROR_FOR_MATERIAL"); } private void assertResult(int httpCode, String msgKey) { assertThat(result.isSuccessful(), is(false)); assertThat(result.httpCode(), is(httpCode)); assertThat(ReflectionUtil.getField((result.localizable()), "key"), is(msgKey)); } private void assertLayerHasDummyNodeWithDependents(List<Node> nodesOfLevel, String... dependents) { for (Node currentNode : nodesOfLevel) { if (currentNode.getType() == DependencyNodeType.DUMMY) { assertThat(currentNode.getChildren().size(), is(dependents.length)); VSMTestHelper.assertNodeHasChildren(currentNode, dependents); } } } private BuildCause createBuildCauseForRevisions(List<DependencyMaterialDetail> dependencyMaterials, List<GitMaterial> gitMaterials, List<Modification> modifications) { MaterialRevisions materialRevisions = new MaterialRevisions(); for (DependencyMaterialDetail dependencyMaterial : dependencyMaterials) { String label = String.format("LABEL-%s-%d", dependencyMaterial.pipelineName, dependencyMaterial.pipelineCounter); materialRevisions.addRevision(ModificationsMother.dependencyMaterialRevision(dependencyMaterial.pipelineName, dependencyMaterial.pipelineCounter, label, "s1", 1, null)); } for (GitMaterial gitMaterial : gitMaterials) { materialRevisions.addRevision(new MaterialRevision(gitMaterial, modifications)); } return BuildCause.createWithModifications(materialRevisions, ""); } private BuildCause createBuildCause(List<String> dependencyMaterials, List<GitMaterial> gitMaterials, int counter) { List<DependencyMaterialDetail> dependencyMaterialDetails = new ArrayList<>(); for (String dependencyMaterial : dependencyMaterials) { dependencyMaterialDetails.add(dependencyMaterial(dependencyMaterial, counter)); } return createBuildCauseForRevisions(dependencyMaterialDetails, gitMaterials, ModificationsMother.multipleModificationList(0)); } private BuildCause createBuildCause(List<String> dependencyMaterials, List<GitMaterial> gitMaterials) { return createBuildCause(dependencyMaterials, gitMaterials, 1); } private void assertNode(int level, final Node node, final String expectedNodeName, final String expectedNodeId, int expectedDummyDependentsCount, String... dependents) { assertThat(node.getLevel(), is(level)); assertThat(node.getName(), is(expectedNodeName)); assertThat(node.getId(), is(expectedNodeId)); assertThat(node.getChildren().size(), is(dependents.length + expectedDummyDependentsCount)); VSMTestHelper.assertNodeHasChildren(node, dependents); int dummyDependentsCount = 0; for (Node child : node.getChildren()) { if (isUUID(child.getId())) { dummyDependentsCount++; } } assertThat(dummyDependentsCount, is(expectedDummyDependentsCount)); } private boolean isUUID(String uuid) { try { UUID.fromString(uuid); return true; } catch (Exception e) { return false; } } private void assertLayerHasNode(final List<Node> nodesOfLevel, final String expectedNodeName, final String expectedNodeId, String... dependents) { for (Node currentNode : nodesOfLevel) { if (currentNode.getId().equals(expectedNodeId)) { assertThat(currentNode.getName(), is(expectedNodeName)); assertThat(currentNode.getId(), is(expectedNodeId)); VSMTestHelper.assertNodeHasChildren(currentNode, dependents); return; } } fail("was expecting to see node " + expectedNodeId); } private void assertThatLevelHasNodes(List<Node> nodesAtLevel, int numberOfDummyNodes, String... nodeIds) { assertThat(nodesAtLevel.size(), is(numberOfDummyNodes + nodeIds.length)); List<String> nodeIdsAtLevel = new ArrayList<>(); for (Node node : nodesAtLevel) { if (!node.getType().equals(DependencyNodeType.DUMMY)) { nodeIdsAtLevel.add(node.getId()); } } assertThat(nodeIdsAtLevel, CoreMatchers.hasItems(nodeIds)); } private DependencyMaterialDetail dependencyMaterial(String pipelineName, int pipelineCounter) { return new DependencyMaterialDetail(pipelineName, pipelineCounter); } private class DependencyMaterialDetail { private final String pipelineName; private final int pipelineCounter; public DependencyMaterialDetail(String pipelineName, int pipelineCounter) { this.pipelineName = pipelineName; this.pipelineCounter = pipelineCounter; } } }