openstacksdk/openstack/tests/unit/test_resource.py

1513 lines
51 KiB
Python

# 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.
import copy
import json
import os
from keystoneauth1 import session
import mock
import requests
from testtools import matchers
from openstack import exceptions
from openstack import format
from openstack import resource
from openstack.tests.unit import base
from openstack import utils
fake_parent = 'robert'
fake_name = 'rey'
fake_id = 99
fake_attr1 = 'lana'
fake_attr2 = 'del'
fake_resource = 'fake'
fake_resources = 'fakes'
fake_arguments = {'parent_name': fake_parent}
fake_base_path = '/fakes/%(parent_name)s/data'
fake_path = '/fakes/rey/data'
fake_data = {'id': fake_id,
'enabled': True,
'name': fake_name,
'parent': fake_parent,
'attr1': fake_attr1,
'attr2': fake_attr2,
'status': None}
fake_body = {fake_resource: fake_data}
class FakeParent(resource.Resource):
id_attribute = "name"
name = resource.prop('name')
class FakeResource(resource.Resource):
resource_key = fake_resource
resources_key = fake_resources
base_path = fake_base_path
allow_create = allow_retrieve = allow_update = True
allow_delete = allow_list = allow_head = True
enabled = resource.prop('enabled', type=format.BoolStr)
name = resource.prop('name')
parent = resource.prop('parent_name')
first = resource.prop('attr1')
second = resource.prop('attr2')
third = resource.prop('attr3', alias='attr_three')
status = resource.prop('status')
class FakeResourceNoKeys(FakeResource):
resource_key = None
resources_key = None
class PropTests(base.TestCase):
def test_with_alias_and_type(self):
class Test(resource.Resource):
attr = resource.prop("attr1", alias="attr2", type=bool)
t = Test(attrs={"attr2": 500})
# Don't test with assertTrue because 500 evaluates to True.
# Need to test that bool(500) happened and attr2 *is* True.
self.assertIs(t.attr, True)
def test_defaults(self):
new_default = "new_default"
class Test(resource.Resource):
attr1 = resource.prop("attr1")
attr2 = resource.prop("attr2", default=new_default)
t = Test()
self.assertIsNone(t.attr1)
self.assertEqual(new_default, t.attr2)
# When the default value is passed in, it is left untouched.
# Check that attr2 is literally the same object we set as default.
t.attr2 = new_default
self.assertIs(new_default, t.attr2)
not_default = 'not default'
t2 = Test({'attr2': not_default})
self.assertEqual(not_default, t2.attr2)
# Assert that if the default is passed in, it overrides the previously
# set value (bug #1425996)
t2.attr2 = new_default
self.assertEqual(new_default, t2.attr2)
def test_get_without_instance(self):
self.assertIsNone(FakeResource.name)
def test_set_ValueError(self):
class Test(resource.Resource):
attr = resource.prop("attr", type=int)
t = Test()
def should_raise():
t.attr = "this is not an int"
self.assertThat(should_raise, matchers.raises(ValueError))
def test_set_TypeError(self):
class Type(object):
def __init__(self):
pass
class Test(resource.Resource):
attr = resource.prop("attr", type=Type)
t = Test()
def should_raise():
t.attr = "this type takes no args"
self.assertThat(should_raise, matchers.raises(TypeError))
def test_resource_type(self):
class FakestResource(resource.Resource):
shortstop = resource.prop("shortstop", type=FakeResource)
third_base = resource.prop("third_base", type=FakeResource)
sot = FakestResource()
id1 = "Ernie Banks"
id2 = "Ron Santo"
sot.shortstop = id1
sot.third_base = id2
resource1 = FakeResource.new(id=id1)
self.assertEqual(resource1, sot.shortstop)
self.assertEqual(id1, sot.shortstop.id)
self.assertEqual(FakeResource, type(sot.shortstop))
resource2 = FakeResource.new(id=id2)
self.assertEqual(resource2, sot.third_base)
self.assertEqual(id2, sot.third_base.id)
self.assertEqual(FakeResource, type(sot.third_base))
sot2 = FakestResource()
sot2.shortstop = resource1
sot2.third_base = resource2
self.assertEqual(resource1, sot2.shortstop)
self.assertEqual(id1, sot2.shortstop.id)
self.assertEqual(FakeResource, type(sot2.shortstop))
self.assertEqual(resource2, sot2.third_base)
self.assertEqual(id2, sot2.third_base.id)
self.assertEqual(FakeResource, type(sot2.third_base))
body = {
"shortstop": id1,
"third_base": id2
}
sot3 = FakestResource(body)
self.assertEqual(FakeResource({"id": id1}), sot3.shortstop)
self.assertEqual(FakeResource({"id": id2}), sot3.third_base)
def test_set_alias_same_name(self):
class Test(resource.Resource):
attr = resource.prop("something", alias="attr")
val = "hey"
args = {"something": val}
sot = Test(args)
self.assertEqual(val, sot._attrs["something"])
self.assertEqual(val, sot.attr)
def test_property_is_none(self):
class Test(resource.Resource):
attr = resource.prop("something", type=dict)
args = {"something": None}
sot = Test(args)
self.assertIsNone(sot._attrs["something"])
self.assertIsNone(sot.attr)
class HeaderTests(base.TestCase):
class Test(resource.Resource):
base_path = "/ramones"
service = "punk"
allow_create = True
allow_update = True
hey = resource.header("vocals")
ho = resource.header("guitar")
letsgo = resource.header("bass")
def test_get(self):
val = "joey"
args = {"vocals": val}
sot = HeaderTests.Test({'headers': args})
self.assertEqual(val, sot.hey)
self.assertIsNone(sot.ho)
self.assertIsNone(sot.letsgo)
def test_set_new(self):
args = {"vocals": "joey", "bass": "deedee"}
sot = HeaderTests.Test({'headers': args})
sot._reset_dirty()
sot.ho = "johnny"
self.assertEqual("johnny", sot.ho)
self.assertTrue(sot.is_dirty)
def test_set_old(self):
args = {"vocals": "joey", "bass": "deedee"}
sot = HeaderTests.Test({'headers': args})
sot._reset_dirty()
sot.letsgo = "cj"
self.assertEqual("cj", sot.letsgo)
self.assertTrue(sot.is_dirty)
def test_set_brand_new(self):
sot = HeaderTests.Test({'headers': {}})
sot._reset_dirty()
sot.ho = "johnny"
self.assertEqual("johnny", sot.ho)
self.assertTrue(sot.is_dirty)
self.assertEqual({'headers': {"guitar": "johnny"}}, sot)
def test_1428342(self):
sot = HeaderTests.Test({'headers':
requests.structures.CaseInsensitiveDict()})
self.assertIsNone(sot.hey)
def test_create_update_headers(self):
sot = HeaderTests.Test()
sot._reset_dirty()
sot.ho = "johnny"
sot.letsgo = "deedee"
response = mock.Mock()
response_body = {'id': 1}
response.json = mock.Mock(return_value=response_body)
response.headers = None
sess = mock.Mock()
sess.post = mock.Mock(return_value=response)
sess.put = mock.Mock(return_value=response)
sot.create(sess)
headers = {'guitar': 'johnny', 'bass': 'deedee'}
sess.post.assert_called_with(HeaderTests.Test.base_path,
headers=headers,
json={})
sot['id'] = 1
sot.letsgo = "cj"
headers = {'guitar': 'johnny', 'bass': 'cj'}
sot.update(sess)
sess.put.assert_called_with('ramones/1',
headers=headers,
json={})
class ResourceTests(base.TestCase):
def setUp(self):
super(ResourceTests, self).setUp()
self.session = mock.Mock(spec=session.Session)
self.session.get_filter = mock.Mock(return_value={})
def assertCalledURL(self, method, url):
# call_args gives a tuple of *args and tuple of **kwargs.
# Check that the first arg in *args (the URL) has our url.
self.assertEqual(method.call_args[0][0], url)
def test_empty_id(self):
resp = FakeResponse(fake_body)
self.session.get.return_value = resp
obj = FakeResource.new(**fake_arguments)
self.assertEqual(obj, obj.get(self.session))
self.assertEqual(fake_id, obj.id)
self.assertEqual(fake_name, obj['name'])
self.assertEqual(fake_attr1, obj['attr1'])
self.assertEqual(fake_attr2, obj['attr2'])
self.assertEqual(fake_name, obj.name)
self.assertEqual(fake_attr1, obj.first)
self.assertEqual(fake_attr2, obj.second)
def test_not_allowed(self):
class Nope(resource.Resource):
allow_create = allow_retrieve = allow_update = False
allow_delete = allow_list = allow_head = False
nope = Nope()
def cant_create():
nope.create_by_id(1, 2)
def cant_retrieve():
nope.get_data_by_id(1, 2)
def cant_update():
nope.update_by_id(1, 2, 3)
def cant_delete():
nope.delete_by_id(1, 2)
def cant_list():
for i in nope.list(1):
pass
def cant_head():
nope.head_data_by_id(1, 2)
self.assertThat(cant_create,
matchers.raises(exceptions.MethodNotSupported))
self.assertThat(cant_retrieve,
matchers.raises(exceptions.MethodNotSupported))
self.assertThat(cant_update,
matchers.raises(exceptions.MethodNotSupported))
self.assertThat(cant_delete,
matchers.raises(exceptions.MethodNotSupported))
self.assertThat(cant_list,
matchers.raises(exceptions.MethodNotSupported))
self.assertThat(cant_head,
matchers.raises(exceptions.MethodNotSupported))
def _test_create_by_id(self, key, response_value, response_body,
attrs, json_body, response_headers=None):
class FakeResource2(FakeResource):
resource_key = key
service = "my_service"
response = mock.Mock()
response.json = mock.Mock(return_value=response_body)
response.headers = response_headers
expected_resp = response_value.copy()
if response_headers:
expected_resp.update({'headers': response_headers})
sess = mock.Mock()
sess.put = mock.Mock(return_value=response)
sess.post = mock.Mock(return_value=response)
resp = FakeResource2.create_by_id(sess, attrs)
self.assertEqual(expected_resp, resp)
sess.post.assert_called_with(FakeResource2.base_path,
json=json_body)
r_id = "my_id"
resp = FakeResource2.create_by_id(sess, attrs, resource_id=r_id)
self.assertEqual(response_value, resp)
sess.put.assert_called_with(
utils.urljoin(FakeResource2.base_path, r_id),
json=json_body)
path_args = {"parent_name": "my_name"}
resp = FakeResource2.create_by_id(sess, attrs, path_args=path_args)
self.assertEqual(response_value, resp)
sess.post.assert_called_with(FakeResource2.base_path % path_args,
json=json_body)
resp = FakeResource2.create_by_id(sess, attrs, resource_id=r_id,
path_args=path_args)
self.assertEqual(response_value, resp)
sess.put.assert_called_with(
utils.urljoin(FakeResource2.base_path % path_args, r_id),
json=json_body)
def test_create_without_resource_key(self):
key = None
response_value = {"a": 1, "b": 2, "c": 3}
response_body = response_value
attrs = response_value
json_body = attrs
self._test_create_by_id(key, response_value, response_body,
attrs, json_body)
def test_create_with_response_headers(self):
key = None
response_value = {"a": 1, "b": 2, "c": 3}
response_body = response_value
response_headers = {'location': 'foo'}
attrs = response_value.copy()
json_body = attrs
self._test_create_by_id(key, response_value, response_body,
attrs, json_body,
response_headers=response_headers)
def test_create_with_resource_key(self):
key = "my_key"
response_value = {"a": 1, "b": 2, "c": 3}
response_body = {key: response_value}
attrs = response_body
json_body = {key: attrs}
self._test_create_by_id(key, response_value, response_body,
attrs, json_body)
def _test_get_data_by_id(self, key, response_value, response_body):
class FakeResource2(FakeResource):
resource_key = key
service = "my_service"
response = FakeResponse(response_body)
sess = mock.Mock()
sess.get = mock.Mock(return_value=response)
r_id = "my_id"
resp = FakeResource2.get_data_by_id(sess, resource_id=r_id)
self.assertEqual(response_value, resp)
sess.get.assert_called_with(
utils.urljoin(FakeResource2.base_path, r_id),
)
path_args = {"parent_name": "my_name"}
resp = FakeResource2.get_data_by_id(sess, resource_id=r_id,
path_args=path_args)
self.assertEqual(response_value, resp)
sess.get.assert_called_with(
utils.urljoin(FakeResource2.base_path % path_args, r_id),
)
def test_get_data_without_resource_key(self):
key = None
response_value = {"a": 1, "b": 2, "c": 3}
response_body = response_value
self._test_get_data_by_id(key, response_value, response_body)
def test_get_data_with_resource_key(self):
key = "my_key"
response_value = {"a": 1, "b": 2, "c": 3}
response_body = {key: response_value}
self._test_get_data_by_id(key, response_value, response_body)
def _test_head_data_by_id(self, key, response_value):
class FakeResource2(FakeResource):
resource_key = key
service = "my_service"
response = mock.Mock()
response.headers = response_value
sess = mock.Mock()
sess.head = mock.Mock(return_value=response)
r_id = "my_id"
resp = FakeResource2.head_data_by_id(sess, resource_id=r_id)
self.assertEqual({'headers': response_value}, resp)
headers = {'Accept': ''}
sess.head.assert_called_with(
utils.urljoin(FakeResource2.base_path, r_id),
headers=headers)
path_args = {"parent_name": "my_name"}
resp = FakeResource2.head_data_by_id(sess, resource_id=r_id,
path_args=path_args)
self.assertEqual({'headers': response_value}, resp)
headers = {'Accept': ''}
sess.head.assert_called_with(
utils.urljoin(FakeResource2.base_path % path_args, r_id),
headers=headers)
def test_head_data_without_resource_key(self):
key = None
response_value = {"key1": "value1", "key2": "value2"}
self._test_head_data_by_id(key, response_value)
def test_head_data_with_resource_key(self):
key = "my_key"
response_value = {"key1": "value1", "key2": "value2"}
self._test_head_data_by_id(key, response_value)
def _test_update_by_id(self, key, response_value, response_body,
attrs, json_body, response_headers=None):
class FakeResource2(FakeResource):
patch_update = True
resource_key = key
service = "my_service"
response = mock.Mock()
response.json = mock.Mock(return_value=response_body)
response.headers = response_headers
expected_resp = response_value.copy()
if response_headers:
expected_resp.update({'headers': response_headers})
sess = mock.Mock()
sess.patch = mock.Mock(return_value=response)
r_id = "my_id"
resp = FakeResource2.update_by_id(sess, r_id, attrs)
self.assertEqual(expected_resp, resp)
sess.patch.assert_called_with(
utils.urljoin(FakeResource2.base_path, r_id),
json=json_body)
path_args = {"parent_name": "my_name"}
resp = FakeResource2.update_by_id(sess, r_id, attrs,
path_args=path_args)
self.assertEqual(expected_resp, resp)
sess.patch.assert_called_with(
utils.urljoin(FakeResource2.base_path % path_args, r_id),
json=json_body)
def test_update_without_resource_key(self):
key = None
response_value = {"a": 1, "b": 2, "c": 3}
response_body = response_value
attrs = response_value
json_body = attrs
self._test_update_by_id(key, response_value, response_body,
attrs, json_body)
def test_update_with_resource_key(self):
key = "my_key"
response_value = {"a": 1, "b": 2, "c": 3}
response_body = {key: response_value}
attrs = response_value
json_body = {key: attrs}
self._test_update_by_id(key, response_value, response_body,
attrs, json_body)
def test_update_with_response_headers(self):
key = "my_key"
response_value = {"a": 1, "b": 2, "c": 3}
response_body = {key: response_value}
response_headers = {'location': 'foo'}
attrs = response_value.copy()
json_body = {key: attrs}
self._test_update_by_id(key, response_value, response_body,
attrs, json_body,
response_headers=response_headers)
def test_delete_by_id(self):
class FakeResource2(FakeResource):
service = "my_service"
sess = mock.Mock()
sess.delete = mock.Mock(return_value=FakeResponse({}))
r_id = "my_id"
resp = FakeResource2.delete_by_id(sess, r_id)
self.assertIsNone(resp)
headers = {'Accept': ''}
sess.delete.assert_called_with(
utils.urljoin(FakeResource2.base_path, r_id),
headers=headers)
path_args = {"parent_name": "my_name"}
resp = FakeResource2.delete_by_id(sess, r_id, path_args=path_args)
self.assertIsNone(resp)
headers = {'Accept': ''}
sess.delete.assert_called_with(
utils.urljoin(FakeResource2.base_path % path_args, r_id),
headers=headers)
def test_create(self):
resp = FakeResponse(fake_body, headers={'location': 'foo'})
self.session.post = mock.Mock(return_value=resp)
# Create resource with subset of attributes in order to
# verify create refreshes all attributes from response.
obj = FakeResource.new(parent_name=fake_parent,
name=fake_name,
enabled=True,
attr1=fake_attr1)
self.assertEqual(obj, obj.create(self.session))
self.assertFalse(obj.is_dirty)
last_req = self.session.post.call_args[1]["json"][
FakeResource.resource_key]
self.assertEqual(4, len(last_req))
self.assertTrue(last_req['enabled'])
self.assertEqual(fake_parent, last_req['parent_name'])
self.assertEqual(fake_name, last_req['name'])
self.assertEqual(fake_attr1, last_req['attr1'])
self.assertTrue(obj['enabled'])
self.assertEqual(fake_name, obj['name'])
self.assertEqual(fake_parent, obj['parent_name'])
self.assertEqual(fake_attr1, obj['attr1'])
self.assertEqual(fake_attr2, obj['attr2'])
self.assertIsNone(obj['status'])
self.assertTrue(obj.enabled)
self.assertEqual(fake_id, obj.id)
self.assertEqual(fake_name, obj.name)
self.assertEqual(fake_parent, obj.parent_name)
self.assertEqual(fake_parent, obj.parent)
self.assertEqual(fake_attr1, obj.first)
self.assertEqual(fake_attr1, obj.attr1)
self.assertEqual(fake_attr2, obj.second)
self.assertEqual(fake_attr2, obj.attr2)
self.assertIsNone(obj.status)
self.assertEqual('foo', obj.location)
def test_get(self):
resp = FakeResponse(fake_body, headers={'location': 'foo'})
self.session.get = mock.Mock(return_value=resp)
# Create resource with subset of attributes in order to
# verify get refreshes all attributes from response.
obj = FakeResource.from_id(str(fake_id))
obj['parent_name'] = fake_parent
self.assertEqual(obj, obj.get(self.session))
# Check that the proper URL is being built.
self.assertCalledURL(self.session.get,
os.path.join(fake_base_path % fake_arguments,
str(fake_id))[1:])
self.assertTrue(obj['enabled'])
self.assertEqual(fake_name, obj['name'])
self.assertEqual(fake_parent, obj['parent_name'])
self.assertEqual(fake_attr1, obj['attr1'])
self.assertEqual(fake_attr2, obj['attr2'])
self.assertIsNone(obj['status'])
self.assertTrue(obj.enabled)
self.assertEqual(fake_id, obj.id)
self.assertEqual(fake_name, obj.name)
self.assertEqual(fake_parent, obj.parent_name)
self.assertEqual(fake_parent, obj.parent)
self.assertEqual(fake_attr1, obj.first)
self.assertEqual(fake_attr1, obj.attr1)
self.assertEqual(fake_attr2, obj.second)
self.assertEqual(fake_attr2, obj.attr2)
self.assertIsNone(obj.status)
self.assertIsNone(obj.location)
def test_get_by_id(self):
resp = FakeResponse(fake_body)
self.session.get = mock.Mock(return_value=resp)
obj = FakeResource.get_by_id(self.session, fake_id,
path_args=fake_arguments)
# Check that the proper URL is being built.
self.assertCalledURL(self.session.get,
os.path.join(fake_base_path % fake_arguments,
str(fake_id))[1:])
self.assertEqual(fake_id, obj.id)
self.assertEqual(fake_name, obj['name'])
self.assertEqual(fake_attr1, obj['attr1'])
self.assertEqual(fake_attr2, obj['attr2'])
self.assertEqual(fake_name, obj.name)
self.assertEqual(fake_attr1, obj.first)
self.assertEqual(fake_attr2, obj.second)
def test_get_by_id_with_headers(self):
header1 = "fake-value1"
header2 = "fake-value2"
headers = {"header1": header1,
"header2": header2}
resp = FakeResponse(fake_body, headers=headers)
self.session.get = mock.Mock(return_value=resp)
class FakeResource2(FakeResource):
header1 = resource.header("header1")
header2 = resource.header("header2")
obj = FakeResource2.get_by_id(self.session, fake_id,
path_args=fake_arguments,
include_headers=True)
self.assertCalledURL(self.session.get,
os.path.join(fake_base_path % fake_arguments,
str(fake_id))[1:])
self.assertEqual(fake_id, obj.id)
self.assertEqual(fake_name, obj['name'])
self.assertEqual(fake_attr1, obj['attr1'])
self.assertEqual(fake_attr2, obj['attr2'])
self.assertEqual(header1, obj['headers']['header1'])
self.assertEqual(header2, obj['headers']['header2'])
self.assertEqual(fake_name, obj.name)
self.assertEqual(fake_attr1, obj.first)
self.assertEqual(fake_attr2, obj.second)
self.assertEqual(header1, obj.header1)
self.assertEqual(header2, obj.header2)
def test_head_by_id(self):
class FakeResource2(FakeResource):
header1 = resource.header("header1")
header2 = resource.header("header2")
resp = FakeResponse(None, headers={"header1": "one", "header2": "two"})
self.session.head = mock.Mock(return_value=resp)
obj = FakeResource2.head_by_id(self.session, fake_id,
path_args=fake_arguments)
self.assertCalledURL(self.session.head,
os.path.join(fake_base_path % fake_arguments,
str(fake_id))[1:])
self.assertEqual('one', obj['headers']['header1'])
self.assertEqual('two', obj['headers']['header2'])
self.assertEqual('one', obj.header1)
self.assertEqual('two', obj.header2)
def test_patch_update(self):
class FakeResourcePatch(FakeResource):
patch_update = True
resp = FakeResponse(fake_body, headers={'location': 'foo'})
self.session.patch = mock.Mock(return_value=resp)
# Create resource with subset of attributes in order to
# verify update refreshes all attributes from response.
obj = FakeResourcePatch.new(id=fake_id, parent_name=fake_parent,
name=fake_name, attr1=fake_attr1)
self.assertTrue(obj.is_dirty)
self.assertEqual(obj, obj.update(self.session))
self.assertFalse(obj.is_dirty)
self.assertCalledURL(self.session.patch,
os.path.join(fake_base_path % fake_arguments,
str(fake_id))[1:])
last_req = self.session.patch.call_args[1]["json"][
FakeResource.resource_key]
self.assertEqual(3, len(last_req))
self.assertEqual(fake_parent, last_req['parent_name'])
self.assertEqual(fake_name, last_req['name'])
self.assertEqual(fake_attr1, last_req['attr1'])
self.assertTrue(obj['enabled'])
self.assertEqual(fake_name, obj['name'])
self.assertEqual(fake_parent, obj['parent_name'])
self.assertEqual(fake_attr1, obj['attr1'])
self.assertEqual(fake_attr2, obj['attr2'])
self.assertIsNone(obj['status'])
self.assertTrue(obj.enabled)
self.assertEqual(fake_id, obj.id)
self.assertEqual(fake_name, obj.name)
self.assertEqual(fake_parent, obj.parent_name)
self.assertEqual(fake_parent, obj.parent)
self.assertEqual(fake_attr1, obj.first)
self.assertEqual(fake_attr1, obj.attr1)
self.assertEqual(fake_attr2, obj.second)
self.assertEqual(fake_attr2, obj.attr2)
self.assertIsNone(obj.status)
self.assertEqual('foo', obj.location)
def test_put_update(self):
class FakeResourcePut(FakeResource):
# This is False by default, but explicit for this test.
update_method = 'PUT'
resp = FakeResponse(fake_body, headers={'location': 'foo'})
self.session.put = mock.Mock(return_value=resp)
# Create resource with subset of attributes in order to
# verify update refreshes all attributes from response.
obj = FakeResourcePut.new(id=fake_id, parent_name=fake_parent,
name=fake_name, attr1=fake_attr1)
self.assertTrue(obj.is_dirty)
self.assertEqual(obj, obj.update(self.session))
self.assertFalse(obj.is_dirty)
self.assertCalledURL(self.session.put,
os.path.join(fake_base_path % fake_arguments,
str(fake_id))[1:])
last_req = self.session.put.call_args[1]["json"][
FakeResource.resource_key]
self.assertEqual(3, len(last_req))
self.assertEqual(fake_parent, last_req['parent_name'])
self.assertEqual(fake_name, last_req['name'])
self.assertEqual(fake_attr1, last_req['attr1'])
self.assertTrue(obj['enabled'])
self.assertEqual(fake_name, obj['name'])
self.assertEqual(fake_parent, obj['parent_name'])
self.assertEqual(fake_attr1, obj['attr1'])
self.assertEqual(fake_attr2, obj['attr2'])
self.assertIsNone(obj['status'])
self.assertTrue(obj.enabled)
self.assertEqual(fake_id, obj.id)
self.assertEqual(fake_name, obj.name)
self.assertEqual(fake_parent, obj.parent_name)
self.assertEqual(fake_parent, obj.parent)
self.assertEqual(fake_attr1, obj.first)
self.assertEqual(fake_attr1, obj.attr1)
self.assertEqual(fake_attr2, obj.second)
self.assertEqual(fake_attr2, obj.attr2)
self.assertIsNone(obj.status)
self.assertEqual('foo', obj.location)
def test_update_early_exit(self):
obj = FakeResource()
obj._dirty = [] # Bail out early if there's nothing to update.
self.assertIsNone(obj.update("session"))
def test_update_no_id_attribute(self):
obj = FakeResource.existing(id=1, attr="value1",
parent_name=fake_parent)
obj.first = "value2" # Make it dirty
obj.update_by_id = mock.Mock(return_value=dict())
# If no id_attribute is returned in the update response, make sure
# we handle the resulting KeyError.
self.assertEqual(obj, obj.update("session"))
def test_delete(self):
obj = FakeResource({"id": fake_id, "parent_name": fake_parent})
self.session.delete.return_value = FakeResponse({})
obj.delete(self.session)
self.assertCalledURL(self.session.delete,
os.path.join(fake_base_path % fake_arguments,
str(fake_id))[1:])
def _test_list(self, resource_class):
results = [fake_data.copy(), fake_data.copy(), fake_data.copy()]
for i in range(len(results)):
results[i]['id'] = fake_id + i
if resource_class.resources_key is not None:
body = {resource_class.resources_key:
self._get_expected_results()}
sentinel = {resource_class.resources_key: []}
else:
body = self._get_expected_results()
sentinel = []
resp1 = mock.Mock()
resp1.json = mock.Mock(return_value=body)
resp2 = mock.Mock()
resp2.json = mock.Mock(return_value=sentinel)
self.session.get.side_effect = [resp1, resp2]
objs = list(resource_class.list(self.session, path_args=fake_arguments,
paginated=True))
params = {'limit': 3, 'marker': results[-1]['id']}
self.assertEqual(params, self.session.get.call_args[1]['params'])
self.assertEqual(3, len(objs))
for obj in objs:
self.assertIn(obj.id, range(fake_id, fake_id + 3))
self.assertEqual(fake_name, obj['name'])
self.assertEqual(fake_name, obj.name)
self.assertIsInstance(obj, FakeResource)
def _get_expected_results(self):
results = [fake_data.copy(), fake_data.copy(), fake_data.copy()]
for i in range(len(results)):
results[i]['id'] = fake_id + i
return results
def test_list_keyed_resource(self):
self._test_list(FakeResource)
def test_list_non_keyed_resource(self):
self._test_list(FakeResourceNoKeys)
def _test_list_call_count(self, paginated):
# Test that we've only made one call to receive all data
results = [fake_data.copy(), fake_data.copy(), fake_data.copy()]
resp = FakeResponse({fake_resources: results})
attrs = {"get.return_value": resp}
session = mock.Mock(**attrs)
list(FakeResource.list(session, params={'limit': len(results) + 1},
path_args=fake_arguments,
paginated=paginated))
# Ensure we only made one call to complete this.
self.assertEqual(1, session.get.call_count)
def test_list_bail_out(self):
# When we get less data than limit, make sure we made one call
self._test_list_call_count(True)
def test_list_nonpaginated(self):
# When we call with paginated=False, make sure we made one call
self._test_list_call_count(False)
def test_determine_limit(self):
full_page = [fake_data.copy(), fake_data.copy(), fake_data.copy()]
last_page = [fake_data.copy()]
session = mock.Mock()
session.get = mock.Mock()
full_response = mock.Mock()
response_body = {FakeResource.resources_key: full_page}
full_response.json = mock.Mock(return_value=response_body)
last_response = mock.Mock()
response_body = {FakeResource.resources_key: last_page}
last_response.json = mock.Mock(return_value=response_body)
pages = [full_response, full_response, last_response]
session.get.side_effect = pages
# Don't specify a limit. Resource.list will determine the limit
# is 3 based on the first `full_page`.
results = list(FakeResource.list(session, path_args=fake_arguments,
paginated=True))
self.assertEqual(session.get.call_count, len(pages))
self.assertEqual(len(full_page + full_page + last_page), len(results))
def test_empty_list(self):
page = []
session = mock.Mock()
session.get = mock.Mock()
full_response = mock.Mock()
response_body = {FakeResource.resources_key: page}
full_response.json = mock.Mock(return_value=response_body)
pages = [full_response]
session.get.side_effect = pages
results = list(FakeResource.list(session, path_args=fake_arguments,
paginated=True))
self.assertEqual(session.get.call_count, len(pages))
self.assertEqual(len(page), len(results))
def test_attrs_name(self):
obj = FakeResource()
self.assertIsNone(obj.name)
del obj.name
def test_to_dict(self):
kwargs = {
'enabled': True,
'name': 'FOO',
'parent': 'dad',
'attr1': 'BAR',
'attr2': ['ZOO', 'BAZ'],
'status': 'Active',
'headers': {
'key': 'value'
}
}
obj = FakeResource(kwargs)
res = obj.to_dict()
self.assertIsInstance(res, dict)
self.assertTrue(res['enabled'])
self.assertEqual('FOO', res['name'])
self.assertEqual('dad', res['parent'])
self.assertEqual('BAR', res['attr1'])
self.assertEqual(['ZOO', 'BAZ'], res['attr2'])
self.assertEqual('Active', res['status'])
self.assertNotIn('headers', res)
def test_composite_attr_happy(self):
obj = FakeResource.existing(**{'attr3': '3'})
try:
self.assertEqual('3', obj.third)
except AttributeError:
self.fail("third was not found as expected")
def test_composite_attr_fallback(self):
obj = FakeResource.existing(**{'attr_three': '3'})
try:
self.assertEqual('3', obj.third)
except AttributeError:
self.fail("third was not found in fallback as expected")
def test_id_del(self):
class Test(resource.Resource):
id_attribute = "my_id"
attrs = {"my_id": 100}
t = Test(attrs=attrs)
self.assertEqual(attrs["my_id"], t.id)
del t.id
self.assertTrue(Test.id_attribute not in t._attrs)
def test_from_name_with_name(self):
name = "Ernie Banks"
obj = FakeResource.from_name(name)
self.assertEqual(name, obj.name)
def test_from_id_with_name(self):
name = "Sandy Koufax"
obj = FakeResource.from_id(name)
self.assertEqual(name, obj.id)
def test_from_id_with_object(self):
name = "Mickey Mantle"
obj = FakeResource.new(name=name)
new_obj = FakeResource.from_id(obj)
self.assertIs(new_obj, obj)
self.assertEqual(obj.name, new_obj.name)
def test_from_id_with_bad_value(self):
def should_raise():
FakeResource.from_id(3.14)
self.assertThat(should_raise, matchers.raises(ValueError))
def test_dirty_list(self):
class Test(resource.Resource):
attr = resource.prop("attr")
# Check if dirty after setting by prop
sot1 = Test()
self.assertFalse(sot1.is_dirty)
sot1.attr = 1
self.assertTrue(sot1.is_dirty)
# Check if dirty after setting by mapping
sot2 = Test()
sot2["attr"] = 1
self.assertTrue(sot1.is_dirty)
# Check if dirty after creation
sot3 = Test({"attr": 1})
self.assertTrue(sot3.is_dirty)
def test_update_attrs(self):
class Test(resource.Resource):
moe = resource.prop("the-attr")
larry = resource.prop("the-attr2")
curly = resource.prop("the-attr3", type=int)
shemp = resource.prop("the-attr4")
value1 = "one"
value2 = "two"
value3 = "3"
value4 = "fore"
value5 = "fiver"
sot = Test({"the-attr": value1})
sot.update_attrs({"the-attr2": value2, "notprop": value4})
self.assertTrue(sot.is_dirty)
self.assertEqual(value1, sot.moe)
self.assertEqual(value1, sot["the-attr"])
self.assertEqual(value2, sot.larry)
self.assertEqual(value4, sot.notprop)
sot._reset_dirty()
sot.update_attrs(curly=value3)
self.assertTrue(sot.is_dirty)
self.assertEqual(int, type(sot.curly))
self.assertEqual(int(value3), sot.curly)
sot._reset_dirty()
sot.update_attrs(**{"the-attr4": value5})
self.assertTrue(sot.is_dirty)
self.assertEqual(value5, sot.shemp)
def test_get_id(self):
class Test(resource.Resource):
pass
ID = "an id"
res = Test({"id": ID})
self.assertEqual(ID, resource.Resource.get_id(ID))
self.assertEqual(ID, resource.Resource.get_id(res))
def test_convert_ids(self):
class TestResourceFoo(resource.Resource):
pass
class TestResourceBar(resource.Resource):
pass
resfoo = TestResourceFoo({'id': 'FAKEFOO'})
resbar = TestResourceBar({'id': 'FAKEBAR'})
self.assertIsNone(resource.Resource.convert_ids(None))
attrs = {
'key1': 'value1'
}
self.assertEqual(attrs, resource.Resource.convert_ids(attrs))
attrs = {
'foo': resfoo,
'bar': resbar,
'other': 'whatever',
}
res = resource.Resource.convert_ids(attrs)
self.assertEqual('FAKEFOO', res['foo'])
self.assertEqual('FAKEBAR', res['bar'])
self.assertEqual('whatever', res['other'])
def test_repr(self):
fr = FakeResource()
fr._loaded = False
fr.first = "hey"
fr.second = "hi"
fr.third = "nah"
the_repr = repr(fr)
the_repr = the_repr.replace('openstack.tests.unit.test_resource.', '')
result = eval(the_repr)
self.assertEqual(fr._loaded, result._loaded)
self.assertEqual(fr.first, result.first)
self.assertEqual(fr.second, result.second)
self.assertEqual(fr.third, result.third)
def test_id_attribute(self):
faker = FakeResource(fake_data)
self.assertEqual(fake_id, faker.id)
faker.id_attribute = 'name'
self.assertEqual(fake_name, faker.id)
faker.id_attribute = 'attr1'
self.assertEqual(fake_attr1, faker.id)
faker.id_attribute = 'attr2'
self.assertEqual(fake_attr2, faker.id)
faker.id_attribute = 'id'
self.assertEqual(fake_id, faker.id)
def test_name_attribute(self):
class Person_ES(resource.Resource):
name_attribute = "nombre"
nombre = resource.prop('nombre')
name = "Brian"
args = {'nombre': name}
person = Person_ES(args)
self.assertEqual(name, person.nombre)
self.assertEqual(name, person.name)
new_name = "Julien"
person.name = new_name
self.assertEqual(new_name, person.nombre)
self.assertEqual(new_name, person.name)
def test_boolstr_prop(self):
faker = FakeResource(fake_data)
self.assertTrue(faker.enabled)
self.assertTrue(faker['enabled'])
faker._attrs['enabled'] = False
self.assertFalse(faker.enabled)
self.assertFalse(faker['enabled'])
# should fail fast
def set_invalid():
faker.enabled = 'INVALID'
self.assertRaises(ValueError, set_invalid)
class ResourceMapping(base.TestCase):
def test__getitem(self):
value = 10
class Test(resource.Resource):
attr = resource.prop("attr")
t = Test(attrs={"attr": value})
self.assertEqual(value, t["attr"])
def test__setitem__existing_item_changed(self):
class Test(resource.Resource):
pass
t = Test()
key = "attr"
value = 1
t[key] = value
self.assertEqual(value, t._attrs[key])
self.assertTrue(key in t._dirty)
def test__setitem__existing_item_unchanged(self):
class Test(resource.Resource):
pass
key = "attr"
value = 1
t = Test(attrs={key: value})
t._reset_dirty() # Clear dirty list so this checks as unchanged.
t[key] = value
self.assertEqual(value, t._attrs[key])
self.assertTrue(key not in t._dirty)
def test__setitem__new_item(self):
class Test(resource.Resource):
pass
t = Test()
key = "attr"
value = 1
t[key] = value
self.assertEqual(value, t._attrs[key])
self.assertTrue(key in t._dirty)
def test__delitem__(self):
class Test(resource.Resource):
pass
key = "attr"
value = 1
t = Test(attrs={key: value})
del t[key]
self.assertTrue(key not in t._attrs)
self.assertTrue(key in t._dirty)
def test__len__(self):
class Test(resource.Resource):
pass
attrs = {"a": 1, "b": 2, "c": 3}
t = Test(attrs=attrs)
self.assertEqual(len(attrs.keys()), len(t))
def test__iter__(self):
class Test(resource.Resource):
pass
attrs = {"a": 1, "b": 2, "c": 3}
t = Test(attrs=attrs)
for attr in t:
self.assertEqual(attrs[attr], t[attr])
def _test_resource_serialization(self, session_method, resource_method):
attr_type = resource.Resource
class Test(resource.Resource):
allow_create = True
attr = resource.prop("attr", type=attr_type)
the_id = 123
sot = Test()
sot.attr = resource.Resource({"id": the_id})
self.assertEqual(attr_type, type(sot.attr))
def fake_call(*args, **kwargs):
attrs = kwargs["json"]
try:
json.dumps(attrs)
except TypeError as e:
self.fail("Unable to serialize _attrs: %s" % e)
resp = FakeResponse(attrs)
return resp
session = mock.Mock()
setattr(session, session_method, mock.Mock(side_effect=fake_call))
if resource_method == "create_by_id":
session.create_by_id(session, sot._attrs)
elif resource_method == "update_by_id":
session.update_by_id(session, None, sot._attrs)
def test_create_serializes_resource_types(self):
self._test_resource_serialization("post", "create_by_id")
def test_update_serializes_resource_types(self):
self._test_resource_serialization("patch", "update_by_id")
class FakeResponse(object):
def __init__(self, response, status_code=200, headers=None):
self.body = response
self.status_code = status_code
headers = headers if headers else {'content-type': 'application/json'}
self.headers = requests.structures.CaseInsensitiveDict(headers)
def json(self):
return self.body
class TestFind(base.TestCase):
NAME = 'matrix'
ID = 'Fishburne'
PROP = 'attribute2'
def setUp(self):
super(TestFind, self).setUp()
self.mock_session = mock.Mock()
self.mock_get = mock.Mock()
self.mock_session.get = self.mock_get
self.matrix = {'id': self.ID, 'name': self.NAME, 'prop': self.PROP}
def test_name(self):
self.mock_get.side_effect = [
exceptions.NotFoundException(),
FakeResponse({FakeResource.resources_key: [self.matrix]})
]
result = FakeResource.find(self.mock_session, self.NAME,
path_args=fake_arguments)
self.assertEqual(self.NAME, result.name)
self.assertEqual(self.PROP, result.prop)
def test_id(self):
self.mock_get.side_effect = [
FakeResponse({FakeResource.resource_key: self.matrix})
]
result = FakeResource.find(self.mock_session, self.ID,
path_args=fake_arguments)
self.assertEqual(self.ID, result.id)
self.assertEqual(self.PROP, result.prop)
path = "fakes/" + fake_parent + "/data/" + self.ID
self.mock_get.assert_any_call(path,)
def test_id_no_retrieve(self):
self.mock_get.side_effect = [
FakeResponse({FakeResource.resources_key: [self.matrix]})
]
class NoRetrieveResource(FakeResource):
allow_retrieve = False
result = NoRetrieveResource.find(self.mock_session, self.ID,
path_args=fake_arguments)
self.assertEqual(self.ID, result.id)
self.assertEqual(self.PROP, result.prop)
def test_dups(self):
dupe = self.matrix.copy()
dupe['id'] = 'different'
self.mock_get.side_effect = [
# Raise a 404 first so we get out of the ID search and into name.
exceptions.NotFoundException(),
FakeResponse({FakeResource.resources_key: [self.matrix, dupe]})
]
self.assertRaises(exceptions.DuplicateResource, FakeResource.find,
self.mock_session, self.NAME)
def test_id_attribute_find(self):
floater = {'ip_address': "127.0.0.1", 'prop': self.PROP}
self.mock_get.side_effect = [
FakeResponse({FakeResource.resource_key: floater})
]
FakeResource.id_attribute = 'ip_address'
FakeResource.id_attribute = 'ip_address'
result = FakeResource.find(self.mock_session, "127.0.0.1",
path_args=fake_arguments)
self.assertEqual("127.0.0.1", result.id)
self.assertEqual(self.PROP, result.prop)
FakeResource.id_attribute = 'id'
p = {'ip_address': "127.0.0.1"}
path = fake_path + "?limit=2"
self.mock_get.called_once_with(path, params=p,)
def test_nada(self):
self.mock_get.side_effect = [
exceptions.NotFoundException(),
FakeResponse({FakeResource.resources_key: []})
]
self.assertIsNone(FakeResource.find(self.mock_session, self.NAME))
def test_no_name(self):
self.mock_get.side_effect = [
exceptions.NotFoundException(),
FakeResponse({FakeResource.resources_key: [self.matrix]})
]
FakeResource.name_attribute = None
self.assertIsNone(FakeResource.find(self.mock_session, self.NAME))
def test_nada_not_ignored(self):
self.mock_get.side_effect = [
exceptions.NotFoundException(),
FakeResponse({FakeResource.resources_key: []})
]
self.assertRaises(exceptions.ResourceNotFound, FakeResource.find,
self.mock_session, self.NAME, ignore_missing=False)
class TestWaitForStatus(base.TestCase):
def __init__(self, *args, **kwargs):
super(TestWaitForStatus, self).__init__(*args, **kwargs)
self.build = FakeResponse(self.body_with_status(fake_body, 'BUILD'))
self.active = FakeResponse(self.body_with_status(fake_body, 'ACTIVE'))
self.error = FakeResponse(self.body_with_status(fake_body, 'ERROR'))
def setUp(self):
super(TestWaitForStatus, self).setUp()
self.sess = mock.Mock()
def body_with_status(self, body, status):
body_copy = copy.deepcopy(body)
body_copy[fake_resource]['status'] = status
return body_copy
def test_wait_for_status_nothing(self):
self.sess.get = mock.Mock()
sot = FakeResource.new(**fake_data)
sot.status = 'ACTIVE'
self.assertEqual(sot, resource.wait_for_status(
self.sess, sot, 'ACTIVE', [], 1, 2))
self.assertEqual([], self.sess.get.call_args_list)
def test_wait_for_status(self):
self.sess.get = mock.Mock()
self.sess.get.side_effect = [self.build, self.active]
sot = FakeResource.new(**fake_data)
self.assertEqual(sot, resource.wait_for_status(
self.sess, sot, 'ACTIVE', [], 1, 2))
def test_wait_for_status_timeout(self):
self.sess.get = mock.Mock()
self.sess.get.side_effect = [self.build, self.build]
sot = FakeResource.new(**fake_data)
self.assertRaises(exceptions.ResourceTimeout, resource.wait_for_status,
self.sess, sot, 'ACTIVE', ['ERROR'], 1, 2)
def test_wait_for_status_failures(self):
self.sess.get = mock.Mock()
self.sess.get.side_effect = [self.build, self.error]
sot = FakeResource.new(**fake_data)
self.assertRaises(exceptions.ResourceFailure, resource.wait_for_status,
self.sess, sot, 'ACTIVE', ['ERROR'], 1, 2)
def test_wait_for_status_no_status(self):
class FakeResourceNoStatus(resource.Resource):
allow_retrieve = True
sot = FakeResourceNoStatus.new(id=123)
self.assertRaises(AttributeError, resource.wait_for_status,
self.sess, sot, 'ACTIVE', ['ERROR'], 1, 2)
class TestWaitForDelete(base.TestCase):
def test_wait_for_delete(self):
sess = mock.Mock()
sot = FakeResource.new(**fake_data)
sot.get = mock.Mock()
sot.get.side_effect = [
sot,
exceptions.NotFoundException(
'not found', FakeResponse({}, status_code=404))]
self.assertEqual(sot, resource.wait_for_delete(sess, sot, 1, 2))
def test_wait_for_delete_fail(self):
sess = mock.Mock()
sot = FakeResource.new(**fake_data)
sot.get = mock.Mock(return_value=sot)
self.assertRaises(exceptions.ResourceTimeout, resource.wait_for_delete,
sess, sot, 1, 2)