/*
* 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.*;
import com.thoughtworks.go.config.materials.git.GitMaterial;
import com.thoughtworks.go.config.materials.mercurial.HgMaterial;
import com.thoughtworks.go.config.materials.mercurial.HgMaterialConfig;
import com.thoughtworks.go.config.remote.ConfigRepoConfig;
import com.thoughtworks.go.config.remote.RepoConfigOrigin;
import com.thoughtworks.go.domain.MaterialRevisions;
import com.thoughtworks.go.domain.Pipeline;
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.git.GitTestRepo;
import com.thoughtworks.go.helper.*;
import com.thoughtworks.go.server.cronjob.GoDiskSpaceMonitor;
import com.thoughtworks.go.server.dao.DatabaseAccessHelper;
import com.thoughtworks.go.server.dao.PipelineDao;
import com.thoughtworks.go.server.domain.Username;
import com.thoughtworks.go.server.materials.*;
import com.thoughtworks.go.server.perf.MDUPerformanceLogger;
import com.thoughtworks.go.server.persistence.MaterialRepository;
import com.thoughtworks.go.server.scheduling.BuildCauseProducerService;
import com.thoughtworks.go.server.scheduling.ScheduleHelper;
import com.thoughtworks.go.server.scheduling.ScheduleOptions;
import com.thoughtworks.go.server.service.result.ServerHealthStateOperationResult;
import com.thoughtworks.go.server.transaction.TransactionTemplate;
import com.thoughtworks.go.serverhealth.ServerHealthService;
import com.thoughtworks.go.util.ConfigElementImplementationRegistryMother;
import com.thoughtworks.go.util.GoConfigFileHelper;
import com.thoughtworks.go.util.SystemEnvironment;
import org.hamcrest.core.IsNot;
import org.junit.After;
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 java.util.HashMap;
import java.util.List;
import java.util.Map;
import static junit.framework.Assert.fail;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
@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 BuildCauseProducerServiceConfigRepoIntegrationTest {
@Autowired private GoConfigDao goConfigDao;
@Autowired private GoConfigService goConfigService;
@Autowired private PipelineDao pipelineDao;
@Autowired private ServerHealthService serverHealthService;
@Autowired PipelineService pipelineService;
@Autowired private ScheduleHelper scheduleHelper;
@Autowired private DatabaseAccessHelper dbHelper;
@Autowired private MaterialDatabaseUpdater materialDatabaseUpdater;
@Autowired private MaterialRepository materialRepository;
@Autowired private MaterialUpdateService materialUpdateService;
@Autowired private SubprocessExecutionContext subprocessExecutionContext;
@Autowired private ConfigMaterialUpdater materialUpdater;
@Autowired private GoRepoConfigDataSource goRepoConfigDataSource;
@Autowired private SystemEnvironment systemEnvironment;
@Autowired private MaterialConfigConverter materialConfigConverter;
@Autowired private ConfigCache configCache;
@Autowired private CachedGoConfig cachedGoConfig;
@Autowired private PipelineScheduleQueue pipelineScheduleQueue;
@Autowired private PipelineScheduler buildCauseProducer;
@Autowired private BuildCauseProducerService buildCauseProducerService;
@Autowired private MaterialChecker materialChecker;
@Autowired private MaterialExpansionService materialExpansionService;
@Autowired private MaterialUpdateCompletedTopic topic;
@Autowired private ConfigMaterialUpdateCompletedTopic configTopic;
@Autowired private TransactionTemplate transactionTemplate;
private GoDiskSpaceMonitor goDiskSpaceMonitor;
private static GoConfigFileHelper configHelper = new GoConfigFileHelper();
private MagicalGoConfigXmlWriter xmlWriter;
private ConfigTestRepo configTestRepo;
private DiskSpaceSimulator diskSpaceSimulator;
private HgTestRepo hgRepo;
private HgMaterialConfig materialConfig;
private MDUPerformanceLogger logger;
private MaterialUpdateListener worker;
private HgMaterial material;
private Pipeline latestPipeline;
private PipelineConfig pipelineConfig;
MaterialRevisions firstRevisions;
private String PIPELINE_NAME;
String fileName = "pipe1.gocd.xml";
@Before
public void setup() throws Exception {
diskSpaceSimulator = new DiskSpaceSimulator();
hgRepo = new HgTestRepo("testHgRepo");
dbHelper.onSetUp();
configHelper.onSetUp();
configHelper.usingCruiseConfigDao(goConfigDao).initializeConfigFile();
materialConfig = hgRepo.materialConfig();
configHelper.addConfigRepo(new ConfigRepoConfig(materialConfig,"gocd-xml"));
logger = mock(MDUPerformanceLogger.class);
TestingEmailSender emailSender = new TestingEmailSender();
SystemDiskSpaceChecker mockDiskSpaceChecker = Mockito.mock(SystemDiskSpaceChecker.class);
StageService stageService = mock(StageService.class);
ConfigDbStateRepository configDbStateRepository = mock(ConfigDbStateRepository.class);
goDiskSpaceMonitor = new GoDiskSpaceMonitor(goConfigService, systemEnvironment,
serverHealthService, emailSender, mockDiskSpaceChecker, mock(ArtifactsService.class),
stageService, configDbStateRepository);
goDiskSpaceMonitor.initialize();
worker = new MaterialUpdateListener(configTopic,materialDatabaseUpdater,logger,goDiskSpaceMonitor);
xmlWriter = new MagicalGoConfigXmlWriter(configCache, ConfigElementImplementationRegistryMother.withNoPlugins());
configTestRepo = new ConfigTestRepo(hgRepo, xmlWriter);
this.material = (HgMaterial)materialConfigConverter.toMaterial(materialConfig);
pipelineConfig = PipelineConfigMother.createPipelineConfigWithStages("pipe1", "build", "test");
pipelineConfig.materialConfigs().clear();
pipelineConfig.materialConfigs().add(materialConfig);
PIPELINE_NAME = CaseInsensitiveString.str(pipelineConfig.name());
configTestRepo.addPipelineToRepositoryAndPush(fileName, pipelineConfig);
materialUpdateService.updateMaterial(material);
// time for messages to pass through all services
waitForMaterialNotInProgress();
pipelineConfig = goConfigService.pipelineConfigNamed(pipelineConfig.name());
pipelineScheduleQueue.clear();
//check test setup
Materials materials = materialConfigConverter.toMaterials(pipelineConfig.materialConfigs());
MaterialRevisions peggedRevisions = new MaterialRevisions();
firstRevisions = materialChecker.findLatestRevisions(peggedRevisions, materials);
assertThat(firstRevisions.isMissingModifications(),is(false));
}
@After
public void teardown() throws Exception {
diskSpaceSimulator.onTearDown();
TestRepo.internalTearDown();
dbHelper.onTearDown();
pipelineScheduleQueue.clear();
configHelper.onTearDown();
}
private void waitForMaterialNotInProgress() throws InterruptedException {
// time for messages to pass through all services
int i = 0;
while (materialUpdateService.isInProgress(material)) {
Thread.sleep(100);
if(i++ > 100)
fail("material is hung - more than 10 seconds in progress");
}
}
@Test
public void shouldSchedulePipelineWhenManuallyTriggered() throws Exception {
configTestRepo.addCodeToRepositoryAndPush("a.java", "added code file", "some java code");
materialUpdateService.updateMaterial(material);
waitForMaterialNotInProgress();
final HashMap<String, String> revisions = new HashMap<>();
final HashMap<String, String> environmentVariables = new HashMap<>();
buildCauseProducer.manualProduceBuildCauseAndSave(PIPELINE_NAME, Username.ANONYMOUS,
new ScheduleOptions(revisions, environmentVariables, new HashMap<>()), new ServerHealthStateOperationResult());
Map<String, BuildCause> afterLoad = scheduleHelper.waitForAnyScheduled(5);
assertThat(afterLoad.keySet(), hasItem(PIPELINE_NAME));
BuildCause cause = afterLoad.get(PIPELINE_NAME);
assertThat(cause.getBuildCauseMessage(), containsString("Forced by anonymous"));
}
@Test
public void shouldSchedulePipeline() throws Exception {
configTestRepo.addCodeToRepositoryAndPush("a.java", "added code file","some java code");
materialUpdateService.updateMaterial(material);
waitForMaterialNotInProgress();
buildCauseProducerService.autoSchedulePipeline(PIPELINE_NAME,new ServerHealthStateOperationResult(),123);
assertThat(scheduleHelper.waitForAnyScheduled(5).keySet(), hasItem(PIPELINE_NAME));
}
@Test
public void shouldNotSchedulePipelineWhenPartIsInvalid() throws Exception {
configTestRepo.addCodeToRepositoryAndPush(fileName, "added broken config file","bad bad config");
materialUpdateService.updateMaterial(material);
waitForMaterialNotInProgress();
assertThat(goRepoConfigDataSource.latestParseHasFailedForMaterial(material.config()),is(true));
buildCauseProducerService.autoSchedulePipeline(PIPELINE_NAME, new ServerHealthStateOperationResult(), 123);
scheduleHelper.waitForNotScheduled(5, PIPELINE_NAME);
}
@Test
public void shouldSchedulePipelineWhenPartIsInvalid_AndManuallyTriggered() throws Exception {
List<Modification> lastPush = configTestRepo.addCodeToRepositoryAndPush(fileName, "added broken config file", "bad bad config");
materialUpdateService.updateMaterial(material);
waitForMaterialNotInProgress();
assertThat(goRepoConfigDataSource.latestParseHasFailedForMaterial(material.config()),is(true));
final HashMap<String, String> revisions = new HashMap<>();
final HashMap<String, String> environmentVariables = new HashMap<>();
buildCauseProducer.manualProduceBuildCauseAndSave(PIPELINE_NAME, Username.ANONYMOUS,
new ScheduleOptions(revisions, environmentVariables, new HashMap<>()), new ServerHealthStateOperationResult());
Map<String, BuildCause> afterLoad = scheduleHelper.waitForAnyScheduled(5);
assertThat(afterLoad.keySet(), hasItem(PIPELINE_NAME));
BuildCause cause = afterLoad.get(PIPELINE_NAME);
assertThat(cause.getBuildCauseMessage(), containsString("Forced by anonymous"));
PipelineConfig pipelineConfigAfterSchedule = goConfigService.pipelineConfigNamed(pipelineConfig.name());
RepoConfigOrigin configOriginAfterSchedule = (RepoConfigOrigin) pipelineConfigAfterSchedule.getOrigin();
String lastValidPushedRevision = this.firstRevisions.latestRevision();
assertThat("revisionOfPipelineConfigOriginShouldMatchLastValidPushedCommit",
configOriginAfterSchedule.getRevision(),is(lastValidPushedRevision));
assertThat("buildCauseRevisionShouldMatchLastPushedCommit",
cause.getMaterialRevisions().latestRevision(), is(lastPush.get(0).getRevision()));
}
@Test
public void shouldNotSchedulePipelineWhenConfigAndMaterialRevisionsMismatch() throws Exception {
// we will use this worker to force material update without updating config
MaterialUpdateListener byPassWorker = new MaterialUpdateListener(topic, materialDatabaseUpdater, logger, goDiskSpaceMonitor);
List<Modification> mod = configTestRepo.addCodeToRepositoryAndPush("a.java", "added code file", "some java code");
byPassWorker.onMessage(new MaterialUpdateMessage(material,123));
//now db should have been updated, but config is still old
RepoConfigOrigin configOrigin = (RepoConfigOrigin) goConfigService.pipelineConfigNamed(new CaseInsensitiveString(PIPELINE_NAME)).getOrigin();
assertThat(configOrigin.getRevision(),is(firstRevisions.latestRevision()));
buildCauseProducerService.autoSchedulePipeline(PIPELINE_NAME,new ServerHealthStateOperationResult(),123);
scheduleHelper.waitForNotScheduled(5, PIPELINE_NAME);
}
@Test
// unfortunately there is no way to know why revisions would mismatch during manual trigger.
// We already let all manual triggers to bypass revision match check
public void shouldSchedulePipelineWhenConfigAndMaterialRevisionsMismatch_AndManuallyTriggered() throws Exception {
// we will use this worker to force material update without updating config
MaterialUpdateListener byPassWorker = new MaterialUpdateListener(topic, materialDatabaseUpdater, logger, goDiskSpaceMonitor);
List<Modification> lastPush = configTestRepo.addCodeToRepositoryAndPush("a.java", "added code file", "some java code");
byPassWorker.onMessage(new MaterialUpdateMessage(material,123));
//now db should have been updated, but config is still old
RepoConfigOrigin configOrigin = (RepoConfigOrigin) goConfigService.pipelineConfigNamed(new CaseInsensitiveString(PIPELINE_NAME)).getOrigin();
assertThat(configOrigin.getRevision(),is(firstRevisions.latestRevision()));
final HashMap<String, String> revisions = new HashMap<>();
final HashMap<String, String> environmentVariables = new HashMap<>();
buildCauseProducer.manualProduceBuildCauseAndSave(PIPELINE_NAME, Username.ANONYMOUS,
new ScheduleOptions(revisions, environmentVariables, new HashMap<>()), new ServerHealthStateOperationResult());
Map<String, BuildCause> afterLoad = scheduleHelper.waitForAnyScheduled(5);
assertThat(afterLoad.keySet(), hasItem(PIPELINE_NAME));
BuildCause cause = afterLoad.get(PIPELINE_NAME);
assertThat(cause.getBuildCauseMessage(), containsString("Forced by anonymous"));
assertThat("buildCauseRevisionShouldMatchLastPushedCommit",
cause.getMaterialRevisions().latestRevision(), is(lastPush.get(0).getRevision()));
}
@Test
public void shouldReloadPipelineConfigurationWhenManuallyTriggered() throws Exception
{
// we change configuration of the pipeline by pushing new stage to config repo
pipelineConfig = PipelineConfigMother.createPipelineConfigWithStages("pipe1", "build", "test","newStage");
pipelineConfig.materialConfigs().clear();
pipelineConfig.materialConfigs().add(materialConfig);
List<Modification> mod = configTestRepo.addPipelineToRepositoryAndPush(fileName, pipelineConfig);
final HashMap<String, String> revisions = new HashMap<>();
final HashMap<String, String> environmentVariables = new HashMap<>();
buildCauseProducer.manualProduceBuildCauseAndSave(PIPELINE_NAME, Username.ANONYMOUS,
new ScheduleOptions(revisions, environmentVariables, new HashMap<>()), new ServerHealthStateOperationResult());
Map<String, BuildCause> afterLoad = scheduleHelper.waitForAnyScheduled(5);
assertThat(afterLoad.keySet(), hasItem(PIPELINE_NAME));
BuildCause cause = afterLoad.get(PIPELINE_NAME);
assertThat(cause.getBuildCauseMessage(), containsString("Forced by anonymous"));
PipelineConfig pipelineConfigAfterSchedule = goConfigService.pipelineConfigNamed(pipelineConfig.name());
RepoConfigOrigin configOriginAfterSchedule = (RepoConfigOrigin) pipelineConfigAfterSchedule.getOrigin();
String lastPushedRevision = mod.get(0).getRevision();
assertThat("revisionOfPipelineConfigOriginShouldMatchLastPushedCommit",
configOriginAfterSchedule.getRevision(),is(lastPushedRevision));
assertThat("buildCauseRevisionShouldMatchLastPushedCommit",
cause.getMaterialRevisions().latestRevision(),is(lastPushedRevision));
}
@Test
public void shouldNotScheduleWhenPipelineRemovedFromConfigRepoWhenManuallyTriggered() throws Exception
{
configTestRepo.addCodeToRepositoryAndPush(fileName, "removed pipeline from configuration",
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<cruise xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"cruise-config.xsd\" schemaVersion=\"38\">\n"
+ "</cruise>");
final HashMap<String, String> revisions = new HashMap<>();
final HashMap<String, String> environmentVariables = new HashMap<>();
buildCauseProducer.manualProduceBuildCauseAndSave(PIPELINE_NAME, Username.ANONYMOUS,
new ScheduleOptions(revisions, environmentVariables, new HashMap<>()), new ServerHealthStateOperationResult());
waitForMaterialNotInProgress();
// config is correct
cachedGoConfig.throwExceptionIfExists();
assertThat(pipelineScheduleQueue.toBeScheduled().keySet(), IsNot.not(hasItem(PIPELINE_NAME)));
assertThat(goConfigService.hasPipelineNamed(pipelineConfig.name()),is(false));
}
@Test
public void shouldReloadPipelineConfigurationAndUpdateNewMaterialWhenManuallyTriggered() throws Exception
{
GitTestRepo otherGitRepo = new GitTestRepo();
pipelineConfig = PipelineConfigMother.createPipelineConfigWithStages("pipe1", "build", "test");
pipelineConfig.materialConfigs().clear();
materialConfig = hgRepo.createMaterialConfig("dest1");
materialConfig.setAutoUpdate(true);
pipelineConfig.materialConfigs().add(materialConfig);
// new material is added
GitMaterial gitMaterial = otherGitRepo.createMaterial("dest2");
gitMaterial.setAutoUpdate(true);
MaterialConfig otherMaterialConfig = gitMaterial.config();
otherMaterialConfig.setAutoUpdate(true);
pipelineConfig.materialConfigs().add(otherMaterialConfig);
List<Modification> mod = configTestRepo.addPipelineToRepositoryAndPush(fileName, pipelineConfig);
final HashMap<String, String> revisions = new HashMap<>();
final HashMap<String, String> environmentVariables = new HashMap<>();
buildCauseProducer.manualProduceBuildCauseAndSave(PIPELINE_NAME, Username.ANONYMOUS,
new ScheduleOptions(revisions, environmentVariables, new HashMap<>()), new ServerHealthStateOperationResult());
cachedGoConfig.throwExceptionIfExists();
Map<String, BuildCause> afterLoad = scheduleHelper.waitForAnyScheduled(20);
assertThat(afterLoad.keySet(), hasItem(PIPELINE_NAME));
BuildCause cause = afterLoad.get(PIPELINE_NAME);
assertThat(cause.getBuildCauseMessage(), containsString("Forced by anonymous"));
PipelineConfig pipelineConfigAfterSchedule = goConfigService.pipelineConfigNamed(pipelineConfig.name());
RepoConfigOrigin configOriginAfterSchedule = (RepoConfigOrigin) pipelineConfigAfterSchedule.getOrigin();
String lastPushedRevision = mod.get(0).getRevision();
assertThat("revisionOfPipelineConfigOriginShouldMatchLastPushedCommit",
configOriginAfterSchedule.getRevision(),is(lastPushedRevision));
assertThat(pipelineConfig.materialConfigs(), hasItem(otherMaterialConfig));
assertThat("buildCauseRevisionShouldMatchLastPushedCommit",
cause.getMaterialRevisions().latestRevision(),is(lastPushedRevision));
// update of commited material happened during manual trigger
MaterialRevisions modificationsInDb = materialRepository.findLatestModification(gitMaterial);
assertThat(modificationsInDb.latestRevision(),is(otherGitRepo.latestModification().get(0).getRevision()));
}
@Test
public void shouldSchedulePipelineRerunWithSpecifiedRevisions() throws Exception
{
List<Modification> firstBuildModifications = configTestRepo.addCodeToRepositoryAndPush("a.java", "added first code file", "some java code");
materialUpdateService.updateMaterial(material);
waitForMaterialNotInProgress();
cachedGoConfig.throwExceptionIfExists();
final HashMap<String, String> revisions = new HashMap<>();
final HashMap<String, String> environmentVariables = new HashMap<>();
buildCauseProducer.manualProduceBuildCauseAndSave(PIPELINE_NAME, Username.ANONYMOUS,
new ScheduleOptions(revisions, environmentVariables, new HashMap<>()), new ServerHealthStateOperationResult());
cachedGoConfig.throwExceptionIfExists();
Map<String, BuildCause> afterLoad = scheduleHelper.waitForAnyScheduled(5);
assertThat(afterLoad.keySet(), hasItem(PIPELINE_NAME));
BuildCause cause = afterLoad.get(PIPELINE_NAME);
assertThat(cause.getBuildCauseMessage(), containsString("Forced by anonymous"));
List<Modification> secondBuildModifications = configTestRepo.addCodeToRepositoryAndPush("a.java", "added second code file", "some java code");
materialUpdateService.updateMaterial(material);
waitForMaterialNotInProgress();
pipelineScheduleQueue.clear();
// revision will be older by 1 commit -
// formally this is scm-config-consistency violation but we let this schedule because of manual trigger
String explicitRevision = firstBuildModifications.get(0).getRevision();
revisions.put(materialConfig.getPipelineUniqueFingerprint(), explicitRevision);
buildCauseProducer.manualProduceBuildCauseAndSave(PIPELINE_NAME, new Username(new CaseInsensitiveString("Admin")),
new ScheduleOptions(revisions, environmentVariables, new HashMap<>()), new ServerHealthStateOperationResult());
cachedGoConfig.throwExceptionIfExists();
afterLoad = scheduleHelper.waitForAnyScheduled(5);
assertThat(afterLoad.keySet(), hasItem(PIPELINE_NAME));
cause = afterLoad.get(PIPELINE_NAME);
assertThat(cause.getBuildCauseMessage(), containsString("Forced by Admin"));
PipelineConfig pipelineConfigAfterSchedule = goConfigService.pipelineConfigNamed(pipelineConfig.name());
RepoConfigOrigin configOriginAfterSchedule = (RepoConfigOrigin) pipelineConfigAfterSchedule.getOrigin();
String lastPushedRevision = secondBuildModifications.get(0).getRevision();
assertThat("revisionOfPipelineConfigOriginShouldMatchLastPushedCommit",
configOriginAfterSchedule.getRevision(),is(lastPushedRevision));
assertThat("buildCauseRevisionShouldMatchSpecifiedRevision",
cause.getMaterialRevisions().latestRevision(),is(explicitRevision));
}
@Test
public void shouldSchedulePipelineWithSameMaterialIn2DestinationsWhenManuallyTriggered_WithSpecifiedRevisions() throws Exception
{
pipelineConfig = PipelineConfigMother.createPipelineConfigWithStages("pipe1", "build", "test");
pipelineConfig.materialConfigs().clear();
materialConfig = hgRepo.createMaterialConfig("dest1");
materialConfig.setAutoUpdate(true);
// new material is added
MaterialConfig otherMaterialConfig = hgRepo.createMaterialConfig("dest2");
otherMaterialConfig.setAutoUpdate(true);
pipelineConfig.materialConfigs().add(materialConfig);
pipelineConfig.materialConfigs().add(otherMaterialConfig);
List<Modification> firstBuildModifications = configTestRepo.addPipelineToRepositoryAndPush(fileName, pipelineConfig);
materialUpdateService.updateMaterial(material);
waitForMaterialNotInProgress();
cachedGoConfig.throwExceptionIfExists();
final HashMap<String, String> revisions = new HashMap<>();
final HashMap<String, String> environmentVariables = new HashMap<>();
buildCauseProducer.manualProduceBuildCauseAndSave(PIPELINE_NAME, Username.ANONYMOUS,
new ScheduleOptions(revisions, environmentVariables, new HashMap<>()), new ServerHealthStateOperationResult());
cachedGoConfig.throwExceptionIfExists();
Map<String, BuildCause> afterLoad = scheduleHelper.waitForAnyScheduled(5);
assertThat(afterLoad.keySet(), hasItem(PIPELINE_NAME));
BuildCause cause = afterLoad.get(PIPELINE_NAME);
assertThat(cause.getBuildCauseMessage(), containsString("Forced by anonymous"));
List<Modification> secondBuildModifications = configTestRepo.addCodeToRepositoryAndPush("a.java", "added code file", "some java code");
materialUpdateService.updateMaterial(material);
waitForMaterialNotInProgress();
pipelineScheduleQueue.clear();
// revision in dest 1 will be older by 1 commit - this is kind of scm-config-consistency violation
String explicitRevision = firstBuildModifications.get(0).getRevision();
revisions.put(materialConfig.getPipelineUniqueFingerprint(), explicitRevision);
buildCauseProducer.manualProduceBuildCauseAndSave(PIPELINE_NAME, new Username(new CaseInsensitiveString("Admin")),
new ScheduleOptions(revisions, environmentVariables, new HashMap<>()), new ServerHealthStateOperationResult());
cachedGoConfig.throwExceptionIfExists();
afterLoad = scheduleHelper.waitForAnyScheduled(5);
assertThat(afterLoad.keySet(), hasItem(PIPELINE_NAME));
cause = afterLoad.get(PIPELINE_NAME);
assertThat(cause.getBuildCauseMessage(), containsString("Forced by Admin"));
PipelineConfig pipelineConfigAfterSchedule = goConfigService.pipelineConfigNamed(pipelineConfig.name());
RepoConfigOrigin configOriginAfterSchedule = (RepoConfigOrigin) pipelineConfigAfterSchedule.getOrigin();
String lastPushedRevision = secondBuildModifications.get(0).getRevision();
assertThat("revisionOfPipelineConfigOriginShouldMatchLastPushedCommit",
configOriginAfterSchedule.getRevision(),is(lastPushedRevision));
assertThat(pipelineConfigAfterSchedule.materialConfigs(), hasItem(otherMaterialConfig));
assertThat("buildCauseRevisionShouldMatchSpecifiedRevision",
cause.getMaterialRevisions().latestRevision(),is(explicitRevision));
}
}