nova/nova/tests/test_api_validation.py

381 lines
13 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 NEC Corporation. All rights reserved.
#
# 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.
from nova.api import validation
from nova import exception
from nova import test
class APIValidationTestCase(test.TestCase):
def check_validation_error(self, method, body, expected_detail):
try:
method(body=body)
except exception.ValidationError as ex:
expected_kwargs = {
'code': 400,
'detail': expected_detail
}
self.assertEqual(ex.kwargs, expected_kwargs)
except Exception as ex:
self.fail('An unexpected exception happens: %s' % ex)
else:
self.fail('Any exception does not happen.')
class RequiredDisableTestCase(APIValidationTestCase):
def setUp(self):
super(RequiredDisableTestCase, self).setUp()
schema = {
'type': 'object',
'properties': {
'foo': {
'type': 'integer',
},
},
}
@validation.schema(request_body_schema=schema)
def post(body):
return 'Validation succeeded.'
self.post = post
def test_validate_required_disable(self):
self.assertEqual(self.post(body={'foo': 1}), 'Validation succeeded.')
self.assertEqual(self.post(body={'abc': 1}), 'Validation succeeded.')
class RequiredEnableTestCase(APIValidationTestCase):
def setUp(self):
super(RequiredEnableTestCase, self).setUp()
schema = {
'type': 'object',
'properties': {
'foo': {
'type': 'integer',
},
},
'required': ['foo']
}
@validation.schema(request_body_schema=schema)
def post(body):
return 'Validation succeeded.'
self.post = post
def test_validate_required_enable(self):
self.assertEqual(self.post(body={'foo': 1}), 'Validation succeeded.')
def test_validate_required_enable_fails(self):
detail = "'foo' is a required property"
self.check_validation_error(self.post, body={'abc': 1},
expected_detail=detail)
class AdditionalPropertiesEnableTestCase(APIValidationTestCase):
def setUp(self):
super(AdditionalPropertiesEnableTestCase, self).setUp()
schema = {
'type': 'object',
'properties': {
'foo': {
'type': 'integer',
},
},
'required': ['foo'],
}
@validation.schema(request_body_schema=schema)
def post(body):
return 'Validation succeeded.'
self.post = post
def test_validate_additionalProperties_enable(self):
self.assertEqual(self.post(body={'foo': 1}), 'Validation succeeded.')
self.assertEqual(self.post(body={'foo': 1, 'ext': 1}),
'Validation succeeded.')
class AdditionalPropertiesDisableTestCase(APIValidationTestCase):
def setUp(self):
super(AdditionalPropertiesDisableTestCase, self).setUp()
schema = {
'type': 'object',
'properties': {
'foo': {
'type': 'integer',
},
},
'required': ['foo'],
'additionalProperties': False,
}
@validation.schema(request_body_schema=schema)
def post(body):
return 'Validation succeeded.'
self.post = post
def test_validate_additionalProperties_disable(self):
self.assertEqual(self.post(body={'foo': 1}), 'Validation succeeded.')
def test_validate_additionalProperties_disable_fails(self):
detail = "Additional properties are not allowed ('ext' was unexpected)"
self.check_validation_error(self.post, body={'foo': 1, 'ext': 1},
expected_detail=detail)
class StringTestCase(APIValidationTestCase):
def setUp(self):
super(StringTestCase, self).setUp()
schema = {
'type': 'object',
'properties': {
'foo': {
'type': 'string',
},
},
}
@validation.schema(request_body_schema=schema)
def post(body):
return 'Validation succeeded.'
self.post = post
def test_validate_string(self):
self.assertEqual(self.post(body={'foo': 'abc'}),
'Validation succeeded.')
self.assertEqual(self.post(body={'foo': '0'}),
'Validation succeeded.')
self.assertEqual(self.post(body={'foo': ''}),
'Validation succeeded.')
def test_validate_string_fails(self):
detail = ("Invalid input for field/attribute foo. Value: 1."
" 1 is not of type 'string'")
self.check_validation_error(self.post, body={'foo': 1},
expected_detail=detail)
detail = ("Invalid input for field/attribute foo. Value: 1.5."
" 1.5 is not of type 'string'")
self.check_validation_error(self.post, body={'foo': 1.5},
expected_detail=detail)
detail = ("Invalid input for field/attribute foo. Value: True."
" True is not of type 'string'")
self.check_validation_error(self.post, body={'foo': True},
expected_detail=detail)
class StringLengthTestCase(APIValidationTestCase):
def setUp(self):
super(StringLengthTestCase, self).setUp()
schema = {
'type': 'object',
'properties': {
'foo': {
'type': 'string',
'minLength': 1,
'maxLength': 10,
},
},
}
@validation.schema(request_body_schema=schema)
def post(body):
return 'Validation succeeded.'
self.post = post
def test_validate_string_length(self):
self.assertEqual(self.post(body={'foo': '0'}),
'Validation succeeded.')
self.assertEqual(self.post(body={'foo': '0123456789'}),
'Validation succeeded.')
def test_validate_string_length_fails(self):
detail = ("Invalid input for field/attribute foo. Value: ."
" '' is too short")
self.check_validation_error(self.post, body={'foo': ''},
expected_detail=detail)
detail = ("Invalid input for field/attribute foo. Value: 0123456789a."
" '0123456789a' is too long")
self.check_validation_error(self.post, body={'foo': '0123456789a'},
expected_detail=detail)
class IntegerTestCase(APIValidationTestCase):
def setUp(self):
super(IntegerTestCase, self).setUp()
schema = {
'type': 'object',
'properties': {
'foo': {
'type': ['integer', 'string'],
'pattern': '^[0-9]+$',
},
},
}
@validation.schema(request_body_schema=schema)
def post(body):
return 'Validation succeeded.'
self.post = post
def test_validate_integer(self):
self.assertEqual(self.post(body={'foo': 1}),
'Validation succeeded.')
self.assertEqual(self.post(body={'foo': '1'}),
'Validation succeeded.')
self.assertEqual(self.post(body={'foo': '0123456789'}),
'Validation succeeded.')
def test_validate_integer_fails(self):
detail = ("Invalid input for field/attribute foo. Value: abc."
" 'abc' does not match '^[0-9]+$'")
self.check_validation_error(self.post, body={'foo': 'abc'},
expected_detail=detail)
detail = ("Invalid input for field/attribute foo. Value: True."
" True is not of type 'integer', 'string'")
self.check_validation_error(self.post, body={'foo': True},
expected_detail=detail)
detail = ("Invalid input for field/attribute foo. Value: 0xffff."
" '0xffff' does not match '^[0-9]+$'")
self.check_validation_error(self.post, body={'foo': '0xffff'},
expected_detail=detail)
detail = ("Invalid input for field/attribute foo. Value: 1.0."
" 1.0 is not of type 'integer', 'string'")
self.check_validation_error(self.post, body={'foo': 1.0},
expected_detail=detail)
detail = ("Invalid input for field/attribute foo. Value: 1.0."
" '1.0' does not match '^[0-9]+$'")
self.check_validation_error(self.post, body={'foo': '1.0'},
expected_detail=detail)
class IntegerRangeTestCase(APIValidationTestCase):
def setUp(self):
super(IntegerRangeTestCase, self).setUp()
schema = {
'type': 'object',
'properties': {
'foo': {
'type': ['integer', 'string'],
'pattern': '^[0-9]+$',
'minimum': 1,
'maximum': 10,
},
},
}
@validation.schema(request_body_schema=schema)
def post(body):
return 'Validation succeeded.'
self.post = post
def test_validate_integer_range(self):
self.assertEqual(self.post(body={'foo': 1}),
'Validation succeeded.')
self.assertEqual(self.post(body={'foo': 10}),
'Validation succeeded.')
self.assertEqual(self.post(body={'foo': '1'}),
'Validation succeeded.')
def test_validate_integer_range_fails(self):
detail = ("Invalid input for field/attribute foo. Value: 0."
" 0.0 is less than the minimum of 1")
self.check_validation_error(self.post, body={'foo': 0},
expected_detail=detail)
detail = ("Invalid input for field/attribute foo. Value: 11."
" 11.0 is greater than the maximum of 10")
self.check_validation_error(self.post, body={'foo': 11},
expected_detail=detail)
detail = ("Invalid input for field/attribute foo. Value: 0."
" 0.0 is less than the minimum of 1")
self.check_validation_error(self.post, body={'foo': '0'},
expected_detail=detail)
detail = ("Invalid input for field/attribute foo. Value: 11."
" 11.0 is greater than the maximum of 10")
self.check_validation_error(self.post, body={'foo': '11'},
expected_detail=detail)
class UuidTestCase(APIValidationTestCase):
def setUp(self):
super(UuidTestCase, self).setUp()
schema = {
'type': 'object',
'properties': {
'foo': {
'type': 'string',
'format': 'uuid',
},
},
}
@validation.schema(request_body_schema=schema)
def post(body):
return 'Validation succeeded.'
self.post = post
def test_validate_uuid(self):
self.assertEqual('Validation succeeded.',
self.post(
body={'foo': '70a599e0-31e7-49b7-b260-868f441e862b'}
))
def test_validate_uuid_fails(self):
detail = ("Invalid input for field/attribute foo."
" Value: 70a599e031e749b7b260868f441e862b."
" '70a599e031e749b7b260868f441e862b' is not a 'uuid'")
self.check_validation_error(self.post,
body={'foo': '70a599e031e749b7b260868f441e862b'},
expected_detail=detail)
detail = ("Invalid input for field/attribute foo. Value: 1."
" '1' is not a 'uuid'")
self.check_validation_error(self.post, body={'foo': '1'},
expected_detail=detail)
detail = ("Invalid input for field/attribute foo. Value: abc."
" 'abc' is not a 'uuid'")
self.check_validation_error(self.post, body={'foo': 'abc'},
expected_detail=detail)