/*
* 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.materials;
import com.thoughtworks.go.config.CruiseConfig;
import com.thoughtworks.go.config.materials.dependency.DependencyMaterial;
import com.thoughtworks.go.config.materials.dependency.DependencyMaterialConfig;
import com.thoughtworks.go.domain.Stage;
import com.thoughtworks.go.domain.materials.Material;
import com.thoughtworks.go.helper.MaterialsMother;
import com.thoughtworks.go.helper.StageMother;
import com.thoughtworks.go.listener.EntityConfigChangedListener;
import com.thoughtworks.go.server.service.GoConfigService;
import com.thoughtworks.go.server.service.MaterialConfigConverter;
import com.thoughtworks.go.serverhealth.ServerHealthService;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.mockito.Mockito.*;
public class DependencyMaterialUpdateNotifierTest {
private DependencyMaterialUpdateNotifier notifier;
private GoConfigService goConfigService;
private MaterialConfigConverter materialConfigConverter;
private MaterialUpdateService materialUpdateService;
private ServerHealthService serverHealthService;
private Material dependencyMaterial = MaterialsMother.dependencyMaterial();
@Before
public void setUp() throws Exception {
goConfigService = mock(GoConfigService.class);
materialConfigConverter = mock(MaterialConfigConverter.class);
materialUpdateService = mock(MaterialUpdateService.class);
serverHealthService = mock(ServerHealthService.class);
}
@Test
public void shouldListenToConfigChange() {
EntityConfigChangedListener entityConfigChangedListener = mock(EntityConfigChangedListener.class);
notifier = new DependencyMaterialUpdateNotifier(goConfigService, materialConfigConverter, materialUpdateService, serverHealthService);
notifier = spy(notifier);
stub(notifier.pipelineConfigChangedListener()).toReturn(entityConfigChangedListener);
notifier.initialize();
verify(goConfigService).register(notifier);
verify(goConfigService).register(entityConfigChangedListener);
}
@Test
public void shouldListenToMaterialUpdateMessage() {
notifier = new DependencyMaterialUpdateNotifier(goConfigService, materialConfigConverter, materialUpdateService, serverHealthService);
notifier.initialize();
verify(materialUpdateService).registerMaterialUpdateCompleteListener(notifier);
}
@Test
public void configLoadShouldScheduleAllDependencyMaterialsForUpdateThrough_onConfigChangeCallback() {
notifier = new DependencyMaterialUpdateNotifier(goConfigService, materialConfigConverter, materialUpdateService, serverHealthService);
notifier.initialize();
Set<DependencyMaterialConfig> schedulableMaterialConfigs = new HashSet<>(Arrays.asList((DependencyMaterialConfig) dependencyMaterial.config()));
when(goConfigService.getSchedulableDependencyMaterials()).thenReturn(schedulableMaterialConfigs);
when(materialConfigConverter.toMaterial(dependencyMaterial.config())).thenReturn(dependencyMaterial);
notifier.onConfigChange(mock(CruiseConfig.class));
verify(materialUpdateService).updateMaterial(dependencyMaterial);
}
@Test
public void shouldScheduleOnlyNewDepenedencyMaterialsForUpdateOnSubsequentConfigChanges() {
DependencyMaterial dependencyMaterialForP1S1 = MaterialsMother.dependencyMaterial("p1", "s1");
Set<DependencyMaterialConfig> schedulableMaterialConfigs = new HashSet<>(Arrays.asList((DependencyMaterialConfig) dependencyMaterialForP1S1.config()));
when(goConfigService.getSchedulableDependencyMaterials()).thenReturn(schedulableMaterialConfigs);
when(materialConfigConverter.toMaterial(dependencyMaterialForP1S1.config())).thenReturn(dependencyMaterialForP1S1);
notifier = new DependencyMaterialUpdateNotifier(goConfigService, materialConfigConverter, materialUpdateService, serverHealthService);
notifier.initialize();
DependencyMaterial dependencyMaterialForP2S2 = MaterialsMother.dependencyMaterial("p2", "s2");
schedulableMaterialConfigs = new HashSet<>(Arrays.asList((DependencyMaterialConfig) dependencyMaterialForP2S2.config()));
when(goConfigService.getSchedulableDependencyMaterials()).thenReturn(schedulableMaterialConfigs);
when(materialConfigConverter.toMaterial(dependencyMaterialForP2S2.config())).thenReturn(dependencyMaterialForP2S2);
notifier.onConfigChange(mock(CruiseConfig.class));
verify(materialUpdateService).updateMaterial(dependencyMaterialForP2S2);
}
@Test
public void shouldDoNothingIfNewDependencyMaterialsAreNotAddedOnConfigChange() {
Set<DependencyMaterialConfig> schedulableMaterialConfigs = new HashSet<>(Arrays.asList((DependencyMaterialConfig) dependencyMaterial.config()));
when(goConfigService.getSchedulableDependencyMaterials()).thenReturn(schedulableMaterialConfigs);
when(materialConfigConverter.toMaterial(dependencyMaterial.config())).thenReturn(dependencyMaterial);
notifier = new DependencyMaterialUpdateNotifier(goConfigService, materialConfigConverter, materialUpdateService, serverHealthService);
notifier.initialize();
notifier.onConfigChange(mock(CruiseConfig.class));
// updated only once during initialization
verify(materialUpdateService, times(1)).updateMaterial(dependencyMaterial);
}
@Test
public void shouldUpdateMaterialOnStageChange() {
Stage stage = StageMother.passedStageInstance("Stage1", "plan", "Pipeline1");
DependencyMaterial dependencyMaterial = MaterialsMother.dependencyMaterial(stage.getIdentifier().getPipelineName(), stage.getName());
Set<DependencyMaterialConfig> schedulableMaterialConfigs = new HashSet<>(Arrays.asList((DependencyMaterialConfig) dependencyMaterial.config()));
when(goConfigService.getSchedulableDependencyMaterials()).thenReturn(schedulableMaterialConfigs);
when(materialConfigConverter.toMaterial(dependencyMaterial.config())).thenReturn(dependencyMaterial);
notifier = new DependencyMaterialUpdateNotifier(goConfigService, materialConfigConverter, materialUpdateService, serverHealthService);
notifier.initialize();
notifier.stageStatusChanged(stage);
verify(materialUpdateService, times(2)).updateMaterial(dependencyMaterial);
}
@Test
public void shouldDoNothingOnStageChangeIfStageDoesNotRepresentADependencyMaterial() {
notifier = new DependencyMaterialUpdateNotifier(goConfigService, materialConfigConverter, materialUpdateService, serverHealthService);
notifier.initialize();
Stage pipeline2Stage2 = StageMother.passedStageInstance("Stage2", "plan", "Pipeline2");
notifier.stageStatusChanged(pipeline2Stage2);
verify(materialUpdateService, never()).updateMaterial(any(DependencyMaterial.class));
}
@Test
public void shouldNotUpdateMaterialUnlessStageResultIsPassedOnStageChange() {
Stage stage = StageMother.scheduledStage("Pipeline1", 1, "Stage1", 1, "buildName");
DependencyMaterial dependencyMaterial = MaterialsMother.dependencyMaterial(stage.getIdentifier().getPipelineName(), stage.getName());
Set<DependencyMaterialConfig> schedulableMaterialConfigs = new HashSet<>(Arrays.asList((DependencyMaterialConfig) dependencyMaterial.config()));
when(goConfigService.getSchedulableDependencyMaterials()).thenReturn(schedulableMaterialConfigs);
when(materialConfigConverter.toMaterial(dependencyMaterial.config())).thenReturn(dependencyMaterial);
notifier = new DependencyMaterialUpdateNotifier(goConfigService, materialConfigConverter, materialUpdateService, serverHealthService);
notifier.initialize();
notifier.stageStatusChanged(stage);
//updated only during initialization
verify(materialUpdateService, times(1)).updateMaterial(dependencyMaterial);
}
@Test
public void shouldRetryUpdatingMaterialsPreviouslyInProgress_OnMaterialUpdate() {
Stage stage = StageMother.passedStageInstance("Stage1", "plan", "Pipeline1");
DependencyMaterial dependencyMaterial = MaterialsMother.dependencyMaterial(stage.getIdentifier().getPipelineName(), stage.getName());
Set<DependencyMaterialConfig> schedulableMaterialConfigs = new HashSet<>(Arrays.asList((DependencyMaterialConfig) dependencyMaterial.config()));
when(goConfigService.getSchedulableDependencyMaterials()).thenReturn(schedulableMaterialConfigs);
when(materialConfigConverter.toMaterial(dependencyMaterial.config())).thenReturn(dependencyMaterial);
when(materialUpdateService.updateMaterial(dependencyMaterial)).thenReturn(true, false);
notifier = new DependencyMaterialUpdateNotifier(goConfigService, materialConfigConverter, materialUpdateService, serverHealthService);
notifier.initialize();
notifier.stageStatusChanged(stage);
notifier.onMaterialUpdate(dependencyMaterial);
verify(materialUpdateService, atMost(3)).updateMaterial(dependencyMaterial);
}
@Test
public void shouldRetryUpdatingMaterialsIfPreviouslyUpdatesFailed_OnMaterialUpdate() {
Stage stage = StageMother.passedStageInstance("Stage1", "plan", "Pipeline1");
DependencyMaterial dependencyMaterial = MaterialsMother.dependencyMaterial(stage.getIdentifier().getPipelineName(), stage.getName());
Set<DependencyMaterialConfig> schedulableMaterialConfigs = new HashSet<>(Arrays.asList((DependencyMaterialConfig) dependencyMaterial.config()));
when(goConfigService.getSchedulableDependencyMaterials()).thenReturn(schedulableMaterialConfigs);
when(materialConfigConverter.toMaterial(dependencyMaterial.config())).thenReturn(dependencyMaterial);
when(materialUpdateService.updateMaterial(dependencyMaterial)).thenThrow(new RuntimeException("some error")).thenReturn(true);
notifier = new DependencyMaterialUpdateNotifier(goConfigService, materialConfigConverter, materialUpdateService, serverHealthService);
notifier.initialize();
notifier.onMaterialUpdate(dependencyMaterial);
verify(materialUpdateService, atMost(2)).updateMaterial(dependencyMaterial);
}
@Test
public void shouldDoNothingIfMaterialUpdateIsDisabled() {
Stage stage = StageMother.passedStageInstance("Stage1", "plan", "Pipeline1");
DependencyMaterial dependencyMaterial = MaterialsMother.dependencyMaterial(stage.getIdentifier().getPipelineName(), stage.getName());
Set<DependencyMaterialConfig> schedulableMaterialConfigs = new HashSet<>(Arrays.asList((DependencyMaterialConfig) dependencyMaterial.config()));
when(goConfigService.getSchedulableDependencyMaterials()).thenReturn(schedulableMaterialConfigs);
when(materialConfigConverter.toMaterial(dependencyMaterial.config())).thenReturn(dependencyMaterial);
notifier = new DependencyMaterialUpdateNotifier(goConfigService, materialConfigConverter, materialUpdateService, serverHealthService);
notifier.disableUpdates();
notifier.initialize();
notifier.stageStatusChanged(stage);
verify(materialUpdateService, never()).updateMaterial(dependencyMaterial);
}
}