/* * 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.domain.activity; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import com.thoughtworks.go.config.GoConfigDao; import com.thoughtworks.go.domain.JobConfigIdentifier; import com.thoughtworks.go.domain.JobIdentifier; import com.thoughtworks.go.domain.JobInstance; import com.thoughtworks.go.domain.JobState; import com.thoughtworks.go.domain.Pipeline; import com.thoughtworks.go.fixture.PipelineWithTwoStages; import com.thoughtworks.go.helper.JobInstanceMother; import com.thoughtworks.go.server.dao.DatabaseAccessHelper; import com.thoughtworks.go.server.dao.StageDao; import com.thoughtworks.go.server.persistence.MaterialRepository; import com.thoughtworks.go.server.transaction.TransactionTemplate; import com.thoughtworks.go.util.GoConfigFileHelper; import org.jmock.Expectations; import org.jmock.Mockery; 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 static org.hamcrest.Matchers.hasItem; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNot.not; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @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 JobStatusCacheTest { @Autowired private GoConfigDao goConfigDao; @Autowired private JobStatusCache jobStatusCache; @Autowired private DatabaseAccessHelper dbHelper; @Autowired private MaterialRepository materialRepository; @Autowired private TransactionTemplate transactionTemplate; private PipelineWithTwoStages pipelineFixture; private static GoConfigFileHelper configFileHelper = new GoConfigFileHelper(); @Before public void setUp() throws Exception { dbHelper.onSetUp(); configFileHelper.usingEmptyConfigFileWithLicenseAllowsUnlimitedAgents(); configFileHelper.usingCruiseConfigDao(goConfigDao); pipelineFixture = new PipelineWithTwoStages(materialRepository, transactionTemplate); pipelineFixture.usingConfigHelper(configFileHelper).usingDbHelper(dbHelper).onSetUp(); } @After public void teardown() throws Exception { dbHelper.onTearDown(); pipelineFixture.onTearDown(); jobStatusCache.clear(); } @Test public void shouldLoadMostRecentInstanceFromDBForTheFirstTime() throws SQLException { pipelineFixture.createdPipelineWithAllStagesPassed(); Pipeline pipeline = pipelineFixture.createPipelineWithFirstStageScheduled(); JobInstance expect = pipeline.getStages().byName(pipelineFixture.devStage).getJobInstances().first(); assertThat(jobStatusCache.currentJob(new JobConfigIdentifier(pipelineFixture.pipelineName, pipelineFixture.devStage, PipelineWithTwoStages.JOB_FOR_DEV_STAGE)), is(expect)); } @Test public void shouldLoadMostRecentInstanceFromDBOnlyOnce() throws SQLException { Mockery mockery = new Mockery(); final StageDao mock = mockery.mock(StageDao.class); final JobInstance instance = JobInstanceMother.passed("linux-firefox"); final List<JobInstance> found = new ArrayList<>(); found.add(instance); mockery.checking(new Expectations() { { one(mock).mostRecentJobsForStage("pipeline", "stage"); will(returnValue(found)); } }); JobConfigIdentifier identifier = new JobConfigIdentifier(instance.getPipelineName(), instance.getStageName(), instance.getName()); JobStatusCache cache = new JobStatusCache(mock); assertThat(cache.currentJob(identifier).getState(), is(instance.getState())); //call currentJob for the second time, should not call jobInstanceDao now assertThat(cache.currentJob(identifier).getState(), is(instance.getState())); } @Test public void shouldRefreshCurrentJobWhenNewJobComes() { JobInstance job = JobInstanceMother.buildingInstance("cruise", "dev", "linux-firefox", "1"); jobStatusCache.jobStatusChanged(job); assertThat(jobStatusCache.currentJob(job.getIdentifier().jobConfigIdentifier()).getState(), is(JobState.Building)); JobInstance passing = job.clone(); passing.changeState(JobState.Completed); jobStatusCache.jobStatusChanged(passing); assertThat(jobStatusCache.currentJob(passing.getIdentifier().jobConfigIdentifier()).getState(), is(JobState.Completed)); } @Test public void shouldHitTheDatabaseOnlyOnceIfTheJobIsNeverScheduledEver() { StageDao dao = mock(StageDao.class); when(dao.mostRecentJobsForStage("cruise", "dev")).thenReturn(new ArrayList<>()); JobStatusCache cache = new JobStatusCache(dao); assertThat(cache.currentJobs(new JobConfigIdentifier("cruise", "dev", "linux-firefox")).isEmpty(), is(true)); assertThat(cache.currentJobs(new JobConfigIdentifier("cruise", "dev", "linux-firefox")).isEmpty(), is(true)); Mockito.verify(dao, times(1)).mostRecentJobsForStage("cruise", "dev"); } @Test public void shouldSkipNeverRunJobsWhenTryingToDealWithOtherJobs() { StageDao dao = mock(StageDao.class); JobInstance random = jobInstance("random"); when(dao.mostRecentJobsForStage("cruise", "dev")).thenReturn(Arrays.asList(random)); JobStatusCache cache = new JobStatusCache(dao); assertThat(cache.currentJobs(new JobConfigIdentifier("cruise", "dev", "linux-firefox")).isEmpty(), is(true)); assertThat(cache.currentJobs(new JobConfigIdentifier("cruise", "dev", "random")).get(0), is(random)); Mockito.verify(dao, times(2)).mostRecentJobsForStage("cruise", "dev"); } @Test public void shouldRemoveTheNeverRunInstanceWhenTheJobRunsForTheFirstTime() { StageDao dao = mock(StageDao.class); when(dao.mostRecentJobsForStage("cruise", "dev")).thenReturn(new ArrayList<>()); JobStatusCache cache = new JobStatusCache(dao); assertThat(cache.currentJobs(new JobConfigIdentifier("cruise", "dev", "linux-firefox")).isEmpty(), is(true)); JobInstance instance = jobInstance("linux-firefox"); cache.jobStatusChanged(instance); assertThat(cache.currentJobs(new JobConfigIdentifier("cruise", "dev", "linux-firefox")).get(0), is(instance)); Mockito.verify(dao, times(1)).mostRecentJobsForStage("cruise", "dev"); } private JobInstance jobInstance(String jobConfigName) { JobInstance instance = JobInstanceMother.scheduled(jobConfigName); instance.setIdentifier(new JobIdentifier("cruise", 1, "1", "dev", "1", jobConfigName)); return instance; } @Test public void shouldReturnNullWhenNoCurrentJob() throws Exception { pipelineFixture.createdPipelineWithAllStagesPassed(); JobConfigIdentifier jobConfigIdentifier = new JobConfigIdentifier(pipelineFixture.pipelineName, pipelineFixture.devStage, "wrong-job"); JobInstance currentJob = jobStatusCache.currentJob(jobConfigIdentifier); assertThat(currentJob, is(nullValue())); } @Test public void shouldFindAllMatchingJobsForJobsThatAreRunOnAllAgents() { JobInstance job1 = JobInstanceMother.instanceForRunOnAllAgents("cruise", "dev", "linux-firefox", "1", 1); JobInstance job2 = JobInstanceMother.instanceForRunOnAllAgents("cruise", "dev", "linux-firefox", "1", 2); jobStatusCache.jobStatusChanged(job1); jobStatusCache.jobStatusChanged(job2); JobConfigIdentifier config = new JobConfigIdentifier("cruise", "dev", "linux-firefox"); List<JobInstance> list = jobStatusCache.currentJobs(config); assertThat(list, hasItem(job1)); assertThat(list, hasItem(job2)); assertThat(list.size(), is(2)); } @Test public void shouldFindAllMatchingJobsForJobsThatAreRunMultipleInstance() { JobInstance job1 = JobInstanceMother.instanceForRunMultipleInstance("cruise", "dev", "linux-firefox", "1", 1); JobInstance job2 = JobInstanceMother.instanceForRunMultipleInstance("cruise", "dev", "linux-firefox", "1", 2); jobStatusCache.jobStatusChanged(job1); jobStatusCache.jobStatusChanged(job2); JobConfigIdentifier config = new JobConfigIdentifier("cruise", "dev", "linux-firefox"); List<JobInstance> list = jobStatusCache.currentJobs(config); assertThat(list, hasItem(job1)); assertThat(list, hasItem(job2)); assertThat(list.size(), is(2)); } @Test public void shouldExcludeJobFromOtherPipelines() { JobInstance job1 = JobInstanceMother.instanceForRunOnAllAgents("cruise", "dev", "linux-firefox", "1", 1); JobInstance job2 = JobInstanceMother.instanceForRunOnAllAgents("cruise", "dev", "linux-firefox", "1", 2); jobStatusCache.jobStatusChanged(job1); jobStatusCache.jobStatusChanged(job2); JobInstance otherPipeline = JobInstanceMother.buildingInstance("different-pipeline", "dev", "linux-firefox", "1"); jobStatusCache.jobStatusChanged(otherPipeline); JobConfigIdentifier config = new JobConfigIdentifier("cruise", "dev", "linux-firefox"); List<JobInstance> list = jobStatusCache.currentJobs(config); assertThat(list, not(hasItem(otherPipeline))); } @Test public void shouldExcludeJobFromOtherStages() { JobInstance job1 = JobInstanceMother.instanceForRunOnAllAgents("cruise", "dev", "linux-firefox", "1", 1); JobInstance job2 = JobInstanceMother.instanceForRunOnAllAgents("cruise", "dev", "linux-firefox", "1", 2); jobStatusCache.jobStatusChanged(job1); jobStatusCache.jobStatusChanged(job2); JobInstance otherStage = JobInstanceMother.buildingInstance("cruise", "different-stage", "linux-firefox", "1"); jobStatusCache.jobStatusChanged(otherStage); JobConfigIdentifier config = new JobConfigIdentifier("cruise", "dev", "linux-firefox"); List<JobInstance> list = jobStatusCache.currentJobs(config); assertThat(list, not(hasItem(otherStage))); } @Test public void shouldExcludeJobFromOtherInstancesOfThisStage() { JobInstance job1 = JobInstanceMother.buildingInstance("cruise", "dev", "linux-firefox", "1"); JobInstance job2 = JobInstanceMother.instanceForRunOnAllAgents("cruise", "dev", "linux-firefox", "2", 1); jobStatusCache.jobStatusChanged(job1); jobStatusCache.jobStatusChanged(job2); JobConfigIdentifier config = new JobConfigIdentifier("cruise", "dev", "linux-firefox"); List<JobInstance> list = jobStatusCache.currentJobs(config); assertThat(list, hasItem(job2)); assertThat(list, not(hasItem(job1))); } }