403 lines
17 KiB
Python
403 lines
17 KiB
Python
#
|
|
# Copyright Ericsson AB 2013. All rights reserved
|
|
#
|
|
# Authors: Ildiko Vancsa <ildiko.vancsa@ericsson.com>
|
|
# Balazs Gibizer <balazs.gibizer@ericsson.com>
|
|
#
|
|
# 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.
|
|
"""Test the methods related to complex query."""
|
|
import datetime
|
|
|
|
import fixtures
|
|
import jsonschema
|
|
import mock
|
|
import wsme
|
|
|
|
from ceilometer.alarm.storage import models as alarm_models
|
|
from ceilometer.api.controllers import v2 as api
|
|
from ceilometer.openstack.common import test
|
|
from ceilometer.storage import models
|
|
|
|
|
|
class FakeComplexQuery(api.ValidatedComplexQuery):
|
|
def __init__(self, db_model, additional_name_mapping=None, metadata=False):
|
|
super(FakeComplexQuery, self).__init__(query=None,
|
|
db_model=db_model,
|
|
additional_name_mapping=(
|
|
additional_name_mapping or
|
|
{}),
|
|
metadata_allowed=metadata)
|
|
|
|
|
|
sample_name_mapping = {"resource": "resource_id",
|
|
"meter": "counter_name",
|
|
"type": "counter_type",
|
|
"unit": "counter_unit",
|
|
"volume": "counter_volume"}
|
|
|
|
|
|
class TestComplexQuery(test.BaseTestCase):
|
|
def setUp(self):
|
|
super(TestComplexQuery, self).setUp()
|
|
self.useFixture(fixtures.MonkeyPatch(
|
|
'pecan.response', mock.MagicMock()))
|
|
self.query = FakeComplexQuery(models.Sample,
|
|
sample_name_mapping,
|
|
True)
|
|
self.query_alarm = FakeComplexQuery(alarm_models.Alarm)
|
|
self.query_alarmchange = FakeComplexQuery(
|
|
alarm_models.AlarmChange)
|
|
|
|
def test_replace_isotime_utc(self):
|
|
filter_expr = {"=": {"timestamp": "2013-12-05T19:38:29Z"}}
|
|
self.query._replace_isotime_with_datetime(filter_expr)
|
|
self.assertEqual(datetime.datetime(2013, 12, 5, 19, 38, 29),
|
|
filter_expr["="]["timestamp"])
|
|
|
|
def test_replace_isotime_timezone_removed(self):
|
|
filter_expr = {"=": {"timestamp": "2013-12-05T20:38:29+01:00"}}
|
|
self.query._replace_isotime_with_datetime(filter_expr)
|
|
self.assertEqual(datetime.datetime(2013, 12, 5, 20, 38, 29),
|
|
filter_expr["="]["timestamp"])
|
|
|
|
def test_replace_isotime_wrong_syntax(self):
|
|
filter_expr = {"=": {"timestamp": "not a valid isotime string"}}
|
|
self.assertRaises(wsme.exc.ClientSideError,
|
|
self.query._replace_isotime_with_datetime,
|
|
filter_expr)
|
|
|
|
def test_replace_isotime_in_complex_filter(self):
|
|
filter_expr = {"and": [{"=": {"timestamp": "2013-12-05T19:38:29Z"}},
|
|
{"=": {"timestamp": "2013-12-06T19:38:29Z"}}]}
|
|
self.query._replace_isotime_with_datetime(filter_expr)
|
|
self.assertEqual(datetime.datetime(2013, 12, 5, 19, 38, 29),
|
|
filter_expr["and"][0]["="]["timestamp"])
|
|
self.assertEqual(datetime.datetime(2013, 12, 6, 19, 38, 29),
|
|
filter_expr["and"][1]["="]["timestamp"])
|
|
|
|
def test_replace_isotime_in_complex_filter_with_unbalanced_tree(self):
|
|
subfilter = {"and": [{"=": {"project_id": 42}},
|
|
{"=": {"timestamp": "2013-12-06T19:38:29Z"}}]}
|
|
|
|
filter_expr = {"or": [{"=": {"timestamp": "2013-12-05T19:38:29Z"}},
|
|
subfilter]}
|
|
|
|
self.query._replace_isotime_with_datetime(filter_expr)
|
|
self.assertEqual(datetime.datetime(2013, 12, 5, 19, 38, 29),
|
|
filter_expr["or"][0]["="]["timestamp"])
|
|
self.assertEqual(datetime.datetime(2013, 12, 6, 19, 38, 29),
|
|
filter_expr["or"][1]["and"][1]["="]["timestamp"])
|
|
|
|
def test_convert_operator_to_lower_case(self):
|
|
filter_expr = {"AND": [{"=": {"project_id": 42}},
|
|
{"=": {"project_id": 44}}]}
|
|
self.query._convert_operator_to_lower_case(filter_expr)
|
|
self.assertEqual("and", filter_expr.keys()[0])
|
|
|
|
filter_expr = {"Or": [{"=": {"project_id": 43}},
|
|
{"anD": [{"=": {"project_id": 44}},
|
|
{"=": {"project_id": 42}}]}]}
|
|
self.query._convert_operator_to_lower_case(filter_expr)
|
|
self.assertEqual("or", filter_expr.keys()[0])
|
|
self.assertEqual("and", filter_expr["or"][1].keys()[0])
|
|
|
|
def test_invalid_filter_misstyped_field_name_samples(self):
|
|
filter = {"=": {"project_id11": 42}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_invalid_filter_misstyped_field_name_alarms(self):
|
|
filter = {"=": {"enabbled": True}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query_alarm._validate_filter,
|
|
filter)
|
|
|
|
def test_invalid_filter_misstyped_field_name_alarmchange(self):
|
|
filter = {"=": {"tpe": "rule change"}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query_alarmchange._validate_filter,
|
|
filter)
|
|
|
|
def test_invalid_complex_filter_wrong_field_names(self):
|
|
filter = {"and":
|
|
[{"=": {"non_existing_field": 42}},
|
|
{"=": {"project_id": 42}}]}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
filter = {"and":
|
|
[{"=": {"project_id": 42}},
|
|
{"=": {"non_existing_field": 42}}]}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query_alarm._validate_filter,
|
|
filter)
|
|
|
|
filter = {"and":
|
|
[{"=": {"project_id11": 42}},
|
|
{"=": {"project_id": 42}}]}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query_alarmchange._validate_filter,
|
|
filter)
|
|
|
|
filter = {"or":
|
|
[{"=": {"non_existing_field": 42}},
|
|
{"and":
|
|
[{"=": {"project_id": 44}},
|
|
{"=": {"project_id": 42}}]}]}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
filter = {"or":
|
|
[{"=": {"project_id": 43}},
|
|
{"and":
|
|
[{"=": {"project_id": 44}},
|
|
{"=": {"non_existing_field": 42}}]}]}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query_alarm._validate_filter,
|
|
filter)
|
|
|
|
def test_convert_orderby(self):
|
|
orderby = []
|
|
self.query._convert_orderby_to_lower_case(orderby)
|
|
self.assertEqual([], orderby)
|
|
|
|
orderby = [{"project_id": "DESC"}]
|
|
self.query._convert_orderby_to_lower_case(orderby)
|
|
self.assertEqual([{"project_id": "desc"}], orderby)
|
|
|
|
orderby = [{"project_id": "ASC"}, {"resource_id": "DESC"}]
|
|
self.query._convert_orderby_to_lower_case(orderby)
|
|
self.assertEqual([{"project_id": "asc"}, {"resource_id": "desc"}],
|
|
orderby)
|
|
|
|
def test_validate_orderby_empty_direction(self):
|
|
orderby = [{"project_id": ""}]
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_orderby,
|
|
orderby)
|
|
orderby = [{"project_id": "asc"}, {"resource_id": ""}]
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_orderby,
|
|
orderby)
|
|
|
|
def test_validate_orderby_wrong_order_string(self):
|
|
orderby = [{"project_id": "not a valid order"}]
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_orderby,
|
|
orderby)
|
|
|
|
def test_validate_orderby_wrong_multiple_item_order_string(self):
|
|
orderby = [{"project_id": "not a valid order"}, {"resource_id": "ASC"}]
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_orderby,
|
|
orderby)
|
|
|
|
def test_validate_orderby_empty_field_name(self):
|
|
orderby = [{"": "ASC"}]
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_orderby,
|
|
orderby)
|
|
orderby = [{"project_id": "asc"}, {"": "desc"}]
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_orderby,
|
|
orderby)
|
|
|
|
def test_validate_orderby_wrong_field_name(self):
|
|
orderby = [{"project_id11": "ASC"}]
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_orderby,
|
|
orderby)
|
|
|
|
def test_validate_orderby_wrong_field_name_multiple_item_orderby(self):
|
|
orderby = [{"project_id": "asc"}, {"resource_id11": "ASC"}]
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_orderby,
|
|
orderby)
|
|
|
|
def test_validate_orderby_metadata_is_not_allowed(self):
|
|
orderby = [{"metadata.display_name": "asc"}]
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_orderby,
|
|
orderby)
|
|
|
|
|
|
class TestFilterSyntaxValidation(test.BaseTestCase):
|
|
def setUp(self):
|
|
super(TestFilterSyntaxValidation, self).setUp()
|
|
self.query = FakeComplexQuery(models.Sample,
|
|
sample_name_mapping,
|
|
True)
|
|
|
|
def test_simple_operator(self):
|
|
filter = {"=": {"project_id": "string_value"}}
|
|
self.query._validate_filter(filter)
|
|
|
|
filter = {"=>": {"project_id": "string_value"}}
|
|
self.query._validate_filter(filter)
|
|
|
|
def test_valid_value_types(self):
|
|
filter = {"=": {"project_id": "string_value"}}
|
|
self.query._validate_filter(filter)
|
|
|
|
filter = {"=": {"project_id": 42}}
|
|
self.query._validate_filter(filter)
|
|
|
|
filter = {"=": {"project_id": 3.14}}
|
|
self.query._validate_filter(filter)
|
|
|
|
filter = {"=": {"project_id": True}}
|
|
self.query._validate_filter(filter)
|
|
|
|
filter = {"=": {"project_id": False}}
|
|
self.query._validate_filter(filter)
|
|
|
|
def test_invalid_simple_operator(self):
|
|
filter = {"==": {"project_id": "string_value"}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
filter = {"": {"project_id": "string_value"}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_more_than_one_operator_is_invalid(self):
|
|
filter = {"=": {"project_id": "string_value"},
|
|
"<": {"": ""}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_empty_expression_is_invalid(self):
|
|
filter = {}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_invalid_field_name(self):
|
|
filter = {"=": {"": "value"}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
filter = {"=": {" ": "value"}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
filter = {"=": {"\t": "value"}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_more_than_one_field_is_invalid(self):
|
|
filter = {"=": {"project_id": "value", "resource_id": "value"}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_missing_field_after_simple_op_is_invalid(self):
|
|
filter = {"=": {}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_and_or(self):
|
|
filter = {"and": [{"=": {"project_id": "string_value"}},
|
|
{"=": {"resource_id": "value"}}]}
|
|
self.query._validate_filter(filter)
|
|
|
|
filter = {"or": [{"and": [{"=": {"project_id": "string_value"}},
|
|
{"=": {"resource_id": "value"}}]},
|
|
{"=": {"counter_name": "value"}}]}
|
|
self.query._validate_filter(filter)
|
|
|
|
filter = {"or": [{"and": [{"=": {"project_id": "string_value"}},
|
|
{"=": {"resource_id": "value"}},
|
|
{"<": {"counter_name": 42}}]},
|
|
{"=": {"counter_name": "value"}}]}
|
|
self.query._validate_filter(filter)
|
|
|
|
def test_complex_operator_with_in(self):
|
|
filter = {"and": [{"<": {"counter_volume": 42}},
|
|
{">=": {"counter_volume": 36}},
|
|
{"in": {"project_id": ["project_id1",
|
|
"project_id2",
|
|
"project_id3"]}}]}
|
|
self.query._validate_filter(filter)
|
|
|
|
def test_invalid_complex_operator(self):
|
|
filter = {"xor": [{"=": {"project_id": "string_value"}},
|
|
{"=": {"resource_id": "value"}}]}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_and_or_with_one_child_is_invalid(self):
|
|
filter = {"or": [{"=": {"project_id": "string_value"}}]}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_complex_operator_with_zero_child_is_invalid(self):
|
|
filter = {"or": []}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_more_than_one_complex_operator_is_invalid(self):
|
|
filter = {"and": [{"=": {"project_id": "string_value"}},
|
|
{"=": {"resource_id": "value"}}],
|
|
"or": [{"=": {"project_id": "string_value"}},
|
|
{"=": {"resource_id": "value"}}]}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_not(self):
|
|
filter = {"not": {"=": {"project_id": "value"}}}
|
|
self.query._validate_filter(filter)
|
|
|
|
filter = {
|
|
"not":
|
|
{"or":
|
|
[{"and":
|
|
[{"=": {"project_id": "string_value"}},
|
|
{"=": {"resource_id": "value"}},
|
|
{"<": {"counter_name": 42}}]},
|
|
{"=": {"counter_name": "value"}}]}}
|
|
self.query._validate_filter(filter)
|
|
|
|
def test_not_with_zero_child_is_invalid(self):
|
|
filter = {"not": {}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_not_with_more_than_one_child_is_invalid(self):
|
|
filter = {"not": {"=": {"project_id": "value"},
|
|
"!=": {"resource_id": "value"}}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|
|
|
|
def test_empty_in_query_not_passing(self):
|
|
filter = {"in": {"resource_id": []}}
|
|
self.assertRaises(jsonschema.ValidationError,
|
|
self.query._validate_filter,
|
|
filter)
|