diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1f6ceec4475f0354e59706f8c13ba02704bc53a1..b315409e5229df19dc4dddc7683e0f9927ba874f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,8 @@ stages: - test - dependencies - deploy - - integration_test + - integration + - functional # include the individual .gitlab-ci.yml of each micro-service include: @@ -13,4 +14,5 @@ include: - local: '/src/context/.gitlab-ci.yml' - local: '/src/device/.gitlab-ci.yml' - local: '/src/service/.gitlab-ci.yml' - - local: '/src/integration_tester/.gitlab-ci.yml' + - local: '/src/tester_integration/.gitlab-ci.yml' + - local: '/src/tester_functional/.gitlab-ci.yml' diff --git a/run_integration_tests.sh b/run_in_kubernetes.sh similarity index 100% rename from run_integration_tests.sh rename to run_in_kubernetes.sh diff --git a/run_local_tests.sh b/run_local_tests.sh new file mode 100755 index 0000000000000000000000000000000000000000..59aef4c79f8e179e432db74b37cfc9cc01a201b2 --- /dev/null +++ b/run_local_tests.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +cd $(dirname $0)/src +RCFILE=~/teraflow/controller/coverage/.coveragerc +COVERAGEFILE=~/teraflow/controller/coverage/.coverage + +# Run unitary tests and analyze coverage of code at same time + +# First destroy old coverage file +rm -f $COVERAGEFILE + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + common/database/tests/test_unitary.py \ + common/database/tests/test_engine_inmemory.py + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + context/tests/test_unitary.py + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + device/tests/test_unitary.py + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + service/tests/test_unitary.py + +# Run integration tests and analyze coverage of code at same time +export DB_ENGINE='redis' +export REDIS_SERVICE_HOST='10.1.7.194' +export REDIS_SERVICE_PORT='31789' +export REDIS_DATABASE_ID='0' +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + common/database/tests/test_engine_redis.py \ + tester_integration/test_context_device_service.py diff --git a/run_unitary_tests.sh b/run_unitary_tests.sh deleted file mode 100755 index 766d9029232b8fb5db94cfb2a5507fb8270d4494..0000000000000000000000000000000000000000 --- a/run_unitary_tests.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -cd $(dirname $0)/src -RCFILE=~/teraflow/controller/coverage/.coveragerc - -# Run unitary tests and analyze coverage of code at same time -coverage run --rcfile=$RCFILE -m pytest --log-level=DEBUG --verbose \ - common/database/tests/test_unitary.py \ - common/database/tests/test_engine_inmemory.py \ - context/tests/test_unitary.py \ - device/tests/test_unitary.py \ - service/tests/test_unitary.py - -## Run integration tests and analyze coverage of code at same time -#coverage run --rcfile=$RCFILE --append -m pytest --log-level=WARN --verbose \ -# common/database/tests/test_integration_redis.py \ -# device/tests/test_integration.py diff --git a/src/common/database/api/context/service/Constraint.py b/src/common/database/api/context/service/Constraint.py index 327c2dc8ba9df6aae4aac23cf959bc5d5f8d6d13..866f98c1761ba399a3d27fc440524515626a019d 100644 --- a/src/common/database/api/context/service/Constraint.py +++ b/src/common/database/api/context/service/Constraint.py @@ -57,7 +57,6 @@ class Constraint(_Entity): def dump(self) -> Dict: attributes = self.attributes.get() - return { - 'constraint_type': self.constraint_type, - 'constraint_value': attributes.get('constraint_value', None), - } + result = self.dump_id() + result.update({'constraint_value': attributes.get('constraint_value', None)}) + return result diff --git a/src/common/tools/service/ServiceCheckers.py b/src/common/tools/service/ServiceCheckers.py index 8026533bf9875d15a0a734ff7a063745e6d304c9..d8bafd1c03db0b1b330633062456752da7cd93c9 100644 --- a/src/common/tools/service/ServiceCheckers.py +++ b/src/common/tools/service/ServiceCheckers.py @@ -3,15 +3,27 @@ from common.database.api.Database import Database from common.exceptions.ServiceException import ServiceException def check_service_exists(database : Database, context_id : str, service_id : str): - db_context = database.context(context_id).create() + if not database.contexts.contains(context_id): + msg = 'Context({}) does not exist in the database.' + msg = msg.format(context_id) + raise ServiceException(grpc.StatusCode.NOT_FOUND, msg) + + db_context = database.context(context_id) if db_context.services.contains(service_id): return + msg = 'Context({})/Service({}) does not exist in the database.' msg = msg.format(context_id, service_id) raise ServiceException(grpc.StatusCode.NOT_FOUND, msg) def check_service_not_exists(database : Database, context_id : str, service_id : str): - db_context = database.context(context_id).create() + if not database.contexts.contains(context_id): + msg = 'Context({}) does not exist in the database.' + msg = msg.format(context_id) + raise ServiceException(grpc.StatusCode.NOT_FOUND, msg) + + db_context = database.context(context_id) if not db_context.services.contains(service_id): return + msg = 'Context({})/Service({}) already exists in the database.' msg = msg.format(context_id, service_id) raise ServiceException(grpc.StatusCode.ALREADY_EXISTS, msg) diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py index c82f62d202b96bcf9ec787e7124a6825bfbc33e1..4827488a77846cc50d08c54e4bf06f5a6ba4c51c 100644 --- a/src/context/tests/test_unitary.py +++ b/src/context/tests/test_unitary.py @@ -2,38 +2,28 @@ import copy, grpc, logging, pytest from google.protobuf.json_format import MessageToDict from common.database.Factory import get_database, DatabaseEngineEnum from common.database.api.Database import Database +from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID from common.database.tests.script import populate_example -from common.tests.Assertions import validate_empty, validate_link_id, validate_topology +from common.tests.Assertions import validate_empty, validate_link_id, validate_topology, validate_topology_has_devices, validate_topology_has_links, validate_topology_is_empty from context.client.ContextClient import ContextClient from context.proto.context_pb2 import Empty, Link, LinkId from context.service.ContextService import ContextService from context.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD -port = 10000 + GRPC_SERVICE_PORT # avoid first 1024 privileged ports to avoid evelating permissions for tests +port = 10000 + GRPC_SERVICE_PORT # avoid privileged ports LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) +# use "copy.deepcopy" to prevent propagating forced changes during tests +CONTEXT_ID = {'contextUuid': {'uuid': DEFAULT_CONTEXT_ID}} +TOPOLOGY_ID = {'contextId': copy.deepcopy(CONTEXT_ID), 'topoId': {'uuid': DEFAULT_TOPOLOGY_ID}} LINK_ID = {'link_id': {'uuid': 'DEV1/EP2 ==> DEV2/EP1'}} LINK = { 'link_id': {'link_id': {'uuid': 'DEV1/EP2 ==> DEV2/EP1'}}, 'endpointList' : [ - { - 'topoId': { - 'contextId': {'contextUuid': {'uuid': 'admin'}}, - 'topoId': {'uuid': 'admin'} - }, - 'dev_id': {'device_id': {'uuid': 'DEV1'}}, - 'port_id': {'uuid' : 'EP2'} - }, - { - 'topoId': { - 'contextId': {'contextUuid': {'uuid': 'admin'}}, - 'topoId': {'uuid': 'admin'} - }, - 'dev_id': {'device_id': {'uuid': 'DEV2'}}, - 'port_id': {'uuid' : 'EP1'} - }, + {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': {'device_id': {'uuid': 'DEV1'}}, 'port_id': {'uuid' : 'EP2'}}, + {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': {'device_id': {'uuid': 'DEV2'}}, 'port_id': {'uuid' : 'EP1'}}, ] } @@ -59,7 +49,7 @@ def context_client(context_service): def test_get_topology_empty(context_client : ContextClient, context_database : Database): # should work context_database.clear_all() - validate_topology(MessageToDict( + validate_topology_is_empty(MessageToDict( context_client.GetTopology(Empty()), including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False)) @@ -67,10 +57,13 @@ def test_get_topology_empty(context_client : ContextClient, context_database : D def test_get_topology_completed(context_client : ContextClient, context_database : Database): # should work populate_example(context_database, add_services=False) - validate_topology(MessageToDict( + topology = MessageToDict( context_client.GetTopology(Empty()), including_default_value_fields=True, preserving_proto_field_name=True, - use_integers_for_enums=False)) + use_integers_for_enums=False) + validate_topology(topology) + validate_topology_has_devices(topology) + validate_topology_has_links(topology) def test_delete_link_empty_uuid(context_client : ContextClient): # should fail with link not found @@ -227,7 +220,10 @@ def test_add_link_default_endpoint_context_topology(context_client : ContextClie def test_get_topology_completed_2(context_client : ContextClient): # should work - validate_topology(MessageToDict( + topology = MessageToDict( context_client.GetTopology(Empty()), including_default_value_fields=True, preserving_proto_field_name=True, - use_integers_for_enums=False)) + use_integers_for_enums=False) + validate_topology(topology) + validate_topology_has_devices(topology) + validate_topology_has_links(topology) diff --git a/src/device/tests/test_unitary.py b/src/device/tests/test_unitary.py index 773da58bcbb18b2c6a91416a365fa5bf18a98c04..95eb0a1af27433ddd85d100160cd122aebc60b8a 100644 --- a/src/device/tests/test_unitary.py +++ b/src/device/tests/test_unitary.py @@ -1,6 +1,7 @@ import copy, grpc, logging, pytest from google.protobuf.json_format import MessageToDict from common.database.Factory import get_database, DatabaseEngineEnum +from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID from common.database.api.context.topology.device.OperationalStatus import OperationalStatus from common.tests.Assertions import validate_device_id, validate_empty from device.client.DeviceClient import DeviceClient @@ -8,47 +9,31 @@ from device.proto.context_pb2 import Device, DeviceId from device.service.DeviceService import DeviceService from device.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD +port = 10000 + GRPC_SERVICE_PORT # avoid privileged ports + LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) +# use "copy.deepcopy" to prevent propagating forced changes during tests +CONTEXT_ID = {'contextUuid': {'uuid': DEFAULT_CONTEXT_ID}} +TOPOLOGY_ID = {'contextId': copy.deepcopy(CONTEXT_ID), 'topoId': {'uuid': DEFAULT_TOPOLOGY_ID}} DEVICE_ID = {'device_id': {'uuid': 'DEV1'}} DEVICE = { - 'device_id': {'device_id': {'uuid': 'DEV1'}}, + 'device_id': copy.deepcopy(DEVICE_ID), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'}, 'devOperationalStatus': OperationalStatus.ENABLED.value, 'endpointList' : [ { - 'port_id': { - 'topoId': { - 'contextId': {'contextUuid': {'uuid': 'admin'}}, - 'topoId': {'uuid': 'admin'} - }, - 'dev_id': {'device_id': {'uuid': 'DEV1'}}, - 'port_id': {'uuid' : 'EP2'} - }, + 'port_id': {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': copy.deepcopy(DEVICE_ID), 'port_id': {'uuid' : 'EP2'}}, 'port_type': 'WDM' }, { - 'port_id': { - 'topoId': { - 'contextId': {'contextUuid': {'uuid': 'admin'}}, - 'topoId': {'uuid': 'admin'} - }, - 'dev_id': {'device_id': {'uuid': 'DEV1'}}, - 'port_id': {'uuid' : 'EP3'} - }, + 'port_id': {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': copy.deepcopy(DEVICE_ID), 'port_id': {'uuid' : 'EP3'}}, 'port_type': 'WDM' }, { - 'port_id': { - 'topoId': { - 'contextId': {'contextUuid': {'uuid': 'admin'}}, - 'topoId': {'uuid': 'admin'} - }, - 'dev_id': {'device_id': {'uuid': 'DEV1'}}, - 'port_id': {'uuid' : 'EP4'} - }, + 'port_id': {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': copy.deepcopy(DEVICE_ID), 'port_id': {'uuid' : 'EP4'}}, 'port_type': 'WDM' }, ] @@ -62,14 +47,14 @@ def device_database(): @pytest.fixture(scope='session') def device_service(device_database): _service = DeviceService( - device_database, port=GRPC_SERVICE_PORT, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD) + device_database, port=port, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD) _service.start() yield _service _service.stop() @pytest.fixture(scope='session') def device_client(device_service): - _client = DeviceClient(address='127.0.0.1', port=GRPC_SERVICE_PORT) + _client = DeviceClient(address='127.0.0.1', port=port) yield _client _client.close() diff --git a/src/integration_tester/definitions.py b/src/integration_tester/definitions.py deleted file mode 100644 index 768b84cd2498f5cf7dda636d77a330c02d9837c7..0000000000000000000000000000000000000000 --- a/src/integration_tester/definitions.py +++ /dev/null @@ -1,148 +0,0 @@ -from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID -from common.database.api.context.service.ServiceState import ServiceState -from common.database.api.context.service.ServiceType import ServiceType -from common.database.api.context.topology.device.OperationalStatus import OperationalStatus - -CONTEXT_ID = {'contextUuid': {'uuid': DEFAULT_CONTEXT_ID}} - -TOPOLOGY_ID = {'contextId': CONTEXT_ID, 'topoId': {'uuid': DEFAULT_TOPOLOGY_ID}} - -DEVICE_ID_DEV1 = {'device_id': {'uuid': 'dev1'}} -DEVICE_DEV1 = { - 'device_id': DEVICE_ID_DEV1, 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'}, - 'devOperationalStatus': OperationalStatus.ENABLED.value, - 'endpointList' : [ - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV1, 'port_id': {'uuid' : 'port2'}}, 'port_type': 'WDM'}, - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV1, 'port_id': {'uuid' : 'port3'}}, 'port_type': 'WDM'}, - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV1, 'port_id': {'uuid' : 'port101'}}, 'port_type': 'OCH'}, - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV1, 'port_id': {'uuid' : 'port102'}}, 'port_type': 'OCH'}, - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV1, 'port_id': {'uuid' : 'port103'}}, 'port_type': 'OCH'}, - ] -} - -DEVICE_ID_DEV2 = {'device_id': {'uuid': 'dev2'}} -DEVICE_DEV2 = { - 'device_id': DEVICE_ID_DEV2, 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'}, - 'devOperationalStatus': OperationalStatus.ENABLED.value, - 'endpointList' : [ - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV2, 'port_id': {'uuid' : 'port1'}}, 'port_type': 'WDM'}, - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV2, 'port_id': {'uuid' : 'port3'}}, 'port_type': 'WDM'}, - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV2, 'port_id': {'uuid' : 'port101'}}, 'port_type': 'OCH'}, - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV2, 'port_id': {'uuid' : 'port102'}}, 'port_type': 'OCH'}, - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV2, 'port_id': {'uuid' : 'port103'}}, 'port_type': 'OCH'}, - ] -} - -DEVICE_ID_DEV3 = {'device_id': {'uuid': 'dev3'}} -DEVICE_DEV3 = { - 'device_id': DEVICE_ID_DEV3, - 'device_type': 'ROADM', - 'device_config': {'device_config': '<config/>'}, - 'devOperationalStatus': OperationalStatus.ENABLED.value, - 'endpointList' : [ - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV3, 'port_id': {'uuid' : 'port1'}}, 'port_type': 'WDM'}, - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV3, 'port_id': {'uuid' : 'port2'}}, 'port_type': 'WDM'}, - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV3, 'port_id': {'uuid' : 'port101'}}, 'port_type': 'OCH'}, - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV3, 'port_id': {'uuid' : 'port102'}}, 'port_type': 'OCH'}, - {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV3, 'port_id': {'uuid' : 'port103'}}, 'port_type': 'OCH'}, - ] -} - -LINK_ID_DEV1_DEV2 = {'link_id': {'uuid': 'dev1/port2 ==> dev2/port1'}} -LINK_DEV1_DEV2 = { - 'link_id': LINK_ID_DEV1_DEV2, - 'endpointList' : [ - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV1, 'port_id': {'uuid' : 'port2'}}, - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV2, 'port_id': {'uuid' : 'port1'}}, - ] -} - -LINK_ID_DEV1_DEV3 = {'link_id': {'uuid': 'dev1/port3 ==> dev3/port1'}} -LINK_DEV1_DEV3 = { - 'link_id': LINK_ID_DEV1_DEV3, - 'endpointList' : [ - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV1, 'port_id': {'uuid' : 'port3'}}, - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV3, 'port_id': {'uuid' : 'port1'}}, - ] -} - -LINK_ID_DEV2_DEV1 = {'link_id': {'uuid': 'dev2/port1 ==> dev1/port2'}} -LINK_DEV2_DEV1 = { - 'link_id': LINK_ID_DEV2_DEV1, - 'endpointList' : [ - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV2, 'port_id': {'uuid' : 'port1'}}, - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV1, 'port_id': {'uuid' : 'port2'}}, - ] -} - -LINK_ID_DEV2_DEV3 = {'link_id': {'uuid': 'dev2/port3 ==> dev3/port2'}} -LINK_DEV2_DEV3 = { - 'link_id': LINK_ID_DEV2_DEV3, - 'endpointList' : [ - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV2, 'port_id': {'uuid' : 'port3'}}, - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV3, 'port_id': {'uuid' : 'port2'}}, - ] -} - -LINK_ID_DEV3_DEV1 = {'link_id': {'uuid': 'dev3/port1 ==> dev1/port3'}} -LINK_DEV3_DEV1 = { - 'link_id': LINK_ID_DEV3_DEV1, - 'endpointList' : [ - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV3, 'port_id': {'uuid' : 'port1'}}, - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV1, 'port_id': {'uuid' : 'port3'}}, - ] -} - -LINK_ID_DEV3_DEV2 = {'link_id': {'uuid': 'dev3/port2 ==> dev2/port3'}} -LINK_DEV3_DEV2 = { - 'link_id': LINK_ID_DEV3_DEV2, - 'endpointList' : [ - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV3, 'port_id': {'uuid' : 'port2'}}, - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV2, 'port_id': {'uuid' : 'port3'}}, - ] -} - -SERVICE_ID_SVC1 = {'contextId': CONTEXT_ID, 'cs_id': {'uuid': 'svc1'}} -SERVICE_SVC1 = { - 'cs_id': SERVICE_ID_SVC1, 'serviceType': ServiceType.L3NM.value, 'serviceConfig': {'serviceConfig': '<config/>'}, - 'serviceState': {'serviceState': ServiceState.PLANNED.value}, - 'constraint': [ - {'constraint_type': 'latency_ms', 'constraint_value': '100'}, - {'constraint_type': 'hops', 'constraint_value': '5'}, - ], - 'endpointList' : [ - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV1, 'port_id': {'uuid' : 'port101'}}, - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV2, 'port_id': {'uuid' : 'port101'}}, - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV3, 'port_id': {'uuid' : 'port101'}}, - ] -} - -SERVICE_ID_SVC2 = {'contextId': CONTEXT_ID, 'cs_id': {'uuid': 'svc2'}} -SERVICE_SVC2 = { - 'cs_id': SERVICE_ID_SVC2, 'serviceType': ServiceType.L3NM.value, 'serviceConfig': {'serviceConfig': '<config/>'}, - 'serviceState': {'serviceState': ServiceState.PLANNED.value}, - 'constraint': [ - {'constraint_type': 'latency_ms', 'constraint_value': '100'}, - {'constraint_type': 'hops', 'constraint_value': '5'}, - ], - 'endpointList' : [ - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV1, 'port_id': {'uuid' : 'port102'}}, - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV2, 'port_id': {'uuid' : 'port102'}}, - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV3, 'port_id': {'uuid' : 'port102'}}, - ] -} - -SERVICE_ID_SVC3 = {'contextId': CONTEXT_ID, 'cs_id': {'uuid': 'svc3'}} -SERVICE_SVC3 = { - 'cs_id': SERVICE_ID_SVC3, 'serviceType': ServiceType.L3NM.value, 'serviceConfig': {'serviceConfig': '<config/>'}, - 'serviceState': {'serviceState': ServiceState.PLANNED.value}, - 'constraint': [ - {'constraint_type': 'latency_ms', 'constraint_value': '100'}, - {'constraint_type': 'hops', 'constraint_value': '5'}, - ], - 'endpointList' : [ - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV1, 'port_id': {'uuid' : 'port103'}}, - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV2, 'port_id': {'uuid' : 'port103'}}, - {'topoId': TOPOLOGY_ID, 'dev_id': DEVICE_ID_DEV3, 'port_id': {'uuid' : 'port103'}}, - ] -} diff --git a/src/service/tests/test_unitary.py b/src/service/tests/test_unitary.py index 800de71dac6e6ad9072d081c849095361ed1ffb1..48d56af038b226caa2ffe6a73b71509f4c011ad1 100644 --- a/src/service/tests/test_unitary.py +++ b/src/service/tests/test_unitary.py @@ -2,6 +2,7 @@ import copy, grpc, logging, pytest from google.protobuf.json_format import MessageToDict from common.database.Factory import get_database, DatabaseEngineEnum from common.database.api.Database import Database +from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID from common.database.tests.script import populate_example from common.tests.Assertions import validate_empty, validate_service, validate_service_id, \ validate_service_list_is_empty, validate_service_list_is_not_empty @@ -11,15 +12,17 @@ from service.proto.context_pb2 import Empty from service.proto.service_pb2 import Service, ServiceId, ServiceStateEnum, ServiceType from service.service.ServiceService import ServiceService +port = 10000 + GRPC_SERVICE_PORT # avoid privileged ports + LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) -SERVICE_ID = { - 'contextId': {'contextUuid': {'uuid': 'admin'}}, - 'cs_id': {'uuid': 'DEV1'}, -} +# use "copy.deepcopy" to prevent propagating forced changes during tests +CONTEXT_ID = {'contextUuid': {'uuid': DEFAULT_CONTEXT_ID}} +TOPOLOGY_ID = {'contextId': copy.deepcopy(CONTEXT_ID), 'topoId': {'uuid': DEFAULT_TOPOLOGY_ID}} +SERVICE_ID = {'contextId': copy.deepcopy(CONTEXT_ID), 'cs_id': {'uuid': 'DEV1'}} SERVICE = { - 'cs_id': SERVICE_ID, + 'cs_id': copy.deepcopy(SERVICE_ID), 'serviceType': ServiceType.L3NM, 'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceStateEnum.PLANNED}, @@ -28,30 +31,9 @@ SERVICE = { {'constraint_type': 'hops', 'constraint_value': '5'}, ], 'endpointList' : [ - { - 'topoId': { - 'contextId': {'contextUuid': {'uuid': 'admin'}}, - 'topoId': {'uuid': 'admin'} - }, - 'dev_id': {'device_id': {'uuid': 'DEV1'}}, - 'port_id': {'uuid' : 'EP5'} - }, - { - 'topoId': { - 'contextId': {'contextUuid': {'uuid': 'admin'}}, - 'topoId': {'uuid': 'admin'} - }, - 'dev_id': {'device_id': {'uuid': 'DEV2'}}, - 'port_id': {'uuid' : 'EP5'} - }, - { - 'topoId': { - 'contextId': {'contextUuid': {'uuid': 'admin'}}, - 'topoId': {'uuid': 'admin'} - }, - 'dev_id': {'device_id': {'uuid': 'DEV3'}}, - 'port_id': {'uuid' : 'EP5'} - }, + {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': {'device_id': {'uuid': 'DEV1'}}, 'port_id': {'uuid' : 'EP5'}}, + {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': {'device_id': {'uuid': 'DEV2'}}, 'port_id': {'uuid' : 'EP5'}}, + {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': {'device_id': {'uuid': 'DEV3'}}, 'port_id': {'uuid' : 'EP5'}}, ] } @@ -64,14 +46,14 @@ def database(): @pytest.fixture(scope='session') def service_service(database): _service = ServiceService( - database, port=GRPC_SERVICE_PORT, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD) + database, port=port, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD) _service.start() yield _service _service.stop() @pytest.fixture(scope='session') def service_client(service_service): - _client = ServiceClient(address='127.0.0.1', port=GRPC_SERVICE_PORT) + _client = ServiceClient(address='127.0.0.1', port=port) yield _client _client.close() @@ -92,6 +74,15 @@ def test_create_service_wrong_service_attributes(service_client : ServiceClient) msg = 'service.cs_id.contextId.contextUuid.uuid() string is empty.' assert e.value.details() == msg + # should fail with service context does not exist + with pytest.raises(grpc._channel._InactiveRpcError) as e: + copy_service = copy.deepcopy(SERVICE) + copy_service['cs_id']['contextId']['contextUuid']['uuid'] = 'wrong-context' + service_client.CreateService(Service(**copy_service)) + assert e.value.code() == grpc.StatusCode.NOT_FOUND + msg = 'Context(wrong-context) does not exist in the database.' + assert e.value.details() == msg + # should fail with wrong service id with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_service = copy.deepcopy(SERVICE) @@ -155,11 +146,12 @@ def test_create_service_wrong_constraint(service_client : ServiceClient): msg = 'Duplicated ConstraintType(latency_ms) in Constraint(#1) of Context(admin)/Service(DEV1).' assert e.value.details() == msg -def test_create_service_wrong_endpoint(service_client : ServiceClient): +def test_create_service_wrong_endpoint(service_client : ServiceClient, database : Database): # should fail with wrong endpoint context with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_service = copy.deepcopy(SERVICE) copy_service['endpointList'][0]['topoId']['contextId']['contextUuid']['uuid'] = 'wrong-context' + print(copy_service) service_client.CreateService(Service(**copy_service)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT msg = ' '.join([ @@ -169,7 +161,7 @@ def test_create_service_wrong_endpoint(service_client : ServiceClient): ]) assert e.value.details() == msg - # should fail with endpoint topology not found + # should fail with wrong endpoint topology with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_service = copy.deepcopy(SERVICE) copy_service['endpointList'][0]['topoId']['topoId']['uuid'] = 'wrong-topo' @@ -234,6 +226,15 @@ def test_create_service_wrong_endpoint(service_client : ServiceClient): assert e.value.details() == msg def test_get_service_does_not_exist(service_client : ServiceClient): + # should fail with service context does not exist + with pytest.raises(grpc._channel._InactiveRpcError) as e: + copy_service_id = copy.deepcopy(SERVICE_ID) + copy_service_id['contextId']['contextUuid']['uuid'] = 'wrong-context' + service_client.GetServiceById(ServiceId(**copy_service_id)) + assert e.value.code() == grpc.StatusCode.NOT_FOUND + msg = 'Context(wrong-context) does not exist in the database.' + assert e.value.details() == msg + # should fail with service does not exist with pytest.raises(grpc._channel._InactiveRpcError) as e: service_client.GetServiceById(ServiceId(**SERVICE_ID)) @@ -298,7 +299,25 @@ def test_update_service(service_client : ServiceClient): use_integers_for_enums=False)) def test_delete_service_wrong_service_id(service_client : ServiceClient): - # should fail with wrong service id / context + # should fail with service context is empty + with pytest.raises(grpc._channel._InactiveRpcError) as e: + copy_service_id = copy.deepcopy(SERVICE_ID) + copy_service_id['contextId']['contextUuid']['uuid'] = '' + service_client.DeleteService(ServiceId(**copy_service_id)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg = 'service_id.contextId.contextUuid.uuid() string is empty.' + assert e.value.details() == msg + + # should fail with service context does not exist + with pytest.raises(grpc._channel._InactiveRpcError) as e: + copy_service_id = copy.deepcopy(SERVICE_ID) + copy_service_id['contextId']['contextUuid']['uuid'] = 'wrong-context' + service_client.DeleteService(ServiceId(**copy_service_id)) + assert e.value.code() == grpc.StatusCode.NOT_FOUND + msg = 'Context(wrong-context) does not exist in the database.' + assert e.value.details() == msg + + # should fail with service id is empty with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_service_id = copy.deepcopy(SERVICE_ID) copy_service_id['cs_id']['uuid'] = '' @@ -307,12 +326,13 @@ def test_delete_service_wrong_service_id(service_client : ServiceClient): msg = 'service_id.cs_id.uuid() string is empty.' assert e.value.details() == msg + # should fail with service id is empty with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_service_id = copy.deepcopy(SERVICE_ID) - copy_service_id['contextId']['contextUuid']['uuid'] = '' + copy_service_id['cs_id']['uuid'] = 'wrong-service' service_client.DeleteService(ServiceId(**copy_service_id)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'service_id.contextId.contextUuid.uuid() string is empty.' + assert e.value.code() == grpc.StatusCode.NOT_FOUND + msg = 'Context(admin)/Service(wrong-service) does not exist in the database.' assert e.value.details() == msg def test_delete_service(service_client : ServiceClient): diff --git a/src/integration_tester/.gitlab-ci.yml b/src/tester_functional/.gitlab-ci.yml similarity index 81% rename from src/integration_tester/.gitlab-ci.yml rename to src/tester_functional/.gitlab-ci.yml index 045b235d3aea7a7e56deedc3e60bf0dcab4dca13..d90552d2d14b4bdcf271764ee34c7ef6d8d964b8 100644 --- a/src/integration_tester/.gitlab-ci.yml +++ b/src/tester_functional/.gitlab-ci.yml @@ -1,7 +1,7 @@ # Build, tag, and push the Docker images to the GitLab Docker registry -build integration_tester: +build tester_functional: variables: - IMAGE_NAME: 'integration_tester' # name of the microservice + IMAGE_NAME: 'tester_functional' # name of the microservice IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) stage: build before_script: @@ -20,13 +20,13 @@ build integration_tester: - .gitlab-ci.yml # Pull, execute, and run unitary tests for the Docker image from the GitLab registry -test integration_tester: +test tester_functional: variables: - IMAGE_NAME: 'integration_tester' # name of the microservice + IMAGE_NAME: 'tester_functional' # name of the microservice IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) stage: test needs: - - build integration_tester + - build tester_functional before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: @@ -40,15 +40,15 @@ test integration_tester: - src/$IMAGE_NAME/** - .gitlab-ci.yml -# Run integration tests in Kubernetes Cluster -integration_test integration_tester: +# Run functional tests in Kubernetes Cluster +functional tester_functional: variables: - IMAGE_NAME: 'integration_tester' # name of the microservice + IMAGE_NAME: 'tester_functional' # name of the microservice IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) - stage: integration_test + stage: functional needs: - - build integration_tester - - test integration_tester + - build tester_functional + - test tester_functional - deploy context - deploy device - deploy service diff --git a/src/tester_functional/Dockerfile b/src/tester_functional/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..36883f30bf76fbee7094ee9c5dbe626c6fdec071 --- /dev/null +++ b/src/tester_functional/Dockerfile @@ -0,0 +1,35 @@ +FROM python:3-slim + +# Install dependencies +RUN apt-get --yes --quiet --quiet update && \ + apt-get --yes --quiet --quiet install wget g++ && \ + rm -rf /var/lib/apt/lists/* + +# Set Python to show logs as they occur +ENV PYTHONUNBUFFERED=0 + +# Get generic Python packages +RUN python3 -m pip install --upgrade pip setuptools wheel pip-tools + +# Set working directory +WORKDIR /var/teraflow + +# Create module sub-folders +RUN mkdir -p /var/teraflow/tester_functional + +# Get Python packages per module +COPY tester_functional/requirements.in tester_functional/requirements.in +RUN pip-compile --output-file=tester_functional/requirements.txt tester_functional/requirements.in +RUN python3 -m pip install -r tester_functional/requirements.in + +# Add files into working directory +COPY common/. common +COPY context/. context +COPY device/. device +COPY service/. service +COPY tester_functional/. tester_functional + +# Run integration tester +ENTRYPOINT ["pytest", "-v", "--log-level=DEBUG", \ + "tester_functional/test_context_device_service.py" \ +] diff --git a/src/integration_tester/__init__.py b/src/tester_functional/__init__.py similarity index 100% rename from src/integration_tester/__init__.py rename to src/tester_functional/__init__.py diff --git a/src/tester_functional/definitions.py b/src/tester_functional/definitions.py new file mode 100644 index 0000000000000000000000000000000000000000..2b38e74bc9d25199e4f192dbb3f093ec033fe80b --- /dev/null +++ b/src/tester_functional/definitions.py @@ -0,0 +1,165 @@ +import copy +from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID +from common.database.api.context.service.ServiceState import ServiceState +from common.database.api.context.service.ServiceType import ServiceType +from common.database.api.context.topology.device.OperationalStatus import OperationalStatus + +# use "copy.deepcopy" to prevent propagating forced changes during tests +dc = copy.deepcopy + +CONTEXT_ID = {'contextUuid': {'uuid': DEFAULT_CONTEXT_ID}} + +TOPOLOGY_ID = {'contextId': dc(CONTEXT_ID), 'topoId': {'uuid': DEFAULT_TOPOLOGY_ID}} + +DEVICE_ID_DEV1 = {'device_id': {'uuid': 'dev1'}} +DEVICE_DEV1 = { + 'device_id': dc(DEVICE_ID_DEV1), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'}, + 'devOperationalStatus': OperationalStatus.ENABLED.value, + 'endpointList' : [ + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port2'}}, + 'port_type': 'WDM'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port3'}}, + 'port_type': 'WDM'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port101'}}, + 'port_type': 'OCH'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port102'}}, + 'port_type': 'OCH'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port103'}}, + 'port_type': 'OCH'}, + ] +} + +DEVICE_ID_DEV2 = {'device_id': {'uuid': 'dev2'}} +DEVICE_DEV2 = { + 'device_id': dc(DEVICE_ID_DEV2), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'}, + 'devOperationalStatus': OperationalStatus.ENABLED.value, + 'endpointList' : [ + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port1'}}, + 'port_type': 'WDM'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port3'}}, + 'port_type': 'WDM'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port101'}}, + 'port_type': 'OCH'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port102'}}, + 'port_type': 'OCH'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port103'}}, + 'port_type': 'OCH'}, + ] +} + +DEVICE_ID_DEV3 = {'device_id': {'uuid': 'dev3'}} +DEVICE_DEV3 = { + 'device_id': dc(DEVICE_ID_DEV3), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'}, + 'devOperationalStatus': OperationalStatus.ENABLED.value, + 'endpointList' : [ + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port1'}}, + 'port_type': 'WDM'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port2'}}, + 'port_type': 'WDM'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port101'}}, + 'port_type': 'OCH'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port102'}}, + 'port_type': 'OCH'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port103'}}, + 'port_type': 'OCH'}, + ] +} + +LINK_ID_DEV1_DEV2 = {'link_id': {'uuid': 'dev1/port2 ==> dev2/port1'}} +LINK_DEV1_DEV2 = { + 'link_id': dc(LINK_ID_DEV1_DEV2), + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port2'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port1'}}, + ] +} + +LINK_ID_DEV1_DEV3 = {'link_id': {'uuid': 'dev1/port3 ==> dev3/port1'}} +LINK_DEV1_DEV3 = { + 'link_id': dc(LINK_ID_DEV1_DEV3), + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port3'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port1'}}, + ] +} + +LINK_ID_DEV2_DEV1 = {'link_id': {'uuid': 'dev2/port1 ==> dev1/port2'}} +LINK_DEV2_DEV1 = { + 'link_id': dc(LINK_ID_DEV2_DEV1), + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port1'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port2'}}, + ] +} + +LINK_ID_DEV2_DEV3 = {'link_id': {'uuid': 'dev2/port3 ==> dev3/port2'}} +LINK_DEV2_DEV3 = { + 'link_id': dc(LINK_ID_DEV2_DEV3), + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port3'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port2'}}, + ] +} + +LINK_ID_DEV3_DEV1 = {'link_id': {'uuid': 'dev3/port1 ==> dev1/port3'}} +LINK_DEV3_DEV1 = { + 'link_id': dc(LINK_ID_DEV3_DEV1), + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port1'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port3'}}, + ] +} + +LINK_ID_DEV3_DEV2 = {'link_id': {'uuid': 'dev3/port2 ==> dev2/port3'}} +LINK_DEV3_DEV2 = { + 'link_id': dc(LINK_ID_DEV3_DEV2), + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port2'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port3'}}, + ] +} + +SERVICE_ID_SVC1 = {'contextId': dc(CONTEXT_ID), 'cs_id': {'uuid': 'svc1'}} +SERVICE_SVC1 = { + 'cs_id': dc(SERVICE_ID_SVC1), 'serviceType': ServiceType.L3NM.value, + 'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceState.PLANNED.value}, + 'constraint': [ + {'constraint_type': 'latency_ms', 'constraint_value': '100'}, + {'constraint_type': 'hops', 'constraint_value': '5'}, + ], + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port101'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port101'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port101'}}, + ] +} + +SERVICE_ID_SVC2 = {'contextId': dc(CONTEXT_ID), 'cs_id': {'uuid': 'svc2'}} +SERVICE_SVC2 = { + 'cs_id': dc(SERVICE_ID_SVC2), 'serviceType': ServiceType.L3NM.value, + 'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceState.PLANNED.value}, + 'constraint': [ + {'constraint_type': 'latency_ms', 'constraint_value': '100'}, + {'constraint_type': 'hops', 'constraint_value': '5'}, + ], + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port102'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port102'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port102'}}, + ] +} + +SERVICE_ID_SVC3 = {'contextId': dc(CONTEXT_ID), 'cs_id': {'uuid': 'svc3'}} +SERVICE_SVC3 = { + 'cs_id': dc(SERVICE_ID_SVC3), 'serviceType': ServiceType.L3NM.value, + 'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceState.PLANNED.value}, + 'constraint': [ + {'constraint_type': 'latency_ms', 'constraint_value': '100'}, + {'constraint_type': 'hops', 'constraint_value': '5'}, + ], + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port103'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port103'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port103'}}, + ] +} diff --git a/src/integration_tester/requirements.in b/src/tester_functional/requirements.in similarity index 100% rename from src/integration_tester/requirements.in rename to src/tester_functional/requirements.in diff --git a/src/integration_tester/test_context_device_service.py b/src/tester_functional/test_context_device_service.py similarity index 93% rename from src/integration_tester/test_context_device_service.py rename to src/tester_functional/test_context_device_service.py index 4f3cf4cfe4db6df6df8afd9c3e581a356abb873b..d1b228b0dffc40b681314ebe66879534b85ed699 100644 --- a/src/integration_tester/test_context_device_service.py +++ b/src/tester_functional/test_context_device_service.py @@ -8,10 +8,10 @@ from common.tests.Assertions import validate_device_id, validate_link_id, valida from context.client.ContextClient import ContextClient from context.proto.context_pb2 import Device, Empty, Link from device.client.DeviceClient import DeviceClient -from integration_tester.definitions import DEVICE_DEV1, DEVICE_DEV2, DEVICE_DEV3 -from integration_tester.definitions import LINK_DEV1_DEV2, LINK_DEV1_DEV3, LINK_DEV2_DEV1, LINK_DEV2_DEV3, \ +from tester_functional.definitions import DEVICE_DEV1, DEVICE_DEV2, DEVICE_DEV3 +from tester_functional.definitions import LINK_DEV1_DEV2, LINK_DEV1_DEV3, LINK_DEV2_DEV1, LINK_DEV2_DEV3, \ LINK_DEV3_DEV1, LINK_DEV3_DEV2 -from integration_tester.definitions import SERVICE_SVC1, SERVICE_SVC2, SERVICE_SVC3 +from tester_functional.definitions import SERVICE_SVC1, SERVICE_SVC2, SERVICE_SVC3 from service.client.ServiceClient import ServiceClient from service.proto.service_pb2 import Service diff --git a/src/tester_integration/.gitlab-ci.yml b/src/tester_integration/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..460fa4414f7b70e0b96146e621b20c773b4184ed --- /dev/null +++ b/src/tester_integration/.gitlab-ci.yml @@ -0,0 +1,52 @@ +# Build, tag, and push the Docker images to the GitLab Docker registry +build tester_integration: + variables: + IMAGE_NAME: 'tester_integration' # name of the microservice + IMAGE_NAME_TEST: 'tester_integration-test' # name of the microservice + IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) + stage: build + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + script: + - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/Dockerfile ./src/ + - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" + - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" + rules: + - changes: + - src/common/** + - src/context/** + - src/device/** + - src/service/** + - src/$IMAGE_NAME/** + - .gitlab-ci.yml + +# Pull, execute, and run unitary tests for the Docker image from the GitLab registry +test tester_integration: + variables: + IMAGE_NAME: 'tester_integration' # name of the microservice + IMAGE_NAME_TEST: 'tester_integration-test' # name of the microservice + IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) + stage: test + needs: + - build tester_integration + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + - if docker network list | grep teraflowbridge; then echo "teraflowbridge is already created"; else docker network create -d bridge teraflowbridge; fi + script: + - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" + - docker run -d -p 6379:6379 --name redis --network=teraflowbridge "redis:6.2" + - docker ps -a + - sleep 5 + - docker ps -a + - docker run -d --name $IMAGE_NAME --network=teraflowbridge --env "DB_ENGINE=redis" --env "REDIS_SERVICE_HOST=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis)" --env "REDIS_SERVICE_PORT=6379" --env "REDIS_DATABASE_ID=0" "$IMAGE_NAME:$IMAGE_TAG" bash -c "pytest --log-level=INFO --verbose common/database/tests/test_engine_redis.py tester_integration/test_context_device_service.py" + after_script: + - docker stop $IMAGE_NAME redis + - docker rm $IMAGE_NAME redis + rules: + - changes: + - src/common/** + - src/context/** + - src/device/** + - src/service/** + - src/$IMAGE_NAME/** + - .gitlab-ci.yml diff --git a/src/integration_tester/Dockerfile b/src/tester_integration/Dockerfile similarity index 63% rename from src/integration_tester/Dockerfile rename to src/tester_integration/Dockerfile index 5c833b1202f8a571cdd86081f550fe19d59280b8..31ab8884b62814d43fbadaf624754e10681c8e7c 100644 --- a/src/integration_tester/Dockerfile +++ b/src/tester_integration/Dockerfile @@ -15,21 +15,21 @@ RUN python3 -m pip install --upgrade pip setuptools wheel pip-tools WORKDIR /var/teraflow # Create module sub-folders -RUN mkdir -p /var/teraflow/integration_tester +RUN mkdir -p /var/teraflow/tester_integration # Get Python packages per module -COPY integration_tester/requirements.in integration_tester/requirements.in -RUN pip-compile --output-file=integration_tester/requirements.txt integration_tester/requirements.in -RUN python3 -m pip install -r integration_tester/requirements.in +COPY tester_integration/requirements.in tester_integration/requirements.in +RUN pip-compile --output-file=tester_integration/requirements.txt tester_integration/requirements.in +RUN python3 -m pip install -r tester_integration/requirements.in # Add files into working directory COPY common/. common COPY context/. context COPY device/. device COPY service/. service -COPY integration_tester/. integration_tester +COPY tester_integration/. tester_integration # Run integration tester ENTRYPOINT ["pytest", "-v", "--log-level=DEBUG", \ - "integration_tester/test_context_device_service.py" \ + "tester_integration/test_context_device_service.py" \ ] diff --git a/src/tester_integration/__init__.py b/src/tester_integration/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/tester_integration/definitions.py b/src/tester_integration/definitions.py new file mode 100644 index 0000000000000000000000000000000000000000..2b38e74bc9d25199e4f192dbb3f093ec033fe80b --- /dev/null +++ b/src/tester_integration/definitions.py @@ -0,0 +1,165 @@ +import copy +from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID +from common.database.api.context.service.ServiceState import ServiceState +from common.database.api.context.service.ServiceType import ServiceType +from common.database.api.context.topology.device.OperationalStatus import OperationalStatus + +# use "copy.deepcopy" to prevent propagating forced changes during tests +dc = copy.deepcopy + +CONTEXT_ID = {'contextUuid': {'uuid': DEFAULT_CONTEXT_ID}} + +TOPOLOGY_ID = {'contextId': dc(CONTEXT_ID), 'topoId': {'uuid': DEFAULT_TOPOLOGY_ID}} + +DEVICE_ID_DEV1 = {'device_id': {'uuid': 'dev1'}} +DEVICE_DEV1 = { + 'device_id': dc(DEVICE_ID_DEV1), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'}, + 'devOperationalStatus': OperationalStatus.ENABLED.value, + 'endpointList' : [ + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port2'}}, + 'port_type': 'WDM'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port3'}}, + 'port_type': 'WDM'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port101'}}, + 'port_type': 'OCH'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port102'}}, + 'port_type': 'OCH'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port103'}}, + 'port_type': 'OCH'}, + ] +} + +DEVICE_ID_DEV2 = {'device_id': {'uuid': 'dev2'}} +DEVICE_DEV2 = { + 'device_id': dc(DEVICE_ID_DEV2), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'}, + 'devOperationalStatus': OperationalStatus.ENABLED.value, + 'endpointList' : [ + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port1'}}, + 'port_type': 'WDM'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port3'}}, + 'port_type': 'WDM'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port101'}}, + 'port_type': 'OCH'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port102'}}, + 'port_type': 'OCH'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port103'}}, + 'port_type': 'OCH'}, + ] +} + +DEVICE_ID_DEV3 = {'device_id': {'uuid': 'dev3'}} +DEVICE_DEV3 = { + 'device_id': dc(DEVICE_ID_DEV3), 'device_type': 'ROADM', 'device_config': {'device_config': '<config/>'}, + 'devOperationalStatus': OperationalStatus.ENABLED.value, + 'endpointList' : [ + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port1'}}, + 'port_type': 'WDM'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port2'}}, + 'port_type': 'WDM'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port101'}}, + 'port_type': 'OCH'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port102'}}, + 'port_type': 'OCH'}, + {'port_id': {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port103'}}, + 'port_type': 'OCH'}, + ] +} + +LINK_ID_DEV1_DEV2 = {'link_id': {'uuid': 'dev1/port2 ==> dev2/port1'}} +LINK_DEV1_DEV2 = { + 'link_id': dc(LINK_ID_DEV1_DEV2), + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port2'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port1'}}, + ] +} + +LINK_ID_DEV1_DEV3 = {'link_id': {'uuid': 'dev1/port3 ==> dev3/port1'}} +LINK_DEV1_DEV3 = { + 'link_id': dc(LINK_ID_DEV1_DEV3), + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port3'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port1'}}, + ] +} + +LINK_ID_DEV2_DEV1 = {'link_id': {'uuid': 'dev2/port1 ==> dev1/port2'}} +LINK_DEV2_DEV1 = { + 'link_id': dc(LINK_ID_DEV2_DEV1), + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port1'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port2'}}, + ] +} + +LINK_ID_DEV2_DEV3 = {'link_id': {'uuid': 'dev2/port3 ==> dev3/port2'}} +LINK_DEV2_DEV3 = { + 'link_id': dc(LINK_ID_DEV2_DEV3), + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port3'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port2'}}, + ] +} + +LINK_ID_DEV3_DEV1 = {'link_id': {'uuid': 'dev3/port1 ==> dev1/port3'}} +LINK_DEV3_DEV1 = { + 'link_id': dc(LINK_ID_DEV3_DEV1), + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port1'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port3'}}, + ] +} + +LINK_ID_DEV3_DEV2 = {'link_id': {'uuid': 'dev3/port2 ==> dev2/port3'}} +LINK_DEV3_DEV2 = { + 'link_id': dc(LINK_ID_DEV3_DEV2), + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port2'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port3'}}, + ] +} + +SERVICE_ID_SVC1 = {'contextId': dc(CONTEXT_ID), 'cs_id': {'uuid': 'svc1'}} +SERVICE_SVC1 = { + 'cs_id': dc(SERVICE_ID_SVC1), 'serviceType': ServiceType.L3NM.value, + 'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceState.PLANNED.value}, + 'constraint': [ + {'constraint_type': 'latency_ms', 'constraint_value': '100'}, + {'constraint_type': 'hops', 'constraint_value': '5'}, + ], + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port101'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port101'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port101'}}, + ] +} + +SERVICE_ID_SVC2 = {'contextId': dc(CONTEXT_ID), 'cs_id': {'uuid': 'svc2'}} +SERVICE_SVC2 = { + 'cs_id': dc(SERVICE_ID_SVC2), 'serviceType': ServiceType.L3NM.value, + 'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceState.PLANNED.value}, + 'constraint': [ + {'constraint_type': 'latency_ms', 'constraint_value': '100'}, + {'constraint_type': 'hops', 'constraint_value': '5'}, + ], + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port102'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port102'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port102'}}, + ] +} + +SERVICE_ID_SVC3 = {'contextId': dc(CONTEXT_ID), 'cs_id': {'uuid': 'svc3'}} +SERVICE_SVC3 = { + 'cs_id': dc(SERVICE_ID_SVC3), 'serviceType': ServiceType.L3NM.value, + 'serviceConfig': {'serviceConfig': '<config/>'}, 'serviceState': {'serviceState': ServiceState.PLANNED.value}, + 'constraint': [ + {'constraint_type': 'latency_ms', 'constraint_value': '100'}, + {'constraint_type': 'hops', 'constraint_value': '5'}, + ], + 'endpointList' : [ + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV1), 'port_id': {'uuid' : 'port103'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV2), 'port_id': {'uuid' : 'port103'}}, + {'topoId': dc(TOPOLOGY_ID), 'dev_id': dc(DEVICE_ID_DEV3), 'port_id': {'uuid' : 'port103'}}, + ] +} diff --git a/src/tester_integration/requirements.in b/src/tester_integration/requirements.in new file mode 100644 index 0000000000000000000000000000000000000000..25abdad1b5767117956a88b816399635348884c7 --- /dev/null +++ b/src/tester_integration/requirements.in @@ -0,0 +1,6 @@ +grpcio-health-checking +grpcio +prometheus-client +pytest +pytest-benchmark +redis diff --git a/src/tester_integration/test_context_device_service.py b/src/tester_integration/test_context_device_service.py new file mode 100644 index 0000000000000000000000000000000000000000..2d128800c2e714de242de328c2d00a6134c378d7 --- /dev/null +++ b/src/tester_integration/test_context_device_service.py @@ -0,0 +1,138 @@ +import logging, pytest +from google.protobuf.json_format import MessageToDict +from common.database.Factory import get_database, DatabaseEngineEnum +from common.database.api.Database import Database +from common.tests.Assertions import validate_device_id, validate_link_id, validate_service_id, \ + validate_service_list_is_not_empty, validate_topology_has_devices, validate_topology_has_links, \ + validate_topology_is_empty +from context.Config import GRPC_SERVICE_PORT as GRPC_CONTEXT_SERVICE_PORT, \ + GRPC_MAX_WORKERS as GRPC_CONTEXT_MAX_WORKERS, GRPC_GRACE_PERIOD as GRPC_CONTEXT_GRACE_PERIOD +from context.client.ContextClient import ContextClient +from context.proto.context_pb2 import Device, Empty, Link +from context.service.ContextService import ContextService +from device.Config import GRPC_SERVICE_PORT as GRPC_DEVICE_SERVICE_PORT, \ + GRPC_MAX_WORKERS as GRPC_DEVICE_MAX_WORKERS, GRPC_GRACE_PERIOD as GRPC_DEVICE_GRACE_PERIOD +from device.client.DeviceClient import DeviceClient +from device.service.DeviceService import DeviceService +from service.service.ServiceService import ServiceService +from tester_integration.definitions import DEVICE_DEV1, DEVICE_DEV2, DEVICE_DEV3 +from tester_integration.definitions import LINK_DEV1_DEV2, LINK_DEV1_DEV3, LINK_DEV2_DEV1, LINK_DEV2_DEV3, \ + LINK_DEV3_DEV1, LINK_DEV3_DEV2 +from tester_integration.definitions import SERVICE_SVC1, SERVICE_SVC2, SERVICE_SVC3 +from service.Config import GRPC_SERVICE_PORT as GRPC_SERVICE_SERVICE_PORT, \ + GRPC_MAX_WORKERS as GRPC_SERVICE_MAX_WORKERS, GRPC_GRACE_PERIOD as GRPC_SERVICE_GRACE_PERIOD +from service.client.ServiceClient import ServiceClient +from service.proto.service_pb2 import Service + +PORT_CONTEXT = 10000 + GRPC_CONTEXT_SERVICE_PORT # avoid privileged ports +PORT_DEVICE = 10000 + GRPC_DEVICE_SERVICE_PORT # avoid privileged ports +PORT_SERVICE = 10000 + GRPC_SERVICE_SERVICE_PORT # avoid privileged ports + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +@pytest.fixture(scope='session') +def redis_database(): + _database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0) + return _database + +@pytest.fixture(scope='session') +def context_service(redis_database : Database): + context_database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0) + _service = ContextService( + context_database, port=PORT_CONTEXT, max_workers=GRPC_CONTEXT_MAX_WORKERS, + grace_period=GRPC_CONTEXT_GRACE_PERIOD) + _service.start() + yield _service + _service.stop() + +@pytest.fixture(scope='session') +def context_client(context_service): + _client = ContextClient(address='127.0.0.1', port=PORT_CONTEXT) + yield _client + _client.close() + +@pytest.fixture(scope='session') +def device_service(redis_database : Database): + device_database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0) + _service = DeviceService( + device_database, port=PORT_DEVICE, max_workers=GRPC_DEVICE_MAX_WORKERS, + grace_period=GRPC_DEVICE_GRACE_PERIOD) + _service.start() + yield _service + _service.stop() + +@pytest.fixture(scope='session') +def device_client(device_service): + _client = DeviceClient(address='127.0.0.1', port=PORT_DEVICE) + yield _client + _client.close() + +@pytest.fixture(scope='session') +def service_service(redis_database : Database): + service_database = get_database(engine=DatabaseEngineEnum.REDIS, REDIS_DATABASE_ID=0) + _service = ServiceService( + service_database, port=PORT_SERVICE, max_workers=GRPC_SERVICE_MAX_WORKERS, + grace_period=GRPC_SERVICE_GRACE_PERIOD) + _service.start() + yield _service + _service.stop() + +@pytest.fixture(scope='session') +def service_client(service_service): + _client = ServiceClient(address='127.0.0.1', port=PORT_SERVICE) + yield _client + _client.close() + +def test_clean_database(redis_database : Database): + # should work + redis_database.clear_all() + +def test_get_topology_empty(context_client : ContextClient): + # should work + validate_topology_is_empty(MessageToDict( + context_client.GetTopology(Empty()), + including_default_value_fields=True, preserving_proto_field_name=True, + use_integers_for_enums=False)) + +def test_add_devices(context_client : ContextClient, device_client : DeviceClient): + # should work + for device in [DEVICE_DEV1, DEVICE_DEV2, DEVICE_DEV3]: + validate_device_id(MessageToDict( + device_client.AddDevice(Device(**device)), + including_default_value_fields=True, preserving_proto_field_name=True, + use_integers_for_enums=False)) + + # should work + validate_topology_has_devices(MessageToDict( + context_client.GetTopology(Empty()), + including_default_value_fields=True, preserving_proto_field_name=True, + use_integers_for_enums=False)) + +def test_add_links(context_client : ContextClient): + # should work + for link in [LINK_DEV1_DEV2, LINK_DEV1_DEV3, LINK_DEV2_DEV1, LINK_DEV2_DEV3, LINK_DEV3_DEV1, LINK_DEV3_DEV2]: + validate_link_id(MessageToDict( + context_client.AddLink(Link(**link)), + including_default_value_fields=True, preserving_proto_field_name=True, + use_integers_for_enums=False)) + + # should work + validate_topology_has_links(MessageToDict( + context_client.GetTopology(Empty()), + including_default_value_fields=True, preserving_proto_field_name=True, + use_integers_for_enums=False)) + +def test_add_services(service_client : ServiceClient): + # should work + for service in [SERVICE_SVC1, SERVICE_SVC2, SERVICE_SVC3]: + validate_service_id(MessageToDict( + service_client.CreateService(Service(**service)), + including_default_value_fields=True, preserving_proto_field_name=True, + use_integers_for_enums=False)) + + # should work + validate_service_list_is_not_empty(MessageToDict( + service_client.GetServiceList(Empty()), + including_default_value_fields=True, preserving_proto_field_name=True, + use_integers_for_enums=False))