/* * Copyright 2016 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.domain.Pipeline; import com.thoughtworks.go.domain.PipelineState; import com.thoughtworks.go.domain.StageIdentifier; import com.thoughtworks.go.helper.PipelineMother; import com.thoughtworks.go.listener.ConfigChangedListener; import com.thoughtworks.go.listener.EntityConfigChangedListener; import com.thoughtworks.go.server.dao.PipelineStateDao; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import java.util.List; import static java.util.Arrays.asList; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.*; public class PipelineLockServiceTest { private PipelineLockService pipelineLockService; private PipelineStateDao pipelineStateDao; private GoConfigService goConfigService; @Before public void setup() throws Exception { pipelineStateDao = mock(PipelineStateDao.class); goConfigService = mock(GoConfigService.class); pipelineLockService = new PipelineLockService(goConfigService, pipelineStateDao); pipelineLockService.initialize(); } @Test public void shouldLockPipeline() throws Exception { when(goConfigService.isLockable("mingle")).thenReturn(true); Pipeline pipeline = PipelineMother.firstStageBuildingAndSecondStageScheduled("mingle", asList("dev", "ft"), asList("test")); pipelineLockService.lockIfNeeded(pipeline); verify(pipelineStateDao).lockPipeline(pipeline); } @Test public void shouldNotLockPipelineWhenNotLockable() throws Exception { when(goConfigService.isLockable("mingle")).thenReturn(false); Pipeline pipeline = PipelineMother.firstStageBuildingAndSecondStageScheduled("mingle", asList("dev", "ft"), asList("test")); pipelineLockService.lockIfNeeded(pipeline); verify(pipelineStateDao, never()).lockPipeline(pipeline); } @Test public void shouldKnowIfPipelineIsLocked() throws Exception { String pipelineName = "mingle"; PipelineState pipelineState = new PipelineState(pipelineName, new StageIdentifier(pipelineName, 1, "1", "stage", "1")); pipelineState.lock(1); when(pipelineStateDao.pipelineStateFor(pipelineName)).thenReturn(pipelineState); assertThat(pipelineLockService.isLocked(pipelineName), is(true)); assertThat(pipelineLockService.isLocked("twist"), is(false)); } @Test public void shouldUnlockPipelineIrrespectiveOfItBeingLockable() throws Exception { pipelineLockService.unlock("mingle"); verify(pipelineStateDao).unlockPipeline("mingle"); } @Test public void shouldAllowStageFromCurrentPipelineToBeScheduled() throws Exception { Pipeline pipeline = PipelineMother.firstStageBuildingAndSecondStageScheduled("mingle", asList("dev", "ft"), asList("test")); when(pipelineStateDao.pipelineStateFor("mingle")).thenReturn(new PipelineState(pipeline.getName(), pipeline.getStages().get(0).getIdentifier())); when(goConfigService.isLockable(pipeline.getName())).thenReturn(true); pipelineLockService.lockIfNeeded(pipeline); assertThat(pipelineLockService.canScheduleStageInPipeline(pipeline.getIdentifier()), is(true)); } @Test public void shouldNotAllowStageFromLockedPipelineToBeScheduled() throws Exception { Pipeline pipeline = PipelineMother.firstStageBuildingAndSecondStageScheduled("mingle", asList("dev", "ft"), asList("test")); PipelineState pipelineState = new PipelineState(pipeline.getName(), new StageIdentifier(pipeline.getName(), 9999, "1.2.9999", "stage", "1")); pipelineState.lock(1); when(pipelineStateDao.pipelineStateFor("mingle")).thenReturn(pipelineState); when(goConfigService.isLockable(pipeline.getName())).thenReturn(true); pipelineLockService.lockIfNeeded(pipeline); assertThat(pipelineLockService.canScheduleStageInPipeline(pipeline.getIdentifier()), is(false)); } @Test public void shouldAllowStageFromAnotherPipelineIfThePipelineIsNotLockabler() throws Exception { Pipeline pipeline = PipelineMother.firstStageBuildingAndSecondStageScheduled("mingle", asList("dev", "ft"), asList("test")); when(pipelineStateDao.pipelineStateFor("mingle")).thenReturn(new PipelineState(pipeline.getName(), new StageIdentifier(pipeline.getName(), 9999, "1.2.9999", "stage", "1"))); when(goConfigService.isLockable(pipeline.getName())).thenReturn(false); pipelineLockService.lockIfNeeded(pipeline); assertThat(pipelineLockService.canScheduleStageInPipeline(pipeline.getIdentifier()), is(true)); } @Test public void shouldAllowStageFromAnotherPipelineIfThePipelineIsLockable() throws Exception { Pipeline pipeline = PipelineMother.firstStageBuildingAndSecondStageScheduled("mingle", asList("dev", "ft"), asList("test")); when(pipelineStateDao.pipelineStateFor(pipeline.getName())).thenReturn(null); when(goConfigService.isLockable(pipeline.getName())).thenReturn(true); pipelineLockService.lockIfNeeded(pipeline); assertThat(pipelineLockService.canScheduleStageInPipeline(pipeline.getIdentifier()), is(true)); } @Test public void shouldUnlockAnyCurrentlyLockedPipelinesThatAreNoLongerLockable() throws Exception { CruiseConfig cruiseConfig = mock(BasicCruiseConfig.class); when(pipelineStateDao.lockedPipelines()).thenReturn(asList("mingle", "twist")); when(cruiseConfig.hasPipelineNamed(new CaseInsensitiveString("mingle"))).thenReturn(true); when(cruiseConfig.isPipelineLocked("mingle")).thenReturn(true); when(cruiseConfig.hasPipelineNamed(new CaseInsensitiveString("twist"))).thenReturn(true); when(cruiseConfig.isPipelineLocked("twist")).thenReturn(false); pipelineLockService.onConfigChange(cruiseConfig); verify(pipelineStateDao, never()).unlockPipeline("mingle"); verify(pipelineStateDao).unlockPipeline("twist"); } @Test public void shouldUnlockAnyCurrentlyLockedPipelinesThatNoLongerExist() throws Exception { CruiseConfig cruiseConfig = mock(BasicCruiseConfig.class); when(pipelineStateDao.lockedPipelines()).thenReturn(asList("mingle", "twist")); when(cruiseConfig.hasPipelineNamed(new CaseInsensitiveString("mingle"))).thenReturn(false); when(cruiseConfig.isPipelineLocked("mingle")).thenThrow(new PipelineNotFoundException("mingle not there")); when(cruiseConfig.hasPipelineNamed(new CaseInsensitiveString("twist"))).thenReturn(true); when(cruiseConfig.isPipelineLocked("twist")).thenReturn(false); pipelineLockService.onConfigChange(cruiseConfig); verify(pipelineStateDao).unlockPipeline("mingle"); verify(pipelineStateDao).unlockPipeline("twist"); } private EntityConfigChangedListener<PipelineConfig> getPipelineConfigEntityConfigChangedListener() { ArgumentCaptor<ConfigChangedListener> captor = ArgumentCaptor.forClass(ConfigChangedListener.class); doNothing().when(goConfigService).register(captor.capture()); pipelineLockService.initialize(); List<ConfigChangedListener> listeners = captor.getAllValues(); assertThat(listeners.get(1) instanceof EntityConfigChangedListener, is(true)); EntityConfigChangedListener<PipelineConfig> pipelineConfigChangeListener = (EntityConfigChangedListener<PipelineConfig>) listeners.get(1); return pipelineConfigChangeListener; } @Test public void shouldUnlockCurrentlyLockedPipelineThatIsNoLongerLockableWhenPipelineConfigChanges() throws Exception { EntityConfigChangedListener<PipelineConfig> changedListener = getPipelineConfigEntityConfigChangedListener(); PipelineConfig pipelineConfig = mock(PipelineConfig.class); when(pipelineStateDao.lockedPipelines()).thenReturn(asList("locked_pipeline", "other_pipeline")); when(pipelineConfig.isLock()).thenReturn(false); when(pipelineConfig.name()).thenReturn(new CaseInsensitiveString("locked_pipeline")); changedListener.onEntityConfigChange(pipelineConfig); verify(pipelineStateDao, never()).unlockPipeline("other_pipeline"); verify(pipelineStateDao).unlockPipeline("locked_pipeline"); } @Test public void shouldNotUnlockCurrentlyLockedPipelineThatContinuesToBeLockableWhenPipelineConfigChanges() throws Exception { EntityConfigChangedListener<PipelineConfig> changedListener = getPipelineConfigEntityConfigChangedListener(); PipelineConfig pipelineConfig = mock(PipelineConfig.class); when(pipelineStateDao.lockedPipelines()).thenReturn(asList("locked_pipeline")); when(pipelineConfig.isLock()).thenReturn(true); when(pipelineConfig.name()).thenReturn(new CaseInsensitiveString("locked_pipeline")); changedListener.onEntityConfigChange(pipelineConfig); verify(pipelineStateDao, never()).unlockPipeline("locked_pipeline"); } @Test public void shouldRegisterItselfAsAConfigChangeListener() throws Exception { GoConfigService mockGoConfigService = mock(GoConfigService.class); PipelineLockService service = new PipelineLockService(mockGoConfigService, pipelineStateDao); service.initialize(); verify(mockGoConfigService).register(service); } }