/* * 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.domain.*; import com.thoughtworks.go.domain.activity.StageStatusCache; import com.thoughtworks.go.domain.feed.Author; import com.thoughtworks.go.domain.feed.FeedEntries; import com.thoughtworks.go.domain.feed.FeedEntry; import com.thoughtworks.go.domain.feed.stage.StageFeedEntry; import com.thoughtworks.go.domain.materials.Modification; import com.thoughtworks.go.helper.*; import com.thoughtworks.go.presentation.pipelinehistory.StageInstanceModels; import com.thoughtworks.go.server.cache.GoCache; import com.thoughtworks.go.server.dao.FeedModifier; import com.thoughtworks.go.server.dao.PipelineDao; import com.thoughtworks.go.server.dao.StageDao; import com.thoughtworks.go.server.dao.sparql.StageRunFinder; import com.thoughtworks.go.server.domain.Username; import com.thoughtworks.go.server.messaging.StageStatusMessage; import com.thoughtworks.go.server.messaging.StageStatusTopic; import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult; import com.thoughtworks.go.server.service.result.HttpOperationResult; import com.thoughtworks.go.server.service.result.LocalizedOperationResult; import com.thoughtworks.go.server.transaction.TestTransactionSynchronizationManager; import com.thoughtworks.go.server.transaction.TestTransactionTemplate; import com.thoughtworks.go.server.transaction.TransactionSynchronizationManager; import com.thoughtworks.go.server.transaction.TransactionTemplate; import com.thoughtworks.go.server.ui.*; import com.thoughtworks.go.server.util.Pagination; import com.thoughtworks.go.util.GoConfigFileHelper; import com.thoughtworks.go.util.TestingClock; import com.thoughtworks.go.util.TimeProvider; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import java.sql.SQLException; import java.util.*; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.*; public class StageServiceTest { private static final String PIPELINE_NAME = "cruise"; private static final String STAGE_NAME = "dev"; private StageDao stageDao; private GoConfigFileHelper configFileHelper = new GoConfigFileHelper(); private JobInstanceService jobInstanceService; private SecurityService securityService; private ChangesetService changesetService; private CruiseConfig cruiseConfig; private GoConfigService goConfigService; private List<CaseInsensitiveString> pipelineNames; private Username user; private TransactionTemplate transactionTemplate; private TestTransactionSynchronizationManager transactionSynchronizationManager; private PipelineDao pipelineDao; private static final Username ALWAYS_ALLOW_USER = new Username(new CaseInsensitiveString("always allowed")); private LocalizedOperationResult operationResult; private GoCache goCache; @Before public void setUp() throws Exception { stageDao = mock(StageDao.class); pipelineDao = mock(PipelineDao.class); jobInstanceService = mock(JobInstanceService.class); securityService = mock(SecurityService.class); pipelineNames = asList(new CaseInsensitiveString("blah-pipeline")); user = new Username(new CaseInsensitiveString("poovan")); operationResult = new HttpLocalizedOperationResult(); cruiseConfig = mock(BasicCruiseConfig.class); goConfigService = mock(GoConfigService.class); changesetService = mock(ChangesetService.class); goCache = mock(GoCache.class); transactionSynchronizationManager = new TestTransactionSynchronizationManager(); transactionTemplate = new TestTransactionTemplate(transactionSynchronizationManager); } @After public void teardown() throws Exception { configFileHelper.initializeConfigFile(); } @Test public void canGetFailureRunForThreeStages() { TransactionSynchronizationManager transactionSynchronizationManager = mock(TransactionSynchronizationManager.class); StageRunFinder runFinder = new StageService(stageDao, jobInstanceService, mock(StageStatusTopic.class), mock(StageStatusCache.class), securityService, pipelineDao, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); List<StageIdentifier> expectedStages = new ArrayList<>(); expectedStages.add(new StageIdentifier(PIPELINE_NAME, 3, STAGE_NAME, "1")); expectedStages.add(new StageIdentifier(PIPELINE_NAME, 2, STAGE_NAME, "2")); expectedStages.add(new StageIdentifier(PIPELINE_NAME, 1, STAGE_NAME, "1")); Pipeline pipeline = pipeline(10.0); Pipeline pipelineThatLastPassed = pipeline(5.0); when(pipelineDao.findPipelineByNameAndCounter(PIPELINE_NAME, 3)).thenReturn(pipeline); when(pipelineDao.findEarlierPipelineThatPassedForStage(PIPELINE_NAME, STAGE_NAME, 10.0)).thenReturn(pipelineThatLastPassed); when(stageDao.findFailedStagesBetween(PIPELINE_NAME, STAGE_NAME, 5.0, 10.0)).thenReturn(asList(identifier(3, "1"), identifier(2, "2"), identifier(1, "1"))); assertEquals(expectedStages, runFinder.findRunForStage(new StageIdentifier(PIPELINE_NAME, 3, STAGE_NAME, "1"))); } @Test public void canGetFailureRunForThreeStagesAtStartOfPipeline() { TransactionSynchronizationManager transactionSynchronizationManager = mock(TransactionSynchronizationManager.class); StageRunFinder runFinder = new StageService(stageDao, jobInstanceService, mock(StageStatusTopic.class), mock(StageStatusCache.class), securityService, pipelineDao, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); List<StageIdentifier> expectedStages = new ArrayList<>(); expectedStages.add(new StageIdentifier(PIPELINE_NAME, 3, STAGE_NAME, "1")); expectedStages.add(new StageIdentifier(PIPELINE_NAME, 2, STAGE_NAME, "2")); expectedStages.add(new StageIdentifier(PIPELINE_NAME, 1, STAGE_NAME, "1")); Pipeline pipeline = pipeline(10.0); when(pipelineDao.findPipelineByNameAndCounter(PIPELINE_NAME, 3)).thenReturn(pipeline); when(pipelineDao.findEarlierPipelineThatPassedForStage(PIPELINE_NAME, STAGE_NAME, 10.0)).thenReturn(null); when(stageDao.findFailedStagesBetween(PIPELINE_NAME, STAGE_NAME, 0.0, 10.0)).thenReturn(asList(identifier(3, "1"), identifier(2, "2"), identifier(1, "1"))); assertEquals(expectedStages, runFinder.findRunForStage(new StageIdentifier(PIPELINE_NAME, 3, STAGE_NAME, "1"))); } @Test public void shouldNotReturnAnythingWhenNothingIsFailing() { TransactionSynchronizationManager transactionSynchronizationManager = mock(TransactionSynchronizationManager.class); StageRunFinder runFinder = new StageService(stageDao, jobInstanceService, mock(StageStatusTopic.class), mock(StageStatusCache.class), securityService, pipelineDao, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); Pipeline pipeline = pipeline(10.0); Pipeline pipelineThatLastPassed = pipeline(5.0); when(pipelineDao.findPipelineByNameAndCounter(PIPELINE_NAME, 3)).thenReturn(pipeline); when(pipelineDao.findEarlierPipelineThatPassedForStage(PIPELINE_NAME, STAGE_NAME, 10.0)).thenReturn(pipelineThatLastPassed); when(stageDao.findFailedStagesBetween(PIPELINE_NAME, STAGE_NAME, 5.0, 10.0)).thenReturn(new ArrayList<>()); assertThat(runFinder.findRunForStage(new StageIdentifier(PIPELINE_NAME, 3, STAGE_NAME, "1")).isEmpty(), is(true)); } @Test public void shouldNotReturnAnythingWhenCurrentStageHasNotFailed() { TransactionSynchronizationManager transactionSynchronizationManager = mock(TransactionSynchronizationManager.class); StageRunFinder runFinder = new StageService(stageDao, jobInstanceService, mock(StageStatusTopic.class), mock(StageStatusCache.class), securityService, pipelineDao, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); Pipeline pipeline = pipeline(10.0); Pipeline pipelineThatLastPassed = pipeline(5.0); when(pipelineDao.findPipelineByNameAndCounter(PIPELINE_NAME, 3)).thenReturn(pipeline); when(pipelineDao.findEarlierPipelineThatPassedForStage(PIPELINE_NAME, STAGE_NAME, 10.0)).thenReturn(pipelineThatLastPassed); when(stageDao.findFailedStagesBetween(PIPELINE_NAME, STAGE_NAME, 5.0, 10.0)).thenReturn(asList(identifier(2, "2"), identifier(1, "1"))); assertThat(runFinder.findRunForStage(new StageIdentifier(PIPELINE_NAME, 3, STAGE_NAME, "1")).isEmpty(), is(true)); } private Pipeline pipeline(double naturalOrder) { Pipeline pipeline = PipelineMother.completedFailedStageInstance(PIPELINE_NAME, STAGE_NAME, "foo", new Date()); pipeline.setNaturalOrder(naturalOrder); return pipeline; } private StageIdentifier identifier(int pipelineCounter, String stageCounter) { return new StageIdentifier(PIPELINE_NAME, pipelineCounter, STAGE_NAME, stageCounter); } @Test public void shouldFindStageSummaryModelForGivenStageIdentifier() throws Exception { SecurityService securityService = alwaysAllow(); TransactionSynchronizationManager transactionSynchronizationManager = mock(TransactionSynchronizationManager.class); StageService service = new StageService(stageDao, null, null, null, securityService, null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); Stage stageRun1 = StageMother.completedStageInstanceWithTwoPlans("stage_name"); stageRun1.setIdentifier(new StageIdentifier("pipeline_name/10/stage_name/1")); stageRun1.setCounter(1); Stage stageRun2 = StageMother.completedStageInstanceWithTwoPlans("stage_name"); stageRun2.setIdentifier(new StageIdentifier("pipeline_name/10/stage_name/2")); stageRun2.setCounter(2); Stages stages = new Stages(stageRun1, stageRun2); StageIdentifier stageId = new StageIdentifier("pipeline_name/10/stage_name/2"); when(stageDao.getAllRunsOfStageForPipelineInstance(stageId.getPipelineName(), stageId.getPipelineCounter(), stageId.getStageName())).thenReturn(stages); StageSummaryModel stageForView = service.findStageSummaryByIdentifier(stageId, ALWAYS_ALLOW_USER, new HttpLocalizedOperationResult()); assertThat(stageForView.getName(), is(stageRun2.getName())); assertThat(stageForView.getState(), is(stageRun2.stageState())); assertThat(stageForView.getStageCounter(), is(String.valueOf(stageRun2.getCounter()))); assertThat(stageForView.getTotalRuns(), is(2)); } private SecurityService alwaysAllow() { SecurityService securityService = mock(SecurityService.class); when(securityService.hasViewPermissionForPipeline(eq(ALWAYS_ALLOW_USER), any(String.class))).thenReturn(true); return securityService; } @Test public void shouldBeAbleToGetAJobsDuration() throws Exception { TestingClock clock = new TestingClock(); JobInstance theJob = JobInstanceMother.building("job", clock.currentTime()); theJob.setClock(clock); clock.addSeconds(9); theJob.completing(JobResult.Passed, clock.currentTime()); theJob.completed(clock.currentTime()); StageIdentifier stageId = new StageIdentifier(theJob.getPipelineName(), 1, "1.0.1", "1"); Stages stages = new Stages(StageMother.custom(theJob.getStageName(), theJob)); TransactionSynchronizationManager transactionSynchronizationManager = mock(TransactionSynchronizationManager.class); StageService service = new StageService(stageDao, null, null, null, alwaysAllow(), null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); when(stageDao.getAllRunsOfStageForPipelineInstance(stageId.getPipelineName(), stageId.getPipelineCounter(), stageId.getStageName())).thenReturn(stages); when(stageDao.getExpectedDurationMillis(theJob.getPipelineName(), theJob.getStageName(), theJob)).thenReturn(10 * 1000L); StageSummaryModel stageForView = service.findStageSummaryByIdentifier(stageId, ALWAYS_ALLOW_USER, new HttpLocalizedOperationResult()); JobInstanceModel job = stageForView.passedJobs().get(0); assertThat(job.getElapsedTime(), is(theJob.getElapsedTime())); assertThat(job.getPercentComplete(), is(90)); verify(stageDao).getExpectedDurationMillis(theJob.getPipelineName(), theJob.getStageName(), theJob); } @Test public void findStageSummaryByIdentifierShouldRespondWith401WhenUserDoesNotHavePermissionToViewThePipeline() throws Exception { SecurityService securityService = mock(SecurityService.class); when(securityService.hasViewPermissionForPipeline(ALWAYS_ALLOW_USER, "pipeline_name")).thenReturn(false); TransactionSynchronizationManager transactionSynchronizationManager = mock(TransactionSynchronizationManager.class); StageService service = new StageService(stageDao, null, null, null, securityService, null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); HttpLocalizedOperationResult result = new HttpLocalizedOperationResult(); StageSummaryModel model = service.findStageSummaryByIdentifier(new StageIdentifier("pipeline_name/10/stage_name/1"), ALWAYS_ALLOW_USER, result); assertThat(result.httpCode(), is(401)); assertThat(model, is(nullValue())); } @Test public void findStageSummaryByIdentifierShouldRespondWith404WhenNoStagesFound() throws Exception { SecurityService securityService = mock(SecurityService.class); when(securityService.hasViewPermissionForPipeline(ALWAYS_ALLOW_USER, "pipeline_does_not_exist")).thenReturn(true); TransactionSynchronizationManager transactionSynchronizationManager = mock(TransactionSynchronizationManager.class); StageService service = new StageService(stageDao, null, null, null, securityService, null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); HttpLocalizedOperationResult result = new HttpLocalizedOperationResult(); StageIdentifier stageId = new StageIdentifier("pipeline_does_not_exist/10/stage_name/1"); when(stageDao.getAllRunsOfStageForPipelineInstance(stageId.getPipelineName(), stageId.getPipelineCounter(), stageId.getStageName())).thenReturn(new Stages()); StageSummaryModel model = service.findStageSummaryByIdentifier(stageId, ALWAYS_ALLOW_USER, result); assertThat(result.httpCode(), is(404)); assertThat(model, is(nullValue())); } @Test public void findStageSummaryByIdentifierShouldRespondWith404WhenStagesHavingGivenCounterIsNotFound() throws Exception { SecurityService securityService = mock(SecurityService.class); when(securityService.hasViewPermissionForPipeline(ALWAYS_ALLOW_USER, "dev")).thenReturn(true); TransactionSynchronizationManager transactionSynchronizationManager = mock(TransactionSynchronizationManager.class); StageService service = new StageService(stageDao, null, null, null, securityService, null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); HttpLocalizedOperationResult result = new HttpLocalizedOperationResult(); Stages stages = new Stages(); Stage stage1 = new Stage(); stage1.setIdentifier(new StageIdentifier("dev/10/stage_name/1")); stages.add(stage1); StageIdentifier stageId = new StageIdentifier("dev/10/stage_name/9999999999"); when(stageDao.getAllRunsOfStageForPipelineInstance(stageId.getPipelineName(), stageId.getPipelineCounter(), stageId.getStageName())).thenReturn(stages); StageSummaryModel model = service.findStageSummaryByIdentifier(stageId, ALWAYS_ALLOW_USER, result); assertThat(model, is(nullValue())); assertThat(result.httpCode(), is(404)); } @Test public void shouldUpdateJobInstanceAndStageOnCancellingJob() throws SQLException { JobInstance job = new JobInstance("job"); job.setIdentifier(new JobIdentifier("pipeline", 10, "label", STAGE_NAME, "5", "job")); TransactionSynchronizationManager transactionSynchronizationManager = mock(TransactionSynchronizationManager.class); StageService service = new StageService(stageDao, jobInstanceService, null, null, null, null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); JobInstance foundJob = new JobInstance("job"); foundJob.setState(JobState.Scheduled); foundJob.setResult(JobResult.Unknown); JobInstances foundJobInstances = new JobInstances(foundJob); Stage foundStage = new Stage(STAGE_NAME, foundJobInstances, "jez", "manual", new TimeProvider()); foundStage.calculateResult(); assertThat(foundStage.getState(), is(not(StageState.Cancelled))); assertThat(foundStage.getResult(), is(not(StageResult.Cancelled))); foundJob.setState(JobState.Completed); foundJob.setResult(JobResult.Cancelled); when(stageDao.findStageWithIdentifier(job.getIdentifier().getStageIdentifier())).thenReturn(foundStage); service.cancelJob(job); assertThat(foundStage.getState(), is(StageState.Cancelled)); assertThat(foundStage.getResult(), is(StageResult.Cancelled)); verify(jobInstanceService).cancelJob(job); verify(stageDao).updateResult(foundStage, StageResult.Cancelled); } @Test public void findCompletedStagesFor_shouldCacheTheResultPerPipeline() { Date updateDate = new Date(); when(stageDao.findCompletedStagesFor("cruise", FeedModifier.Latest, -1, 25)).thenReturn(asList(stageFeedEntry("cruise", updateDate))).thenReturn(asList(stageFeedEntry("cruise", updateDate))); MingleConfig mingleConfig = new MingleConfig("http://foo.bar:7019/baz/", "go-project"); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfigWithMingle("cruise", mingleConfig)); Map<Long, List<ModificationForPipeline>> expectedMap = new HashMap<>(); expectedMap.put(1L, asList(new ModificationForPipeline(new PipelineId("cruise", 1L), ModificationsMother.checkinWithComment("revision", "#123 hello wolrd", updateDate), "Svn", "fooBarBaaz"))); when(changesetService.modificationsOfPipelines(asList(1L), "cruise", Username.ANONYMOUS)).thenReturn(expectedMap); StageService service = new StageService(stageDao, null, null, null, null, null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, new StubGoCache(transactionSynchronizationManager)); FeedEntry expected = stageFeedEntry("cruise", updateDate); FeedEntries feedEntries = service.feed("cruise", Username.ANONYMOUS);//Should prime the cache assertThat(feedEntries, is(new FeedEntries(asList(expected)))); assertThat(feedEntries.get(0).getAuthors(), is(asList(new Author(ModificationsMother.MOD_USER_COMMITTER, ModificationsMother.EMAIL_ADDRESS)))); assertThat(feedEntries.get(0).getMingleCards(), is(asList(new MingleCard(mingleConfig, "123")))); feedEntries = service.feed("cruise", Username.ANONYMOUS);//Should use the cache assertThat(feedEntries, is(new FeedEntries(asList(expected)))); assertThat(feedEntries.get(0).getAuthors(), is(asList(new Author(ModificationsMother.MOD_USER_COMMITTER, ModificationsMother.EMAIL_ADDRESS)))); assertThat(feedEntries.get(0).getMingleCards(), is(asList(new MingleCard(mingleConfig, "123")))); verify(stageDao).findCompletedStagesFor("cruise", FeedModifier.Latest, -1, 25); verify(changesetService).modificationsOfPipelines(asList(1L), "cruise", Username.ANONYMOUS); verifyNoMoreInteractions(stageDao); verifyNoMoreInteractions(changesetService); } @Test public void findCompletedStagesFor_shouldInvalidateCacheOnCompletionOfAStage() { Date updateDate = new Date(); when(stageDao.findCompletedStagesFor("cruise", FeedModifier.Latest, -1, 25)).thenReturn(asList(stageFeedEntry("cruise", updateDate))).thenReturn(asList(stageFeedEntry("cruise", updateDate))); MingleConfig mingleConfig = new MingleConfig("http://foo.bar:7019/baz/", "go-project"); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfigWithMingle("cruise", mingleConfig)); Map<Long, List<ModificationForPipeline>> expectedMap = new HashMap<>(); expectedMap.put(1L, asList(new ModificationForPipeline(new PipelineId("cruise", 1L), ModificationsMother.checkinWithComment("revision", "#123 hello wolrd", updateDate), "Svn", "fooBarBaaz"))); when(changesetService.modificationsOfPipelines(asList(1L), "cruise", Username.ANONYMOUS)).thenReturn(expectedMap); StageService service = new StageService(stageDao, null, null, null, null, null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, new StubGoCache(transactionSynchronizationManager)); FeedEntry expected = stageFeedEntry("cruise", updateDate); FeedEntries feedEntries = service.feed("cruise", Username.ANONYMOUS);//Should cache assertThat(feedEntries, is(new FeedEntries(asList(expected)))); assertThat(feedEntries.get(0).getAuthors(), is(asList(new Author(ModificationsMother.MOD_USER_COMMITTER, ModificationsMother.EMAIL_ADDRESS)))); assertThat(feedEntries.get(0).getMingleCards(), is(asList(new MingleCard(mingleConfig, "123")))); Stage stage = StageMother.createPassedStage("cruise", 1, "stage", 1, "job", updateDate); stage.setIdentifier(new StageIdentifier("cruise", 1, "stage", String.valueOf(1))); service.updateResult(stage);//Should remove from the cache feedEntries = service.feed("cruise", Username.ANONYMOUS);// Should retrieve from db again. assertThat(feedEntries, is(new FeedEntries(asList(expected)))); assertThat(feedEntries.get(0).getAuthors(), is(asList(new Author(ModificationsMother.MOD_USER_COMMITTER, ModificationsMother.EMAIL_ADDRESS)))); assertThat(feedEntries.get(0).getMingleCards(), is(asList(new MingleCard(mingleConfig, "123")))); verify(stageDao, times(2)).findCompletedStagesFor("cruise", FeedModifier.Latest, -1, 25); verify(changesetService, times(2)).modificationsOfPipelines(asList(1L), "cruise", Username.ANONYMOUS); verifyNoMoreInteractions(changesetService); } private CruiseConfig cruiseConfigWithMingle(final String pipelineName, MingleConfig mingleConfig) { CruiseConfig config = new BasicCruiseConfig(); PipelineConfig pipelineConfig = PipelineConfigMother.pipelineConfig(pipelineName); pipelineConfig.setMingleConfig(mingleConfig); config.addPipeline("group", pipelineConfig); return config; } @Test public void findCompletedStagesFor_shouldNotCacheTheResultPerPipelineForFeedsBeforeAGivenID() { Date updateDate = new Date(); when(stageDao.findCompletedStagesFor("cruise", FeedModifier.Before, 1L, 25)).thenReturn(asList(stageFeedEntry("cruise", updateDate))).thenReturn(asList(stageFeedEntry("cruise", updateDate))); //Setup Mingle Config MingleConfig mingleConfig = new MingleConfig("http://foo.bar:7019/baz/", "go-project"); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfigWithMingle("cruise", mingleConfig)); //Setup card numbers Map<Long, List<ModificationForPipeline>> expectedMap = new HashMap<>(); expectedMap.put(1L, asList(new ModificationForPipeline(new PipelineId("cruise", 1L), ModificationsMother.checkinWithComment("revision", "#123 hello wolrd", updateDate), "Svn", "fooBarBaaz"))); when(changesetService.modificationsOfPipelines(asList(1L), "cruise", Username.ANONYMOUS)).thenReturn(expectedMap); StageService service = new StageService(stageDao, null, null, null, null, null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, new StubGoCache(transactionSynchronizationManager)); FeedEntry expected = stageFeedEntry("cruise", updateDate); FeedEntries feedEntries = service.feedBefore(1L, "cruise", Username.ANONYMOUS); assertThat(feedEntries, is(new FeedEntries(asList(expected)))); assertThat(feedEntries.get(0).getAuthors(), is(asList(new Author(ModificationsMother.MOD_USER_COMMITTER, ModificationsMother.EMAIL_ADDRESS)))); assertThat(feedEntries.get(0).getMingleCards(), is(asList(new MingleCard(mingleConfig, "123")))); feedEntries = service.feedBefore(1L, "cruise", Username.ANONYMOUS); assertThat(feedEntries, is(new FeedEntries(asList(expected)))); assertThat(feedEntries.get(0).getAuthors(), is(asList(new Author(ModificationsMother.MOD_USER_COMMITTER, ModificationsMother.EMAIL_ADDRESS)))); assertThat(feedEntries.get(0).getMingleCards(), is(asList(new MingleCard(mingleConfig, "123")))); verify(stageDao, times(2)).findCompletedStagesFor("cruise", FeedModifier.Before, 1L, 25); verifyNoMoreInteractions(stageDao); } @Test public void shouldReturnFeedsEvenIfUpstreamPipelineIsDeleted() { Date updateDate = new Date(); CruiseConfig config = mock(BasicCruiseConfig.class); PipelineConfig pipelineConfig = PipelineConfigMother.pipelineConfig("down"); MingleConfig mingleConfig = new MingleConfig("http://foo.bar:7019/baz/", "go-project"); pipelineConfig.setMingleConfig(mingleConfig); Map<Long, List<ModificationForPipeline>> expectedModMapDown = new HashMap<>(); Modification mod1 = ModificationsMother.checkinWithComment("revision", "#123 hello wolrd", updateDate); expectedModMapDown.put(1L, asList(new ModificationForPipeline(new PipelineId("down", 1L), mod1, "Svn", "fooBarBaaz"))); FeedEntry expected = stageFeedEntry("down", updateDate); when(stageDao.findCompletedStagesFor("down", FeedModifier.Latest, -1, 25)).thenReturn(asList(stageFeedEntry("down", updateDate), stageFeedEntry("down", updateDate))); when(goConfigService.currentCruiseConfig()).thenReturn(config); when(changesetService.modificationsOfPipelines(asList(1L, 1L), "down", Username.ANONYMOUS)).thenReturn(expectedModMapDown); when(config.hasPipelineNamed(any(CaseInsensitiveString.class))).thenReturn(false).thenReturn(true); when(config.pipelineConfigByName(any(CaseInsensitiveString.class))).thenReturn(pipelineConfig); StageService service = new StageService(stageDao, null, null, null, null, null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, new StubGoCache(transactionSynchronizationManager)); FeedEntries feedEntries = service.feed("down", Username.ANONYMOUS); assertThat(feedEntries, is(new FeedEntries(asList(expected, expected)))); assertThat(feedEntries.get(0).getAuthors(), is(asList(new Author(ModificationsMother.MOD_USER_COMMITTER, ModificationsMother.EMAIL_ADDRESS)))); assertEquals(feedEntries.get(0).getMingleCards().size(), 0); assertThat(feedEntries.get(1).getAuthors(), is(asList(new Author(ModificationsMother.MOD_USER_COMMITTER, ModificationsMother.EMAIL_ADDRESS)))); assertThat(feedEntries.get(1).getMingleCards(), is(asList(new MingleCard(mingleConfig, "123")))); } private StageFeedEntry stageFeedEntry(String pipelineName, final Date updateDate) { return new StageFeedEntry(1L, 1L, new StageIdentifier(pipelineName + "/1/dist/1"), 1L, updateDate, StageResult.Passed); } @Test public void shouldSendStageStatusMessageAfterStageIsCancelled() throws SQLException { StageStatusTopic topic = mock(StageStatusTopic.class); final Stage cancelledStage = StageMother.cancelledStage("stage", "job"); cancelledStage.setIdentifier(new StageIdentifier("pipeline/1/stage/1")); final StageService service = new StageService(stageDao, jobInstanceService, topic, new StageStatusCache(stageDao), null, null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, mock(GoCache.class)); transactionTemplate.execute(new TransactionCallbackWithoutResult() { public void doInTransactionWithoutResult(TransactionStatus status) { service.cancelStage(cancelledStage); } }); verify(topic).post(new StageStatusMessage(cancelledStage.getIdentifier(), StageState.Cancelled, StageResult.Cancelled, Username.ANONYMOUS)); verifyNoMoreInteractions(topic); } @Test public void shouldFindLatestStageFromCache() throws SQLException { Stage expectedStage = StageMother.custom("pipeline", "stage", null); StageStatusCache cache = new StageStatusCache(stageDao); cache.stageStatusChanged(expectedStage); TransactionSynchronizationManager transactionSynchronizationManager = mock(TransactionSynchronizationManager.class); StageService service = new StageService(stageDao, jobInstanceService, null, cache, null, null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); Stage actualStage = service.findLatestStage("pipeline", "stage"); assertThat(actualStage, is(expectedStage)); } @Test public void shouldOnlyLoadStagesArtifactOfWhichCanBeDeleted() { StageService service = new StageService(stageDao, null, null, null, securityService, null, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); Stage stageFoo = StageMother.passedStageInstance("stage-foo", "job", "pipeline-baz"); Stage stageBar = StageMother.passedStageInstance("stage-bar", "job", "pipeline-quux"); Stage stageBaz = StageMother.passedStageInstance("stage-baz", "job", "pipeline-foo"); Stage stageQuux = StageMother.passedStageInstance("stage-quux", "job", "pipeline-bar"); when(stageDao.oldestStagesHavingArtifacts()).thenReturn(asList(stageFoo, stageBar, stageBaz, stageQuux)); List<Stage> stages = service.oldestStagesWithDeletableArtifacts(); assertThat(stages.size(), is(4)); assertThat(stages, hasItem(stageFoo)); assertThat(stages, hasItem(stageBar)); assertThat(stages, hasItem(stageBaz)); assertThat(stages, hasItem(stageQuux)); } @Test public void shouldDelegateToDAO_findDetailedStageHistoryByOffset() { when(cruiseConfig.hasPipelineNamed(new CaseInsensitiveString("pipeline"))).thenReturn(true); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); when(securityService.hasViewPermissionForPipeline(Username.valueOf("looser"), "pipeline")).thenReturn(true); final StageService stageService = new StageService(stageDao, jobInstanceService, mock(StageStatusTopic.class), mock(StageStatusCache.class), securityService, pipelineDao, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); Pagination pagination = Pagination.pageStartingAt(1, 1, 1); stageService.findDetailedStageHistoryByOffset("pipeline", "stage", pagination, "looser", new HttpOperationResult()); verify(stageDao).findDetailedStageHistoryByOffset("pipeline", "stage", pagination); } @Test public void shouldPopulateErrorWhenPipelineNotFound_findDetailedStageHistoryByOffset() { when(cruiseConfig.hasPipelineNamed(new CaseInsensitiveString("pipeline"))).thenReturn(false); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); when(securityService.hasViewPermissionForPipeline(Username.valueOf("looser"), "pipeline")).thenReturn(true); final StageService stageService = new StageService(stageDao, jobInstanceService, mock(StageStatusTopic.class), mock(StageStatusCache.class), securityService, pipelineDao, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); Pagination pagination = Pagination.pageStartingAt(1, 1, 1); HttpOperationResult result = new HttpOperationResult(); StageInstanceModels stageInstanceModels = stageService.findDetailedStageHistoryByOffset("pipeline", "stage", pagination, "looser", result); assertThat(stageInstanceModels, is(Matchers.nullValue())); assertThat(result.httpCode(), is(404)); } @Test public void shouldPopulateErrorWhenUnauthorized_findDetailedStageHistoryByOffset() { when(cruiseConfig.hasPipelineNamed(new CaseInsensitiveString("pipeline"))).thenReturn(true); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); when(securityService.hasViewPermissionForPipeline(Username.valueOf("looser"), "pipeline")).thenReturn(false); final StageService stageService = new StageService(stageDao, jobInstanceService, mock(StageStatusTopic.class), mock(StageStatusCache.class), securityService, pipelineDao, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); Pagination pagination = Pagination.pageStartingAt(1, 1, 1); HttpOperationResult result = new HttpOperationResult(); StageInstanceModels stageInstanceModels = stageService.findDetailedStageHistoryByOffset("pipeline", "stage", pagination, "looser", result); assertThat(stageInstanceModels, is(Matchers.nullValue())); assertThat(result.httpCode(), is(401)); } @Test public void shouldPopulateErrorWhenPipelineNotFound_findStageWithIdentifier() { when(cruiseConfig.hasPipelineNamed(new CaseInsensitiveString("pipeline"))).thenReturn(false); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); when(securityService.hasViewPermissionForPipeline(Username.valueOf("looser"), "pipeline")).thenReturn(true); final StageService stageService = new StageService(stageDao, jobInstanceService, mock(StageStatusTopic.class), mock(StageStatusCache.class), securityService, pipelineDao, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); HttpOperationResult result = new HttpOperationResult(); Stage stage = stageService.findStageWithIdentifier("pipeline", 1, "stage", "1", "looser", result); assertThat(stage, is(Matchers.nullValue())); assertThat(result.httpCode(), is(404)); } @Test public void shouldPopulateErrorWhenUnauthorized_findStageWithIdentifier() { when(cruiseConfig.hasPipelineNamed(new CaseInsensitiveString("pipeline"))).thenReturn(true); when(goConfigService.currentCruiseConfig()).thenReturn(cruiseConfig); when(securityService.hasViewPermissionForPipeline(Username.valueOf("looser"), "pipeline")).thenReturn(false); final StageService stageService = new StageService(stageDao, jobInstanceService, mock(StageStatusTopic.class), mock(StageStatusCache.class), securityService, pipelineDao, changesetService, goConfigService, transactionTemplate, transactionSynchronizationManager, goCache); HttpOperationResult result = new HttpOperationResult(); Stage stage = stageService.findStageWithIdentifier("pipeline", 1, "stage", "1", "looser", result); assertThat(stage, is(Matchers.nullValue())); assertThat(result.httpCode(), is(401)); } }