/* * Copyright 2017-2018 the original author or authors. * * 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 * * https://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 org.springframework.cloud.skipper.server.statemachine; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.cloud.skipper.domain.DeleteProperties; import org.springframework.cloud.skipper.domain.Info; import org.springframework.cloud.skipper.domain.InstallProperties; import org.springframework.cloud.skipper.domain.InstallRequest; import org.springframework.cloud.skipper.domain.Manifest; import org.springframework.cloud.skipper.domain.Package; import org.springframework.cloud.skipper.domain.PackageMetadata; import org.springframework.cloud.skipper.domain.Release; import org.springframework.cloud.skipper.domain.ScaleRequest; import org.springframework.cloud.skipper.domain.Status; import org.springframework.cloud.skipper.domain.StatusCode; import org.springframework.cloud.skipper.domain.UpgradeRequest; import org.springframework.cloud.skipper.domain.deployer.ReleaseDifference; import org.springframework.cloud.skipper.server.deployer.ReleaseAnalysisReport; import org.springframework.cloud.skipper.server.deployer.ReleaseManager; import org.springframework.cloud.skipper.server.deployer.strategies.DeployAppStep; import org.springframework.cloud.skipper.server.deployer.strategies.HandleHealthCheckStep; import org.springframework.cloud.skipper.server.deployer.strategies.HealthCheckProperties; import org.springframework.cloud.skipper.server.deployer.strategies.HealthCheckStep; import org.springframework.cloud.skipper.server.deployer.strategies.UpgradeStrategy; import org.springframework.cloud.skipper.server.deployer.strategies.UpgradeStrategyFactory; import org.springframework.cloud.skipper.server.repository.jpa.ReleaseRepository; import org.springframework.cloud.skipper.server.service.PackageService; import org.springframework.cloud.skipper.server.service.ReleaseReportService; import org.springframework.cloud.skipper.server.service.ReleaseService; import org.springframework.cloud.skipper.server.statemachine.SkipperStateMachineService.SkipperEventHeaders; import org.springframework.cloud.skipper.server.statemachine.SkipperStateMachineService.SkipperEvents; import org.springframework.cloud.skipper.server.statemachine.SkipperStateMachineService.SkipperStates; import org.springframework.cloud.skipper.server.statemachine.StateMachineTests.TestConfig; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.core.task.TaskExecutor; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.StateMachineContext; import org.springframework.statemachine.config.StateMachineFactory; import org.springframework.statemachine.persist.StateMachineRuntimePersister; import org.springframework.statemachine.service.StateMachineService; import org.springframework.statemachine.support.DefaultExtendedState; import org.springframework.statemachine.support.DefaultStateMachineContext; import org.springframework.statemachine.test.StateMachineTestPlan; import org.springframework.statemachine.test.StateMachineTestPlanBuilder; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Matchers.any; import static org.mockito.Mockito.never; /** * Generic tests for skipper statemachine logic. In these tests we simply * want to test machine logic meaning we control actions by using * mocks for classes actions are using. * * @author Janne Valkealahti * */ @SuppressWarnings("unchecked") @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = TestConfig.class) @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) public class StateMachineTests { @Autowired private ApplicationContext context; @MockBean private StateMachineRuntimePersister<SkipperStates, SkipperEvents, String> stateMachineRuntimePersister; @MockBean private ReleaseManager releaseManager; @MockBean private PackageService packageService; @MockBean private ReleaseReportService releaseReportService; @MockBean private UpgradeStrategy upgradeStrategy; @MockBean private UpgradeStrategyFactory upgradeStrategyFactory; @MockBean private DeployAppStep deployAppStep; @MockBean private HealthCheckStep healthCheckStep; @MockBean private HandleHealthCheckStep handleHealthCheckStep; @MockBean private ReleaseService releaseService; @MockBean private ReleaseRepository releaseRepository; @MockBean private HealthCheckProperties healthCheckProperties; @SpyBean private UpgradeCancelAction upgradeCancelAction; @SpyBean private ErrorAction errorAction; @Test public void testFactory() { StateMachineFactory<SkipperStates, SkipperEvents> factory = context.getBean(StateMachineFactory.class); assertThat(factory).isNotNull(); StateMachine<SkipperStates, SkipperEvents> stateMachine = factory.getStateMachine("testFactory"); assertThat(stateMachine).isNotNull(); } @Test public void testSimpleInstallShouldNotError() throws Exception { Mockito.when(packageService.downloadPackage(any())) .thenReturn(new org.springframework.cloud.skipper.domain.Package()); Mockito.when(releaseService.install(any(), any())).thenReturn(new Release()); Message<SkipperEvents> message = MessageBuilder .withPayload(SkipperEvents.INSTALL) .setHeader(SkipperEventHeaders.PACKAGE_METADATA, new PackageMetadata()) .setHeader(SkipperEventHeaders.INSTALL_PROPERTIES, new InstallProperties()) .setHeader(SkipperEventHeaders.VERSION, 1) .build(); StateMachineFactory<SkipperStates, SkipperEvents> factory = context.getBean(StateMachineFactory.class); StateMachine<SkipperStates, SkipperEvents> stateMachine = factory.getStateMachine("testInstall"); StateMachineTestPlan<SkipperStates, SkipperEvents> plan = StateMachineTestPlanBuilder.<SkipperStates, SkipperEvents>builder() .defaultAwaitTime(10) .stateMachine(stateMachine) .step() .expectStateMachineStarted(1) .expectStates(SkipperStates.INITIAL) .and() .step() .sendEvent(message) .expectStates(SkipperStates.INITIAL) .expectStateChanged(3) .expectStateEntered(SkipperStates.INSTALL, SkipperStates.INSTALL_INSTALL, SkipperStates.INITIAL) .and() .build(); plan.test(); Mockito.verify(errorAction, never()).execute(any()); } @Test public void testRestoreFromInstallUsingInstallRequest() throws Exception { Mockito.when(releaseService.install(any(InstallRequest.class))).thenReturn(new Release()); DefaultExtendedState extendedState = new DefaultExtendedState(); extendedState.getVariables().put(SkipperEventHeaders.INSTALL_REQUEST, new InstallRequest()); StateMachineContext<SkipperStates, SkipperEvents> stateMachineContext = new DefaultStateMachineContext<>( SkipperStates.INSTALL, SkipperEvents.INSTALL, null, extendedState); Mockito.when(stateMachineRuntimePersister.read(any())).thenReturn(stateMachineContext); StateMachineService<SkipperStates, SkipperEvents> stateMachineService = context.getBean(StateMachineService.class); StateMachine<SkipperStates, SkipperEvents> stateMachine = stateMachineService .acquireStateMachine("testRestoreFromInstallUsingInstallRequest", false); StateMachineTestPlan<SkipperStates, SkipperEvents> plan = StateMachineTestPlanBuilder.<SkipperStates, SkipperEvents>builder() .defaultAwaitTime(10) .stateMachine(stateMachine) .step() .expectStates(SkipperStates.INITIAL) .expectStateChanged(2) .and() .build(); plan.test(); Mockito.verify(upgradeCancelAction, never()).execute(any()); Mockito.verify(errorAction, never()).execute(any()); } @Test public void testRestoreFromUpgradeUsingUpgradeRequest() throws Exception { Manifest manifest = new Manifest(); Release release = new Release(); release.setManifest(manifest); Mockito.when(releaseReportService.createReport(any(), any(), any(boolean.class))).thenReturn(new ReleaseAnalysisReport( new ArrayList<>(), new ReleaseDifference(), release, release)); Mockito.when(upgradeStrategy.checkStatus(any())) .thenReturn(true); Mockito.when(upgradeStrategyFactory.getUpgradeStrategy(any())).thenReturn(upgradeStrategy); DefaultExtendedState extendedState = new DefaultExtendedState(); extendedState.getVariables().put(SkipperEventHeaders.UPGRADE_REQUEST, new UpgradeRequest()); StateMachineContext<SkipperStates, SkipperEvents> stateMachineContext = new DefaultStateMachineContext<>( SkipperStates.UPGRADE, SkipperEvents.UPGRADE, null, extendedState); Mockito.when(stateMachineRuntimePersister.read(any())).thenReturn(stateMachineContext); StateMachineService<SkipperStates, SkipperEvents> stateMachineService = context.getBean(StateMachineService.class); StateMachine<SkipperStates, SkipperEvents> stateMachine = stateMachineService .acquireStateMachine("testRestoreFromUpgradeUsingUpgradeRequest", false); StateMachineTestPlan<SkipperStates, SkipperEvents> plan = StateMachineTestPlanBuilder.<SkipperStates, SkipperEvents>builder() .defaultAwaitTime(10) .stateMachine(stateMachine) .step() .expectStates(SkipperStates.INITIAL) .expectStateChanged(8) .and() .build(); plan.test(); Mockito.verify(upgradeCancelAction, never()).execute(any()); Mockito.verify(errorAction, never()).execute(any()); } @Test public void testRestoreFromInstallUsingInstallProperties() throws Exception { Mockito.when(releaseService.install(any(), any(InstallProperties.class))).thenReturn(new Release()); DefaultExtendedState extendedState = new DefaultExtendedState(); extendedState.getVariables().put(SkipperEventHeaders.INSTALL_PROPERTIES, new InstallProperties()); StateMachineContext<SkipperStates, SkipperEvents> stateMachineContext = new DefaultStateMachineContext<>( SkipperStates.INSTALL, SkipperEvents.INSTALL, null, extendedState); Mockito.when(stateMachineRuntimePersister.read(any())).thenReturn(stateMachineContext); StateMachineService<SkipperStates, SkipperEvents> stateMachineService = context.getBean(StateMachineService.class); StateMachine<SkipperStates, SkipperEvents> stateMachine = stateMachineService .acquireStateMachine("testRestoreFromInstallUsingInstallProperties", false); StateMachineTestPlan<SkipperStates, SkipperEvents> plan = StateMachineTestPlanBuilder.<SkipperStates, SkipperEvents>builder() .defaultAwaitTime(10) .stateMachine(stateMachine) .step() .expectStates(SkipperStates.INITIAL) .expectStateChanged(2) .and() .build(); plan.test(); Mockito.verify(upgradeCancelAction, never()).execute(any()); Mockito.verify(errorAction, never()).execute(any()); } @Test public void testSimpleUpgradeShouldNotError() throws Exception { Manifest manifest = new Manifest(); Release release = new Release(); release.setManifest(manifest); Mockito.when(releaseReportService.createReport(any(), any(), any(boolean.class))).thenReturn(new ReleaseAnalysisReport( new ArrayList<>(), new ReleaseDifference(), release, release)); Mockito.when(upgradeStrategy.checkStatus(any())) .thenReturn(true); Mockito.when(upgradeStrategyFactory.getUpgradeStrategy(any())).thenReturn(upgradeStrategy); UpgradeRequest upgradeRequest = new UpgradeRequest(); Message<SkipperEvents> message1 = MessageBuilder .withPayload(SkipperEvents.UPGRADE) .setHeader(SkipperEventHeaders.UPGRADE_REQUEST, upgradeRequest) .build(); StateMachineFactory<SkipperStates, SkipperEvents> factory = context.getBean(StateMachineFactory.class); StateMachine<SkipperStates, SkipperEvents> stateMachine = factory.getStateMachine("testSimpleUpgradeShouldNotError"); StateMachineTestPlan<SkipperStates, SkipperEvents> plan = StateMachineTestPlanBuilder.<SkipperStates, SkipperEvents>builder() .defaultAwaitTime(10) .stateMachine(stateMachine) .step() .expectStateMachineStarted(1) .expectStates(SkipperStates.INITIAL) .and() .step() .sendEvent(message1) .expectStates(SkipperStates.INITIAL) .expectStateChanged(9) .and() .build(); plan.test(); Mockito.verify(upgradeCancelAction, never()).execute(any()); Mockito.verify(errorAction, never()).execute(any()); } @Test public void testUpgradeFailsNewAppFailToDeploy() throws Exception { Manifest manifest = new Manifest(); Release release = new Release(); release.setManifest(manifest); Mockito.when(releaseReportService.createReport(any(), any(), any(boolean.class))).thenReturn(new ReleaseAnalysisReport( new ArrayList<>(), new ReleaseDifference(), release, release)); Mockito.when(upgradeStrategy.checkStatus(any())) .thenReturn(false); Mockito.when(upgradeStrategyFactory.getUpgradeStrategy(any())).thenReturn(upgradeStrategy); UpgradeRequest upgradeRequest = new UpgradeRequest(); // timeout 0 for things to fail immediately Message<SkipperEvents> message1 = MessageBuilder .withPayload(SkipperEvents.UPGRADE) .setHeader(SkipperEventHeaders.UPGRADE_REQUEST, upgradeRequest) .setHeader(SkipperEventHeaders.UPGRADE_TIMEOUT, 0L) .build(); StateMachineFactory<SkipperStates, SkipperEvents> factory = context.getBean(StateMachineFactory.class); StateMachine<SkipperStates, SkipperEvents> stateMachine = factory.getStateMachine("testUpgradeFailsNewAppFailToDeploy"); StateMachineTestPlan<SkipperStates, SkipperEvents> plan = StateMachineTestPlanBuilder.<SkipperStates, SkipperEvents>builder() .defaultAwaitTime(10) .stateMachine(stateMachine) .step() .expectStateMachineStarted(1) .expectStates(SkipperStates.INITIAL) .and() .step() .sendEvent(message1) .expectStates(SkipperStates.INITIAL) .expectStateChanged(9) .expectStateEntered(SkipperStates.UPGRADE, SkipperStates.UPGRADE_START, SkipperStates.UPGRADE_DEPLOY_TARGET_APPS, SkipperStates.UPGRADE_WAIT_TARGET_APPS, SkipperStates.UPGRADE_CHECK_TARGET_APPS, SkipperStates.UPGRADE_WAIT_TARGET_APPS, SkipperStates.UPGRADE_DEPLOY_TARGET_APPS_FAILED, SkipperStates.UPGRADE_CANCEL, SkipperStates.INITIAL) .and() .build(); plan.test(); Mockito.verify(upgradeCancelAction).execute(any()); Mockito.verify(errorAction, never()).execute(any()); } @Ignore("Flaky, what it tests not actually used yet") @Test public void testUpgradeCancelWhileCheckingApps() throws Exception { Manifest manifest = new Manifest(); Release release = new Release(); release.setManifest(manifest); Mockito.when(releaseReportService.createReport(any(), any(), any(boolean.class))).thenReturn(new ReleaseAnalysisReport( new ArrayList<>(), new ReleaseDifference(), release, release)); Mockito.when(upgradeStrategy.checkStatus(any())) .thenReturn(false); Mockito.when(upgradeStrategyFactory.getUpgradeStrategy(any())).thenReturn(upgradeStrategy); UpgradeRequest upgradeRequest = new UpgradeRequest(); // timeout 60s giving time to try cancel Message<SkipperEvents> message1 = MessageBuilder .withPayload(SkipperEvents.UPGRADE) .setHeader(SkipperEventHeaders.UPGRADE_REQUEST, upgradeRequest) .setHeader(SkipperEventHeaders.UPGRADE_TIMEOUT, 60000L) .build(); Message<SkipperEvents> message2 = MessageBuilder .withPayload(SkipperEvents.UPGRADE_CANCEL) .build(); StateMachineFactory<SkipperStates, SkipperEvents> factory = context.getBean(StateMachineFactory.class); StateMachine<SkipperStates, SkipperEvents> stateMachine = factory.getStateMachine("testUpgradeCancelWhileCheckingApps"); StateMachineTestPlan<SkipperStates, SkipperEvents> plan = StateMachineTestPlanBuilder.<SkipperStates, SkipperEvents>builder() .defaultAwaitTime(10) .stateMachine(stateMachine) .step() .expectStateMachineStarted(1) .expectStates(SkipperStates.INITIAL) .and() .step() .sendEvent(message1) .expectStateChanged(4) .expectStateEntered(SkipperStates.UPGRADE, SkipperStates.UPGRADE_START, SkipperStates.UPGRADE_DEPLOY_TARGET_APPS, SkipperStates.UPGRADE_WAIT_TARGET_APPS) .and() .step() .sendEvent(message2) .expectStateChanged(2) // for now need to do a trick to wait this later //.expectStateEntered(SkipperStates.UPGRADE_CANCEL, // SkipperStates.INITIAL) .and() .build(); plan.test(); SkipperStates result = null; for (int i = 0; i < 10; i++) { SkipperStates s = stateMachine.getState().getId(); if (s == SkipperStates.INITIAL) { result = s; break; } Thread.sleep(200); } assertThat(result).isEqualTo(SkipperStates.INITIAL); Mockito.verify(upgradeCancelAction).execute(any()); Mockito.verify(errorAction, never()).execute(any()); } @Test public void testRollbackInstall() throws Exception { Release release = new Release(); Status status = new Status(); status.setStatusCode(StatusCode.DELETED); Info info = Info.createNewInfo("xxx"); info.setStatus(status); release.setPkg(createPkg()); release.setInfo(info); Mockito.when(releaseRepository.findLatestReleaseForUpdate(any())).thenReturn(release); Mockito.when(releaseRepository.findReleaseToRollback(any())).thenReturn(release); Mockito.when(releaseService.install(any(InstallRequest.class))).thenReturn(release); Message<SkipperEvents> message1 = MessageBuilder .withPayload(SkipperEvents.ROLLBACK) .setHeader(SkipperEventHeaders.RELEASE_NAME, "testRollbackInstall") .setHeader(SkipperEventHeaders.ROLLBACK_VERSION, 0) .build(); StateMachineFactory<SkipperStates, SkipperEvents> factory = context.getBean(StateMachineFactory.class); StateMachine<SkipperStates, SkipperEvents> stateMachine = factory.getStateMachine("testRollbackInstall"); StateMachineTestPlan<SkipperStates, SkipperEvents> plan = StateMachineTestPlanBuilder.<SkipperStates, SkipperEvents>builder() .defaultAwaitTime(10) .stateMachine(stateMachine) .step() .expectStateMachineStarted(1) .expectStates(SkipperStates.INITIAL) .and() .step() .sendEvent(message1) .expectStates(SkipperStates.INITIAL) .expectStateChanged(5) .expectStateEntered(SkipperStates.ROLLBACK, SkipperStates.ROLLBACK_START, SkipperStates.INSTALL, SkipperStates.INSTALL_INSTALL, SkipperStates.INITIAL) .and() .build(); plan.test(); Mockito.verify(errorAction, never()).execute(any()); } @Test public void testDeleteSucceed() throws Exception { Mockito.when(releaseService.delete(any(String.class), any(boolean.class))).thenReturn(new Release()); DeleteProperties deleteProperties = new DeleteProperties(); Message<SkipperEvents> message1 = MessageBuilder .withPayload(SkipperEvents.DELETE) .setHeader(SkipperEventHeaders.RELEASE_NAME, "testDeleteSucceed") .setHeader(SkipperEventHeaders.RELEASE_DELETE_PROPERTIES, deleteProperties) .build(); StateMachineFactory<SkipperStates, SkipperEvents> factory = context.getBean(StateMachineFactory.class); StateMachine<SkipperStates, SkipperEvents> stateMachine = factory.getStateMachine("testDeleteSucceed"); StateMachineTestPlan<SkipperStates, SkipperEvents> plan = StateMachineTestPlanBuilder.<SkipperStates, SkipperEvents>builder() .defaultAwaitTime(10) .stateMachine(stateMachine) .step() .expectStateMachineStarted(1) .expectStates(SkipperStates.INITIAL) .and() .step() .sendEvent(message1) .expectStates(SkipperStates.INITIAL) .expectStateChanged(3) .expectStateEntered(SkipperStates.DELETE, SkipperStates.DELETE_DELETE, SkipperStates.INITIAL) .and() .build(); plan.test(); Mockito.verify(errorAction, never()).execute(any()); } @Test public void testScaleSucceed() throws Exception { Mockito.when(releaseService.scale(any(String.class), any(ScaleRequest.class))).thenReturn(new Release()); ScaleRequest scaleRequest = new ScaleRequest(); Message<SkipperEvents> message1 = MessageBuilder .withPayload(SkipperEvents.SCALE) .setHeader(SkipperEventHeaders.RELEASE_NAME, "testScaleSucceed") .setHeader(SkipperEventHeaders.SCALE_REQUEST, scaleRequest) .build(); StateMachineFactory<SkipperStates, SkipperEvents> factory = context.getBean(StateMachineFactory.class); StateMachine<SkipperStates, SkipperEvents> stateMachine = factory.getStateMachine("testScaleSucceed"); StateMachineTestPlan<SkipperStates, SkipperEvents> plan = StateMachineTestPlanBuilder.<SkipperStates, SkipperEvents>builder() .defaultAwaitTime(10) .stateMachine(stateMachine) .step() .expectStateMachineStarted(1) .expectStates(SkipperStates.INITIAL) .and() .step() .sendEvent(message1) .expectStates(SkipperStates.INITIAL) .expectStateChanged(3) .expectStateEntered(SkipperStates.SCALE, SkipperStates.SCALE_SCALE, SkipperStates.INITIAL) .and() .build(); plan.test(); Mockito.verify(errorAction, never()).execute(any()); } @Test public void testRestoreFromDeleteUsingDeleteProperties() throws Exception { Mockito.when(releaseService.delete(nullable(String.class), any(boolean.class))).thenReturn(new Release()); DeleteProperties deleteProperties = new DeleteProperties(); DefaultExtendedState extendedState = new DefaultExtendedState(); extendedState.getVariables().put(SkipperEventHeaders.RELEASE_DELETE_PROPERTIES, deleteProperties); StateMachineContext<SkipperStates, SkipperEvents> stateMachineContext = new DefaultStateMachineContext<>( SkipperStates.DELETE, SkipperEvents.DELETE, null, extendedState); Mockito.when(stateMachineRuntimePersister.read(any())).thenReturn(stateMachineContext); StateMachineService<SkipperStates, SkipperEvents> stateMachineService = context.getBean(StateMachineService.class); StateMachine<SkipperStates, SkipperEvents> stateMachine = stateMachineService .acquireStateMachine("testRestoreFromDeleteUsingDeleteProperties", false); StateMachineTestPlan<SkipperStates, SkipperEvents> plan = StateMachineTestPlanBuilder.<SkipperStates, SkipperEvents>builder() .defaultAwaitTime(10) .stateMachine(stateMachine) .step() .expectStates(SkipperStates.INITIAL) .expectStateChanged(2) .and() .build(); plan.test(); Mockito.verify(upgradeCancelAction, never()).execute(any()); Mockito.verify(errorAction, never()).execute(any()); } private Package createPkg() { PackageMetadata packageMetadata1 = new PackageMetadata(); packageMetadata1.setApiVersion("skipper.spring.io/v1"); packageMetadata1.setKind("SpringCloudDeployerApplication"); setId(PackageMetadata.class, packageMetadata1, "id", 1L); packageMetadata1.setRepositoryId(1L); packageMetadata1.setName("package1"); packageMetadata1.setVersion("1.0.0"); Package pkg1 = new Package(); pkg1.setMetadata(packageMetadata1); return pkg1; } private static void setId(Class<?> clazz, Object instance, String fieldName, Object value) { try { Field field = ReflectionUtils.findField(clazz, fieldName); field.setAccessible(true); int modifiers = field.getModifiers(); Field modifierField = field.getClass().getDeclaredField("modifiers"); modifiers = modifiers & ~Modifier.FINAL; modifierField.setAccessible(true); modifierField.setInt(field, modifiers); ReflectionUtils.setField(field, instance, value); } catch (ReflectiveOperationException e) { throw new IllegalArgumentException(e); } } @Test public void testInstallDeniedWhileUpgrading() throws Exception { Manifest manifest = new Manifest(); Release release = new Release(); release.setManifest(manifest); Mockito.when(releaseReportService.createReport(any(), any(), any(boolean.class))).thenReturn(new ReleaseAnalysisReport( new ArrayList<>(), new ReleaseDifference(), release, release)); Mockito.when(upgradeStrategy.checkStatus(any())) .thenReturn(false); Mockito.when(upgradeStrategyFactory.getUpgradeStrategy(any())).thenReturn(upgradeStrategy); UpgradeRequest upgradeRequest = new UpgradeRequest(); Message<SkipperEvents> message1 = MessageBuilder .withPayload(SkipperEvents.UPGRADE) .setHeader(SkipperEventHeaders.UPGRADE_REQUEST, upgradeRequest) .build(); Message<SkipperEvents> message2 = MessageBuilder .withPayload(SkipperEvents.INSTALL) .setHeader(SkipperEventHeaders.PACKAGE_METADATA, new PackageMetadata()) .setHeader(SkipperEventHeaders.INSTALL_PROPERTIES, new InstallProperties()) .setHeader(SkipperEventHeaders.VERSION, 1) .build(); StateMachineFactory<SkipperStates, SkipperEvents> factory = context.getBean(StateMachineFactory.class); StateMachine<SkipperStates, SkipperEvents> stateMachine = factory.getStateMachine("testInstallDeniedWhileUpgrading"); StateMachineTestPlan<SkipperStates, SkipperEvents> plan = StateMachineTestPlanBuilder.<SkipperStates, SkipperEvents>builder() .defaultAwaitTime(10) .stateMachine(stateMachine) .step() .expectStateMachineStarted(1) .expectStates(SkipperStates.INITIAL) .and() .step() .sendEvent(message1) .expectStateChanged(6) .and() .build(); plan.test(); // install event is not accepted boolean accepted = stateMachine.sendEvent(message2); assertThat(accepted).isFalse(); } @Import(StateMachineConfiguration.class) static class TestConfig { @Bean public TaskExecutor skipperStateMachineTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(1); return executor; } } }