import copy, logging, pytest
from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
from common.orm.Database import Database
from common.orm.Factory import get_database_backend, BackendEnum
from context.client.ContextClient import ContextClient
from context.proto.context_pb2 import ConfigActionEnum, Context, ContextId, Device, DeviceDriverEnum, DeviceId, DeviceOperationalStatusEnum, Empty, Link, LinkId, Service, ServiceId, ServiceStatusEnum, ServiceTypeEnum, Topology, TopologyId
from context.service.grpc_server.ContextService import ContextService
from context.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD #, RESTAPI_SERVICE_PORT, \
#    RESTAPI_BASE_URL
#from context.service.rest_server.Server import Server
#from context.service.rest_server.resources.Context import Context
#from .populate_database import populate_example

grpc_port = 10000 + GRPC_SERVICE_PORT # avoid privileged ports
#restapi_port = 10000 + RESTAPI_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 = {'context_uuid': {'uuid': DEFAULT_CONTEXT_UUID}}
CONTEXT = {'context_id': copy.deepcopy(CONTEXT_ID)}

TOPOLOGY_ID = {
    'context_id': copy.deepcopy(CONTEXT_ID),
    'topology_uuid': {'uuid': DEFAULT_TOPOLOGY_UUID},
}
TOPOLOGY = {
    'topology_id': copy.deepcopy(TOPOLOGY_ID),
    'device_ids': [],
    'link_ids': [],
}

DEVICE1_UUID = 'DEV1'
DEVICE1_ID = {'device_uuid': {'uuid': DEVICE1_UUID}}
DEVICE1 = {
    'device_id': copy.deepcopy(DEVICE1_ID),
    'device_type': 'packet-router',
    'device_config': {'config_rules': [
        {'action': ConfigActionEnum.CONFIGACTION_SET,
         'resource_key': 'device/resource-1/value',
         'resource_value': 'value1'},
        {'action': ConfigActionEnum.CONFIGACTION_SET,
         'resource_key': 'device/resource-2/value',
         'resource_value': 'value2'},
        {'action': ConfigActionEnum.CONFIGACTION_SET,
         'resource_key': 'device/resource-3/value',
         'resource_value': 'value3'},
    ]},
    'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED,
    'device_drivers': [DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, DeviceDriverEnum.DEVICEDRIVER_P4],
    'device_endpoints': [
        {'endpoint_id': {
            'topology_id': copy.deepcopy(TOPOLOGY_ID),
            'device_id': copy.deepcopy(DEVICE1_ID),
            'endpoint_uuid': {'uuid': 'EP1'},
        }, 'endpoint_type': 'port-packet-100G'},
        {'endpoint_id': {
            'topology_id': copy.deepcopy(TOPOLOGY_ID),
            'device_id': copy.deepcopy(DEVICE1_ID),
            'endpoint_uuid': {'uuid': 'EP2'},
        }, 'endpoint_type': 'port-packet-100G'},
        {'endpoint_id': {
            'topology_id': copy.deepcopy(TOPOLOGY_ID),
            'device_id': copy.deepcopy(DEVICE1_ID),
            'endpoint_uuid': {'uuid': 'EP3'},
        }, 'endpoint_type': 'port-packet-100G'},
    ],
}

DEVICE2_UUID = 'DEV2'
DEVICE2_ID = {'device_uuid': {'uuid': DEVICE2_UUID}}
DEVICE2 = {
    'device_id': copy.deepcopy(DEVICE2_ID),
    'device_type': 'packet-router',
    'device_config': {'config_rules': [
        {'action': ConfigActionEnum.CONFIGACTION_SET,
         'resource_key': 'device/resource-1/value',
         'resource_value': 'value4'},
        {'action': ConfigActionEnum.CONFIGACTION_SET,
         'resource_key': 'device/resource-2/value',
         'resource_value': 'value5'},
        {'action': ConfigActionEnum.CONFIGACTION_SET,
         'resource_key': 'device/resource-3/value',
         'resource_value': 'value6'},
    ]},
    'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED,
    'device_drivers': [DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, DeviceDriverEnum.DEVICEDRIVER_P4],
    'device_endpoints': [
        {'endpoint_id': {
            'topology_id': copy.deepcopy(TOPOLOGY_ID),
            'device_id': copy.deepcopy(DEVICE2_ID),
            'endpoint_uuid': {'uuid': 'EP1'},
        }, 'endpoint_type': 'port-packet-100G'},
        {'endpoint_id': {
            'topology_id': copy.deepcopy(TOPOLOGY_ID),
            'device_id': copy.deepcopy(DEVICE2_ID),
            'endpoint_uuid': {'uuid': 'EP2'},
        }, 'endpoint_type': 'port-packet-100G'},
        {'endpoint_id': {
            'topology_id': copy.deepcopy(TOPOLOGY_ID),
            'device_id': copy.deepcopy(DEVICE2_ID),
            'endpoint_uuid': {'uuid': 'EP3'},
        }, 'endpoint_type': 'port-packet-100G'},
    ],
}

LINK_UUID = 'DEV1/EP2 ==> DEV2/EP1'
LINK_ID = {'link_uuid': {'uuid': LINK_UUID}}
LINK = {
    'link_id': copy.deepcopy(LINK_ID),
    'link_endpoint_ids' : [
        {'topology_id': copy.deepcopy(TOPOLOGY_ID),
         'device_id': copy.deepcopy(DEVICE1_ID),
         'endpoint_uuid': {'uuid' : 'EP2'}},
        {'topology_id': copy.deepcopy(TOPOLOGY_ID),
         'device_id': copy.deepcopy(DEVICE2_ID),
         'endpoint_uuid': {'uuid' : 'EP1'}},
    ]
}

SERVICE_UUID = 'SVC:DEV1/EP2-DEV2/EP1'
SERVICE_ID = {
    'context_id': copy.deepcopy(CONTEXT_ID),
    'service_uuid': {'uuid': SERVICE_UUID},
}
SERVICE = {
    'service_id': copy.deepcopy(SERVICE_ID),
    'service_type': ServiceTypeEnum.SERVICETYPE_L3NM,
    'service_endpoint_ids' : [
        {'topology_id': copy.deepcopy(TOPOLOGY_ID),
         'device_id': copy.deepcopy(DEVICE1_ID),
         'endpoint_uuid': {'uuid' : 'EP2'}},
        {'topology_id': copy.deepcopy(TOPOLOGY_ID),
         'device_id': copy.deepcopy(DEVICE2_ID),
         'endpoint_uuid': {'uuid' : 'EP1'}},
    ],
    'service_constraints': [
        {'constraint_type': 'latency_ms', 'constraint_value': '15.2'},
        {'constraint_type': 'jitter_us', 'constraint_value': '1.2'},
    ],
    'service_status': {'service_status': ServiceStatusEnum.SERVICESTATUS_ACTIVE},
    'service_config': {'config_rules': [
        {'action': ConfigActionEnum.CONFIGACTION_SET,
         'resource_key': 'service/resource-1/value',
         'resource_value': 'value7'},
        {'action': ConfigActionEnum.CONFIGACTION_SET,
         'resource_key': 'service/resource-2/value',
         'resource_value': 'value8'},
        {'action': ConfigActionEnum.CONFIGACTION_SET,
         'resource_key': 'service/resource-3/value',
         'resource_value': 'value9'},
    ]},
}

@pytest.fixture(scope='session')
def context_database():
    database_backend = get_database_backend(engine=BackendEnum.INMEMORY)
    _database = Database(database_backend)
    return _database

@pytest.fixture(scope='session')
def context_service(context_database : Database): # pylint: disable=redefined-outer-name
    _service = ContextService(
        context_database, port=grpc_port, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD)
    _service.start()
    yield _service
    _service.stop()

@pytest.fixture(scope='session')
def context_client(context_service : ContextService): # pylint: disable=redefined-outer-name
    _client = ContextClient(address='127.0.0.1', port=grpc_port)
    yield _client
    _client.close()

def test_context_instances(
    context_client : ContextClient, context_database : Database): # pylint: disable=redefined-outer-name

    context_database.clear_all()
    response = context_client.ListContextIds(Empty())
    assert len(response.context_ids) == 0

    response = context_client.ListContexts(Empty())
    assert len(response.contexts) == 0

    response = context_client.GetContext(ContextId(**CONTEXT_ID))
    assert len(response.context_id.context_uuid.uuid) == 0
    assert len(response.topology_ids) == 0
    assert len(response.service_ids) == 0

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 0

    response = context_client.SetContext(Context(**CONTEXT))
    assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry))
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 2

    response = context_client.GetContext(ContextId(**CONTEXT_ID))
    assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert len(response.topology_ids) == 0
    assert len(response.service_ids) == 0

    response = context_client.ListContextIds(Empty())
    assert len(response.context_ids) == 1
    assert response.context_ids[0].context_uuid.uuid == DEFAULT_CONTEXT_UUID

    response = context_client.ListContexts(Empty())
    assert len(response.contexts) == 1
    assert response.contexts[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert len(response.contexts[0].topology_ids) == 0
    assert len(response.contexts[0].service_ids) == 0

    context_client.RemoveContext(ContextId(**CONTEXT_ID))

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 0

def test_topology_instances(
    context_client : ContextClient, context_database : Database): # pylint: disable=redefined-outer-name

    context_database.clear_all()

    response = context_client.SetContext(Context(**CONTEXT))
    assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID

    response = context_client.ListTopologyIds(ContextId(**CONTEXT_ID))
    assert len(response.topology_ids) == 0

    response = context_client.ListTopologies(ContextId(**CONTEXT_ID))
    assert len(response.topologies) == 0

    response = context_client.GetTopology(TopologyId(**TOPOLOGY_ID))
    assert len(response.topology_id.topology_uuid.uuid) == 0
    assert len(response.topology_id.context_id.context_uuid.uuid) == 0
    assert len(response.device_ids) == 0
    assert len(response.link_ids) == 0

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry))
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 2

    response = context_client.SetTopology(Topology(**TOPOLOGY))
    LOGGER.info('response={:s}'.format(str(response)))
    assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry))
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 5

    response = context_client.ListTopologyIds(ContextId(**CONTEXT_ID))
    assert len(response.topology_ids) == 1
    assert response.topology_ids[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.topology_ids[0].topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID

    response = context_client.ListTopologies(ContextId(**CONTEXT_ID))
    assert len(response.topologies) == 1
    assert response.topologies[0].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.topologies[0].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
    assert len(response.topologies[0].device_ids) == 0
    assert len(response.topologies[0].link_ids) == 0

    response = context_client.GetTopology(TopologyId(**TOPOLOGY_ID))
    assert response.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
    assert len(response.device_ids) == 0
    assert len(response.link_ids) == 0

    context_client.RemoveTopology(TopologyId(**TOPOLOGY_ID))
    context_client.RemoveContext(ContextId(**CONTEXT_ID))

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 0

def test_device_instances(
    context_client : ContextClient, context_database : Database): # pylint: disable=redefined-outer-name

    context_database.clear_all()

    response = context_client.SetContext(Context(**CONTEXT))
    assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID

    response = context_client.SetTopology(Topology(**TOPOLOGY))
    assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID

    response = context_client.ListDeviceIds(Empty())
    assert len(response.device_ids) == 0

    response = context_client.ListDevices(Empty())
    assert len(response.devices) == 0

    response = context_client.GetDevice(DeviceId(**DEVICE1_ID))
    assert len(response.device_id.device_uuid.uuid) == 0
    assert len(response.device_type) == 0
    assert len(response.device_config.config_rules) == 0
    assert response.device_operational_status == DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_UNDEFINED
    assert len(response.device_drivers) == 0
    assert len(response.device_endpoints) == 0

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry))
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 5

    response = context_client.SetDevice(Device(**DEVICE1))
    assert response.device_uuid.uuid == DEVICE1_UUID

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry))
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 25

    response = context_client.ListDeviceIds(Empty())
    assert len(response.device_ids) == 1
    assert response.device_ids[0].device_uuid.uuid == DEVICE1_UUID

    response = context_client.ListDevices(Empty())
    assert len(response.devices) == 1
    assert response.devices[0].device_id.device_uuid.uuid == DEVICE1_UUID
    assert response.devices[0].device_type == 'packet-router'
    assert len(response.devices[0].device_config.config_rules) == 3
    assert response.devices[0].device_operational_status == DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED
    assert len(response.devices[0].device_drivers) == 2
    assert len(response.devices[0].device_endpoints) == 3

    response = context_client.GetDevice(DeviceId(**DEVICE1_ID))
    assert response.device_id.device_uuid.uuid == DEVICE1_UUID
    assert response.device_type == 'packet-router'
    assert len(response.device_config.config_rules) == 3
    assert response.device_operational_status == DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED
    assert len(response.device_drivers) == 2
    assert len(response.device_endpoints) == 3

    TOPOLOGY_WITH_DEVICE = copy.deepcopy(TOPOLOGY)
    TOPOLOGY_WITH_DEVICE['device_ids'].append(DEVICE1_ID)
    response = context_client.SetTopology(Topology(**TOPOLOGY_WITH_DEVICE))
    assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID

    response = context_client.GetTopology(TopologyId(**TOPOLOGY_ID))
    assert response.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
    assert len(response.device_ids) == 1
    assert response.device_ids[0].device_uuid.uuid == DEVICE1_UUID
    assert len(response.link_ids) == 0

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 25

    context_client.RemoveDevice(DeviceId(**DEVICE1_ID))
    context_client.RemoveTopology(TopologyId(**TOPOLOGY_ID))
    context_client.RemoveContext(ContextId(**CONTEXT_ID))

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 0

def test_link_instances(
    context_client : ContextClient, context_database : Database): # pylint: disable=redefined-outer-name

    context_database.clear_all()

    response = context_client.SetContext(Context(**CONTEXT))
    assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID

    response = context_client.SetTopology(Topology(**TOPOLOGY))
    assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID

    response = context_client.ListLinkIds(Empty())
    assert len(response.link_ids) == 0

    response = context_client.ListLinks(Empty())
    assert len(response.links) == 0

    response = context_client.GetLink(LinkId(**LINK_ID))
    assert len(response.link_id.link_uuid.uuid) == 0
    assert len(response.link_endpoint_ids) == 0

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry))
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 5

    response = context_client.SetLink(Link(**LINK))
    assert response.link_uuid.uuid == LINK_UUID

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry))
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 30

    response = context_client.ListLinkIds(Empty())
    assert len(response.link_ids) == 1
    assert response.link_ids[0].link_uuid.uuid == LINK_UUID

    response = context_client.ListLinks(Empty())
    assert len(response.links) == 1
    assert response.links[0].link_id.link_uuid.uuid == LINK_UUID
    assert len(response.links[0].link_endpoint_ids) == 2

    response = context_client.GetLink(LinkId(**LINK_ID))
    assert response.link_id.link_uuid.uuid == LINK_UUID
    assert len(response.link_endpoint_ids) == 2

    TOPOLOGY_WITH_LINK = copy.deepcopy(TOPOLOGY)
    TOPOLOGY_WITH_LINK['link_ids'].append(LINK_ID)
    response = context_client.SetTopology(Topology(**TOPOLOGY_WITH_LINK))
    assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID

    response = context_client.GetTopology(TopologyId(**TOPOLOGY_ID))
    assert response.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
    assert len(response.device_ids) == 2
    assert response.device_ids[0].device_uuid.uuid == DEVICE1_UUID
    assert response.device_ids[1].device_uuid.uuid == DEVICE2_UUID
    assert len(response.link_ids) == 1
    assert response.link_ids[0].link_uuid.uuid == LINK_UUID

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 32

    context_client.RemoveLink(LinkId(**LINK_ID))
    context_client.RemoveDevice(DeviceId(**DEVICE1_ID))
    context_client.RemoveDevice(DeviceId(**DEVICE2_ID))
    context_client.RemoveTopology(TopologyId(**TOPOLOGY_ID))
    context_client.RemoveContext(ContextId(**CONTEXT_ID))

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 0

def test_service_instances(
    context_client : ContextClient, context_database : Database): # pylint: disable=redefined-outer-name

    context_database.clear_all()

    response = context_client.SetContext(Context(**CONTEXT))
    assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID

    response = context_client.ListServiceIds(ContextId(**CONTEXT_ID))
    assert len(response.service_ids) == 0

    response = context_client.ListServices(ContextId(**CONTEXT_ID))
    assert len(response.services) == 0

    response = context_client.GetService(ServiceId(**SERVICE_ID))
    assert len(response.service_id.service_uuid.uuid) == 0
    assert len(response.service_id.context_id.context_uuid.uuid) == 0
    assert response.service_type == ServiceTypeEnum.SERVICETYPE_UNKNOWN
    assert len(response.service_endpoint_ids) == 0
    assert len(response.service_constraints) == 0
    assert response.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_UNDEFINED
    assert len(response.service_config.config_rules) == 0

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry))
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 2

    response = context_client.SetService(Service(**SERVICE))
    assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.service_uuid.uuid == SERVICE_UUID

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry))
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 42

    response = context_client.ListServiceIds(ContextId(**CONTEXT_ID))
    assert len(response.service_ids) == 1
    assert response.service_ids[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.service_ids[0].service_uuid.uuid == SERVICE_UUID

    response = context_client.ListServices(ContextId(**CONTEXT_ID))
    assert len(response.services) == 1
    assert response.services[0].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.services[0].service_id.service_uuid.uuid == SERVICE_UUID
    assert response.services[0].service_type == ServiceTypeEnum.SERVICETYPE_L3NM
    assert len(response.services[0].service_endpoint_ids) == 2
    assert len(response.services[0].service_constraints) == 2
    assert response.services[0].service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE
    assert len(response.services[0].service_config.config_rules) == 3

    response = context_client.GetService(ServiceId(**SERVICE_ID))
    assert response.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
    assert response.service_id.service_uuid.uuid == SERVICE_UUID
    assert response.service_type == ServiceTypeEnum.SERVICETYPE_L3NM
    assert len(response.service_endpoint_ids) == 2
    assert len(response.service_constraints) == 2
    assert response.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE
    assert len(response.service_config.config_rules) == 3

    context_client.RemoveService(ServiceId(**SERVICE_ID))
    context_client.RemoveDevice(DeviceId(**DEVICE1_ID))
    context_client.RemoveDevice(DeviceId(**DEVICE2_ID))
    context_client.RemoveTopology(TopologyId(**TOPOLOGY_ID))
    context_client.RemoveContext(ContextId(**CONTEXT_ID))

    db_entries = context_database.dump()
    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
    for db_entry in db_entries:
        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
    LOGGER.info('-----------------------------------------------------------')
    assert len(db_entries) == 0

#@pytest.fixture(scope='session')
#def context_service_rest(context_database : Database):
#    _rest_server = Server(port=restapi_port, base_url=RESTAPI_BASE_URL)
#    _rest_server.add_resource(Context, '/context', endpoint='api.context', resource_class_args=(context_database,))
#    _rest_server.start()
#    time.sleep(1) # bring time for the server to start
#    yield _rest_server
#    _rest_server.shutdown()
#    _rest_server.join()
#
#def test_get_topology_completed_rest_api(context_service_rest : Server):
#    # should work
#    request_url = 'http://127.0.0.1:{}{}/context'.format(restapi_port, RESTAPI_BASE_URL)
#    LOGGER.warning('Request: GET {}'.format(str(request_url)))
#    reply = requests.get(request_url)
#    LOGGER.warning('Reply: {}'.format(str(reply.text)))
#    assert reply.status_code == 200, 'Reply failed with code {}'.format(reply.status_code)
#    json_reply = reply.json()
#    topology = MessageToDict(
#        Topology(**json_reply['topologies'][0]),
#        including_default_value_fields=True, preserving_proto_field_name=True,
#        use_integers_for_enums=False)
#    validate_topology(topology)
#    validate_topology_has_devices(topology)
#    validate_topology_has_links(topology)
#