/* * 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.CaseInsensitiveString; import com.thoughtworks.go.config.materials.PackageMaterial; import com.thoughtworks.go.config.materials.PluggableSCMMaterial; import com.thoughtworks.go.config.materials.SubprocessExecutionContext; import com.thoughtworks.go.config.materials.dependency.DependencyMaterial; import com.thoughtworks.go.config.materials.git.GitMaterial; import com.thoughtworks.go.config.materials.git.GitMaterialConfig; import com.thoughtworks.go.config.materials.mercurial.HgMaterial; import com.thoughtworks.go.config.materials.perforce.P4Material; import com.thoughtworks.go.config.materials.svn.SvnMaterial; import com.thoughtworks.go.config.materials.tfs.TfsMaterial; import com.thoughtworks.go.domain.MaterialInstance; import com.thoughtworks.go.domain.MaterialRevision; import com.thoughtworks.go.domain.MaterialRevisions; import com.thoughtworks.go.domain.config.Configuration; import com.thoughtworks.go.domain.materials.*; import com.thoughtworks.go.domain.materials.git.GitMaterialInstance; import com.thoughtworks.go.domain.materials.packagematerial.PackageMaterialRevision; import com.thoughtworks.go.domain.materials.scm.PluggableSCMMaterialRevision; import com.thoughtworks.go.domain.packagerepository.PackageDefinition; import com.thoughtworks.go.domain.packagerepository.PackageRepositoryMother; import com.thoughtworks.go.helper.MaterialsMother; import com.thoughtworks.go.i18n.LocalizedMessage; import com.thoughtworks.go.plugin.access.packagematerial.PackageRepositoryExtension; import com.thoughtworks.go.plugin.access.scm.SCMExtension; import com.thoughtworks.go.plugin.access.scm.SCMPropertyConfiguration; import com.thoughtworks.go.plugin.access.scm.material.MaterialPollResult; import com.thoughtworks.go.plugin.access.scm.revision.SCMRevision; import com.thoughtworks.go.plugin.api.material.packagerepository.PackageConfiguration; import com.thoughtworks.go.plugin.api.material.packagerepository.PackageRevision; import com.thoughtworks.go.plugin.api.material.packagerepository.RepositoryConfiguration; import com.thoughtworks.go.security.GoCipher; import com.thoughtworks.go.server.domain.Username; import com.thoughtworks.go.server.persistence.MaterialRepository; import com.thoughtworks.go.server.service.result.LocalizedOperationResult; import com.thoughtworks.go.server.transaction.TransactionTemplate; import com.thoughtworks.go.server.util.Pagination; import com.thoughtworks.go.serverhealth.HealthStateScope; import com.thoughtworks.go.serverhealth.HealthStateType; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; import org.junit.experimental.theories.DataPoint; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.runner.RunWith; import org.mockito.Mock; import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import static com.thoughtworks.go.domain.packagerepository.PackageDefinitionMother.create; import static java.util.Arrays.asList; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; @RunWith(Theories.class) public class MaterialServiceTest { private static List MODIFICATIONS = new ArrayList<Modification>(); @Mock private MaterialRepository materialRepository; @Mock private GoConfigService goConfigService; @Mock private SecurityService securityService; @Mock private PackageRepositoryExtension packageRepositoryExtension; @Mock private SCMExtension scmExtension; @Mock private TransactionTemplate transactionTemplate; private MaterialService materialService; @Before public void setUp() { initMocks(this); materialService = new MaterialService(materialRepository, goConfigService, securityService, packageRepositoryExtension, scmExtension, transactionTemplate); } @Test public void shouldUnderstandIfMaterialHasModifications() { assertHasModifcation(new MaterialRevisions(new MaterialRevision(new HgMaterial("foo.com", null), new Modification(new Date(), "2", "MOCK_LABEL-12", null))), true); assertHasModifcation(new MaterialRevisions(), false); } @Test public void shouldNotBeAuthorizedToViewAPipeline() { Username pavan = Username.valueOf("pavan"); when(securityService.hasViewPermissionForPipeline(pavan, "pipeline")).thenReturn(false); LocalizedOperationResult operationResult = mock(LocalizedOperationResult.class); materialService.searchRevisions("pipeline", "sha", "search-string", pavan, operationResult); verify(operationResult).unauthorized(LocalizedMessage.cannotViewPipeline("pipeline"), HealthStateType.general(HealthStateScope.forPipeline("pipeline"))); } @Test public void shouldReturnTheRevisionsThatMatchTheGivenSearchString() { Username pavan = Username.valueOf("pavan"); when(securityService.hasViewPermissionForPipeline(pavan, "pipeline")).thenReturn(true); LocalizedOperationResult operationResult = mock(LocalizedOperationResult.class); MaterialConfig materialConfig = mock(MaterialConfig.class); when(goConfigService.materialForPipelineWithFingerprint("pipeline", "sha")).thenReturn(materialConfig); List<MatchedRevision> expected = asList(new MatchedRevision("23", "revision", "revision", "user", new DateTime(2009, 10, 10, 12, 0, 0, 0).toDate(), "comment")); when(materialRepository.findRevisionsMatching(materialConfig, "23")).thenReturn(expected); assertThat(materialService.searchRevisions("pipeline", "sha", "23", pavan, operationResult), is(expected)); } @Test public void shouldReturnNotFoundIfTheMaterialDoesNotBelongToTheGivenPipeline() { Username pavan = Username.valueOf("pavan"); when(securityService.hasViewPermissionForPipeline(pavan, "pipeline")).thenReturn(true); LocalizedOperationResult operationResult = mock(LocalizedOperationResult.class); when(goConfigService.materialForPipelineWithFingerprint("pipeline", "sha")).thenThrow(new RuntimeException("Not found")); materialService.searchRevisions("pipeline", "sha", "23", pavan, operationResult); verify(operationResult).notFound(LocalizedMessage.materialWithFingerPrintNotFound("pipeline", "sha"), HealthStateType.general(HealthStateScope.forPipeline("pipeline"))); } @DataPoint public static RequestDataPoints GIT_LATEST_MODIFICATIONS = new RequestDataPoints(new GitMaterial("url") { @Override public List<Modification> latestModification(File baseDir, SubprocessExecutionContext execCtx) { return (List<Modification>) MODIFICATIONS; } @Override public GitMaterial withShallowClone(boolean value) { return this; } @Override public List<Modification> modificationsSince(File baseDir, Revision revision, SubprocessExecutionContext execCtx) { return (List<Modification>) MODIFICATIONS; } }, GitMaterial.class); @DataPoint public static RequestDataPoints SVN_LATEST_MODIFICATIONS = new RequestDataPoints(new SvnMaterial("url", "username", "password", true) { @Override public List<Modification> latestModification(File baseDir, SubprocessExecutionContext execCtx) { return (List<Modification>) MODIFICATIONS; } @Override public List<Modification> modificationsSince(File baseDir, Revision revision, SubprocessExecutionContext execCtx) { return (List<Modification>) MODIFICATIONS; } }, SvnMaterial.class); @DataPoint public static RequestDataPoints HG_LATEST_MODIFICATIONS = new RequestDataPoints(new HgMaterial("url", null) { @Override public List<Modification> latestModification(File baseDir, SubprocessExecutionContext execCtx) { return (List<Modification>) MODIFICATIONS; } @Override public List<Modification> modificationsSince(File baseDir, Revision revision, SubprocessExecutionContext execCtx) { return (List<Modification>) MODIFICATIONS; } }, HgMaterial.class); @DataPoint public static RequestDataPoints TFS_LATEST_MODIFICATIONS = new RequestDataPoints(new TfsMaterial(mock(GoCipher.class)) { @Override public List<Modification> latestModification(File baseDir, SubprocessExecutionContext execCtx) { return (List<Modification>) MODIFICATIONS; } @Override public List<Modification> modificationsSince(File baseDir, Revision revision, SubprocessExecutionContext execCtx) { return (List<Modification>) MODIFICATIONS; } }, TfsMaterial.class); @DataPoint public static RequestDataPoints P4_LATEST_MODIFICATIONS = new RequestDataPoints(new P4Material("url", "view", "user") { @Override public List<Modification> latestModification(File baseDir, SubprocessExecutionContext execCtx) { return (List<Modification>) MODIFICATIONS; } @Override public List<Modification> modificationsSince(File baseDir, Revision revision, SubprocessExecutionContext execCtx) { return (List<Modification>) MODIFICATIONS; } }, P4Material.class); @DataPoint public static RequestDataPoints DEPENDENCY_LATEST_MODIFICATIONS = new RequestDataPoints(new DependencyMaterial(new CaseInsensitiveString("p1"), new CaseInsensitiveString("s1")) { @Override public List<Modification> latestModification(File baseDir, SubprocessExecutionContext execCtx) { return (List<Modification>) MODIFICATIONS; } @Override public List<Modification> modificationsSince(File baseDir, Revision revision, SubprocessExecutionContext execCtx) { return (List<Modification>) MODIFICATIONS; } }, DependencyMaterial.class); @Theory public void shouldGetLatestModificationsForGivenMaterial(RequestDataPoints data) { MaterialService spy = spy(materialService); doReturn(data.klass).when(spy).getMaterialClass(data.material); List<Modification> actual = spy.latestModification(data.material, null, null); assertThat(actual, is(MODIFICATIONS)); } @Theory public void shouldGetModificationsSinceARevisionForGivenMaterial(RequestDataPoints data) { Revision revision = mock(Revision.class); MaterialService spy = spy(materialService); doReturn(data.klass).when(spy).getMaterialClass(data.material); List<Modification> actual = spy.modificationsSince(data.material, null, revision, null); assertThat(actual, is(MODIFICATIONS)); } @Test public void shouldThrowExceptionWhenPollerForMaterialNotFound() { try { materialService.latestModification(mock(Material.class), null, null); fail("Should have thrown up"); } catch (RuntimeException e) { assertThat(e.getMessage(), is("unknown material type null")); } } @Test public void shouldGetLatestModificationForPackageMaterial() { PackageMaterial material = new PackageMaterial(); PackageDefinition packageDefinition = create("id", "package", new Configuration(), PackageRepositoryMother.create("id", "name", "plugin-id", "plugin-version", new Configuration())); material.setPackageDefinition(packageDefinition); when(packageRepositoryExtension.getLatestRevision(eq("plugin-id"), any(PackageConfiguration.class), any(RepositoryConfiguration.class))).thenReturn(new PackageRevision("blah-123", new Date(), "user")); List<Modification> modifications = materialService.latestModification(material, null, null); assertThat(modifications.get(0).getRevision(), is("blah-123")); } @Test public void shouldGetModificationSinceAGivenRevision() { PackageMaterial material = new PackageMaterial(); PackageDefinition packageDefinition = create("id", "package", new Configuration(), PackageRepositoryMother.create("id", "name", "plugin-id", "plugin-version", new Configuration())); material.setPackageDefinition(packageDefinition); when(packageRepositoryExtension.latestModificationSince(eq("plugin-id"), any(PackageConfiguration.class), any(RepositoryConfiguration.class), any(PackageRevision.class))).thenReturn(new PackageRevision("new-revision-456", new Date(), "user")); List<Modification> modifications = materialService.modificationsSince(material, null, new PackageMaterialRevision("revision-124", new Date()), null); assertThat(modifications.get(0).getRevision(), is("new-revision-456")); } @Test public void shouldGetLatestModification_PluggableSCMMaterial() { PluggableSCMMaterial pluggableSCMMaterial = MaterialsMother.pluggableSCMMaterial(); MaterialInstance materialInstance = pluggableSCMMaterial.createMaterialInstance(); when(materialRepository.findMaterialInstance(any(Material.class))).thenReturn(materialInstance); MaterialPollResult materialPollResult = new MaterialPollResult(null, new SCMRevision("blah-123", new Date(), "user", "comment", null, null)); when(scmExtension.getLatestRevision(any(String.class), any(SCMPropertyConfiguration.class), any(Map.class), any(String.class))).thenReturn(materialPollResult); List<Modification> modifications = materialService.latestModification(pluggableSCMMaterial, new File("/tmp/flyweight"), null); assertThat(modifications.get(0).getRevision(), is("blah-123")); } @Test public void shouldGetModificationSince_PluggableSCMMaterial() { PluggableSCMMaterial pluggableSCMMaterial = MaterialsMother.pluggableSCMMaterial(); MaterialInstance materialInstance = pluggableSCMMaterial.createMaterialInstance(); when(materialRepository.findMaterialInstance(any(Material.class))).thenReturn(materialInstance); MaterialPollResult materialPollResult = new MaterialPollResult(null, asList(new SCMRevision("new-revision-456", new Date(), "user", "comment", null, null))); when(scmExtension.latestModificationSince(any(String.class), any(SCMPropertyConfiguration.class), any(Map.class), any(String.class), any(SCMRevision.class))).thenReturn(materialPollResult); PluggableSCMMaterialRevision previouslyKnownRevision = new PluggableSCMMaterialRevision("revision-124", new Date()); List<Modification> modifications = materialService.modificationsSince(pluggableSCMMaterial, new File("/tmp/flyweight"), previouslyKnownRevision, null); assertThat(modifications.get(0).getRevision(), is("new-revision-456")); } @Test public void shouldDelegateToMaterialRepository_getTotalModificationsFor() { GitMaterialConfig materialConfig = new GitMaterialConfig("http://test.com"); GitMaterialInstance gitMaterialInstance = new GitMaterialInstance("http://test.com", null, null, "flyweight"); when(materialRepository.findMaterialInstance(materialConfig)).thenReturn(gitMaterialInstance); when(materialRepository.getTotalModificationsFor(gitMaterialInstance)).thenReturn(1L); Long totalCount = materialService.getTotalModificationsFor(materialConfig); assertThat(totalCount, is(1L)); } @Test public void shouldDelegateToMaterialRepository_getModificationsFor() { GitMaterialConfig materialConfig = new GitMaterialConfig("http://test.com"); GitMaterialInstance gitMaterialInstance = new GitMaterialInstance("http://test.com", null, null, "flyweight"); Pagination pagination = Pagination.pageStartingAt(0, 10, 10); Modifications modifications = new Modifications(); modifications.add(new Modification("user", "comment", "email", new Date(), "revision")); when(materialRepository.findMaterialInstance(materialConfig)).thenReturn(gitMaterialInstance); when(materialRepository.getModificationsFor(gitMaterialInstance, pagination)).thenReturn(modifications); Modifications gotModifications = materialService.getModificationsFor(materialConfig, pagination); assertThat(gotModifications, is(modifications)); } private void assertHasModifcation(MaterialRevisions materialRevisions, boolean b) { HgMaterial hgMaterial = new HgMaterial("foo.com", null); when(materialRepository.findLatestModification(hgMaterial)).thenReturn(materialRevisions); assertThat(materialService.hasModificationFor(hgMaterial), is(b)); } private static class RequestDataPoints<T extends Material> { final T material; final Class klass; public RequestDataPoints(T material, Class klass) { this.material = material; this.klass = klass; } } }