/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015 ForgeRock AS. */ package org.forgerock.openidm.shell.impl; import static org.forgerock.json.JsonValue.array; import static org.forgerock.json.JsonValue.field; import static org.forgerock.json.JsonValue.json; import static org.forgerock.json.JsonValue.object; import static org.forgerock.openidm.shell.impl.UpdateCommand.MAINTENANCE_ACTION_DISABLE; import static org.forgerock.openidm.shell.impl.UpdateCommand.MAINTENANCE_ACTION_ENABLE; import static org.forgerock.openidm.shell.impl.UpdateCommand.MAINTENANCE_ROUTE; import static org.forgerock.openidm.shell.impl.UpdateCommand.SCHEDULER_ACTION_LIST_JOBS; import static org.forgerock.openidm.shell.impl.UpdateCommand.SCHEDULER_ACTION_PAUSE; import static org.forgerock.openidm.shell.impl.UpdateCommand.SCHEDULER_ACTION_RESUME_JOBS; import static org.forgerock.openidm.shell.impl.UpdateCommand.SCHEDULER_ROUTE; import static org.forgerock.openidm.shell.impl.UpdateCommand.UPDATE_ACTION_AVAIL; import static org.forgerock.openidm.shell.impl.UpdateCommand.UPDATE_ACTION_GET_LICENSE; import static org.forgerock.openidm.shell.impl.UpdateCommand.UPDATE_ACTION_UPDATE; import static org.forgerock.openidm.shell.impl.UpdateCommand.UPDATE_LOG_ROUTE; import static org.forgerock.openidm.shell.impl.UpdateCommand.UPDATE_ROUTE; import static org.forgerock.openidm.shell.impl.UpdateCommand.UPDATE_STATUS_COMPLETE; import static org.forgerock.openidm.shell.impl.UpdateCommand.UPDATE_STATUS_FAILED; import static org.forgerock.openidm.shell.impl.UpdateCommand.UpdateStep.ENABLE_SCHEDULER; import static org.forgerock.openidm.shell.impl.UpdateCommand.UpdateStep.ENTER_MAINTENANCE_MODE; import static org.forgerock.openidm.shell.impl.UpdateCommand.UpdateStep.FORCE_RESTART; import static org.forgerock.openidm.shell.impl.UpdateCommand.UpdateStep.INSTALL_ARCHIVE; import static org.forgerock.openidm.shell.impl.UpdateCommand.UpdateStep.PAUSING_SCHEDULER; import static org.forgerock.openidm.shell.impl.UpdateCommand.UpdateStep.PREVIEW_ARCHIVE; import static org.forgerock.openidm.shell.impl.UpdateCommand.UpdateStep.WAIT_FOR_INSTALL_DONE; import static org.forgerock.openidm.shell.impl.UpdateCommand.UpdateStep.WAIT_FOR_JOBS_TO_COMPLETE; import static org.mockito.Matchers.any; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; import org.apache.felix.service.command.CommandSession; import org.forgerock.json.JsonValue; import org.forgerock.json.resource.ActionRequest; import org.forgerock.json.resource.ActionResponse; import org.forgerock.json.resource.ReadRequest; import org.forgerock.json.resource.ResourceException; import org.forgerock.json.resource.ResourceResponse; import org.forgerock.json.resource.Responses; import org.forgerock.services.context.Context; import org.forgerock.services.context.RootContext; import org.mockito.ArgumentMatcher; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests the UpdateCommand and its various exit points. * * @see UpdateCommand */ public class UpdateCommandTest { private CommandSession session; @BeforeClass public void setup() { session = mock(CommandSession.class); when(session.getConsole()).thenReturn(System.out); } @BeforeMethod public void beforeMethod() { System.out.println("-------- Update Command test START ---------"); } @AfterMethod public void afterMethod() { System.out.println("-------- Update Command test END ---------"); } @Test public void testCantFindArchive() throws Exception { HttpRemoteJsonResource resource = mockResource( mc(UPDATE_ROUTE, UPDATE_ACTION_AVAIL, json(array(object(field("archive", "xyz.zip"))))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_RESUME_JOBS, json(object(field("success", true)))), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_DISABLE, json(object(field("maintenanceEnabled", false)))) ); UpdateCommandConfig config = new UpdateCommandConfig() .setUpdateArchive("test.zip") .setLogFilePath(null) .setQuietMode(false) .setAcceptedLicense(true) .setMaxJobsFinishWaitTimeMs(1000L) .setMaxUpdateWaitTimeMs(1000L); UpdateCommand updateCommand = new UpdateCommand(session, resource, config); UpdateExecutionState executionState = updateCommand.execute(new RootContext()); assertEquals(executionState.getLastAttemptedStep(), PREVIEW_ARCHIVE); assertNull(executionState.getCompletedInstallStatus()); assertEquals(executionState.getLastRecoveryStep(), ENABLE_SCHEDULER); } @Test public void testCantPauseScheduler() throws Exception { HttpRemoteJsonResource resource = mockResource( mc(UPDATE_ROUTE, UPDATE_ACTION_AVAIL, json(array(object(field("archive", "test.zip"))))), mc(UPDATE_ROUTE, UPDATE_ACTION_GET_LICENSE, json(object(field("license", "This is the license")))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_PAUSE, json(object(field("success", false)))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_RESUME_JOBS, json(object(field("success", true)))), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_DISABLE, json(object(field("maintenanceEnabled", false)))) ); UpdateCommandConfig config = new UpdateCommandConfig() .setUpdateArchive("test.zip") .setLogFilePath(null) .setQuietMode(false) .setAcceptedLicense(true) .setMaxJobsFinishWaitTimeMs(1000L) .setMaxUpdateWaitTimeMs(1000L); UpdateCommand updateCommand = new UpdateCommand(session, resource, config); UpdateExecutionState executionState = updateCommand.execute(new RootContext()); assertEquals(executionState.getLastAttemptedStep(), PAUSING_SCHEDULER); assertNull(executionState.getCompletedInstallStatus()); assertEquals(executionState.getLastRecoveryStep(), ENABLE_SCHEDULER); } @Test public void testWaitForRunningJobsToFinish() throws Exception { HttpRemoteJsonResource resource = mockResource( mc(UPDATE_ROUTE, UPDATE_ACTION_AVAIL, json(array(object(field("archive", "test.zip"))))), mc(UPDATE_ROUTE, UPDATE_ACTION_GET_LICENSE, json(object(field("license", "This is the license")))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_PAUSE, json(object(field("success", true)))), // mock our running jobs listing responses, with jobs running. mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_LIST_JOBS, json(array(object()))), // mock the next step to fail. mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_ENABLE, json(object(field("maintenanceEnabled", false)))), // mock the calls on recovery. mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_RESUME_JOBS, json(object(field("success", true)))), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_DISABLE, json(object(field("maintenanceEnabled", false)))) ); // with the timeout set to 10 and the retry set to 20, it should timeout waiting for jobs to complete. UpdateCommandConfig config = new UpdateCommandConfig() .setUpdateArchive("test.zip") .setLogFilePath(null) .setQuietMode(false) .setAcceptedLicense(true) .setMaxJobsFinishWaitTimeMs(10L) .setCheckJobsRunningFrequency(20L) .setMaxUpdateWaitTimeMs(1000L); UpdateCommand updateCommand = new UpdateCommand(session, resource, config); UpdateExecutionState executionState = updateCommand.execute(new RootContext()); assertEquals(executionState.getLastAttemptedStep(), WAIT_FOR_JOBS_TO_COMPLETE); assertNull(executionState.getCompletedInstallStatus()); assertEquals(executionState.getLastRecoveryStep(), ENABLE_SCHEDULER); //now that timeout testing worked, test that is can pass waiting for jobs to complete resource = mockResource( mc(UPDATE_ROUTE, UPDATE_ACTION_AVAIL, json(array(object(field("archive", "test.zip"))))), mc(UPDATE_ROUTE, UPDATE_ACTION_GET_LICENSE, json(object(field("license", "This is the license")))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_PAUSE, json(object(field("success", true)))), // mock our running jobs listing responses, with 3 iterations of mocked responses. // this will help simulate waiting for a job to finish. mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_LIST_JOBS, json(array(object())), json(array(object())), json(array(object())), json(array())), // mock the next step to fail. mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_ENABLE, json(object(field("maintenanceEnabled", false)))), // mock the calls on recovery. mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_RESUME_JOBS, json(object(field("success", true)))), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_DISABLE, json(object(field("maintenanceEnabled", false)))) ); config = new UpdateCommandConfig() .setUpdateArchive("test.zip") .setLogFilePath(null) .setQuietMode(false) .setAcceptedLicense(true) .setMaxJobsFinishWaitTimeMs(200L) .setCheckJobsRunningFrequency(10L) .setMaxUpdateWaitTimeMs(1000L); updateCommand = new UpdateCommand(session, resource, config); executionState = updateCommand.execute(new RootContext()); assertEquals(executionState.getLastAttemptedStep(), ENTER_MAINTENANCE_MODE); assertNull(executionState.getCompletedInstallStatus()); assertEquals(executionState.getLastRecoveryStep(), ENABLE_SCHEDULER); } @Test public void testEnterMaintenanceMode() throws Exception { HttpRemoteJsonResource resource = mockResource( mc(UPDATE_ROUTE, UPDATE_ACTION_AVAIL, json(array(object(field("archive", "test.zip"), field("restartRequired", "false"))))), mc(UPDATE_ROUTE, UPDATE_ACTION_GET_LICENSE, json(object(field("license", "This is the license")))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_PAUSE, json(object(field("success", true)))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_LIST_JOBS, json(array(object())), json(array())), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_ENABLE, json(object(field("maintenanceEnabled", true)))), // mock with empty response to simulate early error mc(UPDATE_ROUTE, UPDATE_ACTION_UPDATE, json(object())), // mock the calls on recovery. mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_RESUME_JOBS, json(object(field("success", true)))), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_DISABLE, json(object(field("maintenanceEnabled", false)))) ); UpdateCommandConfig config = new UpdateCommandConfig() .setUpdateArchive("test.zip") .setLogFilePath(null) .setQuietMode(false) .setAcceptedLicense(true) .setMaxJobsFinishWaitTimeMs(50L) .setCheckJobsRunningFrequency(10L) .setMaxUpdateWaitTimeMs(1000L); UpdateCommand updateCommand = new UpdateCommand(session, resource, config); UpdateExecutionState executionState = updateCommand.execute(new RootContext()); assertEquals(executionState.getLastAttemptedStep(), INSTALL_ARCHIVE); assertNull(executionState.getCompletedInstallStatus()); assertEquals(executionState.getLastRecoveryStep(), ENABLE_SCHEDULER); } @Test public void testTimeoutInstallUpdateArchive() throws Exception { HttpRemoteJsonResource resource = mockResource( mc(UPDATE_ROUTE, UPDATE_ACTION_AVAIL, json(array(object(field("archive", "test.zip"), field("restartRequired", "false"))))), mc(UPDATE_ROUTE, UPDATE_ACTION_GET_LICENSE, json(object(field("license", "This is the license")))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_PAUSE, json(object(field("success", true)))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_LIST_JOBS, json(array())), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_ENABLE, json(object(field("maintenanceEnabled", true)))), mc(UPDATE_ROUTE, UPDATE_ACTION_UPDATE, json(object(field("status", "IN_PROGRESS"), field(ResourceResponse.FIELD_CONTENT_ID, "1234")))), mc(UPDATE_LOG_ROUTE, null, json(object( field(ResourceResponse.FIELD_CONTENT_ID, "1234"), field(ResourceResponse.FIELD_CONTENT_REVISION, "1"), field("status", "IN_PROGRESS") )) ), // mock the calls on recovery. mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_RESUME_JOBS, json(object(field("success", true)))), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_DISABLE, json(object(field("maintenanceEnabled", false)))) ); UpdateCommandConfig config = new UpdateCommandConfig() .setUpdateArchive("test.zip") .setLogFilePath(null) .setQuietMode(false) .setAcceptedLicense(true) .setMaxJobsFinishWaitTimeMs(50L) .setCheckJobsRunningFrequency(10L) .setMaxUpdateWaitTimeMs(10L) .setCheckCompleteFrequency(20L); UpdateCommand updateCommand = new UpdateCommand(session, resource, config); UpdateExecutionState executionState = updateCommand.execute(new RootContext()); assertEquals(executionState.getLastAttemptedStep(), WAIT_FOR_INSTALL_DONE); assertNull(executionState.getCompletedInstallStatus()); assertEquals(executionState.getLastRecoveryStep(), ENABLE_SCHEDULER); } @Test public void testFailedInstallUpdateArchive() throws Exception { HttpRemoteJsonResource resource = mockResource( mc(UPDATE_ROUTE, UPDATE_ACTION_AVAIL, json(array(object(field("archive", "test.zip"), field("restartRequired", "false"))))), mc(UPDATE_ROUTE, UPDATE_ACTION_GET_LICENSE, json(object(field("license", "This is the license")))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_PAUSE, json(object(field("success", true)))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_LIST_JOBS, json(array(object())), json(array())), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_ENABLE, json(object(field("maintenanceEnabled", true)))), mc(UPDATE_ROUTE, UPDATE_ACTION_UPDATE, json(object(field("status", "IN_PROGRESS"), field(ResourceResponse.FIELD_CONTENT_ID, "1234")))), mc(UPDATE_LOG_ROUTE, null, json(object( field(ResourceResponse.FIELD_CONTENT_ID, "1234"), field(ResourceResponse.FIELD_CONTENT_REVISION, "1"), field("status", "IN_PROGRESS") )), json(object( field(ResourceResponse.FIELD_CONTENT_ID, "1234"), field(ResourceResponse.FIELD_CONTENT_REVISION, "1"), field("status", UPDATE_STATUS_FAILED) ))), // mock the calls on recovery. mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_RESUME_JOBS, json(object(field("success", true)))), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_DISABLE, json(object(field("maintenanceEnabled", false)))) ); UpdateCommandConfig config = new UpdateCommandConfig() .setUpdateArchive("test.zip") .setLogFilePath(null) .setQuietMode(false) .setAcceptedLicense(true) .setMaxJobsFinishWaitTimeMs(50L) .setCheckJobsRunningFrequency(10L) .setMaxUpdateWaitTimeMs(100L) .setCheckCompleteFrequency(10L); UpdateCommand updateCommand = new UpdateCommand(session, resource, config); UpdateExecutionState executionState = updateCommand.execute(new RootContext()); assertEquals(executionState.getLastAttemptedStep(), WAIT_FOR_INSTALL_DONE); assertEquals(executionState.getCompletedInstallStatus(), UPDATE_STATUS_FAILED); assertEquals(executionState.getLastRecoveryStep(), ENABLE_SCHEDULER); } @Test public void testSuccessfulInstall() throws Exception { HttpRemoteJsonResource resource = mockResource( mc(UPDATE_ROUTE, UPDATE_ACTION_AVAIL, json(array(object(field("archive", "test.zip"), field("restartRequired", "false"))))), mc(UPDATE_ROUTE, UPDATE_ACTION_GET_LICENSE, json(object(field("license", "This is the license")))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_PAUSE, json(object(field("success", true)))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_LIST_JOBS, json(array(object())), json(array())), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_ENABLE, json(object(field("maintenanceEnabled", true)))), mc(UPDATE_ROUTE, UPDATE_ACTION_UPDATE, json(object(field("status", "IN_PROGRESS"), field(ResourceResponse.FIELD_CONTENT_ID, "1234")))), mc(UPDATE_LOG_ROUTE, null, json(object( field(ResourceResponse.FIELD_CONTENT_ID, "1234"), field(ResourceResponse.FIELD_CONTENT_REVISION, "1"), field("status", "IN_PROGRESS") )), json(object( field(ResourceResponse.FIELD_CONTENT_ID, "1234"), field(ResourceResponse.FIELD_CONTENT_REVISION, "1"), field("status", "IN_PROGRESS") )), json(object( field(ResourceResponse.FIELD_CONTENT_ID, "1234"), field(ResourceResponse.FIELD_CONTENT_REVISION, "1"), field("status", UPDATE_STATUS_COMPLETE) ))), // mock the calls on recovery. mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_RESUME_JOBS, json(object(field("success", true)))), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_DISABLE, json(object(field("maintenanceEnabled", false)))) ); UpdateCommandConfig config = new UpdateCommandConfig() .setUpdateArchive("test.zip") .setLogFilePath(null) .setQuietMode(false) .setAcceptedLicense(true) .setMaxJobsFinishWaitTimeMs(50L) .setCheckJobsRunningFrequency(10L) .setMaxUpdateWaitTimeMs(200L) .setCheckCompleteFrequency(10L); UpdateCommand updateCommand = new UpdateCommand(session, resource, config); UpdateExecutionState executionState = updateCommand.execute(new RootContext()); assertEquals(executionState.getLastAttemptedStep(), WAIT_FOR_INSTALL_DONE); assertEquals(executionState.getCompletedInstallStatus(), UPDATE_STATUS_COMPLETE); assertEquals(executionState.getLastRecoveryStep(), ENABLE_SCHEDULER); } @Test public void testSuccessfulInstallWithRestart() throws Exception { HttpRemoteJsonResource resource = mockResource( mc(UPDATE_ROUTE, UPDATE_ACTION_AVAIL, json(array(object(field("archive", "test.zip"), field("restartRequired", "true"))))), mc(UPDATE_ROUTE, UPDATE_ACTION_GET_LICENSE, json(object(field("license", "This is the license")))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_PAUSE, json(object(field("success", true)))), mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_LIST_JOBS, json(array(object())), json(array())), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_ENABLE, json(object(field("maintenanceEnabled", true)))), mc(UPDATE_ROUTE, UPDATE_ACTION_UPDATE, json(object(field("status", "IN_PROGRESS"), field(ResourceResponse.FIELD_CONTENT_ID, "1234")))), mc(UPDATE_LOG_ROUTE, null, json(object( field(ResourceResponse.FIELD_CONTENT_ID, "1234"), field(ResourceResponse.FIELD_CONTENT_REVISION, "1"), field("status", "IN_PROGRESS") )), json(object( field(ResourceResponse.FIELD_CONTENT_ID, "1234"), field(ResourceResponse.FIELD_CONTENT_REVISION, "1"), field("status", "IN_PROGRESS") )), json(object( field(ResourceResponse.FIELD_CONTENT_ID, "1234"), field(ResourceResponse.FIELD_CONTENT_REVISION, "1"), field("status", UPDATE_STATUS_COMPLETE) ))), // mock the calls on recovery. mc(SCHEDULER_ROUTE, SCHEDULER_ACTION_RESUME_JOBS, json(object(field("success", true)))), mc(MAINTENANCE_ROUTE, MAINTENANCE_ACTION_DISABLE, json(object(field("maintenanceEnabled", false)))) ); UpdateCommandConfig config = new UpdateCommandConfig() .setUpdateArchive("test.zip") .setLogFilePath(null) .setQuietMode(false) .setAcceptedLicense(true) .setMaxJobsFinishWaitTimeMs(50L) .setCheckJobsRunningFrequency(10L) .setMaxUpdateWaitTimeMs(200L) .setCheckCompleteFrequency(10L); UpdateCommand updateCommand = new UpdateCommand(session, resource, config); UpdateExecutionState executionState = updateCommand.execute(new RootContext()); assertEquals(executionState.getLastAttemptedStep(), WAIT_FOR_INSTALL_DONE); assertEquals(executionState.getCompletedInstallStatus(), UPDATE_STATUS_COMPLETE); assertEquals(executionState.getLastRecoveryStep(), FORCE_RESTART); } private HttpRemoteJsonResource mockResource(MockCriteria... mockCriterion) throws ResourceException { HttpRemoteJsonResource resource = mock(HttpRemoteJsonResource.class); for (MockCriteria mockCriteria : mockCriterion) { mockCriteria.mockResponse(resource); } return resource; } private static MockCriteria mc(String route, String action, JsonValue... responseContent) { if (null == action) { return new MockReadCriteria(route, responseContent); } else { return new MockActionCriteria(route, action, responseContent); } } private static class MockReadCriteria extends MockCriteria { public MockReadCriteria(String route, JsonValue... responseContent) { super(route, null, responseContent); } @Override public void mockResponse(HttpRemoteJsonResource resource) throws ResourceException { JsonValue[] responseContent = this.getResponseContent(); ResourceResponse firstResponse = Responses.newResourceResponse( responseContent[0].get(ResourceResponse.FIELD_CONTENT_ID).asString(), responseContent[0].get(ResourceResponse.FIELD_CONTENT_REVISION).asString(), responseContent[0]); if (responseContent.length > 1) { ResourceResponse[] secondOnwardResponses = new ResourceResponse[responseContent.length - 1]; for (int i = 1; i < responseContent.length; i++) { secondOnwardResponses[i - 1] = Responses.newResourceResponse( responseContent[i].get(ResourceResponse.FIELD_CONTENT_ID).asString(), responseContent[i].get(ResourceResponse.FIELD_CONTENT_REVISION).asString(), responseContent[i]); } when(resource.read( any(Context.class), argThat(new IsRouteMatcher(this.getRoute())))) .thenReturn(firstResponse, secondOnwardResponses); } else { when(resource.read( any(Context.class), argThat(new IsRouteMatcher(this.getRoute())))) .thenReturn(firstResponse); } } } private static class MockActionCriteria extends MockCriteria { public MockActionCriteria(String route, String action, JsonValue... responseContent) { super(route, action, responseContent); } @Override public void mockResponse(HttpRemoteJsonResource resource) throws ResourceException { JsonValue[] responseContent = this.getResponseContent(); if (responseContent.length > 1) { ActionResponse firstResponse = Responses.newActionResponse(responseContent[0]); ActionResponse[] secondOnwardResponses = new ActionResponse[responseContent.length - 1]; for (int i = 1; i < responseContent.length; i++) { secondOnwardResponses[i - 1] = Responses.newActionResponse(responseContent[i]); } when(resource.action( any(Context.class), argThat(new IsActionMatcher(this.getRoute(), this.getAction())))) .thenReturn(firstResponse, secondOnwardResponses); } else { ActionResponse response = Responses.newActionResponse(responseContent[0]); when(resource.action( any(Context.class), argThat(new IsActionMatcher(this.getRoute(), this.getAction())))) .thenReturn(response); } } } private abstract static class MockCriteria { private final String route; private final String action; private final JsonValue[] responseContent; public MockCriteria(String route, String action, JsonValue... responseContent) { this.route = route; this.action = action; this.responseContent = responseContent; } public String getRoute() { return route; } public String getAction() { return action; } public JsonValue[] getResponseContent() { return responseContent; } public abstract void mockResponse(HttpRemoteJsonResource resource) throws ResourceException; } private static class IsActionMatcher extends ArgumentMatcher<ActionRequest> { private final String route; private final String action; public IsActionMatcher(String route, String action) { this.route = route; this.action = action; } @Override public boolean matches(Object actionToMatch) { if (null == actionToMatch) { return false; } ActionRequest actionRequest = (ActionRequest) actionToMatch; return actionRequest.getResourcePath().equals(route) && actionRequest.getAction().equals(this.action); } } private static class IsRouteMatcher extends ArgumentMatcher<ReadRequest> { private final String route; public IsRouteMatcher(String route) { this.route = route; } @Override public boolean matches(Object requestToMatch) { return (null != requestToMatch && ((ReadRequest) requestToMatch).getResourcePath().startsWith(route)); } } }