blob: 9b11c47a0d0c0d0d0b1b4feffffcb3d1b70a7a61 [file] [log] [blame]
/*
* 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.accumulo.monitor.it;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicReference;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Application;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.MessageBodyWriter;
import org.apache.accumulo.core.clientImpl.Tables;
import org.apache.accumulo.core.conf.DefaultConfiguration;
import org.apache.accumulo.core.data.TableId;
import org.apache.accumulo.monitor.Monitor;
import org.apache.accumulo.monitor.Monitor.MonitorFactory;
import org.apache.accumulo.monitor.view.WebViews;
import org.apache.accumulo.server.ServerContext;
import org.apache.accumulo.test.categories.MonitorTests;
import org.easymock.EasyMock;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
/**
* Basic tests for parameter validation constraints
*/
@Category(MonitorTests.class)
@RunWith(PowerMockRunner.class)
@PrepareForTest({Monitor.class, Tables.class})
@PowerMockIgnore({"javax.xml.*"})
public class WebViewsIT extends JerseyTest {
@Override
public Application configure() {
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
ResourceConfig config = new ResourceConfig(WebViews.class);
config.register(new MonitorFactory(monitor.get()));
config.register(WebViewsIT.HashMapWriter.class);
return config;
}
@Override
protected void configureClient(ClientConfig config) {
super.configureClient(config);
config.register(WebViewsIT.HashMapWriter.class);
}
private static AtomicReference<Monitor> monitor = new AtomicReference<>(null);
@BeforeClass
public static void createMocks() {
ServerContext contextMock = EasyMock.createMock(ServerContext.class);
expect(contextMock.getConfiguration()).andReturn(DefaultConfiguration.getInstance()).anyTimes();
expect(contextMock.getInstanceID()).andReturn("foo").atLeastOnce();
expect(contextMock.getInstanceName()).andReturn("foo").anyTimes();
Monitor monitorMock = EasyMock.createMock(Monitor.class);
expect(monitorMock.getContext()).andReturn(contextMock).anyTimes();
EasyMock.replay(contextMock, monitorMock);
monitor.set(monitorMock);
}
@AfterClass
public static void finishMocks() {
Monitor m = monitor.get();
verify(m.getContext(), m);
}
/**
* Expect to fail the constraint validation on the REST endpoint. The constraint is the
* pre-defined word character class Pattern so passing a table name with punctuation will cause a
* 400 response code.
*/
@Test
public void testGetTablesConstraintViolations() {
Response output = target("tables/f+o*o").request().get();
assertEquals("should return status 400", 400, output.getStatus());
}
/**
* Test path tables/{tableID} which passes constraints. On passing constraints underlying logic
* will be executed so we need to mock a certain amount of it. Note: If you get test failures here
* due to 500 server response, it's likely an underlying class or method call was added/modified
* and needs mocking. Note: To get the proper response code back, you need to make sure jersey has
* a registered MessageBodyWriter capable of serializing/writing the object returned from your
* endpoint. We're using a simple stubbed out inner class HashMapWriter for this.
*
* @throws Exception
* not expected
*/
@Test
public void testGetTablesConstraintPassing() throws Exception {
PowerMock.mockStatic(Tables.class);
expect(Tables.getTableName(monitor.get().getContext(), TableId.of("foo"))).andReturn("bar");
PowerMock.replayAll();
// Using the mocks we can verify that the getModel method gets called via debugger
// however it's difficult to continue to mock through the jersey MVC code for the properly built
// response.
// Our silly HashMapWriter registered in the configure method gets wired in and used here.
Response output = target("tables/foo").request().get();
assertEquals("should return status 200", 200, output.getStatus());
String responseBody = output.readEntity(String.class);
assertTrue(responseBody.contains("tableID=foo") && responseBody.contains("table=bar"));
}
/**
* Test minutes parameter constraints. When outside range we should get a 400 response.
*/
@Test
public void testGetTracesSummaryValidationConstraint() {
// Test upper bounds of constraint
Response output = target("trace/summary").queryParam("minutes", 5000000).request().get();
assertEquals("should return status 400", 400, output.getStatus());
// Test lower bounds of constraint
output = target("trace/summary").queryParam("minutes", -27).request().get();
assertEquals("should return status 400", 400, output.getStatus());
}
/**
* Silly stub to handle MessageBodyWriter for Hashmap. Registered in configure method and
* auto-wired by Jersey.
*/
public static class HashMapWriter implements MessageBodyWriter<HashMap<?,?>> {
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType) {
return true;
}
@Override
public long getSize(HashMap<?,?> hashMap, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return 0;
}
@Override
public void writeTo(HashMap<?,?> hashMap, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType, MultivaluedMap<String,Object> httpHeaders,
OutputStream entityStream) throws IOException, WebApplicationException {
String s = hashMap.toString();
entityStream.write(s.getBytes());
}
}
}