/*
* 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.persistence;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import com.rits.cloning.Cloner;
import com.thoughtworks.go.config.CruiseConfig;
import com.thoughtworks.go.config.GoConfigDao;
import com.thoughtworks.go.config.PipelineConfig;
import com.thoughtworks.go.config.materials.MaterialConfigs;
import com.thoughtworks.go.config.materials.git.GitMaterial;
import com.thoughtworks.go.config.materials.mercurial.HgMaterial;
import com.thoughtworks.go.config.materials.svn.SvnMaterial;
import com.thoughtworks.go.domain.DefaultSchedulingContext;
import com.thoughtworks.go.domain.MaterialRevision;
import com.thoughtworks.go.domain.MaterialRevisions;
import com.thoughtworks.go.domain.Pipeline;
import com.thoughtworks.go.domain.PipelineTimelineEntry;
import com.thoughtworks.go.domain.User;
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.helper.MaterialsMother;
import com.thoughtworks.go.server.dao.DatabaseAccessHelper;
import com.thoughtworks.go.server.dao.PipelineSqlMapDao;
import com.thoughtworks.go.server.dao.UserSqlMapDao;
import com.thoughtworks.go.server.domain.PipelineTimeline;
import com.thoughtworks.go.server.domain.user.PipelineSelections;
import com.thoughtworks.go.server.service.GoConfigService;
import com.thoughtworks.go.server.service.InstanceFactory;
import com.thoughtworks.go.server.service.ScheduleTestUtil;
import com.thoughtworks.go.server.transaction.TransactionSynchronizationManager;
import com.thoughtworks.go.server.transaction.TransactionTemplate;
import com.thoughtworks.go.util.GoConfigFileHelper;
import com.thoughtworks.go.util.TimeProvider;
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.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import static com.thoughtworks.go.helper.ModificationsMother.oneModifiedFile;
import static com.thoughtworks.go.helper.PipelineConfigMother.createPipelineConfig;
import static com.thoughtworks.go.helper.PipelineConfigMother.pipelineConfig;
import static com.thoughtworks.go.util.DataStructureUtils.a;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
@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 PipelineRepositoryIntegrationTest {
@Autowired
PipelineSqlMapDao pipelineSqlMapDao;
@Autowired DatabaseAccessHelper dbHelper;
@Autowired PipelineRepository pipelineRepository;
@Autowired MaterialRepository materialRepository;
@Autowired UserSqlMapDao userSqlMapDao;
@Autowired private TransactionTemplate transactionTemplate;
@Autowired private TransactionSynchronizationManager transactionSynchronizationManager;
@Autowired private GoConfigService goConfigService;
@Autowired private GoConfigDao goConfigDao;
@Autowired private InstanceFactory instanceFactory;
private GoConfigFileHelper configHelper = new GoConfigFileHelper();
private static final String PIPELINE_NAME = "pipeline";
public static final Cloner CLONER = new Cloner();
@Before
public void setup() throws Exception {
dbHelper.onSetUp();
configHelper.usingCruiseConfigDao(goConfigDao);
configHelper.onSetUp();
}
@After
public void teardown() throws Exception {
configHelper.onTearDown();
dbHelper.onTearDown();
}
@Test
public void shouldConsider_firstRevision_forAFlyweight_asInDb_whilePickingFromMultipleDeclarations() {
ScheduleTestUtil u = new ScheduleTestUtil(transactionTemplate, materialRepository, dbHelper, configHelper);
int i = 1;
GitMaterial git1 = u.wf(new GitMaterial("git"), "folder1");
u.checkinInOrder(git1, "g1");
GitMaterial git2 = u.wf(new GitMaterial("git"), "folder2");
ScheduleTestUtil.AddedPipeline p = u.saveConfigWith("P", u.m(git1), u.m(git2));
CruiseConfig cruiseConfig = goConfigDao.load();
u.checkinInOrder(git1, u.d(i++), "g2");
u.runAndPass(p, "g1", "g2");
u.runAndPass(p, "g2", "g1");
PipelineTimeline timeline = new PipelineTimeline(pipelineRepository, transactionTemplate, transactionSynchronizationManager);
timeline.updateTimelineOnInit();
List<PipelineTimelineEntry> timelineEntries = new ArrayList<>(timeline.getEntriesFor("P"));
assertThat(timelineEntries.get(0).getPipelineLocator().getCounter(), is(1));
assertThat(timelineEntries.get(0).naturalOrder(), is(1.0));
List<PipelineTimelineEntry.Revision> flyweightsRevs = new ArrayList<>(timelineEntries.get(0).revisions().values()).get(0);
assertThat(flyweightsRevs.get(0).revision, is("g1"));
assertThat(flyweightsRevs.get(1).revision, is("g2"));
assertThat(timelineEntries.get(1).getPipelineLocator().getCounter(), is(2));
assertThat(timelineEntries.get(1).naturalOrder(), is(2.0));
flyweightsRevs = new ArrayList<>(timelineEntries.get(1).revisions().values()).get(0);
assertThat(flyweightsRevs.get(0).revision, is("g2"));
assertThat(flyweightsRevs.get(1).revision, is("g1"));
MaterialConfigs materials = CLONER.deepClone(p.config.materialConfigs());
Collections.reverse(materials);
configHelper.setMaterialConfigForPipeline("P", materials.toArray(new MaterialConfig[0]));
goConfigDao.load();
timeline = new PipelineTimeline(pipelineRepository, transactionTemplate, transactionSynchronizationManager);
timeline.updateTimelineOnInit();
timelineEntries = new ArrayList<>(timeline.getEntriesFor("P"));
assertThat(timelineEntries.get(0).getPipelineLocator().getCounter(), is(1));
assertThat(timelineEntries.get(0).naturalOrder(), is(1.0));
flyweightsRevs = new ArrayList<>(timelineEntries.get(0).revisions().values()).get(0);
assertThat(flyweightsRevs.get(0).revision, is("g1"));
assertThat(flyweightsRevs.get(1).revision, is("g2"));
assertThat(timelineEntries.get(1).getPipelineLocator().getCounter(), is(2));
assertThat(timelineEntries.get(1).naturalOrder(), is(2.0));
flyweightsRevs = new ArrayList<>(timelineEntries.get(1).revisions().values()).get(0);
assertThat(flyweightsRevs.get(0).revision, is("g2"));
assertThat(flyweightsRevs.get(1).revision, is("g1"));
}
@Test
public void shouldReturnEarliestPMRFor1Material() throws Exception {
HgMaterial hgmaterial = MaterialsMother.hgMaterial("first");
PipelineConfig pipelineConfig = createPipelineConfig(PIPELINE_NAME, "stage", "job");
pipelineConfig.setMaterialConfigs(new MaterialConfigs(hgmaterial.config()));
DateTime date = new DateTime(1984, 12, 23, 0, 0, 0, 0);
long firstId = createPipeline(hgmaterial, pipelineConfig, 1,
oneModifiedFile("3", date.plusDays(2).toDate()),
oneModifiedFile("2", date.plusDays(2).toDate()),
oneModifiedFile("1", date.plusDays(3).toDate()));
long secondId = createPipeline(hgmaterial, pipelineConfig, 2,
oneModifiedFile("5", date.plusDays(1).toDate()),
oneModifiedFile("4", date.toDate()));
PipelineTimeline pipelineTimeline = new PipelineTimeline(pipelineRepository, transactionTemplate, transactionSynchronizationManager);
ArrayList<PipelineTimelineEntry> entries = new ArrayList<>();
pipelineRepository.updatePipelineTimeline(pipelineTimeline, entries);
assertThat(pipelineTimeline.getEntriesFor(PIPELINE_NAME).size(), is(2));
assertThat(entries.size(), is(2));
assertThat(entries, hasItem(expected(firstId,
Collections.singletonMap(hgmaterial.getFingerprint(), a(new PipelineTimelineEntry.Revision(date.plusDays(2).toDate(), "123", hgmaterial.getFingerprint(), 10))), 1)));
assertThat(entries, hasItem(expected(secondId,
Collections.singletonMap(hgmaterial.getFingerprint(), a(new PipelineTimelineEntry.Revision(date.plusDays(1).toDate(), "12", hgmaterial.getFingerprint(), 8))), 2)));
assertThat(pipelineTimeline.getEntriesFor(PIPELINE_NAME), hasItem(expected(firstId,
Collections.singletonMap(hgmaterial.getFingerprint(), a(new PipelineTimelineEntry.Revision(date.plusDays(2).toDate(), "123", hgmaterial.getFingerprint(), 10))), 1)));
assertThat(pipelineTimeline.getEntriesFor(PIPELINE_NAME), hasItem(expected(secondId,
Collections.singletonMap(hgmaterial.getFingerprint(), a(new PipelineTimelineEntry.Revision(date.plusDays(1).toDate(), "12", hgmaterial.getFingerprint(), 8))), 2)));
assertThat(pipelineTimeline.maximumId(), is(secondId));
long thirdId = createPipeline(hgmaterial, pipelineConfig, 3, oneModifiedFile("30", date.plusDays(10).toDate()));
pipelineRepository.updatePipelineTimeline(pipelineTimeline, new ArrayList<>());
assertThat(pipelineTimeline.getEntriesFor(PIPELINE_NAME).size(), is(3));
assertThat(pipelineTimeline.getEntriesFor(PIPELINE_NAME), hasItem(expected(thirdId,
Collections.singletonMap(hgmaterial.getFingerprint(), a(new PipelineTimelineEntry.Revision(date.plusDays(10).toDate(), "1234", hgmaterial.getFingerprint(), 12))), 3)));
assertThat(pipelineTimeline.maximumId(), is(thirdId));
assertThat(pipelineSqlMapDao.pipelineByIdWithMods(firstId).getNaturalOrder(), is(1.0));
assertThat(pipelineSqlMapDao.pipelineByIdWithMods(secondId).getNaturalOrder(), is(0.5));
assertThat(pipelineSqlMapDao.pipelineByIdWithMods(thirdId).getNaturalOrder(), is(2.0));
PipelineTimeline pipelineTimeline2 = new PipelineTimeline(pipelineRepository, transactionTemplate, transactionSynchronizationManager);
pipelineRepository.updatePipelineTimeline(pipelineTimeline2, new ArrayList<>());
}
@Test
public void shouldAddExistingPipelinesToTimelineForNewTimeline() {
HgMaterial hgmaterial = MaterialsMother.hgMaterial(UUID.randomUUID().toString());
PipelineConfig pipelineConfig = createPipelineConfig(PIPELINE_NAME, "stage", "job");
pipelineConfig.setMaterialConfigs(new MaterialConfigs(hgmaterial.config()));
DateTime date = new DateTime(1984, 12, 23, 0, 0, 0, 0);
long firstId = createPipeline(hgmaterial, pipelineConfig, 1,
oneModifiedFile("3", date.plusDays(2).toDate()),
oneModifiedFile("2", date.plusDays(2).toDate()),
oneModifiedFile("1", date.plusDays(3).toDate()));
long secondId = createPipeline(hgmaterial, pipelineConfig, 2,
oneModifiedFile("5", date.plusDays(1).toDate()),
oneModifiedFile("4", date.toDate()));
PipelineTimeline mods = new PipelineTimeline(pipelineRepository, transactionTemplate, transactionSynchronizationManager);
mods.update();
assertThat(pipelineSqlMapDao.pipelineByIdWithMods(firstId).getNaturalOrder(), is(1.0));
assertThat(pipelineSqlMapDao.pipelineByIdWithMods(secondId).getNaturalOrder(), is(0.5));
PipelineTimeline modsAfterReboot = new PipelineTimeline(pipelineRepository, transactionTemplate, transactionSynchronizationManager);
modsAfterReboot.update();
}
@Test
public void shouldReturnEarliestPMRForMultipleMaterial() throws Exception {
final HgMaterial hgmaterial = MaterialsMother.hgMaterial("first");
final SvnMaterial svnMaterial = MaterialsMother.svnMaterial();
PipelineConfig pipelineConfig = createPipelineConfig(PIPELINE_NAME, "stage", "job");
pipelineConfig.setMaterialConfigs(new MaterialConfigs(hgmaterial.config(), svnMaterial.config()));
final DateTime date = new DateTime(1984, 12, 23, 0, 0, 0, 0);
long first = save(pipelineConfig, 1, 1.0,
new MaterialRevision(hgmaterial,
oneModifiedFile("13", date.plusDays(2).toDate()),
oneModifiedFile("12", date.plusDays(2).toDate()),
oneModifiedFile("11", date.plusDays(3).toDate())),
new MaterialRevision(svnMaterial,
oneModifiedFile("23", date.plusDays(6).toDate()),
oneModifiedFile("22", date.plusDays(2).toDate()),
oneModifiedFile("21", date.plusDays(2).toDate()))
);
long second = save(pipelineConfig, 2, 0.0,
new MaterialRevision(hgmaterial,
oneModifiedFile("15", date.plusDays(3).toDate()),
oneModifiedFile("14", date.plusDays(2).toDate())),
new MaterialRevision(svnMaterial,
oneModifiedFile("25", date.plusDays(5).toDate())));
PipelineTimeline pipelineTimeline = new PipelineTimeline(pipelineRepository, transactionTemplate, transactionSynchronizationManager);
pipelineRepository.updatePipelineTimeline(pipelineTimeline, new ArrayList<>());
Collection<PipelineTimelineEntry> modifications = pipelineTimeline.getEntriesFor(PIPELINE_NAME);
assertThat(modifications.size(), is(2));
assertThat(modifications, hasItem(expected(first, new HashMap<String, List<PipelineTimelineEntry.Revision>>() {{
put(hgmaterial.getFingerprint(), a(new PipelineTimelineEntry.Revision(date.plusDays(2).toDate(), "123", hgmaterial.getFingerprint(), 8)));
put(svnMaterial.getFingerprint(), a(new PipelineTimelineEntry.Revision(date.plusDays(6).toDate(), "456", svnMaterial.getFingerprint(), 12)));
}}, 1)));
assertThat(modifications, hasItem(expected(second, new HashMap<String, List<PipelineTimelineEntry.Revision>>() {{
put(hgmaterial.getFingerprint(), a(new PipelineTimelineEntry.Revision(date.plusDays(3).toDate(), "234", hgmaterial.getFingerprint(), 9)));
put(svnMaterial.getFingerprint(), a(new PipelineTimelineEntry.Revision(date.plusDays(5).toDate(), "345", svnMaterial.getFingerprint(), 10)));
}}, 2)));
}
private PipelineTimelineEntry expected(long first, Map<String, List<PipelineTimelineEntry.Revision>> map, int counter) {
return new PipelineTimelineEntry(PIPELINE_NAME, first, counter, map);
}
private long createPipeline(HgMaterial hgmaterial, PipelineConfig pipelineConfig, int counter, Modification... modifications) {
return save(pipelineConfig, counter, new MaterialRevision(hgmaterial, modifications));
}
private long save(PipelineConfig pipelineConfig, int counter, MaterialRevision... materialRevisions) {
return save(pipelineConfig, counter, 0.0, materialRevisions);
}
private long save(final PipelineConfig pipelineConfig, final int counter, final double naturalOrder, final MaterialRevision... materialRevisions) {
return (Long) transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
MaterialRevisions revisions = new MaterialRevisions(materialRevisions);
materialRepository.save(revisions);
Pipeline instance = instanceFactory.createPipelineInstance(pipelineConfig, BuildCause.createWithModifications(revisions, "me"), new DefaultSchedulingContext(), "md5-test", new TimeProvider());
instance.setCounter(counter);
instance.setNaturalOrder(naturalOrder);
return pipelineSqlMapDao.save(instance).getId();
}
});
}
@Test
public void shouldReturnNullForInvalidIds() {
assertThat(pipelineRepository.findPipelineSelectionsById(null), is(nullValue()));
assertThat(pipelineRepository.findPipelineSelectionsById(""), is(nullValue()));
assertThat(pipelineRepository.findPipelineSelectionsById("123"), is(nullValue()));
try {
pipelineRepository.findPipelineSelectionsById("foo");
fail("should throw error");
} catch (NumberFormatException e) {
}
}
@Test
public void shouldSaveSelectedPipelinesWithoutUserId() {
Date date = new Date();
List<String> unSelected = Arrays.asList("pipeline1", "pipeline2");
long id = pipelineRepository.saveSelectedPipelines(new PipelineSelections(unSelected, date, null, true));
PipelineSelections found = pipelineRepository.findPipelineSelectionsById(id);
assertHasPipelines(found, new String[]{"pipeline3", "pipeline4"});
assertHasPipelines(found, new String[]{"pipeline1", "pipeline2"}, false);
assertThat(found.userId(), is(nullValue()));
assertEquals(date, found.lastUpdated());
}
@Test
public void shouldSaveSelectedPipelinesWithUserId() {
User user = createUser();
List<String> unSelected = Arrays.asList("pipeline1", "pipeline2");
long id = pipelineRepository.saveSelectedPipelines(new PipelineSelections(unSelected, new Date(), user.getId(), true));
assertThat(pipelineRepository.findPipelineSelectionsById(id).userId(), is(user.getId()));
}
@Test
public void shouldSaveSelectedPipelinesWithBlacklistPreferenceFalse() {
User user = createUser();
List<String> unSelected = Arrays.asList("pipeline1", "pipeline2");
long id = pipelineRepository.saveSelectedPipelines(new PipelineSelections(unSelected, new Date(), user.getId(), false));
assertThat(pipelineRepository.findPipelineSelectionsById(id).isBlacklist(), is(false));
}
@Test
public void shouldSaveSelectedPipelinesWithBlacklistPreferenceTrue() {
User user = createUser();
List<String> unSelected = Arrays.asList("pipeline1", "pipeline2");
long id = pipelineRepository.saveSelectedPipelines(new PipelineSelections(unSelected, new Date(), user.getId(), true));
assertThat(pipelineRepository.findPipelineSelectionsById(id).isBlacklist(), is(true));
}
@Test
public void shouldFindSelectedPipelinesByUserId() {
User user = createUser();
List<String> unSelected = Arrays.asList("pipeline1", "pipeline2");
long id = pipelineRepository.saveSelectedPipelines(new PipelineSelections(unSelected, new Date(), user.getId(), true));
assertThat(pipelineRepository.findPipelineSelectionsByUserId(user.getId()).getId(), is(id));
}
@Test
public void shouldReturnNullAsPipelineSelectionsIfUserIdIsNull() {
assertThat(pipelineRepository.findPipelineSelectionsByUserId(null), is(nullValue()));
}
@Test
public void shouldReturnNullAsPipelineSelectionsIfSelectionsExistForUser() {
assertThat(pipelineRepository.findPipelineSelectionsByUserId(10L), is(nullValue()));
}
private User createUser() {
userSqlMapDao.saveOrUpdate(new User("loser"));
return userSqlMapDao.findUser("loser");
}
private void assertHasPipelines(PipelineSelections pipelineSelections, String[] pipelines) {
assertHasPipelines(pipelineSelections, pipelines, true);
}
private void assertHasPipelines(PipelineSelections pipelineSelections, String[] pipelines, boolean has) {
for (String pipeline : pipelines) {
assertThat(pipelineSelections.includesPipeline(pipelineConfig(pipeline)),is(has));
}
}
}