Add fake resources generator

In a variety of places we need to have an easy way to have resources
populated with fake data. It costs quite a lot of lines of code and can
be easily automated. With this change it can be as easy as `_fake =
sdk_fakes.generate_fake_resource(Project)`. The code is implemented not to
require estabilshed connection to ease use in tests.

Change-Id: I47312f4036a0b389cd3689466ab220ba558aa39a
This commit is contained in:
Artem Goncharov 2023-05-09 13:25:45 +02:00
parent aba2b4179c
commit d2a166a98e
4 changed files with 200 additions and 0 deletions

View File

122
openstack/test/fakes.py Normal file
View File

@ -0,0 +1,122 @@
# 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 inspect
from random import choice
from random import randint
from random import random
import uuid
from openstack import format as _format
from openstack import resource
def generate_fake_resource(resource_type, **attrs):
"""Generate fake resource
:param type resource_type: Object class
:param dict attrs: Optional attributes to be set on resource
:return: Instance of `resource_type` class populated with fake
values of expected types.
"""
base_attrs = dict()
for name, value in inspect.getmembers(
resource_type,
predicate=lambda x: isinstance(x, (resource.Body, resource.URI)),
):
if isinstance(value, resource.Body):
target_type = value.type
if target_type is None:
if (
name == "properties"
and hasattr(
resource_type, "_store_unknown_attrs_as_properties"
)
and resource_type._store_unknown_attrs_as_properties
):
# virtual "properties" attr which hosts all unknown attrs
# (i.e. Image)
base_attrs[name] = dict()
else:
# Type not defined - string
base_attrs[name] = uuid.uuid4().hex
elif issubclass(target_type, resource.Resource):
# Attribute is of another Resource type
base_attrs[name] = generate_fake_resource(target_type)
elif issubclass(target_type, list) and value.list_type is not None:
# List of ...
item_type = value.list_type
if issubclass(item_type, resource.Resource):
# item is of Resource type
base_attrs[name] = generate_fake_resource(item_type)
elif issubclass(item_type, dict):
base_attrs[name] = dict()
elif issubclass(item_type, str):
base_attrs[name] = [uuid.uuid4().hex]
else:
# Everything else
msg = "Fake value for %s.%s can not be generated" % (
resource_type.__name__,
name,
)
raise NotImplementedError(msg)
elif issubclass(target_type, list) and value.list_type is None:
# List of str
base_attrs[name] = [uuid.uuid4().hex]
elif issubclass(target_type, str):
# definitely string
base_attrs[name] = uuid.uuid4().hex
elif issubclass(target_type, int):
# int
base_attrs[name] = randint(1, 100)
elif issubclass(target_type, float):
# float
base_attrs[name] = random()
elif issubclass(target_type, bool) or issubclass(
target_type, _format.BoolStr
):
# bool
base_attrs[name] = choice([True, False])
elif issubclass(target_type, dict):
# some dict - without further details leave it empty
base_attrs[name] = dict()
else:
# Everything else
msg = "Fake value for %s.%s can not be generated" % (
resource_type.__name__,
name,
)
raise NotImplementedError(msg)
if isinstance(value, resource.URI):
# For URI we just generate something
base_attrs[name] = uuid.uuid4().hex
base_attrs.update(**attrs)
fake = resource_type(**base_attrs)
return fake
def generate_fake_resources(resource_type, count=1, attrs=None):
"""Generate given number of fake resource entities
:param type resource_type: Object class
:param int count: Number of objects to return
:param dict attrs: Attribute values to set into each instance
:return: Array of `resource_type` class instances populated with fake
values of expected types.
"""
if not attrs:
attrs = {}
for _ in range(count):
yield generate_fake_resource(resource_type, **attrs)

View File

@ -0,0 +1,73 @@
# 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 openstack import format as _format
from openstack import resource
from openstack.test import fakes
from openstack.tests.unit import base
class TestGetFake(base.TestCase):
def test_generate_fake_resource_one(self):
res = fakes.generate_fake_resource(resource.Resource)
self.assertIsInstance(res, resource.Resource)
def test_generate_fake_resource_list(self):
res = list(fakes.generate_fake_resources(resource.Resource, 2))
self.assertEqual(2, len(res))
self.assertIsInstance(res[0], resource.Resource)
def test_generate_fake_resource_types(self):
class Fake(resource.Resource):
a = resource.Body("a", type=str)
b = resource.Body("b", type=int)
c = resource.Body("c", type=bool)
d = resource.Body("d", type=_format.BoolStr)
e = resource.Body("e", type=dict)
f = resource.URI("path")
res = fakes.generate_fake_resource(Fake)
self.assertIsInstance(res.a, str)
self.assertIsInstance(res.b, int)
self.assertIsInstance(res.c, bool)
self.assertIsInstance(res.d, bool)
self.assertIsInstance(res.e, dict)
self.assertIsInstance(res.f, str)
def test_generate_fake_resource_attrs(self):
class Fake(resource.Resource):
a = resource.Body("a", type=str)
b = resource.Body("b", type=str)
res = fakes.generate_fake_resource(Fake, b="bar")
self.assertIsInstance(res.a, str)
self.assertIsInstance(res.b, str)
self.assertEqual("bar", res.b)
def test_generate_fake_resource_types_inherit(self):
class Fake(resource.Resource):
a = resource.Body("a", type=str)
class FakeInherit(resource.Resource):
a = resource.Body("a", type=Fake)
res = fakes.generate_fake_resource(FakeInherit)
self.assertIsInstance(res.a, Fake)
self.assertIsInstance(res.a.a, str)
def test_unknown_attrs_as_props(self):
class Fake(resource.Resource):
properties = resource.Body("properties")
_store_unknown_attrs_as_properties = True
res = fakes.generate_fake_resource(Fake)
self.assertIsInstance(res.properties, dict)

View File

@ -0,0 +1,5 @@
---
features:
- |
Add fake resource generator to ease unit testing in packages that depend on
openstacksdk.