/*
* Copyright 2014 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.codahale.metrics.Meter;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.gson.Gson;
import com.google.gson.internal.LazilyParsedNumber;
import com.netflix.astyanax.serializers.AbstractSerializer;
import com.rackspacecloud.blueflood.inputs.formats.AggregatedPayload;
import com.rackspacecloud.blueflood.io.Instrumentation;
import com.rackspacecloud.blueflood.io.serializers.Serializers;
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.*;
import com.rackspacecloud.blueflood.utils.DefaultClockImpl;
import com.rackspacecloud.blueflood.utils.TimeValue;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static com.rackspacecloud.blueflood.TestUtils.getJsonFromFile;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
public class HttpAggregatedIngestionHandlerTest extends HandlerTestsBase {
private String payloadJson;
private HttpAggregatedIngestionHandler handler;
private HttpMetricsIngestionServer.Processor processor;
private ChannelHandlerContext context;
private Channel channel;
private ChannelFuture channelFuture;
private Meter ingestedMetrics;
private Meter ingestedDelayedMetrics;
private final String postfix = ".post";
private static final String TENANT = "tenant";
@Before
public void setup() throws Exception {
processor = mock(HttpMetricsIngestionServer.Processor.class);
handler = new HttpAggregatedIngestionHandler(processor, new TimeValue(5, TimeUnit.SECONDS));
channel = mock(Channel.class);
context = mock(ChannelHandlerContext.class);
channelFuture = mock(ChannelFuture.class);
when(context.channel()).thenReturn(channel);
when(channel.write(anyString())).thenReturn(channelFuture);
ListenableFuture mockFuture = mock(ListenableFuture.class);
when(processor.apply(any(MetricsCollection.class))).thenReturn(mockFuture);
when(mockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(new ArrayList<Boolean>());
ingestedMetrics = Instrumentation.getIngestedMetricsMeter(TENANT);
ingestedDelayedMetrics = Instrumentation.getIngestedDelayedMetricsMeter(TENANT);
}
@Before
public void buildPayload() throws IOException {
payloadJson = getJsonFromFile("sample_payload.json", postfix);
}
@Test(expected = NumberFormatException.class)
public void testExpectedGsonConversionFailure() {
new LazilyParsedNumber("2.321").longValue();
}
@Test
public void testGsonNumberConversions() {
AggregatedPayload payload = AggregatedPayload.create(payloadJson);
Number doubleNum = new LazilyParsedNumber("2.321");
assertEquals( Double.parseDouble( "2.321" ), PreaggregateConversions.resolveNumber( doubleNum ) );
Number longNum = new LazilyParsedNumber("12345");
assertEquals(Long.parseLong("12345"), PreaggregateConversions.resolveNumber(longNum));
}
@Test
public void testCounters() {
AggregatedPayload payload = AggregatedPayload.create(payloadJson);
Collection<PreaggregatedMetric> counters = PreaggregateConversions.convertCounters("1", 1, 15000, payload.getCounters());
assertEquals( 6, counters.size() );
ensureSerializability(counters);
}
@Test
public void testGauges() {
AggregatedPayload payload = AggregatedPayload.create(payloadJson);
Collection<PreaggregatedMetric> gauges = PreaggregateConversions.convertGauges("1", 1, payload.getGauges());
assertEquals( 4, gauges.size() );
ensureSerializability(gauges);
}
@Test
public void testSets() {
AggregatedPayload payload = AggregatedPayload.create(payloadJson);
Collection<PreaggregatedMetric> sets = PreaggregateConversions.convertSets("1", 1, payload.getSets());
assertEquals( 2, sets.size() );
ensureSerializability(sets);
}
@Test
public void testTimers() {
AggregatedPayload payload = AggregatedPayload.create(payloadJson);
Collection<PreaggregatedMetric> timers = PreaggregateConversions.convertTimers("1", 1, payload.getTimers());
assertEquals( 4, timers.size() );
ensureSerializability(timers);
}
// ok. while we're out it, let's test serialization. Just for fun. The reasoning is that these metrics
// follow a different creation path that what we currently have in tests.
private static void ensureSerializability(Collection<PreaggregatedMetric> metrics) {
for (PreaggregatedMetric metric : metrics) {
AbstractSerializer serializer = Serializers.serializerFor(metric.getMetricValue().getClass());
serializer.toByteBuffer(metric.getMetricValue());
}
}
@Test
public void testEmptyRequest() throws IOException {
String requestBody = "";
FullHttpRequest request = createIngestRequest(requestBody);
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 error message", "Invalid request body", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testEmptyJsonRequest() throws IOException {
String requestBody = "{}"; //causes JsonMappingException
FullHttpRequest request = createIngestRequest(requestBody);
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
verify(channel).write(argument.capture());
String errorResponseBody = argument.getValue().content().toString(Charset.defaultCharset());
ErrorResponse errorResponse = getErrorResponse(errorResponseBody);
assertEquals("Number of errors invalid", 3, errorResponse.getErrors().size());
assertEquals("Invalid tenant", "", errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testEmptyTenantId() throws IOException {
BluefloodGauge gauge = new BluefloodGauge("gauge.a.b", 5);
FullHttpRequest request = createIngestRequest(createRequestBody("",
new DefaultClockImpl().now().getMillis(), 0 , new BluefloodGauge[]{gauge}, null, null, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 error message", "may not be empty", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid source", "tenantId", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", "", errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid metric name", "", errorResponse.getErrors().get(0).getMetricName());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testInvalidFlushInterval() throws IOException {
BluefloodGauge gauge = new BluefloodGauge("gauge.a.b", 5);
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), -1 , new BluefloodGauge[]{gauge}, null, null, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 error message", "must be between 0 and 9223372036854775807", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid source", "flushInterval", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid metric name", "", errorResponse.getErrors().get(0).getMetricName());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testCollectionTimeInPast() throws IOException {
BluefloodGauge gauge = new BluefloodGauge("gauge.a.b", 5);
long collectionTimeInPast = new DefaultClockImpl().now().getMillis() - 1000
- Configuration.getInstance().getLongProperty( CoreConfig.BEFORE_CURRENT_COLLECTIONTIME_MS );
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
collectionTimeInPast, 0 , new BluefloodGauge[]{gauge}, null, null, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 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 source", "timestamp", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid metric name", "", errorResponse.getErrors().get(0).getMetricName());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testCollectionTimeInFuture() throws IOException {
BluefloodGauge gauge = new BluefloodGauge("gauge.a.b", 5);
long collectionTimeInFuture = new DefaultClockImpl().now().getMillis() + 1000
+ Configuration.getInstance().getLongProperty( CoreConfig.AFTER_CURRENT_COLLECTIONTIME_MS );
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
collectionTimeInFuture, 0 , new BluefloodGauge[]{gauge}, null, null, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 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 source", "timestamp", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid metric name", "", errorResponse.getErrors().get(0).getMetricName());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testAggregatedMetricsNotSet() throws IOException {
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0 , null, null, null, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 error message", "At least one of the aggregated metrics(gauges, counters, timers, sets) " +
"are expected", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid source", "", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid metric name", "", errorResponse.getErrors().get(0).getMetricName());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testGaugeEmptyMetricName() throws IOException {
BluefloodGauge gauge = new BluefloodGauge("", 5);
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0, new BluefloodGauge[]{gauge}, null, null, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 error message", "may not be empty", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid source", "gauges[0].name", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testGaugeEmptyMetricValue() throws IOException {
String metricName = "gauge.a.b";
BluefloodGauge gauge = new BluefloodGauge(metricName, null);
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0, new BluefloodGauge[]{gauge}, null, null, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 error message", "may not be null", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid source", "gauges[0].value", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid metric name", metricName, errorResponse.getErrors().get(0).getMetricName());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testCounterEmptyMetricName() throws IOException {
BluefloodCounter counter = new BluefloodCounter("", 5, 0.1);
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0, null, new BluefloodCounter[]{counter}, null, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 error message", "may not be empty", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid source", "counters[0].name", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testCounterEmptyMetricValue() throws IOException {
String metricName = "counter.a.b";
BluefloodCounter counter = new BluefloodCounter(metricName, null, 0.1);
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0, null, new BluefloodCounter[]{counter}, null, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 error message", "may not be null", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid source", "counters[0].value", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid metric name", metricName, errorResponse.getErrors().get(0).getMetricName());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testCounterEmptyMetricRate() throws IOException {
String metricName = "counter.a.b";
BluefloodCounter counter = new BluefloodCounter(metricName, 5, null);
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0, null, new BluefloodCounter[]{counter}, null, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 error message", "may not be null", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid source", "counters[0].rate", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid metric name", metricName, errorResponse.getErrors().get(0).getMetricName());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testTimerEmptyMetricName() throws IOException {
BluefloodTimer timer = new BluefloodTimer("", 5);
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0, null, null, new BluefloodTimer[]{timer}, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 error message", "may not be empty", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid source", "timers[0].name", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testTimerEmptyMetricCount() throws IOException {
String metricName = "timer.a.b";
BluefloodTimer timer = new BluefloodTimer(metricName, null);
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0, null, null, new BluefloodTimer[]{timer}, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 error message", "may not be null", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid source", "timers[0].count", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid metric name", metricName, errorResponse.getErrors().get(0).getMetricName());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testSetEmptyMetricName() throws IOException {
BluefloodSet sets = new BluefloodSet("", new String[]{});
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0, null, null, null, new BluefloodSet[]{sets}));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
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 error message", "may not be empty", errorResponse.getErrors().get(0).getMessage());
assertEquals("Invalid source", "sets[0].name", errorResponse.getErrors().get(0).getSource());
assertEquals("Invalid tenant", TENANT, errorResponse.getErrors().get(0).getTenantId());
assertEquals("Invalid status", HttpResponseStatus.BAD_REQUEST, argument.getValue().getStatus());
}
@Test
public void testValidGauge() throws IOException {
BluefloodGauge gauge = new BluefloodGauge("gauge.a.b", 5);
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0, new BluefloodGauge[]{gauge}, null, null, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
verify(channel).write(argument.capture());
String responseBody = argument.getValue().content().toString(Charset.defaultCharset());
assertEquals("Invalid response", "", responseBody);
assertEquals("Invalid status", HttpResponseStatus.OK, argument.getValue().getStatus());
}
@Test
public void testValidCounter() throws IOException {
BluefloodCounter counter = new BluefloodCounter("counter.a.b", 5, 0.1);
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0, null, new BluefloodCounter[]{counter}, null, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
verify(channel).write(argument.capture());
String responseBody = argument.getValue().content().toString(Charset.defaultCharset());
assertEquals("Invalid response", "", responseBody);
assertEquals("Invalid status", HttpResponseStatus.OK, argument.getValue().getStatus());
}
@Test
public void testValidTimer() throws IOException {
BluefloodTimer timer = new BluefloodTimer("timer.a.b", 5);
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0, null, null, new BluefloodTimer[]{timer}, null));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
verify(channel).write(argument.capture());
String responseBody = argument.getValue().content().toString(Charset.defaultCharset());
assertEquals("Invalid response", "", responseBody);
assertEquals("Invalid status", HttpResponseStatus.OK, argument.getValue().getStatus());
}
@Test
public void testValidSet() throws IOException {
BluefloodSet set = new BluefloodSet("set.a.b", new String[]{"", ""});;
FullHttpRequest request = createIngestRequest(createRequestBody(TENANT,
new DefaultClockImpl().now().getMillis(), 0, null, null, null, new BluefloodSet[]{set}));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
verify(channel).write(argument.capture());
String responseBody = argument.getValue().content().toString(Charset.defaultCharset());
assertEquals("Invalid response", "", responseBody);
assertEquals("Invalid status", HttpResponseStatus.OK, argument.getValue().getStatus());
}
@Test
public void perTenantMetricsOn_emptyRequest_shouldNotRecordAnything() throws IOException {
String requestBody = "{}";
FullHttpRequest request = createIngestRequest(requestBody);
long ingestedMetricsBefore = ingestedMetrics.getCount();
long ingestedDelayedMetricsBefore = ingestedDelayedMetrics.getCount();
HttpAggregatedIngestionHandler handler = spy(new HttpAggregatedIngestionHandler(processor, new TimeValue(5, TimeUnit.SECONDS), true));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
verify(channel).write(argument.capture());
verify(handler, never()).recordPerTenantMetrics(eq(TENANT), anyInt(), anyInt());
assertEquals("ingested metrics count", 0, ingestedMetrics.getCount() - ingestedMetricsBefore);
assertEquals("ingested delayed metrics count", 0, ingestedDelayedMetrics.getCount() - ingestedDelayedMetricsBefore);
}
@Test
public void perTenantMetricsOn_shouldRecordDelayedMetrics() throws Exception {
BluefloodSet set1 = new BluefloodSet("delayed.me.1.set.a.b", new String[]{"", ""});
BluefloodSet set2 = new BluefloodSet("delayed.me.2.set.a.b", new String[]{"", ""});
long delayedTime = new DefaultClockImpl().now().getMillis() - 100 -
Configuration.getInstance().getLongProperty(CoreConfig.ROLLUP_DELAY_MILLIS);
FullHttpRequest request = createIngestRequest(
createRequestBody(TENANT, delayedTime, 0, null, null,
null, new BluefloodSet[]{set1, set2}));
long ingestedMetricsBefore = ingestedMetrics.getCount();
long ingestedDelayedMetricsBefore = ingestedDelayedMetrics.getCount();
ListenableFuture<List<Boolean>> futures = mock(ListenableFuture.class);
List<Boolean> answers = new ArrayList<>();
answers.add(Boolean.TRUE);
when(processor.apply(any())).thenReturn(futures);
when(futures.get(anyLong(), any())).thenReturn(answers);
HttpAggregatedIngestionHandler handler = spy(new HttpAggregatedIngestionHandler(processor, new TimeValue(5, TimeUnit.SECONDS), true));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
verify(channel).write(argument.capture());
verify(handler, times(1)).recordPerTenantMetrics(eq(TENANT), eq(0), eq(2));
assertEquals("ingested metrics count", 0, ingestedMetrics.getCount() - ingestedMetricsBefore);
assertEquals("ingested delayed metrics count", 2, ingestedDelayedMetrics.getCount() - ingestedDelayedMetricsBefore);
}
@Test
public void perTenantMetricsOn_shouldRecordNonDelayedMetrics() throws Exception {
BluefloodSet set1 = new BluefloodSet("i.am.on.time.1.set.a.b", new String[]{"", ""});
BluefloodSet set2 = new BluefloodSet("i.am.on.time.2.set.a.b", new String[]{"", ""});
long timestamp = new DefaultClockImpl().now().getMillis();
FullHttpRequest request = createIngestRequest(
createRequestBody(TENANT, timestamp, 0, null, null,
null, new BluefloodSet[]{set1, set2}));
long ingestedMetricsBefore = ingestedMetrics.getCount();
long ingestedDelayedMetricsBefore = ingestedDelayedMetrics.getCount();
ListenableFuture<List<Boolean>> futures = mock(ListenableFuture.class);
List<Boolean> answers = new ArrayList<>();
answers.add(Boolean.TRUE);
when(processor.apply(any())).thenReturn(futures);
when(futures.get(anyLong(), any())).thenReturn(answers);
HttpAggregatedIngestionHandler handler = spy(new HttpAggregatedIngestionHandler(processor, new TimeValue(5, TimeUnit.SECONDS), true));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
verify(channel).write(argument.capture());
verify(handler, times(1)).recordPerTenantMetrics(eq(TENANT), eq(2), eq(0));
assertEquals("ingested metrics count", 2, ingestedMetrics.getCount() - ingestedMetricsBefore);
assertEquals("ingested delayed metrics count", 0, ingestedDelayedMetrics.getCount() - ingestedDelayedMetricsBefore);
}
@Test
public void perTenantMetricsOff_shouldNotRecordMetrics() throws Exception {
BluefloodSet set1 = new BluefloodSet("i.am.on.time.1.set.a.b", new String[]{"", ""});
BluefloodSet set2 = new BluefloodSet("i.am.on.time.2.set.a.b", new String[]{"", ""});
long timestamp = new DefaultClockImpl().now().getMillis();
FullHttpRequest request = createIngestRequest(
createRequestBody(TENANT, timestamp, 0, null, null,
null, new BluefloodSet[]{set1, set2}));
long ingestedMetricsBefore = ingestedMetrics.getCount();
long ingestedDelayedMetricsBefore = ingestedDelayedMetrics.getCount();
ListenableFuture<List<Boolean>> futures = mock(ListenableFuture.class);
List<Boolean> answers = new ArrayList<>();
answers.add(Boolean.TRUE);
when(processor.apply(any())).thenReturn(futures);
when(futures.get(anyLong(), any())).thenReturn(answers);
// turn off per tenant metrics tracking
HttpAggregatedIngestionHandler handler = spy(new HttpAggregatedIngestionHandler(processor, new TimeValue(5, TimeUnit.SECONDS), false));
ArgumentCaptor<FullHttpResponse> argument = ArgumentCaptor.forClass(FullHttpResponse.class);
handler.handle(context, request);
verify(channel).write(argument.capture());
verify(handler, times(1)).recordPerTenantMetrics(eq(TENANT), eq(2), eq(0));
assertEquals("ingested metrics count", 0, ingestedMetrics.getCount() - ingestedMetricsBefore);
assertEquals("ingested delayed metrics count", 0, ingestedDelayedMetrics.getCount() - ingestedDelayedMetricsBefore);
}
private String createRequestBody(String tenantId, long collectionTime, long flushInterval, BluefloodGauge[] gauges,
BluefloodCounter[] counters, BluefloodTimer[] timers, BluefloodSet[] sets) {
AggregatedPayload payload = new AggregatedPayload(tenantId, collectionTime, flushInterval,
gauges, counters, timers, sets);
return new Gson().toJson(payload, AggregatedPayload.class);
}
private FullHttpRequest createIngestRequest(String requestBody) {
return super.createPostRequest("/v2.0/" + TENANT + "/aggregated/", requestBody);
}
}