/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.management.internal.web.controllers; import static org.junit.Assert.*; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.shell.core.CommandMarker; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.apache.geode.management.cli.CliMetaData; import org.apache.geode.management.internal.cli.util.ClasspathScanLoadHelper; import org.apache.geode.management.internal.web.domain.Link; import org.apache.geode.management.internal.web.domain.LinkIndex; import org.apache.geode.management.internal.web.util.UriUtils; import org.apache.geode.test.junit.categories.UnitTest; /** * The ShellCommandsControllerJUnitTest class is a test suite of test cases testing the contract and * functionality of the ShellCommandsController class, and specifically ensuring that all GemFire * Gfsh commands have a corresponding Management REST API call and web service endpoint in the * GemFire Management REST Interface. * <p/> * * @see org.junit.Test * @see org.apache.geode.management.internal.web.controllers.ShellCommandsController * @since GemFire 8.0 */ @Category(UnitTest.class) public class ShellCommandsControllerJUnitTest { private static ShellCommandsController controller; @BeforeClass public static void setupBeforeClass() { controller = new ShellCommandsController(); MockHttpServletRequest request = new MockHttpServletRequest(); request.setContextPath("gemfire"); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); } @AfterClass public static void tearDownAfterClass() { controller = null; } private List<String> getCliCommands() { try { Set<Class<?>> commandClasses = ClasspathScanLoadHelper.loadAndGet( "org.apache.geode.management.internal.cli.commands", CommandMarker.class, true); List<String> commands = new ArrayList<>(commandClasses.size()); for (Class<?> commandClass : commandClasses) { for (Method method : commandClass.getMethods()) { if (method.isAnnotationPresent(CliCommand.class)) { if (!(method.isAnnotationPresent(CliMetaData.class) && method.getAnnotation(CliMetaData.class).shellOnly())) { CliCommand commandAnnotation = method.getAnnotation(CliCommand.class); commands.addAll(Arrays.asList(commandAnnotation.value())); } } } } return commands; } catch (Exception e) { throw new RuntimeException(e); } } private List<String> getControllerWebServiceEndpoints() { RequestAttributes requestAttrs = RequestContextHolder.getRequestAttributes(); HttpServletRequest servletRequest = ((ServletRequestAttributes) requestAttrs).getRequest(); String scheme = servletRequest.getScheme(); try { Set<Class<?>> controllerClasses = ClasspathScanLoadHelper.loadAndGet("org.apache.geode.management.internal.web.controllers", AbstractCommandsController.class, true); List<String> controllerWebServiceEndpoints = new ArrayList<>(controllerClasses.size()); for (Class<?> controllerClass : controllerClasses) { if (!AbstractCommandsController.class.equals(controllerClass)) { for (Method method : controllerClass.getMethods()) { if (method.isAnnotationPresent(RequestMapping.class)) { RequestMapping requestMappingAnnotation = method.getAnnotation(RequestMapping.class); String webServiceEndpoint = String.format("%1$s %2$s", requestMappingAnnotation.method()[0], UriUtils.decode( controller.toUri(requestMappingAnnotation.value()[0], scheme).toString())); String[] requestParameters = requestMappingAnnotation.params(); if (requestParameters.length > 0) { webServiceEndpoint += "?".concat( org.apache.geode.internal.lang.StringUtils.concat(requestParameters, "&")); } controllerWebServiceEndpoints.add(webServiceEndpoint); } } } } return controllerWebServiceEndpoints; } catch (Exception e) { throw new RuntimeException(e); } } @Test public void testUniqueIndex() { LinkIndex linkIndex = controller.index("https"); List<String> conflicts = new ArrayList<>(); Map<String, String> uriRelationMapping = new HashMap<>(linkIndex.size()); for (Link link : linkIndex) { if (uriRelationMapping.containsKey(link.toHttpRequestLine())) { conflicts.add(String.format( "REST API endpoint (%1$s) for (%2$s) conflicts with the REST API endpoint for (%3$s)", link.toHttpRequestLine(), link.getRelation(), uriRelationMapping.get(link.toHttpRequestLine()))); } else { uriRelationMapping.put(link.toHttpRequestLine(), link.getRelation()); } } assertTrue(String.format("Conflicts: %1$s!", conflicts), conflicts.isEmpty()); } @Test public void testIndex() { List<String> commands = getCliCommands(); assertNotNull(commands); assertFalse(commands.isEmpty()); LinkIndex linkIndex = controller.index("https"); assertNotNull(linkIndex); assertFalse(linkIndex.isEmpty()); List<String> linkCommands = new ArrayList<>(linkIndex.size()); for (Link link : linkIndex) { linkCommands.add(link.getRelation()); } assertEquals(linkIndex.size(), linkCommands.size()); List<String> missingLinkCommands = new ArrayList<>(commands); missingLinkCommands.removeAll(linkCommands); assertTrue(String.format( "The GemFire Management REST API Link Index is missing Link(s) for the following command(s): %1$s", missingLinkCommands), missingLinkCommands.isEmpty()); } @Test public void testCommandHasRestApiControllerWebServiceEndpoint() { List<String> controllerWebServiceEndpoints = getControllerWebServiceEndpoints(); assertNotNull(controllerWebServiceEndpoints); assertFalse(controllerWebServiceEndpoints.isEmpty()); LinkIndex linkIndex = controller.index("http"); assertNotNull(linkIndex); assertFalse(linkIndex.isEmpty()); List<String> linkWebServiceEndpoints = new ArrayList<>(linkIndex.size()); for (Link link : linkIndex) { linkWebServiceEndpoints.add(link.toHttpRequestLine()); } assertEquals(linkIndex.size(), linkWebServiceEndpoints.size()); List<String> missingControllerWebServiceEndpoints = new ArrayList<>(linkWebServiceEndpoints); missingControllerWebServiceEndpoints.removeAll(controllerWebServiceEndpoints); assertTrue( String.format( "The Management REST API Web Service Controllers in (%1$s) are missing the following REST API Web Service Endpoint(s): %2$s!", getClass().getPackage().getName(), missingControllerWebServiceEndpoints), missingControllerWebServiceEndpoints.isEmpty()); } @Test public void testIndexUrisHaveCorrectScheme() { String versionCmd = "version"; List<String> controllerWebServiceEndpoints = getControllerWebServiceEndpoints(); assertNotNull(controllerWebServiceEndpoints); assertFalse(controllerWebServiceEndpoints.isEmpty()); String testScheme = "xyz"; LinkIndex linkIndex = controller.index(testScheme); assertNotNull(linkIndex); assertFalse(linkIndex.isEmpty()); assertTrue(String.format("Link does not have correct scheme %1$s", linkIndex.find(versionCmd)), testScheme.equals(linkIndex.find(versionCmd).getHref().getScheme())); } }