/* * Copyright 2016 KairosDB Authors * * 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 org.kairosdb.core.http.rest.json; import com.google.common.base.Charsets; import com.google.common.io.Resources; import org.joda.time.DateTimeZone; import org.junit.Before; import org.junit.Test; import org.kairosdb.core.aggregator.TestAggregatorFactory; import org.kairosdb.core.datastore.Duration; import org.kairosdb.core.datastore.QueryMetric; import org.kairosdb.core.datastore.TimeUnit; import org.kairosdb.core.exception.KairosDBException; import org.kairosdb.core.groupby.TestGroupByFactory; import org.kairosdb.core.http.rest.BeanValidationException; import org.kairosdb.core.http.rest.QueryException; import org.kairosdb.rollup.RollupTask; import java.io.IOException; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.Assert.fail; public class QueryParserTest { private QueryParser parser; @Before public void setup() throws KairosDBException { parser = new QueryParser(new TestAggregatorFactory(), new TestGroupByFactory(), new TestQueryPluginFactory()); } @Test public void test_absolute_dates() throws Exception { String json = Resources.toString(Resources.getResource("query-metric-absolute-dates-with-groupby.json"), Charsets.UTF_8); List<QueryMetric> results = parser.parseQueryMetric(json); assertThat(results.size(), equalTo(1)); QueryMetric queryMetric = results.get(0); assertThat(queryMetric.getName(), equalTo("abc.123")); assertThat(queryMetric.getStartTime(), equalTo(784041330L)); assertThat(queryMetric.getEndTime(), equalTo(788879730L)); assertThat(queryMetric.getAggregators().size(), equalTo(1)); assertThat(queryMetric.getGroupBys().size(), equalTo(2)); } @Test public void test_withNoAggregators() throws Exception { String json = Resources.toString(Resources.getResource("invalid-query-metric-no-aggregators.json"), Charsets.UTF_8); List<QueryMetric> results = parser.parseQueryMetric(json); assertThat(results.size(), equalTo(1)); QueryMetric queryMetric = results.get(0); assertThat(queryMetric.getName(), equalTo("abc.123")); assertThat(queryMetric.getStartTime(), equalTo(784041330L)); assertThat(queryMetric.getEndTime(), equalTo(788879730L)); assertThat(queryMetric.getAggregators().size(), equalTo(0)); assertThat(queryMetric.getGroupBys().size(), equalTo(2)); } @Test public void test_underscoreConverter() { assertThat(QueryParser.getUnderscorePropertyName("groupBy"), equalTo("group_by")); assertThat(QueryParser.getUnderscorePropertyName("groupByValue"), equalTo("group_by_value")); assertThat(QueryParser.getUnderscorePropertyName("ABC"), equalTo("_a_b_c")); } @Test(expected = BeanValidationException.class) public void test_noName() throws Exception { String json = Resources.toString(Resources.getResource("invalid-query-metric-no-name.json"), Charsets.UTF_8); parser.parseQueryMetric(json); } @Test public void test_noTags() throws Exception { String json = Resources.toString(Resources.getResource("query-metric-no-tags.json"), Charsets.UTF_8); List<QueryMetric> results = parser.parseQueryMetric(json); assertThat(results.size(), equalTo(1)); QueryMetric queryMetric = results.get(0); assertThat(queryMetric.getTags(), notNullValue()); } @Test public void test_oneTag() throws Exception { String json = Resources.toString(Resources.getResource("query-metric-one-tag.json"), Charsets.UTF_8); List<QueryMetric> results = parser.parseQueryMetric(json); assertThat(results.size(), equalTo(1)); QueryMetric queryMetric = results.get(0); assertThat(queryMetric.getTags(), notNullValue()); assertThat(queryMetric.getTags().get("host").size(), equalTo(1)); assertThat(queryMetric.getTags().get("host"), hasItem("bar")); } @Test public void test_twoTags() throws Exception { String json = Resources.toString(Resources.getResource("query-metric-two-tags.json"), Charsets.UTF_8); List<QueryMetric> results = parser.parseQueryMetric(json); assertThat(results.size(), equalTo(1)); QueryMetric queryMetric = results.get(0); assertThat(queryMetric.getCacheString(), equalTo("784041330:788879730:bob:host=bar:host=foo:")); assertThat(queryMetric.getTags(), notNullValue()); assertThat(queryMetric.getTags().get("host").size(), equalTo(2)); assertThat(queryMetric.getTags().get("host"), hasItem("bar")); assertThat(queryMetric.getTags().get("host"), hasItem("foo")); } @Test public void test_excludeTags() throws Exception { String json = Resources.toString(Resources.getResource("query-metric-exclude-tags.json"), Charsets.UTF_8); List<QueryMetric> results = parser.parseQueryMetric(json); assertThat(results.size(), equalTo(1)); QueryMetric queryMetric = results.get(0); assertThat(queryMetric.getCacheString(), equalTo("784041330:788879730:bob:host=bar:host=foo:")); assertThat(queryMetric.isExcludeTags(), equalTo(true)); assertThat(queryMetric.getTags(), notNullValue()); assertThat(queryMetric.getTags().get("host").size(), equalTo(2)); assertThat(queryMetric.getTags().get("host"), hasItem("bar")); assertThat(queryMetric.getTags().get("host"), hasItem("foo")); } @Test public void test_cacheTime_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-cache-time.json"), Charsets.UTF_8); assertBeanValidation(json, "cache_time must be greater than or equal to 0"); } @Test public void test_no_start_time_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-no-start_date.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].start_time relative or absolute time must be set"); } @Test public void test_absoluteStartTime_before_epoch_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("query-metric-start_absolute-before-epoch.json"), Charsets.UTF_8); parser.parseQueryMetric(json); } @Test public void test_relativeStartTime_before_epoch_valid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("query-metric-relative-startTime-before-epoch.json"), Charsets.UTF_8); parser.parseQueryMetric(json); } @Test public void test_absoluteEndTime_before_startTime_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-absoluteEndTime-less-than-startTime.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].end_time must be greater than the start time"); } @Test public void test_relativeEndTime_before_startTime_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-relativeEndTime-less-than-startTime.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].end_time must be greater than the start time"); } @Test public void test_relativeStartTime_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-relative-startTime-value.json"), Charsets.UTF_8); assertBeanValidation(json, "start_relative.value must be greater than or equal to 1"); } @Test public void test_relativeEndTime_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-relative-endTime-value.json"), Charsets.UTF_8); assertBeanValidation(json, "end_relative.value must be greater than or equal to 1"); } @Test public void test_missingMetric_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-no-metric.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[] must have a size of at least 1"); } @Test public void test_emptyMetricName_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-empty-name.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].name may not be empty"); } @Test public void test_nullTagValueInArray_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("query-metric-null-tag-value-in-array.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].tags[0].host value must not be null or empty"); } @Test public void test_emptyTagName_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-tag-empty-name.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].tags[0] name must not be empty"); } @Test public void test_emptyTagValueInArray_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-tag-empty-value-in-array.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].tags[0].host value must not be null or empty"); } @Test public void test_nullTagValue_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("query-metric-null-tag-value.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].tags[0].host value must not be null or empty"); } @Test public void test_emptyTagValue_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-tag-empty-value.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].tags[0].host value must not be null or empty"); } @Test public void test_aggregator_missingName_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-aggregators-no-name.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].aggregators[0] must have a name"); } @Test public void test_aggregator_emptyName_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-aggregators-empty-name.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].aggregators[0] must have a name"); } @Test public void test_aggregator_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-aggregators.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].aggregators[0].bogus invalid aggregator name"); } @Test public void test_aggregator_sampling_value_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-aggregators-sampling-value.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].aggregators[0].sampling.value must be greater than or equal to 1"); } @Test public void test_aggregator_sampling_timezone_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-aggregators-sampling-timezone.json"), Charsets.UTF_8); assertBeanValidation(json, "query.bogus is not a valid time zone, must be one of " + DateTimeZone.getAvailableIDs()); } @Test public void test_aggregator_sum_noSampling_valid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-aggregators-sum-no-sampling.json"), Charsets.UTF_8); parser.parseQueryMetric(json); } @Test public void test_aggregator_div_no_divisor_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-aggregators-div-no-divisor.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].aggregators[0].m_divisor may not be zero"); } @Test public void test_aggregator_div_divisor_zero_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-aggregators-div-divisor-zero.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].aggregators[0].m_divisor may not be zero"); } @Test public void test_aggregator_percentile_no_percentile_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-aggregators-percentile-no-percentile.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].aggregators[0].percentile may not be zero"); } @Test public void test_aggregator_percentile_percentile_zero_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-aggregators-percentile-percentile-zero.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].aggregators[0].percentile may not be zero"); } @Test public void test_aggregator_percentile_percentile_numberFormat_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-aggregators-percentile-percentile.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].aggregators[0].percentile multiple points"); } @Test public void test_aggregator_sampling_unit_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-aggregators-sampling-unit.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].aggregators[0].bogus is not a valid time unit, must be one of MILLISECONDS,SECONDS,MINUTES,HOURS,DAYS,WEEKS,MONTHS,YEARS"); } @Test public void test_groupby_missingName_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-group_by-no-name.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].group_by[0] must have a name"); } @Test public void test_groupby_emptyName_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-group_by-empty-name.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].group_by[1] must have a name"); } @Test public void test_groupby_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-group_by.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].group_by[0].bogus invalid group_by name"); } @Test public void test_groupby_tag_missing_tags_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-group_by-tag-missing-tags.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].group_by[0].tags may not be null"); } @Test public void test_groupby_tag_emtpy_tags_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-group_by-tag-empty-tags.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].group_by[0].tags may not be empty"); } @Test public void test_groupby_time_missing_range_size_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-group_by-time-missing-range_size.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].group_by[0].rangeSize may not be null"); } @Test public void test_groupby_time_range_size_value_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-group_by-time-range_size_value.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].group_by[0].range_size.value must be greater than or equal to 1"); } @Test public void test_groupby_time_range_size_unit_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-group_by-time-range_size_unit.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].group_by[0].bogus is not a valid time unit, must be one of MILLISECONDS,SECONDS,MINUTES,HOURS,DAYS,WEEKS,MONTHS,YEARS"); } @Test public void test_groupby_time_missing_group_count_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-group_by-time-missing-group_count.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].group_by[0].groupCount must be greater than or equal to 1"); } @Test public void test_groupby_value_range_size_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-query-metric-group_by-value-range_size.json"), Charsets.UTF_8); assertBeanValidation(json, "query.metric[0].group_by[0].range_size.value must be greater than or equal to 1"); } @Test public void test_parseRollUpTask_empty_name_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-rollup-no-name-empty.json"), Charsets.UTF_8); assertRollupBeanValidation(json, "name may not be empty"); } @Test public void test_parseRollUpTask_no_execution_interval_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-rollup-no-execution_interval.json"), Charsets.UTF_8); assertRollupBeanValidation(json, "executionInterval may not be null"); } @Test public void test_parseRollUpTask_empty_saveAs_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-rollup-no-saveAs.json"), Charsets.UTF_8); assertRollupBeanValidation(json, "rollup[0].saveAs may not be empty"); } /** Test the parsing of the query. Only a sanity check since it parseRollupTask reuses parseQuery. */ @Test public void test_parseRollUpTask_empty_query_time_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-rollup-no-query_time.json"), Charsets.UTF_8); assertRollupBeanValidation(json, "rollup[0].query.metric[0].start_time relative or absolute time must be set"); } @Test public void test_parseRollUpTask_noRangeAggregator_invalid() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("invalid-rollup-no-range-aggregator.json"), Charsets.UTF_8); assertRollupBeanValidation(json, "rollup[0].query[0].aggregator At least one aggregator must be a range aggregator"); } @Test public void test_parseRollupTask() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("rolluptask1.json"), Charsets.UTF_8); RollupTask task = parser.parseRollupTask(json); assertThat(task.getName(), equalTo("Rollup1")); assertThat(task.getExecutionInterval(), equalTo(new Duration(1, TimeUnit.HOURS))); assertThat(task.getRollups().size(), equalTo(1)); assertThat(task.getRollups().get(0).getSaveAs(), equalTo("kairosdb.http.query_time_rollup")); assertThat(task.getRollups().get(0).getQueryMetrics().size(), equalTo(1)); assertThat(task.getRollups().get(0).getQueryMetrics().get(0).getName(), equalTo("kairosdb.http.query_time")); } @Test public void test_parseRollupTasks() throws IOException, QueryException { String json = Resources.toString(Resources.getResource("rolluptasks.json"), Charsets.UTF_8); List<RollupTask> tasks = parser.parseRollupTasks(json); assertThat(tasks.size(), equalTo(2)); assertThat(tasks.get(0).getName(), equalTo("Rollup1")); assertThat(tasks.get(0).getExecutionInterval(), equalTo(new Duration(1, TimeUnit.HOURS))); assertThat(tasks.get(0).getRollups().size(), equalTo(1)); assertThat(tasks.get(0).getRollups().get(0).getSaveAs(), equalTo("kairosdb.http.query_time_rollup")); assertThat(tasks.get(0).getRollups().get(0).getQueryMetrics().size(), equalTo(1)); assertThat(tasks.get(0).getRollups().get(0).getQueryMetrics().get(0).getName(), equalTo("kairosdb.http.query_time")); assertThat(tasks.get(1).getName(), equalTo("Rollup2")); assertThat(tasks.get(1).getExecutionInterval(), equalTo(new Duration(1, TimeUnit.MINUTES))); assertThat(tasks.get(1).getRollups().size(), equalTo(1)); assertThat(tasks.get(1).getRollups().get(0).getSaveAs(), equalTo("kairosdb.http.foo_rollup")); assertThat(tasks.get(1).getRollups().get(0).getQueryMetrics().size(), equalTo(1)); assertThat(tasks.get(1).getRollups().get(0).getQueryMetrics().get(0).getName(), equalTo("kairosdb.http.foo")); } private void assertRollupBeanValidation(String json, String expectedMessage) { try { parser.parseRollupTask(json); fail("Expected BeanValidationException"); } catch (QueryException e) { fail("Expected BeanValidationException"); } catch (BeanValidationException e) { assertThat(e.getErrorMessages().size(), equalTo(1)); assertThat(e.getErrorMessages().get(0), equalTo(expectedMessage)); } } private void assertBeanValidation(String json, String expectedMessage) { try { parser.parseQueryMetric(json); fail("Expected BeanValidationException"); } catch (QueryException e) { fail("Expected BeanValidationException"); } catch (BeanValidationException e) { assertThat(e.getErrorMessages().size(), equalTo(1)); assertThat(e.getErrorMessages().get(0), equalTo(expectedMessage)); } } }