/*
* 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.dao;
import com.thoughtworks.go.config.*;
import com.thoughtworks.go.config.elastic.ElasticProfile;
import com.thoughtworks.go.domain.*;
import com.thoughtworks.go.domain.config.ConfigurationKey;
import com.thoughtworks.go.domain.config.ConfigurationProperty;
import com.thoughtworks.go.domain.config.ConfigurationValue;
import com.thoughtworks.go.helper.BuildPlanMother;
import com.thoughtworks.go.helper.JobInstanceMother;
import com.thoughtworks.go.helper.PipelineMother;
import com.thoughtworks.go.server.cache.GoCache;
import com.thoughtworks.go.server.service.InstanceFactory;
import com.thoughtworks.go.server.service.JobInstanceService;
import com.thoughtworks.go.server.service.ScheduleService;
import com.thoughtworks.go.server.transaction.TransactionTemplate;
import com.thoughtworks.go.server.ui.SortOrder;
import com.thoughtworks.go.util.GoConfigFileHelper;
import com.thoughtworks.go.util.LogFixture;
import com.thoughtworks.go.util.TimeProvider;
import com.thoughtworks.go.util.command.EnvironmentVariableContext;
import org.apache.log4j.Level;
import org.hamcrest.core.Is;
import org.joda.time.DateTime;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import java.sql.SQLException;
import java.util.*;
import static com.thoughtworks.go.helper.JobInstanceMother.*;
import static com.thoughtworks.go.helper.ModificationsMother.modifySomeFiles;
import static com.thoughtworks.go.server.dao.PersistentObjectMatchers.hasSameId;
import static com.thoughtworks.go.util.DataStructureUtils.a;
import static com.thoughtworks.go.util.DataStructureUtils.m;
import static com.thoughtworks.go.util.GoConstants.DEFAULT_APPROVED_BY;
import static com.thoughtworks.go.util.LogFixture.logFixtureFor;
import static com.thoughtworks.go.util.TestUtils.sizeIs;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.sameInstance;
import static org.hamcrest.collection.IsArrayContaining.hasItemInArray;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertThat;
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 JobInstanceSqlMapDaoTest {
@Autowired
private JobInstanceSqlMapDao jobInstanceDao;
@Autowired
private JobInstanceService jobInstanceService;
@Autowired
private EnvironmentVariableDao environmentVariableDao;
@Autowired
private PipelineDao pipelineDao;
@Autowired
private DatabaseAccessHelper dbHelper;
@Autowired
private StageDao stageDao;
@Autowired
private GoCache goCache;
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private ScheduleService scheduleService;
@Autowired
private GoConfigDao goConfigDao;
@Autowired
private InstanceFactory instanceFactory;
@Autowired
private JobAgentMetadataDao jobAgentMetadataDao;
private GoConfigFileHelper configHelper = new GoConfigFileHelper();
private long stageId;
private static final String JOB_NAME = "functional";
private static final String JOB_NAME_IN_DIFFERENT_CASE = "FUnctiONAl";
private final String projectOne = "project1";
private static final String STAGE_NAME = "mingle";
private static final String PIPELINE_NAME = "pipeline";
private PipelineConfig pipelineConfig;
private Pipeline savedPipeline;
private Stage savedStage;
private static final Date MOST_RECENT_DATE = new DateTime().plusMinutes(20).toDate();
private int counter;
private static final String OTHER_JOB_NAME = "unit";
private DefaultSchedulingContext schedulingContext;
private SqlMapClientTemplate actualSqlClientTemplate;
@Before
public void setUp() throws Exception {
dbHelper.onSetUp();
goCache.clear();
pipelineConfig = PipelineMother.withSingleStageWithMaterials(PIPELINE_NAME, STAGE_NAME, BuildPlanMother.withBuildPlans(JOB_NAME, OTHER_JOB_NAME));
schedulingContext = new DefaultSchedulingContext(DEFAULT_APPROVED_BY);
savedPipeline = instanceFactory.createPipelineInstance(pipelineConfig, modifySomeFiles(pipelineConfig), schedulingContext, "md5-test", new TimeProvider());
dbHelper.savePipelineWithStagesAndMaterials(savedPipeline);
actualSqlClientTemplate = jobInstanceDao.getSqlMapClientTemplate();
savedStage = savedPipeline.getFirstStage();
stageId = savedStage.getId();
counter = savedPipeline.getFirstStage().getCounter();
JobInstance job = savedPipeline.getStages().first().getJobInstances().first();
job.setIgnored(true);
goCache.clear();
configHelper.usingCruiseConfigDao(goConfigDao);
configHelper.onSetUp();
}
@After
public void teardown() throws Exception {
jobInstanceDao.setSqlMapClientTemplate(actualSqlClientTemplate);
dbHelper.onTearDown();
configHelper.onTearDown();
}
@Test
public void shouldSaveAndRetrieveIncompleteBuild() throws Exception {
JobInstance expected = scheduled(JOB_NAME, new Date());
expected = jobInstanceDao.save(stageId, expected);
assertThat(expected.getId(), is(not(0L)));
JobInstance actual = jobInstanceDao.buildByIdWithTransitions(expected.getId());
assertThat(actual, hasSameId(expected));
}
@Test
public void shouldGetBuildIngBuildAsMostRecentBuildByPipelineLabelAndStageCounter() throws Exception {
JobInstance expected = JobInstanceMother.building(JOB_NAME);
expected.setScheduledDate(MOST_RECENT_DATE);
expected = jobInstanceDao.save(stageId, expected);
JobInstance actual = jobInstanceDao.mostRecentJobWithTransitions(
new JobIdentifier(PIPELINE_NAME, savedPipeline.getLabel(), STAGE_NAME,
String.valueOf(counter), JOB_NAME));
assertThat(actual.getId(), is(expected.getId()));
assertThat("JobInstance should match", actual.getId(), is(expected.getId()));
assertThat(actual.getTransitions(), is(expected.getTransitions()));
}
@Test
public void shouldGetCompletedBuildAsMostRecentBuildByPipelineLabelAndStageCounter() throws Exception {
JobInstance expected = JobInstanceMother.completed(JOB_NAME, JobResult.Unknown);
expected.setScheduledDate(MOST_RECENT_DATE);
expected = jobInstanceDao.save(stageId, expected);
JobInstance actual = jobInstanceDao.mostRecentJobWithTransitions(
new JobIdentifier(PIPELINE_NAME, savedPipeline.getLabel(), STAGE_NAME,
String.valueOf(counter), JOB_NAME));
assertThat("JobInstance should match", actual.getId(), is(expected.getId()));
}
@Test
public void shouldFindJobByPipelineCounterWhenTwoPipelinesWithSameLabel() {
pipelineConfig.setLabelTemplate("fixed-label");
Pipeline oldPipeline = createNewPipeline(pipelineConfig);
Pipeline newPipeline = createNewPipeline(pipelineConfig);
JobInstance expected = oldPipeline.getFirstStage().getJobInstances().first();
JobInstance actual = jobInstanceDao.mostRecentJobWithTransitions(
new JobIdentifier(oldPipeline, oldPipeline.getFirstStage(), expected));
assertThat(actual.getId(), is(expected.getId()));
}
private Pipeline createNewPipeline(PipelineConfig pipelineConfig) {
Pipeline pipeline = instanceFactory.createPipelineInstance(pipelineConfig, modifySomeFiles(pipelineConfig), new DefaultSchedulingContext(
DEFAULT_APPROVED_BY), "md5-test", new TimeProvider());
dbHelper.savePipelineWithStagesAndMaterials(pipeline);
return pipeline;
}
@Test
public void shouldFindJobIdByPipelineCounter() throws Exception {
long actual = jobInstanceDao.findOriginalJobIdentifier(new StageIdentifier(savedPipeline, savedStage), JOB_NAME).getBuildId();
assertThat(actual, is(savedStage.getJobInstances().getByName(JOB_NAME).getId()));
}
@Test
public void shouldInvalidateSessionAndFetchJobIdentifier_WhenNewJobIsInserted() throws Exception {
configHelper.addPipeline(pipelineConfig);
configHelper.turnOffSecurity();
StageIdentifier stageIdentifier = new StageIdentifier(savedPipeline.getName(), savedPipeline.getCounter(), savedPipeline.getLabel(), savedStage.getName(), Integer.toString(savedStage.getCounter() + 1));
assertThat(jobInstanceDao.findOriginalJobIdentifier(stageIdentifier, JOB_NAME), is(nullValue()));
dbHelper.passStage(savedStage);
Stage stage = scheduleService.rerunStage(savedPipeline.getName(), savedPipeline.getLabel(), savedStage.getName());
JobIdentifier actual = jobInstanceDao.findOriginalJobIdentifier(stageIdentifier, JOB_NAME);
assertThat(actual, is(notNullValue()));
assertThat(actual, is(new JobIdentifier(stageIdentifier, JOB_NAME, stage.getFirstJob().getId())));
}
@Test
public void shouldFindJobIdByPipelineLabel() throws Exception {
long actual = jobInstanceDao.findOriginalJobIdentifier(new StageIdentifier(PIPELINE_NAME, null, savedPipeline.getLabel(), STAGE_NAME, String.valueOf(counter)), JOB_NAME).getBuildId();
assertThat(actual, is(savedStage.getJobInstances().getByName(JOB_NAME).getId()));
}
@Test
public void findByJobIdShouldBeJobNameCaseAgnostic() throws Exception {
long actual = jobInstanceDao.findOriginalJobIdentifier(new StageIdentifier(PIPELINE_NAME, null, savedPipeline.getLabel(), STAGE_NAME, String.valueOf(counter)), JOB_NAME_IN_DIFFERENT_CASE).getBuildId();
assertThat(actual, is(savedStage.getJobInstances().getByName(JOB_NAME).getId()));
}
@Test
public void shouldCacheJobIdentifierForGivenAttributes() throws Exception {
SqlMapClientTemplate template = mock(SqlMapClientTemplate.class);
jobInstanceDao.setSqlMapClientTemplate(template);
Map attrs = m(
"pipelineName", PIPELINE_NAME,
"pipelineCounter", null,
"pipelineLabel", savedPipeline.getLabel(),
"stageName", STAGE_NAME,
"stageCounter", counter,
"jobName", JOB_NAME_IN_DIFFERENT_CASE);
JobIdentifier jobIdentifier = new JobIdentifier(PIPELINE_NAME, savedPipeline.getCounter(), savedPipeline.getLabel(), STAGE_NAME, String.valueOf(counter), JOB_NAME);
when(template.queryForObject("findJobId", attrs)).thenReturn(jobIdentifier);
assertThat(jobInstanceDao.findOriginalJobIdentifier(new StageIdentifier(PIPELINE_NAME, null, savedPipeline.getLabel(), STAGE_NAME, String.valueOf(counter)), JOB_NAME_IN_DIFFERENT_CASE), is(jobIdentifier));
verify(template).queryForObject("findJobId", attrs);
assertThat(jobInstanceDao.findOriginalJobIdentifier(new StageIdentifier(PIPELINE_NAME, null, savedPipeline.getLabel(), STAGE_NAME, String.valueOf(counter)), JOB_NAME_IN_DIFFERENT_CASE), not(sameInstance(jobIdentifier)));
assertThat(jobInstanceDao.findOriginalJobIdentifier(new StageIdentifier(PIPELINE_NAME, null, savedPipeline.getLabel(), STAGE_NAME, String.valueOf(counter)), JOB_NAME), is(jobIdentifier));
verifyNoMoreInteractions(template);
}
@Test
public void findByJobIdShouldLoadOriginalJobWhenCopiedForJobRerun() throws Exception {
Stage firstOldStage = savedPipeline.getStages().get(0);
Stage newStage = instanceFactory.createStageForRerunOfJobs(firstOldStage, a(JOB_NAME), new DefaultSchedulingContext("loser", new Agents()), pipelineConfig.get(0), new TimeProvider(), "md5");
stageDao.saveWithJobs(savedPipeline, newStage);
dbHelper.passStage(newStage);
JobIdentifier oldJobIdentifierThroughOldJob = jobInstanceDao.findOriginalJobIdentifier(new StageIdentifier(PIPELINE_NAME, null, savedPipeline.getLabel(), STAGE_NAME, String.valueOf(counter)), OTHER_JOB_NAME);
JobIdentifier oldJobIdentifierThroughCopiedNewJob = jobInstanceDao.findOriginalJobIdentifier(new StageIdentifier(PIPELINE_NAME, null, savedPipeline.getLabel(), STAGE_NAME, String.valueOf(newStage.getCounter())), OTHER_JOB_NAME);
JobIdentifier newJobIdentifierThroughRerunJob = jobInstanceDao.findOriginalJobIdentifier(new StageIdentifier(PIPELINE_NAME, null, savedPipeline.getLabel(), STAGE_NAME, String.valueOf(newStage.getCounter())), JOB_NAME);
assertThat(oldJobIdentifierThroughOldJob, is(firstOldStage.getJobInstances().getByName(OTHER_JOB_NAME).getIdentifier()));
assertThat(oldJobIdentifierThroughCopiedNewJob, is(firstOldStage.getJobInstances().getByName(OTHER_JOB_NAME).getIdentifier()));
assertThat(newJobIdentifierThroughRerunJob, is(newStage.getJobInstances().getByName(JOB_NAME).getIdentifier()));
}
@Test
public void findJobIdShouldExcludeIgnoredJob() throws Exception {
JobInstance oldJob = savedStage.getJobInstances().getByName(JOB_NAME);
jobInstanceDao.ignore(oldJob);
JobInstance expected = JobInstanceMother.scheduled(JOB_NAME);
expected = jobInstanceDao.save(stageId, expected);
long actual = jobInstanceDao.findOriginalJobIdentifier(new StageIdentifier(savedPipeline, savedStage), JOB_NAME).getBuildId();
assertThat(actual, is(expected.getId()));
}
@Test
public void shouldFindAllInstancesOfJobsThatAreRunOnAllAgents() throws Exception {
List<JobInstance> before = stageDao.mostRecentJobsForStage(PIPELINE_NAME, STAGE_NAME);
String uuid1 = UUID.randomUUID().toString();
String uuid2 = UUID.randomUUID().toString();
JobInstance instance1 = savedJobForAgent(JOB_NAME + "-" + uuid1, uuid1, true, false);
JobInstance instance2 = savedJobForAgent(JOB_NAME + "-" + uuid2, uuid2, true, false);
List<JobInstance> after = stageDao.mostRecentJobsForStage(PIPELINE_NAME, STAGE_NAME);
after.removeAll(before);
assertThat(after.toArray(), hasItemInArray(hasProperty("name", is(instance1.getName()))));
assertThat(after.toArray(), hasItemInArray(hasProperty("name", is(instance2.getName()))));
assertThat("Expected 2 but got " + after, after.size(), is(2));
}
@Test
public void shouldFindAllInstancesOfJobsThatAreRunMultipleInstance() throws Exception {
List<JobInstance> before = stageDao.mostRecentJobsForStage(PIPELINE_NAME, STAGE_NAME);
JobInstance instance1 = savedJobForAgent(RunMultipleInstance.CounterBasedJobNameGenerator.appendMarker("job", 1), null, false, true);
JobInstance instance2 = savedJobForAgent(RunMultipleInstance.CounterBasedJobNameGenerator.appendMarker("job", 2), null, false, true);
List<JobInstance> after = stageDao.mostRecentJobsForStage(PIPELINE_NAME, STAGE_NAME);
after.removeAll(before);
assertThat("Expected 2 but got " + after, after.size(), is(2));
assertThat(after.toArray(), hasItemInArray(hasProperty("name", is(instance1.getName()))));
assertThat(after.toArray(), hasItemInArray(hasProperty("name", is(instance2.getName()))));
}
private JobInstance savedJobForAgent(final String jobName, final String uuid, final boolean runOnAllAgents, final boolean runMultipleInstance) {
return (JobInstance) transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
JobInstance jobInstance = scheduled(jobName, new DateTime().plusMinutes(1).toDate());
jobInstance.setRunOnAllAgents(runOnAllAgents);
jobInstance.setRunMultipleInstance(runMultipleInstance);
jobInstanceService.save(savedStage.getIdentifier(), stageId, jobInstance);
jobInstance.changeState(JobState.Building);
jobInstance.setAgentUuid(uuid);
jobInstanceDao.updateStateAndResult(jobInstance);
return jobInstance;
}
});
}
@Test(expected = DataRetrievalFailureException.class)
public void shouldReturnNullObjectWhenNoBuildInstanceFound() throws Exception {
JobInstance actual = jobInstanceDao.buildByIdWithTransitions(999);
assertThat(actual.isNull(), is(true));
}
@Test
public void shouldReturnBuildInstanceIfItExists() throws Exception {
JobInstance jobInstance = JobInstanceMother.completed("Baboon", JobResult.Passed);
JobInstance instance = jobInstanceDao.save(stageId, jobInstance);
JobInstance actual = jobInstanceDao.buildByIdWithTransitions(instance.getId());
assertThat(actual.isNull(), is(false));
}
private JobInstance[] createJobs(final String name, int count) {
JobInstance[] before = new JobInstance[count];
for (int i = 0; i < count; i++) {
before[i] = saveJob(name + "-" + i);
}
return before;
}
@Test
public void shouldLogStatusUpdatesOfCompletedJobs() throws Exception {
try (LogFixture logFixture = logFixtureFor(JobInstanceSqlMapDao.class, Level.DEBUG)) {
JobInstance instance = runningJob("1");
completeJobs(instance);
instance.schedule();
jobInstanceDao.updateStateAndResult(instance);
assertThat(logFixture.getLog(), logFixture.contains(Level.WARN, "State change for a completed Job is not allowed."), is(true));
}
}
private JobInstance[] completeJobs(JobInstance... instances) {
for (JobInstance instance : instances) {
complete(instance);
}
return instances;
}
private JobInstance saveJob(String jobName) {
JobInstance jobInstance = JobInstanceMother.completed(jobName, JobResult.Passed);
jobInstanceDao.save(stageId, jobInstance);
return jobInstance;
}
private JobInstance runningJob(final String name) {
JobInstance jobInstance = JobInstanceMother.buildingInstance("pipeline", "stage", name, "1");
jobInstanceDao.save(stageId, jobInstance);
return jobInstance;
}
private void complete(JobInstance jobInstance) {
jobInstance.completing(JobResult.Passed);
jobInstance.completed(new Date());
jobInstanceDao.updateStateAndResult(jobInstance);
}
@Test
public void shouldUpdateBuildResult() throws Exception {
JobInstance jobInstance = JobInstanceMother.scheduled("Baboon");
jobInstanceDao.save(stageId, jobInstance);
jobInstance.cancel();
jobInstanceDao.updateStateAndResult(jobInstance);
JobInstance instance = jobInstanceDao.buildByIdWithTransitions(jobInstance.getId());
assertThat(instance.getResult(), is(JobResult.Cancelled));
jobInstance.fail();
jobInstanceDao.updateStateAndResult(jobInstance);
instance = jobInstanceDao.buildByIdWithTransitions(jobInstance.getId());
assertThat(instance.getResult(), is(JobResult.Failed));
}
@Test(expected = DataRetrievalFailureException.class)
public void shouldReturnNullObjectIfItNotExists() throws Exception {
JobInstance actual = jobInstanceDao.buildByIdWithTransitions(1);
assertThat(actual.isNull(), is(true));
}
@Test
public void shouldNotGetJobsFromBeforeTheJobNameIsChanged() throws Exception {
String oldName = "oldName";
createSomeJobs(oldName, 15);
String newName = "newName";
createSomeJobs(newName, 10);
JobInstances myinstances = jobInstanceDao.latestCompletedJobs(PIPELINE_NAME, STAGE_NAME, newName, 25);
assertThat(myinstances.size(), is(10));
assertThat(myinstances.get(0).getName(), is(not(oldName)));
assertThat(myinstances.get(0).getName(), is(newName));
}
private long createSomeJobs(String jobName, int count) throws SQLException {
long stageId = 0;
for (int i = 0; i < count; i++) {
Pipeline newPipeline = createNewPipeline(pipelineConfig);
stageId = newPipeline.getFirstStage().getId();
JobInstance job = JobInstanceMother.completed(jobName, JobResult.Passed);
jobInstanceDao.save(stageId, job);
}
return stageId;
}
private void createCopiedJobs(long stageId, String jobName, int count) throws SQLException {
for (int i = 0; i < count; i++) {
JobInstance job = JobInstanceMother.completed(jobName, JobResult.Failed);
job.setOriginalJobId(1L);
jobInstanceDao.save(stageId, job);
}
}
@Test
public void shouldGetMostRecentCompletedBuildsWhenTotalBuildsIsLessThan25() throws Exception {
JobInstance jobInstance = JobInstanceMother.completed("shouldnotload", JobResult.Passed);
jobInstanceDao.save(stageId, jobInstance);
createSomeJobs(JOB_NAME, 3);
JobInstances instances = jobInstanceDao.latestCompletedJobs(PIPELINE_NAME, STAGE_NAME, JOB_NAME, 25);
assertThat(instances.size(), is(3));
}
@Test
public void shouldLoadStageCounter() throws Exception {
JobInstance jobInstance = JobInstanceMother.completed("shouldnotload", JobResult.Passed);
jobInstanceDao.save(stageId, jobInstance);
createSomeJobs(JOB_NAME, 3);
JobInstances instances = jobInstanceDao.latestCompletedJobs(PIPELINE_NAME, STAGE_NAME, JOB_NAME, 25);
for (JobInstance instance : instances) {
Pipeline pipeline = pipelineDao.pipelineByBuildIdWithMods(instance.getId());
String locator = pipeline.getName() +
"/" + pipeline.getLabel() + "/" + savedStage.getName() + "/1/" + JOB_NAME;
assertThat(instance.getIdentifier().buildLocator(), Is.is(locator));
}
}
@Test
public void shouldGet25Builds() throws Exception {
createSomeJobs(JOB_NAME, 30);
JobInstances instances = jobInstanceDao.latestCompletedJobs(PIPELINE_NAME, STAGE_NAME, JOB_NAME, 25);
assertThat(instances.size(), is(25));
for (JobInstance instance : instances) {
assertThat(instance.getIdentifier().getBuildName(), is(JOB_NAME));
}
}
@Test
public void shouldGet25Builds_AlthoughFirst5AreCopied() throws Exception {
long stageId = createSomeJobs(JOB_NAME, 30);
createCopiedJobs(stageId, JOB_NAME, 5);
JobInstances instances = jobInstanceDao.latestCompletedJobs(PIPELINE_NAME, STAGE_NAME, JOB_NAME, 25);
assertThat(instances.size(), is(25));
for (JobInstance instance : instances) {
assertThat(instance.getIdentifier().getBuildName(), is(JOB_NAME));
assertThat("Should not have retrieved copied-over jobs", instance.isCopy(), is(false));
}
}
@Test
public void shouldGetMostRecentCompletedBuildsWhenTwoStagesWithIdenticalStageNamesAndBuildPlanNames()
throws Exception {
Pipeline otherPipeline = PipelineMother.passedPipelineInstance(PIPELINE_NAME + "2", STAGE_NAME, JOB_NAME);
dbHelper.savePipelineWithStagesAndMaterials(otherPipeline);
for (int i = 0; i < 2; i++) {
Pipeline completedPipeline = PipelineMother.passedPipelineInstance(PIPELINE_NAME, STAGE_NAME, JOB_NAME);
dbHelper.savePipelineWithStagesAndMaterials(completedPipeline);
}
assertThat(jobInstanceDao.latestCompletedJobs(PIPELINE_NAME, STAGE_NAME, JOB_NAME, 25).size(), is(2));
assertThat(jobInstanceDao.latestCompletedJobs(otherPipeline.getName(), STAGE_NAME, JOB_NAME, 25).size(), is(1));
}
@Test
public void shouldIgnoreBuildingBuilds() throws Exception {
JobInstance instance = JobInstanceMother.completed("shouldnotload", JobResult.Passed);
jobInstanceDao.save(stageId, instance);
JobInstance building = JobInstanceMother.building(JOB_NAME);
JobInstance saved = jobInstanceDao.save(stageId, building);
JobInstances instances =
jobInstanceDao.latestCompletedJobs(PIPELINE_NAME, STAGE_NAME, JOB_NAME, 25);
assertThat(instances.size(), is(0));
}
@Test
public void shouldCorrectly_getJobHistoryCount_findJobHistoryPage() throws Exception {
// has a scheduled job
long stageId = createSomeJobs(JOB_NAME, 2); // create 4 instances completed, scheduled, completed, scheduled
createCopiedJobs(stageId, JOB_NAME, 2);
JobInstance shouldNotLoadInstance = JobInstanceMother.completed("shouldnotload", JobResult.Passed); // create job with a different name
jobInstanceDao.save(stageId, shouldNotLoadInstance);
JobInstance building = JobInstanceMother.building(JOB_NAME); // create a building job
JobInstance saved = jobInstanceDao.save(stageId, building);
int jobHistoryCount = jobInstanceDao.getJobHistoryCount(PIPELINE_NAME, STAGE_NAME, JOB_NAME);
assertThat(jobHistoryCount, is(6));
JobInstances instances = jobInstanceDao.findJobHistoryPage(PIPELINE_NAME, STAGE_NAME, JOB_NAME, 4, 0);
assertThat(instances.size(), is(4));
assertThat(instances.get(0).getState(), is(JobState.Building));
assertThat(instances.get(1).getState(), is(JobState.Completed));
assertThat(instances.get(2).getState(), is(JobState.Scheduled));
assertThat(instances.get(3).getState(), is(JobState.Completed));
assertJobHistoryCorrectness(instances, JOB_NAME);
instances = jobInstanceDao.findJobHistoryPage(PIPELINE_NAME, STAGE_NAME, JOB_NAME, 4, 4);
assertThat(instances.size(), is(2));
assertThat(instances.get(0).getState(), is(JobState.Scheduled));
assertThat(instances.get(1).getState(), is(JobState.Scheduled));
assertJobHistoryCorrectness(instances, JOB_NAME);
}
private void assertJobHistoryCorrectness(JobInstances instances, String jobName) {
for (JobInstance instance : instances) {
assertThat(instance.getIdentifier().getBuildName(), is(jobName));
assertThat("Should not have retrieved copied-over jobs", instance.isCopy(), is(false));
}
}
@Test
public void shouldLoadRerunOfCounterValueForScheduledBuilds() {
List<JobPlan> jobPlans = jobInstanceDao.orderedScheduledBuilds();
assertThat(jobPlans.size(), is(2));
assertThat(jobPlans.get(0).getIdentifier().getRerunOfCounter(), is(nullValue()));
assertThat(jobPlans.get(1).getIdentifier().getRerunOfCounter(), is(nullValue()));
dbHelper.passStage(savedStage);
Stage stage = instanceFactory.createStageForRerunOfJobs(savedStage, a(JOB_NAME), schedulingContext, pipelineConfig.getStage(new CaseInsensitiveString(STAGE_NAME)), new TimeProvider(), "md5");
dbHelper.saveStage(savedPipeline, stage, stage.getOrderId() + 1);
jobPlans = jobInstanceDao.orderedScheduledBuilds();
assertThat(jobPlans.size(), is(1));
assertThat(jobPlans.get(0).getIdentifier().getRerunOfCounter(), is(savedStage.getCounter()));
}
@Test
public void shouldGetAllScheduledBuildsInOrder() throws Exception {
// in setup, we created 2 scheduled builds
assertThat(jobInstanceDao.orderedScheduledBuilds().size(), is(2));
JobIdentifier jobIdentifier = new JobIdentifier(PIPELINE_NAME, "LABEL-1", STAGE_NAME, "1", JOB_NAME);
long newestId = schedule(JOB_NAME, stageId, new Date(10001), jobIdentifier);
long olderId = schedule(JOB_NAME, stageId, new Date(10000), jobIdentifier);
long oldestId = schedule(JOB_NAME, stageId, new Date(999), jobIdentifier);
List<JobPlan> jobPlans = jobInstanceDao.orderedScheduledBuilds();
assertThat(jobPlans.size(), is(5));
assertJobInstance(jobPlans.get(0), oldestId, PIPELINE_NAME, STAGE_NAME);
assertJobInstance(jobPlans.get(1), olderId, PIPELINE_NAME, STAGE_NAME);
assertJobInstance(jobPlans.get(2), newestId, PIPELINE_NAME, STAGE_NAME);
}
private long schedule(String jobName, long stageId, Date date, JobIdentifier jobIdentifier) {
JobInstance newest = new JobInstance(jobName);
newest.setScheduledDate(date);
jobInstanceDao.save(stageId, newest);
jobInstanceDao.save(newest.getId(), new DefaultJobPlan(new Resources(), new ArtifactPlans(), new ArtifactPropertiesGenerators(), -1, jobIdentifier, null, new EnvironmentVariablesConfig(), new EnvironmentVariablesConfig(), null));
return newest.getId();
}
private void assertJobInstance(JobPlan actual, long expect, String pipelineName, String stageName) {
assertThat(actual.getPipelineName(), is(pipelineName));
assertThat(actual.getStageName(), is(stageName));
assertThat("JobInstance should match", actual.getJobId(), is(expect));
}
@Test
public void shouldUpdateStateTransitions() throws Exception {
JobInstance expected = scheduled(JOB_NAME, new Date(1000));
jobInstanceDao.save(stageId, expected);
JobInstance actual = jobInstanceDao.buildByIdWithTransitions(expected.getId());
assertThat(actual.getTransitions(), sizeIs(1));
expected.changeState(JobState.Assigned);
jobInstanceDao.updateStateAndResult(expected);
actual = jobInstanceDao.buildByIdWithTransitions(expected.getId());
assertThat(actual.getTransitions(), sizeIs(2));
for (JobStateTransition transition : actual.getTransitions()) {
assertThat(transition.getStageId(), is(stageId));
}
}
@Test
public void shouldUpdateBuildStatus() throws Exception {
JobInstance expected = scheduled(JOB_NAME);
jobInstanceDao.save(stageId, expected);
expected.changeState(JobState.Building);
jobInstanceDao.updateStateAndResult(expected);
JobInstance actual = jobInstanceDao.buildByIdWithTransitions(expected.getId());
assertThat(actual.getState(), is(JobState.Building));
assertThat(actual.getTransitions().size(), is(2));
}
@Test
public void shouldUpdateAssignedInfo() throws Exception {
JobInstance expected = scheduled(JOB_NAME);
jobInstanceDao.save(stageId, expected);
expected.changeState(JobState.Building);
expected.setAgentUuid("uuid");
jobInstanceDao.updateAssignedInfo(expected);
JobInstance actual = jobInstanceDao.buildByIdWithTransitions(expected.getId());
assertThat(actual.getState(), is(JobState.Building));
assertThat(actual.getTransitions().size(), is(2));
assertThat(actual.getAgentUuid(), is("uuid"));
}
@Test
public void shouldUpdateCompletingInfo() throws Exception {
JobInstance expected = scheduled(JOB_NAME);
jobInstanceDao.save(stageId, expected);
expected.changeState(JobState.Completing);
expected.setResult(JobResult.Failed);
jobInstanceDao.updateStateAndResult(expected);
JobInstance actual = jobInstanceDao.buildByIdWithTransitions(expected.getId());
assertThat(actual.getState(), is(JobState.Completing));
assertThat(actual.getTransitions().size(), is(2));
assertThat(actual.getResult(), is(JobResult.Failed));
}
@Test
public void shouldSaveTransitionsCorrectly() {
JobInstance jobInstance = scheduled(projectOne, new Date(1));
jobInstance.completing(JobResult.Failed, new Date(3));
jobInstanceDao.save(stageId, jobInstance);
JobInstance loaded = jobInstanceDao.buildByIdWithTransitions(jobInstance.getId());
JobStateTransitions actualTransitions = loaded.getTransitions();
assertThat(actualTransitions, sizeIs(2));
assertThat(actualTransitions.first().getCurrentState(), is(JobState.Scheduled));
}
@Test
public void shouldSaveResources() {
JobInstance instance = jobInstanceDao.save(stageId, new JobInstance(JOB_NAME));
instance.setIdentifier(new JobIdentifier(savedPipeline, savedStage, instance));
JobPlan plan = new DefaultJobPlan(new Resources("something"), new ArtifactPlans(), new ArtifactPropertiesGenerators(), instance.getId(), instance.getIdentifier(), null, new EnvironmentVariablesConfig(), new EnvironmentVariablesConfig(), null);
jobInstanceDao.save(instance.getId(), plan);
JobPlan retrieved = jobInstanceDao.loadPlan(plan.getJobId());
assertThat(retrieved, is(plan));
}
@Test
public void shouldSaveJobAgentMetadata() throws Exception {
JobInstance instance = jobInstanceDao.save(stageId, new JobInstance(JOB_NAME));
instance.setIdentifier(new JobIdentifier(savedPipeline, savedStage, instance));
ElasticProfile elasticProfile = new ElasticProfile("foo", "cd.go.elastic-agent:docker", Arrays.asList(new ConfigurationProperty(new ConfigurationKey("key"), new ConfigurationValue("value"))));
JobPlan plan = new DefaultJobPlan(new Resources("something"), new ArtifactPlans(), new ArtifactPropertiesGenerators(), instance.getId(), instance.getIdentifier(), null, new EnvironmentVariablesConfig(), new EnvironmentVariablesConfig(), elasticProfile);
jobInstanceDao.save(instance.getId(), plan);
JobPlan retrieved = jobInstanceDao.loadPlan(plan.getJobId());
assertThat(retrieved.getElasticProfile(), is(elasticProfile));
}
@Test
public void shouldNotThrowUpWhenJobAgentMetadataIsNull() throws Exception {
JobInstance instance = jobInstanceDao.save(stageId, new JobInstance(JOB_NAME));
instance.setIdentifier(new JobIdentifier(savedPipeline, savedStage, instance));
ElasticProfile elasticProfile = null;
JobPlan plan = new DefaultJobPlan(new Resources("something"), new ArtifactPlans(), new ArtifactPropertiesGenerators(), instance.getId(), instance.getIdentifier(), null, new EnvironmentVariablesConfig(), new EnvironmentVariablesConfig(), elasticProfile);
jobInstanceDao.save(instance.getId(), plan);
JobPlan retrieved = jobInstanceDao.loadPlan(plan.getJobId());
assertThat(retrieved.getElasticProfile(), is(elasticProfile));
}
@Test
public void shouldSaveEnvironmentVariables() {
JobInstance instance = jobInstanceDao.save(stageId, new JobInstance(JOB_NAME));
instance.setIdentifier(new JobIdentifier(savedPipeline, savedStage, instance));
EnvironmentVariablesConfig variables = new EnvironmentVariablesConfig();
variables.add("VARIABLE_NAME", "variable value");
variables.add("TRIGGER_VAR", "junk val");
JobPlan plan = new DefaultJobPlan(new Resources(), new ArtifactPlans(),
new ArtifactPropertiesGenerators(), instance.getId(),
instance.getIdentifier(), null, variables, new EnvironmentVariablesConfig(), null);
jobInstanceDao.save(instance.getId(), plan);
environmentVariableDao.save(savedPipeline.getId(), EnvironmentVariableDao.EnvironmentVariableType.Trigger, GoConfigFileHelper.env("TRIGGER_VAR", "trigger val"));
JobPlan retrieved = jobInstanceDao.loadPlan(plan.getJobId());
assertThat(retrieved.getVariables(), is(plan.getVariables()));
EnvironmentVariableContext context = new EnvironmentVariableContext();
retrieved.applyTo(context);
assertThat(context.getProperty("VARIABLE_NAME"), is("variable value"));
assertThat(context.getProperty("TRIGGER_VAR"), is("trigger val"));
}
@Test
public void shouldLoadEnvironmentVariablesForScheduledJobs() {
JobInstance newInstance = new JobInstance(JOB_NAME);
newInstance.schedule();
JobInstance instance = jobInstanceDao.save(stageId, newInstance);
instance.setIdentifier(new JobIdentifier(savedPipeline, savedStage, instance));
EnvironmentVariablesConfig variables = new EnvironmentVariablesConfig();
variables.add("VARIABLE_NAME", "variable value");
variables.add("TRIGGER_VAR", "junk val");
JobPlan plan = new DefaultJobPlan(new Resources(), new ArtifactPlans(),
new ArtifactPropertiesGenerators(), instance.getId(),
instance.getIdentifier(), null, variables, new EnvironmentVariablesConfig(), null);
jobInstanceDao.save(instance.getId(), plan);
environmentVariableDao.save(savedPipeline.getId(), EnvironmentVariableDao.EnvironmentVariableType.Trigger, GoConfigFileHelper.env("TRIGGER_VAR", "trigger val"));
List<JobPlan> retrieved = jobInstanceDao.orderedScheduledBuilds();
JobPlan reloadedPlan = planForJob(retrieved, plan.getJobId());
EnvironmentVariableContext context = new EnvironmentVariableContext();
reloadedPlan.applyTo(context);
assertThat(reloadedPlan.getVariables(), is(plan.getVariables()));
assertThat(context.getProperty("VARIABLE_NAME"), is("variable value"));
assertThat(context.getProperty("TRIGGER_VAR"), is("trigger val"));
}
private JobPlan planForJob(List<JobPlan> retrieved, long expectedJobId) {
for (JobPlan loadedJobPlan : retrieved) {
if (loadedJobPlan.getJobId() == expectedJobId) {
return loadedJobPlan;
}
}
return null;
}
@Test
public void shouldLoadArtifactPropertiesGeneratorsInOrderForAssignment() throws Exception {
ArtifactPropertiesGenerator prop1 = new ArtifactPropertiesGenerator("test1", "src", "//xpath");
ArtifactPropertiesGenerator prop2 = new ArtifactPropertiesGenerator("test2", "src", "//xpath");
ArtifactPropertiesGenerator prop3 = new ArtifactPropertiesGenerator("test3", "src", "//xpath");
ArtifactPropertiesGenerator prop4 = new ArtifactPropertiesGenerator("test4", "src", "//xpath");
ArtifactPropertiesGenerator prop5 = new ArtifactPropertiesGenerator("test5", "src", "//xpath");
JobInstance instance = jobInstanceDao.save(stageId, new JobInstance(projectOne));
instance.setIdentifier(new JobIdentifier(savedPipeline, savedStage, instance));
JobPlan savedPlan = new DefaultJobPlan(new Resources(), artifactPlans(), new ArtifactPropertiesGenerators(prop1, prop2, prop3, prop4, prop5), instance.getId(), instance.getIdentifier(), null, new EnvironmentVariablesConfig(), new EnvironmentVariablesConfig(), null);
jobInstanceDao.save(instance.getId(), savedPlan);
JobPlan plan = findPlan(jobInstanceDao.orderedScheduledBuilds(), projectOne);
List<ArtifactPropertiesGenerator> generators = plan.getPropertyGenerators();
assertThat(generators.size(), is(5));
assertThat(generators.get(0), is(prop1));
assertThat(generators.get(1), is(prop2));
assertThat(generators.get(2), is(prop3));
assertThat(generators.get(3), is(prop4));
assertThat(generators.get(4), is(prop5));
}
@Test
public void shouldLoadArtifactsAndResourcesForAssignment() throws Exception {
JobInstance instance = jobInstanceDao.save(stageId, new JobInstance(projectOne));
instance.setIdentifier(new JobIdentifier(savedPipeline, savedStage, instance));
Resources resources = new Resources("one, two, three");
JobPlan savedPlan = new DefaultJobPlan(resources, artifactPlans(), new ArtifactPropertiesGenerators(), instance.getId(), instance.getIdentifier(), null, new EnvironmentVariablesConfig(), new EnvironmentVariablesConfig(), null);
jobInstanceDao.save(instance.getId(), savedPlan);
final List<JobPlan> planList = jobInstanceDao.orderedScheduledBuilds();
final List<JobPlan> plans = findPlans(planList, projectOne);
assertThat(plans.size(), is(1));
assertThat(plans.get(0).getResources(), is(resources));
}
@Test
public void shouldLoadJobIdentifierForAssignment() throws Exception {
JobInstance jobInstance = scheduled(projectOne);
jobInstanceDao.save(stageId, jobInstance);
JobPlan job = findPlan(jobInstanceDao.orderedScheduledBuilds(), projectOne);
assertThat(job.getIdentifier(), is(jobIdentifier(jobInstance)));
}
@Test
public void shouldLoadAgentUuidForAssignment() throws Exception {
JobInstance jobInstance = scheduled(projectOne);
jobInstance.setAgentUuid("uuid1");
jobInstanceDao.save(stageId, jobInstance);
JobPlan job = findPlan(jobInstanceDao.orderedScheduledBuilds(), projectOne);
assertThat(job.getAgentUuid(), is("uuid1"));
}
@Test
public void shouldLoadRunOnAllAgentsForAssignment() throws Exception {
JobInstance jobInstance = scheduled(projectOne);
jobInstance.setRunOnAllAgents(true);
jobInstanceDao.save(stageId, jobInstance);
JobInstance reloaded = jobInstanceDao.buildByIdWithTransitions(jobInstance.getId());
assertThat(reloaded.isRunOnAllAgents(), is(true));
}
@Test
public void shouldLoadRunMultipleInstanceForAssignment() throws Exception {
JobInstance jobInstance = scheduled(projectOne);
jobInstance.setRunMultipleInstance(true);
jobInstanceDao.save(stageId, jobInstance);
JobInstance reloaded = jobInstanceDao.buildByIdWithTransitions(jobInstance.getId());
assertThat(reloaded.isRunMultipleInstance(), is(true));
}
private JobIdentifier jobIdentifier(JobInstance jobInstance) {
return new JobIdentifier(savedPipeline, savedStage, jobInstance);
}
private JobPlan findPlan(List<JobPlan> list, String jobName) {
final List<JobPlan> planList = findPlans(list, jobName);
if (planList.size() > 0) {
return planList.get(0);
}
return null;
}
private List<JobPlan> findPlans(List<JobPlan> list, String jobName) {
List<JobPlan> result = new ArrayList<>();
for (JobPlan buildNameBean : list) {
if (jobName.equals(buildNameBean.getName())) {
result.add(buildNameBean);
}
}
return result;
}
@Test
public void shouldGetLatestInProgressBuildByAgentUuid() throws Exception {
JobInstance buildingJob = building(projectOne, new Date(1));
final String uuid = "uuid";
buildingJob.setAgentUuid(uuid);
jobInstanceDao.save(stageId, buildingJob);
JobInstance completedJob = JobInstanceMother.completed("anotherBuild", JobResult.Passed);
completedJob.setAgentUuid(uuid);
jobInstanceDao.save(stageId, completedJob);
JobInstance jobInstance = jobInstanceDao.getLatestInProgressBuildByAgentUuid(uuid);
assertThat(jobInstance, hasSameId(buildingJob));
assertThat(jobInstance.getIdentifier(), is(jobIdentifier(jobInstance)));
}
@Test
public void shouldGetInProgressJobs() throws Exception {
JobInstance buildingJob1 = building(projectOne, new Date(1));
buildingJob1.setAgentUuid("uuid1");
jobInstanceDao.save(stageId, buildingJob1);
JobInstance buildingJob2 = building("project2", new Date(2));
buildingJob2.setAgentUuid("uuid2");
jobInstanceDao.save(stageId, buildingJob2);
JobInstance buildingJob3 = building("project3", new Date(3));
buildingJob3.setAgentUuid("uuid3");
jobInstanceDao.save(stageId, buildingJob3);
List<String> liveAgentIds = new ArrayList<String>() {
{
add("uuid1");
add("uuid2");
}
};
JobInstances list = jobInstanceDao.findHungJobs(liveAgentIds);
assertThat(list.size(), is(1));
JobInstance reloaded = list.get(0);
assertThat(reloaded, hasSameId(buildingJob3));
assertThat(reloaded.getIdentifier(), is(jobIdentifier(buildingJob3)));
}
@Test
public void shouldIgnore() throws Exception {
JobInstance instance = scheduled(projectOne);
jobInstanceDao.save(stageId, instance);
jobInstanceDao.ignore(instance);
JobInstance reloaded = jobInstanceDao.buildByIdWithTransitions(instance.getId());
assertThat(reloaded.isIgnored(), is(true));
}
@Test
public void shouldKnowIfBuildIdentifierIsValid() throws Exception {
assertThat(jobInstanceDao.isValid(PIPELINE_NAME, STAGE_NAME, JOB_NAME), is(true));
assertThat(jobInstanceDao.isValid("unknown", STAGE_NAME, JOB_NAME), is(false));
}
@Test
public void shouldGetCompletedJobsOnAgentForARange() {
String agentUuid = "special_uuid";
JobInstance buildingJob = building("job1", new Date(1));
buildingJob.setAgentUuid(agentUuid);
jobInstanceDao.save(stageId, buildingJob);
JobInstance completedJob = completed("job2", JobResult.Passed, new Date(1));
completedJob.setAgentUuid(agentUuid);
jobInstanceDao.save(stageId, completedJob);
JobInstance cancelledJob = cancelled("job3");
cancelledJob.setAgentUuid("something_different");//Different UUID. Should not be considered
jobInstanceDao.save(stageId, cancelledJob);
JobInstance rescheduledJob = rescheduled("rescheduled", agentUuid);
jobInstanceDao.save(stageId, rescheduledJob);
jobInstanceDao.ignore(rescheduledJob);
List<JobInstance> jobInstances = jobInstanceDao.completedJobsOnAgent(agentUuid, JobInstanceService.JobHistoryColumns.stage, SortOrder.ASC, 0, 10);
assertThat(jobInstances.size(), is(2));
JobInstance actual = jobInstances.get(0);
assertThat(actual.getName(), is(completedJob.getName()));
completedJob.setIdentifier(actual.getIdentifier());
assertThat(actual, is(completedJob));
actual = jobInstances.get(1);
assertThat(actual.getName(), is(rescheduledJob.getName()));
rescheduledJob.setIdentifier(actual.getIdentifier());
assertThat(actual, is(rescheduledJob));
}
@Test
public void shouldGetTotalNumberOfCompletedJobsForAnAgent() {
String agentUuid = "special_uuid";
JobInstance buildingJob = building("job1", new Date(1));
buildingJob.setAgentUuid(agentUuid);
jobInstanceDao.save(stageId, buildingJob);
JobInstance completedJob = completed("job2", JobResult.Passed, new Date(1));
completedJob.setAgentUuid(agentUuid);
jobInstanceDao.save(stageId, completedJob);
JobInstance rescheduledJob = rescheduled("rescheduled", agentUuid);
jobInstanceDao.save(stageId, rescheduledJob);
jobInstanceDao.ignore(rescheduledJob);
JobInstance cancelledJob = cancelled("job3");
cancelledJob.setAgentUuid("something_different");//Different UUID. Should not be counted
jobInstanceDao.save(stageId, cancelledJob);
JobInstance simpleJob = failed("simpleJob");
simpleJob.setAgentUuid(agentUuid);
jobInstanceDao.save(stageId, simpleJob);
assertThat(jobInstanceDao.totalCompletedJobsOnAgent(agentUuid), is(3));
}
private ArtifactPlans artifactPlans() {
ArtifactPlans artifactPlans = new ArtifactPlans();
artifactPlans.add(new ArtifactPlan("src", "dest"));
artifactPlans.add(new ArtifactPlan("src1", "dest2"));
return artifactPlans;
}
}