/*
* 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.CruiseConfig;
import com.thoughtworks.go.config.JobConfig;
import com.thoughtworks.go.config.JobConfigs;
import com.thoughtworks.go.domain.JobInstance;
import com.thoughtworks.go.domain.MaterialRevisions;
import com.thoughtworks.go.domain.Pipeline;
import com.thoughtworks.go.domain.Stage;
import com.thoughtworks.go.domain.activity.JobStatusCache;
import com.thoughtworks.go.domain.activity.StageStatusCache;
import com.thoughtworks.go.domain.buildcause.BuildCause;
import com.thoughtworks.go.domain.materials.Modification;
import com.thoughtworks.go.helper.PipelineConfigMother;
import com.thoughtworks.go.helper.PipelineMother;
import com.thoughtworks.go.helper.StageMother;
import com.thoughtworks.go.plugin.infra.PluginManager;
import com.thoughtworks.go.server.cache.GoCache;
import com.thoughtworks.go.server.dao.JobInstanceDao;
import com.thoughtworks.go.server.dao.PipelineDao;
import com.thoughtworks.go.server.dao.PipelineSqlMapDao;
import com.thoughtworks.go.server.dao.StageDao;
import com.thoughtworks.go.server.domain.JobStatusListener;
import com.thoughtworks.go.server.domain.PipelineConfigDependencyGraph;
import com.thoughtworks.go.server.domain.PipelineTimeline;
import com.thoughtworks.go.server.domain.StageStatusListener;
import com.thoughtworks.go.server.messaging.JobResultTopic;
import com.thoughtworks.go.server.messaging.StageStatusTopic;
import com.thoughtworks.go.server.persistence.MaterialRepository;
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.serverhealth.ServerHealthService;
import com.thoughtworks.go.serverhealth.ServerHealthStates;
import com.thoughtworks.go.util.SystemEnvironment;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static com.thoughtworks.go.helper.ModificationsMother.*;
import static org.mockito.Mockito.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"classpath:WEB-INF/applicationContext-global.xml",
"classpath:WEB-INF/applicationContext-dataLocalAccess.xml",
"classpath:WEB-INF/applicationContext-acegi-security.xml"
})
public class PipelineServiceTest {
private PipelineTimeline pipelineTimeline;
private PipelineService service;
private PipelineSqlMapDao pipelineDao;
private MaterialRepository materialRepository;
private Modification first;
private Modification third;
private Modification second;
@Autowired private TransactionTemplate actualTransactionTemplate;
@Autowired private TransactionSynchronizationManager transactionSynchronizationManager;
@Autowired private GoConfigService goConfigService;
@Autowired private GoCache goCache;
@Autowired private SystemEnvironment systemEnvironment;
@Autowired private PluginManager pluginManager;
@Autowired private MaterialConfigConverter materialConfigConverter;
@Before
public void setUp() throws Exception {
pipelineTimeline = mock(PipelineTimeline.class);
pipelineDao = mock(PipelineSqlMapDao.class);
materialRepository = mock(MaterialRepository.class);
TestTransactionSynchronizationManager mockTransactionSynchronizationManager = new TestTransactionSynchronizationManager();
TransactionTemplate mockTransactionTemplate = new TestTransactionTemplate(mockTransactionSynchronizationManager);
service = new PipelineService(pipelineDao, mock(StageService.class), mock(PipelineLockService.class), pipelineTimeline, materialRepository, mockTransactionTemplate, systemEnvironment, null,
materialConfigConverter);
first = oneModifiedFile("1");
third = oneModifiedFile("3");
second = oneModifiedFile("2");
first.setId(1);
third.setId(3);
second.setId(2);
}
@Test
public void shouldTellPipelineMaterialModificationsToUpdateItselfOnSave() throws Exception {
Pipeline pipeline = PipelineMother.pipeline("cruise");
when(pipelineDao.save(pipeline)).thenReturn(pipeline);
when(pipelineTimeline.pipelineBefore(anyLong())).thenReturn(9L);
when(pipelineTimeline.pipelineAfter(pipeline.getId())).thenReturn(-1L);
when(materialRepository.findMaterialRevisionsForPipeline(9L)).thenReturn(MaterialRevisions.EMPTY);
service.save(pipeline);
Mockito.verify(pipelineTimeline).update();
}
@Test
public void shouldNotNotifyStatusListenersWhenTransactionRollsback() throws Exception {
StageStatusListener stageStatusListener = mock(StageStatusListener.class);
JobStatusListener jobStatusListener = mock(JobStatusListener.class);
Pipeline pipeline = stubPipelineSaveForStatusListener(stageStatusListener, jobStatusListener);
Mockito.doThrow(new RuntimeException()).when(pipelineTimeline).update();
try {
service.save(pipeline);
} catch (RuntimeException e) {
//ignore
}
verify(stageStatusListener, never()).stageStatusChanged(any(Stage.class));
verify(jobStatusListener, never()).jobStatusChanged(any(JobInstance.class));
}
@Test
public void shouldNotifyStageStatusListenersOnlyWhenTransactionCommits() throws Exception {
StageStatusListener stageStatusListener = mock(StageStatusListener.class);
JobStatusListener jobStatusListener = mock(JobStatusListener.class);
Pipeline pipeline = stubPipelineSaveForStatusListener(stageStatusListener, jobStatusListener);
service.save(pipeline);
verify(stageStatusListener).stageStatusChanged(any(Stage.class));
verify(jobStatusListener).jobStatusChanged(any(JobInstance.class));
}
private Pipeline stubPipelineSaveForStatusListener(StageStatusListener stageStatusListener, JobStatusListener jobStatusListener) {
StageDao stageDao = mock(StageDao.class);
ServerHealthService serverHealthService = mock(ServerHealthService.class);
when(serverHealthService.getAllLogs()).thenReturn(new ServerHealthStates());
JobInstanceService jobInstanceService = new JobInstanceService(mock(JobInstanceDao.class), mock(PropertiesService.class), mock(JobResultTopic.class), mock(JobStatusCache.class),
actualTransactionTemplate, transactionSynchronizationManager, null, null, goConfigService, null, pluginManager, serverHealthService, jobStatusListener);
StageService stageService = new StageService(stageDao, jobInstanceService, mock(StageStatusTopic.class), mock(StageStatusCache.class), mock(SecurityService.class), mock(PipelineDao.class),
mock(ChangesetService.class), mock(GoConfigService.class), actualTransactionTemplate, transactionSynchronizationManager,
goCache);
Stage savedStage = StageMother.passedStageInstance("stage", "job", "pipeline-name");
when(stageDao.save(any(Pipeline.class), any(Stage.class))).thenReturn(savedStage);
stageService.addStageStatusListener(stageStatusListener);
service = new PipelineService(pipelineDao, stageService, mock(PipelineLockService.class), pipelineTimeline, materialRepository, actualTransactionTemplate, systemEnvironment, null, materialConfigConverter);
Pipeline pipeline = PipelineMother.pipeline("cruise", savedStage);
when(pipelineDao.save(pipeline)).thenReturn(pipeline);
when(pipelineTimeline.pipelineBefore(anyLong())).thenReturn(9L);
when(pipelineTimeline.pipelineAfter(pipeline.getId())).thenReturn(-1L);
when(materialRepository.findMaterialRevisionsForPipeline(9L)).thenReturn(MaterialRevisions.EMPTY);
return pipeline;
}
@Test
public void shouldUpdateTheToAndFromRevisionOfThePipelineAfterThePipelineBeingSaved() throws Exception {
MaterialRevisions scheduleTime = createHgMaterialWithMultipleRevisions(1L, first);
scheduleTime.addAll(createSvnMaterialWithMultipleRevisions(2, first));
MaterialRevisions next = createHgMaterialWithMultipleRevisions(1L, third, second, first);
next.addAll(createSvnMaterialWithMultipleRevisions(2, third, second, first));
MaterialRevisions expected = createHgMaterialWithMultipleRevisions(1L, third, second);
expected.addAll(createSvnMaterialWithMultipleRevisions(2L, third, second));
Pipeline pipeline = pipeline(scheduleTime, jobs());
Pipeline savedPipeline = pipeline(scheduleTime, jobs());
when(pipelineTimeline.pipelineAfter(pipeline.getId())).thenReturn(11L);
when(pipelineTimeline.pipelineBefore(pipeline.getId())).thenReturn(-1L);
when(materialRepository.findMaterialRevisionsForPipeline(11L)).thenReturn(next);
when(pipelineDao.save(pipeline)).thenReturn(savedPipeline);
service.save(pipeline);
}
@Test
public void shouldReturnTheOrderedListOfStageIdentifiers() throws Exception {
//TODO: does it? while we trust it, may be its a good idea to validate --shilpa & jj
}
private JobConfigs jobs() {
JobConfigs configs = new JobConfigs();
configs.add(new JobConfig("job"));
return configs;
}
private Pipeline pipeline(MaterialRevisions scheduleTime, JobConfigs configs) {
Pipeline pipeline = PipelineMother.schedule(PipelineConfigMother.pipelineConfig("mummy", scheduleTime.getMaterialRevision(0).getMaterial().config(), configs),
BuildCause.createWithModifications(scheduleTime, "me"));
pipeline.setId(10);
return pipeline;
}
private CruiseConfig createCruiseConfigFromGraph(CruiseConfig cruiseConfig, final PipelineConfigDependencyGraph pdg) {
String groupName = "defaultGroup";
cruiseConfig.addPipeline(groupName, pdg.getCurrent());
for (PipelineConfigDependencyGraph upstreamDependency : pdg.getUpstreamDependencies()) {
createCruiseConfigFromGraph(cruiseConfig, upstreamDependency);
}
return cruiseConfig;
}
}