/*
* Copyright 2013 Rackspace
*
* 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
*
* 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 com.rackspacecloud.blueflood.inputs.handlers;
import com.rackspacecloud.blueflood.http.HttpRequestWithDecodedQueryParams;
import com.rackspacecloud.blueflood.io.EventsIO;
import com.rackspacecloud.blueflood.outputs.formats.ErrorResponse;
import com.rackspacecloud.blueflood.outputs.handlers.HandlerTestsBase;
import com.rackspacecloud.blueflood.service.Configuration;
import com.rackspacecloud.blueflood.service.CoreConfig;
import com.rackspacecloud.blueflood.types.Event;
import com.rackspacecloud.blueflood.utils.DefaultClockImpl;
import io.netty.buffer.Unpooled;
import org.codehaus.jackson.map.ObjectMapper;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
import static org.mockito.Mockito.*;
import static junit.framework.Assert.*;
public class HttpEventsIngestionHandlerTest extends HandlerTestsBase {
private EventsIO searchIO;
private HttpEventsIngestionHandler handler;
private ChannelHandlerContext context;
private Channel channel;
private ChannelFuture channelFuture;
private static final String TENANT = "tenant";
public HttpEventsIngestionHandlerTest() {
searchIO = mock(EventsIO.class);
handler = new HttpEventsIngestionHandler(searchIO);
channel = mock(Channel.class);
context = mock(ChannelHandlerContext.class);
channelFuture = mock(ChannelFuture.class);
when(context.channel()).thenReturn(channel);
when(channel.write(anyString())).thenReturn(channelFuture);
}
private Map<String, Object> createRandomEvent() {
Event event = new Event() {
{
setWhat("1");
setWhen( System.currentTimeMillis() );
setData("3");
setTags("4");
}
};
return event.toMap();
}
private FullHttpRequest createPutOneEventRequest(Map<String, Object> event) throws IOException {
List<Map<String, Object>> events = new ArrayList<Map<String, Object>>();
events.add(event);
final String requestBody = new ObjectMapper().writeValueAsString(events.get(0));
return createRequest(HttpMethod.POST, "", requestBody);
}
private FullHttpRequest createRequest(HttpMethod method, String uri, String requestBody) {
DefaultFullHttpRequest rawRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, method, "/v2.0/" + TENANT + "/events/" + uri);
rawRequest.headers().set("tenantId", TENANT);
if (!requestBody.equals(""))
rawRequest.content().writeBytes(Unpooled.copiedBuffer(requestBody.getBytes()));
return HttpRequestWithDecodedQueryParams.create(rawRequest);
}
@Test
public void testElasticSearchInsertCalledWhenPut() throws Exception {
List<Map<String, Object>> events = new ArrayList<Map<String, Object>>();
Map<String, Object> event = createRandomEvent();
events.add(event);
handler.handle(context, createPutOneEventRequest(event));
verify(searchIO).insert(TENANT, events);
}
@Test
public void testInvalidRequestBody() throws Exception {
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, createRequest(HttpMethod.POST, "", "{\"xxx\": \"yyy\"}"));
verify(searchIO, never()).insert(anyString(), anyList());
verify(channel).write(argument.capture());
String errorResponseBody = argument.getValue().content().toString(Charset.defaultCharset());
ErrorResponse errorResponse = getErrorResponse(errorResponseBody);
assertEquals("Number of errors invalid", 1, errorResponse.getErrors().size());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testMalformedEventPut() throws Exception {
final String malformedJSON = "{\"when\":, what]}"; //causes JsonParseException
handler.handle(context, createRequest(HttpMethod.POST, "", malformedJSON));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
verify(searchIO, never()).insert(anyString(), anyList());
verify(channel).write(argument.capture());
String errorResponseBody = argument.getValue().content().toString(Charset.defaultCharset());
ErrorResponse errorResponse = getErrorResponse(errorResponseBody);
assertEquals("Number of errors invalid", 1, errorResponse.getErrors().size());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testEmptyPut() throws Exception {
Map<String, Object> event = new HashMap<String, Object>();
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, createPutOneEventRequest(event));
verify(searchIO, never()).insert(anyString(), anyList());
verify(channel).write(argument.capture());
String errorResponseBody = argument.getValue().content().toString(Charset.defaultCharset());
ErrorResponse errorResponse = getErrorResponse(errorResponseBody);
System.out.println(errorResponse);
assertEquals("Number of errors invalid", 2, errorResponse.getErrors().size());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testEmptyWhatField() throws Exception {
Map<String, Object> event = new HashMap<String, Object>();
event.put(Event.FieldLabels.what.name(), "");
event.put(Event.FieldLabels.when.name(), System.currentTimeMillis());
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, createPutOneEventRequest(event));
verify(searchIO, never()).insert(anyString(), anyList());
verify(channel).write(argument.capture());
String errorResponseBody = argument.getValue().content().toString(Charset.defaultCharset());
ErrorResponse errorResponse = getErrorResponse(errorResponseBody);
assertEquals("Number of errors invalid", 1, errorResponse.getErrors().size());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid error message", "may not be empty", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testWhenFieldInThePast() throws Exception {
long collectionTimeInPast = new DefaultClockImpl().now().getMillis() - 10000
- Configuration.getInstance().getLongProperty( CoreConfig.BEFORE_CURRENT_COLLECTIONTIME_MS );
Map<String, Object> event = new HashMap<String, Object>();
event.put(Event.FieldLabels.what.name(), "xxxx");
event.put(Event.FieldLabels.when.name(), collectionTimeInPast);
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, createPutOneEventRequest(event));
verify(searchIO, never()).insert(anyString(), anyList());
verify(channel).write(argument.capture());
String errorResponseBody = argument.getValue().content().toString(Charset.defaultCharset());
ErrorResponse errorResponse = getErrorResponse(errorResponseBody);
assertEquals("Number of errors invalid", 1, errorResponse.getErrors().size());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid error message", "Out of bounds. Cannot be more than 259200000 milliseconds into the past." +
" Cannot be more than 600000 milliseconds into the future", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testWhenFieldInTheFuture() throws Exception {
long collectionTimeInFuture = new DefaultClockImpl().now().getMillis() + 10000
+ Configuration.getInstance().getLongProperty( CoreConfig.AFTER_CURRENT_COLLECTIONTIME_MS );
Map<String, Object> event = new HashMap<String, Object>();
event.put(Event.FieldLabels.what.name(), "xxxx");
event.put(Event.FieldLabels.when.name(), collectionTimeInFuture);
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, createPutOneEventRequest(event));
verify(searchIO, never()).insert(anyString(), anyList());
verify(channel).write(argument.capture());
String errorResponseBody = argument.getValue().content().toString(Charset.defaultCharset());
ErrorResponse errorResponse = getErrorResponse(errorResponseBody);
assertEquals("Number of errors invalid", 1, errorResponse.getErrors().size());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid error message", "Out of bounds. Cannot be more than 259200000 milliseconds into the past." +
" Cannot be more than 600000 milliseconds into the future", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testMinimumEventPut() throws Exception {
Map<String, Object> event = new HashMap<String, Object>();
event.put(Event.FieldLabels.data.name(), "data");
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, createPutOneEventRequest(event));
verify(searchIO, never()).insert(anyString(), anyList());
verify(channel).write(argument.capture());
String errorResponseBody = argument.getValue().content().toString(Charset.defaultCharset());
ErrorResponse errorResponse = getErrorResponse(errorResponseBody);
assertEquals("Number of errors invalid", 2, errorResponse.getErrors().size());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
}