package org.ovirt.engine.api.restapi.resource; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.ovirt.engine.api.restapi.test.util.TestHelper.eqParams; import static org.ovirt.engine.api.restapi.test.util.TestHelper.eqSearchParams; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import javax.ws.rs.Path; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.junit.Test; import org.ovirt.engine.api.model.BaseResource; import org.ovirt.engine.api.model.Fault; import org.ovirt.engine.api.model.Link; import org.ovirt.engine.core.common.action.VdcActionParametersBase; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.businessentities.AsyncTaskStatus; import org.ovirt.engine.core.common.businessentities.Cluster; import org.ovirt.engine.core.common.interfaces.SearchType; import org.ovirt.engine.core.common.queries.GetTasksStatusesByTasksIDsParameters; import org.ovirt.engine.core.common.queries.SearchParameters; import org.ovirt.engine.core.common.queries.VdcQueryParametersBase; import org.ovirt.engine.core.common.queries.VdcQueryReturnValue; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.Version; public abstract class AbstractBackendCollectionResourceTest<R extends BaseResource, Q /* extends IVdcQueryable */, C extends AbstractBackendCollectionResource<R, Q>> extends AbstractBackendResourceTest<R, Q> { protected static final String QUERY = "name=s* AND id=*0"; protected C collection; protected SearchType searchType; protected String prefix; protected AbstractBackendCollectionResourceTest(C collection, SearchType searchType, String prefix) { this.collection = collection; this.searchType = searchType; this.prefix = prefix; } protected abstract List<R> getCollection(); @Override protected void init() { initResource(collection); } @Test public void testList() throws Exception { UriInfo uriInfo = setUpUriExpectations(null); setUpQueryExpectations(""); collection.setUriInfo(uriInfo); verifyCollection(getCollection()); } @Test public void testQuery() throws Exception { UriInfo uriInfo = setUpUriExpectations(QUERY); setUpQueryExpectations(QUERY); collection.setUriInfo(uriInfo); verifyCollection(getCollection()); } @Test public void testListFailure() throws Exception { UriInfo uriInfo = setUpUriExpectations(null); setUpQueryExpectations("", FAILURE); collection.setUriInfo(uriInfo); try { getCollection(); fail("expected WebApplicationException"); } catch (WebApplicationException wae) { assertTrue(wae.getResponse().getEntity() instanceof Fault); assertEquals(mockl10n(FAILURE), ((Fault) wae.getResponse().getEntity()).getDetail()); } } @Test public void testListCrash() throws Exception { UriInfo uriInfo = setUpUriExpectations(null); Throwable t = new RuntimeException(FAILURE); setUpQueryExpectations("", t); collection.setUriInfo(uriInfo); try { getCollection(); fail("expected WebApplicationException"); } catch (WebApplicationException wae) { verifyFault(wae, BACKEND_FAILED_SERVER_LOCALE, t); } } @Test public void testListCrashClientLocale() throws Exception { UriInfo uriInfo = setUpUriExpectations(null); locales.add(CLIENT_LOCALE); Throwable t = new RuntimeException(FAILURE); setUpQueryExpectations("", t); collection.setUriInfo(uriInfo); try { getCollection(); fail("expected WebApplicationException"); } catch (WebApplicationException wae) { verifyFault(wae, BACKEND_FAILED_CLIENT_LOCALE, t); } finally { locales.clear(); } } @SuppressWarnings("unchecked") @Test public void testSubResourceInjection() throws Exception { // walk super-interface hierarchy to find non-inherited method annotations for (Class<?> resourceInterface : collection.getClass().getInterfaces()) { for (Method method : resourceInterface.getDeclaredMethods()) { if (method.isAnnotationPresent(Path.class) && isSubResourceLocator(method)) { Object rawSubResource = method.invoke(collection, getSubResourceId()); if (rawSubResource instanceof AbstractBackendResource) { AbstractBackendResource<R, Q> subResource = (AbstractBackendResource<R, Q>)rawSubResource; assertNotNull(subResource.getBackend()); assertNotNull(subResource.getMappingLocator()); } } } } } protected String getSubResourceId() { return GUIDS[3].toString(); } protected boolean isSubResourceLocator(Method method) { return method.getName().startsWith("get") && method.getName().toLowerCase().endsWith("resource") && method.getParameterTypes().length == 1 && String.class.equals(method.getParameterTypes()[0]) && method.getReturnType() != null; } protected void setUriInfo(UriInfo uriInfo) { collection.setUriInfo(uriInfo); } @SuppressWarnings("unchecked") protected UriInfo setUpUriExpectations(String query) { UriInfo uriInfo = setUpBasicUriExpectations(); MultivaluedMap<String, String> queries = mock(MultivaluedMap.class); if (!(query == null || "".equals(query))) { query = QUERY; } when(queries.containsKey("search")).thenReturn(query != null); when(queries.getFirst("search")).thenReturn(query); when(uriInfo.getQueryParameters()).thenReturn(queries); return uriInfo; } protected void setUpQueryExpectations(String query) throws Exception { setUpQueryExpectations(query, null); } protected void setUpQueryExpectations(String query, Object failure) throws Exception { VdcQueryReturnValue queryResult = mock(VdcQueryReturnValue.class); SearchParameters params = new SearchParameters(prefix + query, searchType); when(queryResult.getSucceeded()).thenReturn(failure == null); if (failure == null) { List<Q> entities = new ArrayList<>(); for (int i = 0; i < NAMES.length; i++) { entities.add(getEntity(i)); } when(queryResult.getReturnValue()).thenReturn(entities); } else { if (failure instanceof String) { when(queryResult.getExceptionString()).thenReturn((String) failure); setUpL10nExpectations((String)failure); } else if (failure instanceof Exception) { when(queryResult.getExceptionString()).thenThrow((Exception) failure); } } when(backend.runQuery(eq(VdcQueryType.Search), eqSearchParams(params))).thenReturn( queryResult); enqueueInteraction(() -> verify(backend, atLeastOnce()).runQuery(eq(VdcQueryType.Search), eqSearchParams(params))); } protected void setUpCreationExpectations(VdcActionType task, Class<? extends VdcActionParametersBase> taskClass, String[] taskNames, Object[] taskValues, boolean valid, boolean success, Object taskReturn, VdcQueryType query, Class<? extends VdcQueryParametersBase> queryClass, String[] queryNames, Object[] queryValues, Object queryReturn) { setUpCreationExpectations(task, taskClass, taskNames, taskValues, valid, success, taskReturn, null, null, query, queryClass, queryNames, queryValues, queryReturn); } protected void setUpCreationExpectations(VdcActionType task, Class<? extends VdcActionParametersBase> taskClass, String[] taskNames, Object[] taskValues, boolean valid, boolean success, Object taskReturn, ArrayList<Guid> asyncTasks, ArrayList<AsyncTaskStatus> asyncStatuses, VdcQueryType query, Class<? extends VdcQueryParametersBase> queryClass, String[] queryNames, Object[] queryValues, Object queryReturn) { VdcReturnValueBase taskResult = mock(VdcReturnValueBase.class); when(taskResult.isValid()).thenReturn(valid); if (valid) { when(taskResult.getSucceeded()).thenReturn(success); if (success) { when(taskResult.getActionReturnValue()).thenReturn(taskReturn); } else { when(taskResult.getExecuteFailedMessages()).thenReturn(asList(FAILURE)); setUpL10nExpectations(asList(FAILURE)); } } else { when(taskResult.getValidationMessages()).thenReturn(asList(CANT_DO)); setUpL10nExpectations(asList(CANT_DO)); } when(taskResult.getHasAsyncTasks()).thenReturn(asyncTasks != null); if (asyncTasks != null) { when(taskResult.getVdsmTaskIdList()).thenReturn(asyncTasks); VdcQueryReturnValue monitorResult = mock(VdcQueryReturnValue.class); when(monitorResult.getSucceeded()).thenReturn(success); when(monitorResult.getReturnValue()).thenReturn(asyncStatuses); when(backend.runQuery(eq(VdcQueryType.GetTasksStatusesByTasksIDs), eqParams(GetTasksStatusesByTasksIDsParameters.class, addSession(), addSession(new Object[]{})))).thenReturn(monitorResult); enqueueInteraction(() -> verify(backend, atLeastOnce()).runQuery(eq(VdcQueryType.GetTasksStatusesByTasksIDs), eqParams(GetTasksStatusesByTasksIDsParameters.class, addSession(), addSession(new Object[]{})))); } when(backend.runAction(eq(task), eqParams(taskClass, addSession(taskNames), addSession(taskValues)))) .thenReturn(taskResult); enqueueInteraction(() -> verify(backend, atLeastOnce()).runAction(eq(task), eqParams(taskClass, addSession(taskNames), addSession(taskValues)))); if (valid && success && query != null) { setUpEntityQueryExpectations(query, queryClass, queryNames, queryValues, queryReturn); } } protected void setUpHttpHeaderExpectations(String name, String value) { when(httpHeaders.getRequestHeader(eq(name))).thenReturn(asList(value)); } protected Cluster setUpCluster(Guid id) { Cluster cluster = mock(Cluster.class); when(cluster.getId()).thenReturn(id); when(cluster.getCompatibilityVersion()).thenReturn(Version.getLast()); return cluster; } protected void verifyCollection(List<R> collection) throws Exception { assertNotNull(collection); assertEquals(NAMES.length, collection.size()); for (int i = 0; i < NAMES.length; i++) { verifyModel(collection.get(i), i); } } @Override protected void verifyRemove(Response response) { assertNotNull(response); assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); } protected static Link getLinkByName(BaseResource model, String name) { for (Link link : model.getLinks()) { if (link.getRel().equals(name)) { return link; } } return null; } }