1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-03-12 04:58:34 +03:00

Adding testing mechaniccs

This commit is contained in:
Adolfo Gómez García 2024-02-18 16:48:20 +01:00
parent 9614323a1b
commit 57be9c80b6
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
11 changed files with 187 additions and 21 deletions

View File

@ -153,13 +153,23 @@ class Environment:
return Environment(name, id_generators) return Environment(name, id_generators)
@staticmethod @staticmethod
def environment_for_type(type_) -> 'Environment': def type_environment(type_: typing.Type) -> 'Environment':
""" """
Obtains an environment associated with a type instead of a record Obtains an environment associated with a type instead of a record
@param type_: Type @param type_: Type
@return Associated Environment @return Associated Environment
""" """
return Environment('type-' + str(type_)) return Environment('type-' + str(type_))
@staticmethod
def private_environment(owner: typing.Any) -> 'Environment':
"""
Obtains an environment with an unique identifier
@return: An environment with an unique identifier
"""
return Environment(
'#_#' + str(id(owner)) + '#^#'
)
@staticmethod @staticmethod
def temporary_environment() -> 'Environment': def temporary_environment() -> 'Environment':
@ -187,7 +197,7 @@ class Environment:
return env return env
@staticmethod @staticmethod
def ommon_environment() -> 'Environment': def common_environment() -> 'Environment':
""" """
Provides global environment Provides global environment
""" """

View File

@ -50,7 +50,7 @@ class DelayedTask(Environmentable):
""" """
Remember to invoke parent init in derived clases using super(myClass,self).__init__() to let this initialize its own variables Remember to invoke parent init in derived clases using super(myClass,self).__init__() to let this initialize its own variables
""" """
super().__init__(environment or Environment.environment_for_type(self.__class__)) super().__init__(environment or Environment.type_environment(self.__class__))
def execute(self) -> None: def execute(self) -> None:
""" """

View File

@ -28,8 +28,8 @@
""" """
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
""" """
import base64
import time import time
import codecs
import pickle # nosec: pickle is safe here import pickle # nosec: pickle is safe here
import threading import threading
from socket import gethostname from socket import gethostname
@ -71,6 +71,7 @@ class DelayedTaskThread(threading.Thread):
except Exception as e: except Exception as e:
logger.exception("Exception in thread %s: %s", e.__class__, e) logger.exception("Exception in thread %s: %s", e.__class__, e)
finally: finally:
# This is run on a different thread, so we ensure the connection is closed
connections['default'].close() connections['default'].close()
@ -120,9 +121,9 @@ class DelayedTaskRunner(metaclass=singleton.Singleton):
) # @UndefinedVariable ) # @UndefinedVariable
if task.insert_date > now + timedelta(seconds=30): if task.insert_date > now + timedelta(seconds=30):
logger.warning('Executed %s due to insert_date being in the future!', task.type) logger.warning('Executed %s due to insert_date being in the future!', task.type)
taskInstanceDump = codecs.decode(task.instance.encode(), 'base64') task_instance_dump = base64.b64decode(task.instance.encode())
task.delete() task.delete()
taskInstance = pickle.loads(taskInstanceDump) # nosec: controlled pickle task_instance = pickle.loads(task_instance_dump) # nosec: controlled pickle
except IndexError: except IndexError:
return # No problem, there is no waiting delayed task return # No problem, there is no waiting delayed task
except OperationalError: except OperationalError:
@ -134,11 +135,11 @@ class DelayedTaskRunner(metaclass=singleton.Singleton):
logger.exception('Obtainint one task for execution') logger.exception('Obtainint one task for execution')
return return
if taskInstance: if task_instance:
logger.debug('Executing delayedTask:>%s<', task) logger.debug('Executing delayedTask:>%s<', task)
# Re-create environment data # Re-create environment data
taskInstance.env = Environment.environment_for_type(taskInstance.__class__) task_instance.env = Environment.type_environment(task_instance.__class__)
DelayedTaskThread(taskInstance).start() DelayedTaskThread(task_instance).start()
def _insert(self, instance: DelayedTask, delay: int, tag: str) -> None: def _insert(self, instance: DelayedTask, delay: int, tag: str) -> None:
now = sql_datetime() now = sql_datetime()
@ -148,7 +149,7 @@ class DelayedTaskRunner(metaclass=singleton.Singleton):
# Save "env" from delayed task, set it to None and restore it after save # Save "env" from delayed task, set it to None and restore it after save
env = instance.env env = instance.env
instance.env = None # type: ignore # clean env before saving pickle, save space (the env will be created again when executing) instance.env = None # type: ignore # clean env before saving pickle, save space (the env will be created again when executing)
instance_dump = codecs.encode(pickle.dumps(instance), 'base64').decode() instance_dump = base64.b64encode(pickle.dumps(instance)).decode()
instance.env = env instance.env = env
type_name = str(cls.__module__ + '.' + cls.__name__) type_name = str(cls.__module__ + '.' + cls.__name__)

View File

@ -47,7 +47,7 @@ from uds.core import types, consts
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from .user_service import UserService from .user_service import UserService
from .publication import Publication from .publication import Publication
from uds.core import services from uds.core import services, environment
from uds.core.util.unique_name_generator import UniqueNameGenerator from uds.core.util.unique_name_generator import UniqueNameGenerator
from uds.core.util.unique_mac_generator import UniqueMacGenerator from uds.core.util.unique_mac_generator import UniqueMacGenerator
from uds.core.util.unique_gid_generator import UniqueGIDGenerator from uds.core.util.unique_gid_generator import UniqueGIDGenerator
@ -211,7 +211,7 @@ class Service(Module):
def __init__( def __init__(
self, self,
environment, environment: 'environment.Environment',
parent: 'services.ServiceProvider', parent: 'services.ServiceProvider',
values: Module.ValuesType = None, values: Module.ValuesType = None,
uuid: typing.Optional[str] = None, uuid: typing.Optional[str] = None,

View File

@ -83,7 +83,7 @@ class TestEnvironment(UDSTransactionTestCase):
self.assertEqual(env.cache.get('test'), None) self.assertEqual(env.cache.get('test'), None)
def test_global_environment(self) -> None: def test_global_environment(self) -> None:
env = environment.Environment.ommon_environment() env = environment.Environment.common_environment()
self._check_environment(env, environment.COMMON_ENV, True) self._check_environment(env, environment.COMMON_ENV, True)
def test_temporary_environment(self) -> None: def test_temporary_environment(self) -> None:
@ -99,8 +99,8 @@ class TestEnvironment(UDSTransactionTestCase):
self._check_environment(env, 't-test_table-123', True) self._check_environment(env, 't-test_table-123', True)
def test_environment_for_type(self) -> None: def test_environment_for_type(self) -> None:
env = environment.Environment.environment_for_type('test_type') env = environment.Environment.type_environment(TestEnvironment)
self._check_environment(env, 'type-test_type', True) self._check_environment(env, 'type-' + str(TestEnvironment), True)
def test_exclusive_temporary_environment(self) -> None: def test_exclusive_temporary_environment(self) -> None:
unique_key: str = '' unique_key: str = ''

View File

@ -96,7 +96,7 @@ class PhysicalMachinesMultiSerializationTest(UDSTestCase):
environment: Environment environment: Environment
def setUp(self) -> None: def setUp(self) -> None:
self.environment = Environment.environment_for_type('test') self.environment = Environment.temporary_environment()
self.environment.storage.save_to_db('ips', pickle.dumps(STORED_IPS)) self.environment.storage.save_to_db('ips', pickle.dumps(STORED_IPS))
def check(self, version: str, instance: 'service_multi.IPMachinesService') -> None: def check(self, version: str, instance: 'service_multi.IPMachinesService') -> None:

View File

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2024 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
Author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import typing
import collections.abc
from unittest import mock
from ...utils.autospec import autospec, AutoSpecMethodInfo
from uds.services.Proxmox import provider, client
METHODS_INFO: typing.Final[list[AutoSpecMethodInfo]] = [
AutoSpecMethodInfo('test', method=mock.Mock(return_value=True)),
]
class TestProxmovProvider:
def test_provider(self) -> None:
"""
Test the provider
"""
client = autospec(provider.ProxmoxProvider, METHODS_INFO)
assert client.test() is True

View File

@ -35,9 +35,8 @@ import typing
# We use storage, so we need transactional tests # We use storage, so we need transactional tests
from tests.utils.test import UDSTransactionTestCase from tests.utils.test import UDSTransactionTestCase
from uds.core.util import autoserializable from uds.core.services import service
from uds.core.environment import Environment from uds.core.environment import Environment
from uds.services import Proxmox
from uds.services.Proxmox.deployment import Operation as Operation, ProxmoxDeployment as Deployment from uds.services.Proxmox.deployment import Operation as Operation, ProxmoxDeployment as Deployment
@ -95,7 +94,7 @@ class ProxmoxDeploymentSerializationTest(UDSTransactionTestCase):
environment = Environment.testing_environment() environment = Environment.testing_environment()
def _create_instance(unmarshal_data: 'bytes|None' = None) -> Deployment: def _create_instance(unmarshal_data: 'bytes|None' = None) -> Deployment:
instance = Deployment(environment=environment, service=None) instance = Deployment(environment=environment, service=service.Service(Environment.testing_environment(), )
if unmarshal_data: if unmarshal_data:
instance.unmarshal(unmarshal_data) instance.unmarshal(unmarshal_data)
return instance return instance

View File

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2024 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
Author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import collections.abc
import typing
import dataclasses
from unittest import mock
@dataclasses.dataclass
class AutoSpecMethodInfo:
name: str
return_value: typing.Any = None
method: 'typing.Callable|None' = None
def autospec(cls: typing.Type, metods_info: collections.abc.Iterable, **kwargs: typing.Any) -> typing.Any:
"""
This is a helper function that will create a mock object with the same methods as the class passed as parameter.
This is useful for testing purposes, where you want to mock a class and still have the same methods available.
"""
obj = mock.create_autospec(cls, **kwargs)
for method_info in metods_info:
setattr(obj, method_info.name, method_info.method or mock.Mock(return_value=method_info.return_value))
return obj

View File

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2024 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
Author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import random
import uuid
import typing
import collections.abc
from uds.core import services, environment
def fake_service_provider() -> services.ServiceProvider:
uuid_ = str(uuid.uuid4())
return services.ServiceProvider(
environment=environment.Environment.private_environment(uuid_),
uuid=uuid_,
)
def fake_service() -> services.Service:
uuid_ = str(uuid.uuid4())
return services.Service(
environment=environment.Environment.private_environment(uuid_),
parent=fake_service_provider(),
uuid=uuid_,
)

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2022 Virtual Cable S.L. # Copyright (c) 2022-2024 Virtual Cable S.L.U.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -26,7 +26,7 @@
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
""" """
@author: Adolfo Gómez, dkmaster at dkmon dot com Author: Adolfo Gómez, dkmaster at dkmon dot com
""" """
import random import random