#!/usr/bin/env python3 # -*- coding: utf-8 -*- # @Author: Esther Le Rouzic # @Date: 2018-06-15 """Adding tests to check the parser non regression convention of naming of test files: - ..._expected.json for the reference output tests: - generation of topology json - reading of Eqpt sheet w and W/ power mode - consistency of autodesign - generation of service list based on service sheet - writing of results in csv - writing of results in json (same keys) """ from pathlib import Path from os import unlink import shutil from pandas import read_csv from xlrd import open_workbook import pytest from copy import deepcopy from gnpy.core.utils import automatic_nch, lin2db from gnpy.core.network import build_network, add_missing_elements_in_network from gnpy.core.exceptions import ServiceError, ConfigurationError from gnpy.topology.request import (jsontocsv, requests_aggregation, compute_path_dsjctn, deduplicate_disjunctions, compute_path_with_disjunction, ResultElement, PathRequest) from gnpy.topology.spectrum_assignment import build_oms_list, pth_assign_spectrum from gnpy.tools.convert import convert_file from gnpy.tools.json_io import (load_json, load_network, save_network, load_equipment, requests_from_json, disjunctions_from_json, network_to_json, network_from_json) from gnpy.tools.service_sheet import read_service_sheet, correct_xls_route_list, Request_element, Request TEST_DIR = Path(__file__).parent DATA_DIR = TEST_DIR / 'data' eqpt_filename = DATA_DIR / 'eqpt_config.json' equipment = load_equipment(eqpt_filename) @pytest.mark.parametrize('xls_input,expected_json_output', { DATA_DIR / 'CORONET_Global_Topology.xlsx': DATA_DIR / 'CORONET_Global_Topology_expected.json', DATA_DIR / 'testTopology.xls': DATA_DIR / 'testTopology_expected.json', DATA_DIR / 'perdegreemeshTopologyExampleV2.xls': DATA_DIR / 'perdegreemeshTopologyExampleV2_expected.json' }.items()) def test_excel_json_generation(tmpdir, xls_input, expected_json_output): """tests generation of topology json""" xls_copy = Path(tmpdir) / xls_input.name shutil.copyfile(xls_input, xls_copy) convert_file(xls_copy) actual_json_output = xls_copy.with_suffix('.json') actual = load_json(actual_json_output) unlink(actual_json_output) assert actual == load_json(expected_json_output) # assume xls entries # test that the build network gives correct results in gain mode @pytest.mark.parametrize('xls_input,expected_json_output', {DATA_DIR / 'CORONET_Global_Topology.xlsx': DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json', DATA_DIR / 'testTopology.xls': DATA_DIR / 'testTopology_auto_design_expected.json', }.items()) def test_auto_design_generation_fromxlsgainmode(tmpdir, xls_input, expected_json_output): """tests generation of topology json and that the build network gives correct results in gain mode""" equipment = load_equipment(eqpt_filename) network = load_network(xls_input, equipment) add_missing_elements_in_network(network, equipment) # in order to test the Eqpt sheet and load gain target, # change the power-mode to False (to be in gain mode) equipment['Span']['default'].power_mode = False # Build the network once using the default power defined in SI in eqpt config p_db = equipment['SI']['default'].power_dbm p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min, equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) build_network(network, equipment, p_db, p_total_db) actual_json_output = tmpdir / xls_input.with_name(xls_input.stem + '_auto_design').with_suffix('.json').name save_network(network, actual_json_output) actual = load_json(actual_json_output) unlink(actual_json_output) assert actual == load_json(expected_json_output) # test that autodesign creates same file as an input file already autodesigned @pytest.mark.parametrize('json_input, power_mode', {DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json': False, DATA_DIR / 'testTopology_auto_design_expected.json': False, DATA_DIR / 'perdegreemeshTopologyExampleV2_auto_design_expected.json': True }.items()) def test_auto_design_generation_fromjson(tmpdir, json_input, power_mode): """test that autodesign creates same file as an input file already autodesigned""" equipment = load_equipment(eqpt_filename) network = load_network(json_input, equipment) # in order to test the Eqpt sheet and load gain target, # change the power-mode to False (to be in gain mode) equipment['Span']['default'].power_mode = power_mode # Build the network once using the default power defined in SI in eqpt config p_db = equipment['SI']['default'].power_dbm p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min, equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) add_missing_elements_in_network(network, equipment) build_network(network, equipment, p_db, p_total_db) actual_json_output = tmpdir / json_input.with_name(json_input.stem + '_auto_design').with_suffix('.json').name save_network(network, actual_json_output) actual = load_json(actual_json_output) unlink(actual_json_output) assert actual == load_json(json_input) # test services creation @pytest.mark.parametrize('xls_input, expected_json_output', { DATA_DIR / 'testTopology.xls': DATA_DIR / 'testTopology_services_expected.json', DATA_DIR / 'testService.xls': DATA_DIR / 'testService_services_expected.json' }.items()) def test_excel_service_json_generation(xls_input, expected_json_output): """test services creation""" equipment = load_equipment(eqpt_filename) network = load_network(DATA_DIR / 'testTopology.xls', equipment) # Build the network once using the default power defined in SI in eqpt config p_db = equipment['SI']['default'].power_dbm p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min, equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) build_network(network, equipment, p_db, p_total_db) from_xls = read_service_sheet(xls_input, equipment, network, network_filename=DATA_DIR / 'testTopology.xls') assert from_xls == load_json(expected_json_output) # TODO verify that requested bandwidth is not zero ! # test xls answers creation @pytest.mark.parametrize('json_input', (DATA_DIR / 'testTopology_response.json', ) ) def test_csv_response_generation(tmpdir, json_input): """tests if generated csv is consistant with expected generation same columns (order not important)""" json_data = load_json(json_input) equipment = load_equipment(eqpt_filename) csv_filename = Path(tmpdir / json_input.name).with_suffix('.csv') with open(csv_filename, 'w', encoding='utf-8') as fcsv: jsontocsv(json_data, equipment, fcsv) expected_csv_filename = json_input.parent / (json_input.stem + '_expected.csv') # expected header # csv_header = \ # [ # 'response-id', # 'source', # 'destination', # 'path_bandwidth', # 'Pass?', # 'nb of tsp pairs', # 'total cost', # 'transponder-type', # 'transponder-mode', # 'OSNR-0.1nm', # 'SNR-0.1nm', # 'SNR-bandwidth', # 'baud rate (Gbaud)', # 'input power (dBm)', # 'path', # 'spectrum (N,M)', # 'reversed path OSNR-0.1nm', # 'reversed path SNR-0.1nm', # 'reversed path SNR-bandwidth' # ] resp = read_csv(csv_filename) print(resp) unlink(csv_filename) expected_resp = read_csv(expected_csv_filename) print(expected_resp) resp_header = list(resp.head(0)) expected_resp_header = list(expected_resp.head(0)) # check that headers are the same resp_header.sort() expected_resp_header.sort() print('headers are differents') print(resp_header) print(expected_resp_header) assert resp_header == expected_resp_header # for each header checks that the output are as expected resp.sort_values(by=['response-id']) expected_resp.sort_values(by=['response-id']) for column in expected_resp: assert list(resp[column].fillna('')) == list(expected_resp[column].fillna('')) print('results are different') print(list(resp[column])) print(list(expected_resp[column])) print(type(list(resp[column])[-1])) # test json answers creation @pytest.mark.parametrize('xls_input, expected_response_file', { DATA_DIR / 'testTopology.xls': DATA_DIR / 'testTopology_response.json', }.items()) def test_json_response_generation(xls_input, expected_response_file): """tests if json response is correctly generated for all combinations of requests""" equipment = load_equipment(eqpt_filename) network = load_network(xls_input, equipment) p_db = equipment['SI']['default'].power_dbm p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min, equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) build_network(network, equipment, p_db, p_total_db) data = read_service_sheet(xls_input, equipment, network) # change one of the request with bidir option to cover bidir case as well data['path-request'][2]['bidirectional'] = True oms_list = build_oms_list(network, equipment) rqs = requests_from_json(data, equipment) dsjn = disjunctions_from_json(data) dsjn = deduplicate_disjunctions(dsjn) rqs, dsjn = requests_aggregation(rqs, dsjn) pths = compute_path_dsjctn(network, equipment, rqs, dsjn) propagatedpths, reversed_pths, reversed_propagatedpths = \ compute_path_with_disjunction(network, equipment, rqs, pths, redesign=True) pth_assign_spectrum(pths, rqs, oms_list, reversed_pths) result = [] for i, pth in enumerate(propagatedpths): # test ServiceError handling : when M is None at this point, the # json result should not be created if there is no blocking reason if i == 1: my_rq = deepcopy(rqs[i]) my_rq.M = None my_rq.N = None with pytest.raises(ServiceError): ResultElement(my_rq, pth, reversed_propagatedpths[i]).json my_rq.blocking_reason = 'NO_SPECTRUM' ResultElement(my_rq, pth, reversed_propagatedpths[i]).json result.append(ResultElement(rqs[i], pth, reversed_propagatedpths[i])) temp = { 'response': [n.json for n in result] } expected = load_json(expected_response_file) for i, response in enumerate(temp['response']): if i == 2: # compare response must be False because z-a metric is missing # (request with bidir option to cover bidir case) assert expected['response'][i] != response print(f'response {response["response-id"]} should not match') expected['response'][2]['path-properties']['z-a-path-metric'] = [ {'metric-type': 'SNR-bandwidth', 'accumulative-value': 22.809999999999999}, {'metric-type': 'SNR-0.1nm', 'accumulative-value': 26.890000000000001}, {'metric-type': 'OSNR-bandwidth', 'accumulative-value': 26.239999999999998}, {'metric-type': 'OSNR-0.1nm', 'accumulative-value': 30.32}, {'metric-type': 'reference_power', 'accumulative-value': 0.0012589254117941673}, {'metric-type': 'path_bandwidth', 'accumulative-value': 60000000000.0}] # test should be OK now else: assert expected['response'][i] == response @pytest.mark.parametrize('source, destination, route_list, hoptype, expected_correction', [ ('Brest_KLA', 'Vannes_KBE', 'roadm Brest_KLA | roadm Lannion_CAS | roadm Lorient_KMA | roadm Vannes_KBE', 'no', ['roadm Brest_KLA', 'roadm Lannion_CAS', 'roadm Lorient_KMA', 'roadm Vannes_KBE']), ('Brest_KLA', 'Vannes_KBE', 'trx Brest_KLA | roadm Lannion_CAS | roadm Lorient_KMA | roadm Vannes_KBE', 'No', ['roadm Lannion_CAS', 'roadm Lorient_KMA', 'roadm Vannes_KBE']), ('Lannion_CAS', 'Rennes_STA', 'trx Rennes_STA', 'yes', []), ('Lannion_CAS', 'Lorient_KMA', 'toto', 'yes', []), ('Lannion_CAS', 'Lorient_KMA', 'toto', 'no', 'Fail'), ('Lannion_CAS', 'Lorient_KMA', 'Corlay | Loudeac | Lorient_KMA', 'yes', ['west fused spans in Corlay', 'west fused spans in Loudeac', 'roadm Lorient_KMA']), ('Lannion_CAS', 'Lorient_KMA', 'Ploermel | Vannes_KBE', 'yes', ['east edfa in Ploermel to Vannes_KBE', 'roadm Vannes_KBE']), ('Rennes_STA', 'Brest_KLA', 'Vannes_KBE | Quimper | Brest_KLA', 'yes', ['roadm Vannes_KBE', 'west edfa in Quimper to Lorient_KMA', 'roadm Brest_KLA']), ('Brest_KLA', 'Rennes_STA', 'Brest_KLA | Quimper | Lorient_KMA', 'yes', ['roadm Brest_KLA', 'east edfa in Quimper to Lorient_KMA', 'roadm Lorient_KMA']), ('Brest_KLA', 'trx Rennes_STA', '', 'yes', 'Fail'), ('trx Brest_KLA', 'Rennes_STA', '', 'yes', 'Fail'), ('trx Brest_KLA', 'trx Rennes_STA', '', 'yes', 'Fail'), ('Brest_KLA', 'trx Rennes_STA', '', 'no', 'Fail'), ('Brest_KLA', 'Rennes_STA', 'trx Rennes_STA', 'no', []), ('Brest_KLA', 'Rennes_STA', None, '', []), ('Brest_KLA', 'Rennes_STA', 'Brest_KLA | Quimper | Ploermel', 'yes', ['roadm Brest_KLA']), ('Brest_KLA', 'Rennes_STA', 'Brest_KLA | Quimper | Ploermel', 'no', ['roadm Brest_KLA']), ('Brest_KLA', 'Rennes_STA', 'Brest_KLA | trx Quimper', 'yes', ['roadm Brest_KLA']), ('Brest_KLA', 'Rennes_STA', 'Brest_KLA | trx Lannion_CAS', 'yes', ['roadm Brest_KLA']), ('Brest_KLA', 'Rennes_STA', 'Brest_KLA | trx Lannion_CAS', 'no', 'Fail') ]) def test_excel_ila_constraints(source, destination, route_list, hoptype, expected_correction): """add different kind of constraints to test all correct_route cases""" service_xls_input = DATA_DIR / 'testTopology.xls' network_json_input = DATA_DIR / 'testTopology_auto_design_expected.json' network = load_network(network_json_input, equipment) # increase length of one span to trigger automatic fiber splitting included by autodesign # so that the test also covers this case next(node for node in network.nodes() if node.uid == 'fiber (Brest_KLA → Quimper)-').length = 200000 next(node for node in network.nodes() if node.uid == 'fiber (Quimper → Brest_KLA)-').length = 200000 default_si = equipment['SI']['default'] p_db = default_si.power_dbm p_total_db = p_db + lin2db(automatic_nch(default_si.f_min, default_si.f_max, default_si.spacing)) build_network(network, equipment, p_db, p_total_db) # create params for a xls request based on input (note that this not the same type as PathRequest) params = { 'request_id': '0', 'source': source, 'destination': destination, 'trx_type': 'Voyager', 'spacing': 50, 'nodes_list': route_list, 'is_loose': hoptype, 'nb_channel': 0, 'power': 0, 'path_bandwidth': 0, } request = Request_element(Request(**params), equipment, False) if expected_correction != 'Fail': [request] = correct_xls_route_list(service_xls_input, network, [request]) assert request.nodes_list == expected_correction else: with pytest.raises(ServiceError): [request] = correct_xls_route_list(service_xls_input, network, [request]) @pytest.mark.parametrize('route_list, hoptype, expected_amp_route', [ ('node1 | siteE | node2', 'no', ['roadm node1', 'west edfa in siteE', 'roadm node2']), ('node2 | siteE | node1', 'no', ['roadm node2', 'east edfa in siteE', 'roadm node1']), ('node1 | siteF | node2', 'no', ['roadm node1', 'west edfa in siteF', 'roadm node2']), ('node1 | siteA | siteB', 'yes', ['roadm node1', 'west edfa in siteA']), ('node1 | siteA | siteB | node2', 'yes', ['roadm node1', 'west edfa in siteA', 'west edfa in siteB', 'roadm node2']), ('node1 | siteC | node2', 'yes', ['roadm node1', 'east edfa in siteC', 'roadm node2']), ('node1 | siteD | node2', 'no', ['roadm node1', 'west edfa in siteD to node1', 'roadm node2']), ('roadm node1 | Edfa_booster_roadm node1_to_fiber (node1 → siteE)-CABLES#19 | west edfa in siteE | node2', 'no', ['roadm node1', 'Edfa_booster_roadm node1_to_fiber (node1 → siteE)-CABLES#19', 'west edfa in siteE', 'roadm node2'])]) def test_excel_ila_constraints2(route_list, hoptype, expected_amp_route): """Check different cases for ILA constraints definition """ network_xls_input = DATA_DIR / 'ila_constraint.xlsx' network = load_network(network_xls_input, equipment) add_missing_elements_in_network(network, equipment) default_si = equipment['SI']['default'] p_db = default_si.power_dbm p_total_db = p_db + lin2db(automatic_nch(default_si.f_min, default_si.f_max, default_si.spacing)) build_network(network, equipment, p_db, p_total_db) # create params for a request based on input params = { 'request_id': '0', 'source': 'node1', 'destination': 'node2', 'trx_type': 'Voyager', 'mode': None, 'spacing': 50, 'nodes_list': route_list, 'is_loose': hoptype, 'nb_channel': 80, 'power': 0, 'path_bandwidth': 100, } request = Request_element(Request(**params), equipment, False) [request] = correct_xls_route_list(network_xls_input, network, [request]) assert request.nodes_list == expected_amp_route def setup_per_degree(case): """common setup for degree: returns the dict network for different cases""" json_network = load_json(DATA_DIR / 'testTopology_expected.json') json_network_auto = load_json(DATA_DIR / 'testTopology_auto_design_expected.json') if case == 'no': return json_network elif case == 'all': return json_network_auto elif case == 'Lannion_CAS and all': elem = next(e for e in json_network['elements'] if e['uid'] == 'roadm Lannion_CAS') elem['params'] = {'per_degree_pch_out_db': { "east edfa in Lannion_CAS to Corlay": -17, "east edfa in Lannion_CAS to Stbrieuc": -18, "east edfa in Lannion_CAS to Morlaix": -21}} return json_network elif case == 'Lannion_CAS and one': elem = next(e for e in json_network['elements'] if e['uid'] == 'roadm Lannion_CAS') elem['params'] = {'per_degree_pch_out_db': { "east edfa in Lannion_CAS to Corlay": -17, "east edfa in Lannion_CAS to Stbrieuc": -18}} return json_network @pytest.mark.parametrize('case', ['no', 'all', 'Lannion_CAS and all', 'Lannion_CAS and one']) def test_target_pch_out_db_global(case): """check that per degree attributes are correctly created with global values if none are given""" json_network = setup_per_degree(case) per_degree = {} for elem in json_network['elements']: if 'type' in elem.keys() and elem['type'] == 'Roadm' and 'params' in elem.keys() \ and 'per_degree_pch_out_db' in elem['params']: # records roadms that have a per degree target per_degree[elem['uid']] = {k: v for k, v in elem['params']['per_degree_pch_out_db'].items()} network = network_from_json(json_network, equipment) # Build the network once using the default power defined in SI in eqpt config # power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by # spacing, f_min and f_max p_db = equipment['SI']['default'].power_dbm p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min, equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) build_network(network, equipment, p_db, p_total_db) data = network_to_json(network) for elem in data['elements']: if 'type' in elem.keys() and elem['type'] == 'Roadm': # check that power target attributes exist and are filled with correct values # first check that global 'target_pch_out_db' is correctly filled assert elem['params']['target_pch_out_db'] == equipment['Roadm']['default'].target_pch_out_db for degree, power in elem['params']['per_degree_pch_out_db'].items(): if elem['uid'] not in per_degree.keys(): # second: check that per degree 'target_pch_out_db' is correctly filled with global value # when there was no per degree specification on network input assert power == equipment['Roadm']['default'].target_pch_out_db else: if degree not in per_degree[elem['uid']].keys(): # third: check that per degree 'target_pch_out_db' is correctly filled with global value # on degrees that had no specification when other degrees are filled assert power == equipment['Roadm']['default'].target_pch_out_db else: # fourth: check that per degree 'target_pch_out_db' is correctly filled with specified values assert power == per_degree[elem['uid']][degree] def all_rows(sh, start=0): """reads excel sheet row per row""" return (sh.row(x) for x in range(start, sh.nrows)) class Amp: """Node element contains uid, list of connected nodes and eqpt type""" def __init__(self, uid, to_node, eqpt=None, west=None): self.uid = uid self.to_node = to_node self.eqpt = eqpt self.west = west def test_eqpt_creation(tmpdir): """tests that convert correctly creates equipment according to equipment sheet including all cominations in testTopologyconvert.xls: if a line exists the amplifier should be created even if no values are provided. """ xls_input = DATA_DIR / 'testTopologyconvert.xls' xls_copy = Path(tmpdir) / xls_input.name shutil.copyfile(xls_input, xls_copy) convert_file(xls_copy) actual_json_output = xls_copy.with_suffix('.json') actual = load_json(actual_json_output) unlink(actual_json_output) connections = {elem['from_node']: elem['to_node'] for elem in actual['connections']} jsonconverted = {} for elem in actual['elements']: if 'type' in elem.keys() and elem['type'] == 'Edfa': print(elem['uid']) if 'type_variety' in elem.keys(): jsonconverted[elem['uid']] = Amp(elem['uid'], connections[elem['uid']], elem['type_variety']) else: jsonconverted[elem['uid']] = Amp(elem['uid'], connections[elem['uid']]) with open_workbook(xls_input) as wobo: # reading Eqpt sheet assuming header is node A, Node Z, amp variety # fused should not be recorded as an amp eqpt_sheet = wobo.sheet_by_name('Eqpt') raw_eqpts = {} for row in all_rows(eqpt_sheet, start=5): if row[0].value not in raw_eqpts.keys(): raw_eqpts[row[0].value] = Amp(row[0].value, [row[1].value], [row[2].value], [row[7].value]) else: raw_eqpts[row[0].value].to_node.append(row[1].value) raw_eqpts[row[0].value].eqpt.append(row[2].value) raw_eqpts[row[0].value].west.append(row[7].value) # create the possible names similarly to what convert should do possiblename = [f'east edfa in {xlsname} to {node}' for xlsname, value in raw_eqpts.items() for i, node in enumerate(value.to_node) if value.eqpt[i] != 'fused'] +\ [f'west edfa in {xlsname} to {node}' for xlsname, value in raw_eqpts.items() for i, node in enumerate(value.to_node) if value.west[i] != 'fused'] # check that all lines in eqpt sheet correctly converts to an amp element for name in possiblename: assert name in jsonconverted.keys() # check that all amp in the converted files corresponds to an eqpt line for ampuid in jsonconverted.keys(): assert ampuid in possiblename def test_service_json_constraint_order(): """test that the constraints are read in correct order""" unsorted_request = { "request-id": "unsorted", "source": "trx Brest_KLA", "destination": "trx Vannes_KBE", "src-tp-id": "trx Brest_KLA", "dst-tp-id": "trx Vannes_KBE", "bidirectional": False, "path-constraints": { "te-bandwidth": { "technology": "flexi-grid", "trx_type": "Voyager", "trx_mode": "mode 1", "spacing": 50000000000.0, "output-power": 0.001, "path_bandwidth": 10000000000.0 } }, "explicit-route-objects": { "route-object-include-exclude": [ { "explicit-route-usage": "route-include-ero", "index": 2, "num-unnum-hop": { "node-id": "roadm Lorient_KMA", "link-tp-id": "link-tp-id is not used", "hop-type": "STRICT" } }, { "explicit-route-usage": "route-include-ero", "index": 3, "num-unnum-hop": { "node-id": "roadm Vannes_KBE", "link-tp-id": "link-tp-id is not used", "hop-type": "STRICT" } }, { "explicit-route-usage": "route-include-ero", "index": 1, "num-unnum-hop": { "node-id": "roadm Lannion_CAS", "link-tp-id": "link-tp-id is not used", "hop-type": "LOOSE" } }, { "explicit-route-usage": "route-include-ero", "index": 0, "num-unnum-hop": { "node-id": "roadm Brest_KLA", "link-tp-id": "link-tp-id is not used", "hop-type": "STRICT" } } ] } } data = {'path-request': [unsorted_request]} rqs = requests_from_json(data, equipment) assert rqs[0].nodes_list == ['roadm Brest_KLA', 'roadm Lannion_CAS', 'roadm Lorient_KMA', 'roadm Vannes_KBE'] assert rqs[0].loose_list == ['STRICT', 'LOOSE', 'STRICT', 'STRICT'] @pytest.mark.parametrize('type_variety, target_pch_out_db, correct_variety', [(None, -20, True), ('example_test', -18, True), ('example', None, False)]) def test_roadm_type_variety(type_variety, target_pch_out_db, correct_variety): """Checks that if element has no variety, the default one is applied, and if it has one that the type_variety is correctly applied """ json_data = { "elements": [{ "uid": "roadm Oakland", "type": "Roadm", }], "connections": [] } expected_roadm = { "uid": "roadm Oakland", "type": "Roadm", "params": { "restrictions": { "preamp_variety_list": [], "booster_variety_list": [] } }, 'metadata': { 'location': { 'city': None, 'latitude': 0, 'longitude': 0, 'region': None } } } if type_variety is not None: json_data['elements'][0]['type_variety'] = type_variety expected_roadm['type_variety'] = type_variety else: # Do not add type variety in json_data to test that it creates a 'default' type_variety expected_roadm['type_variety'] = 'default' expected_roadm['params']['target_pch_out_db'] = target_pch_out_db equipment = load_equipment(eqpt_filename) if correct_variety: network = network_from_json(json_data, equipment) roadm = [n for n in network.nodes()][0] assert roadm.to_json == expected_roadm else: with pytest.raises(ConfigurationError): network = network_from_json(json_data, equipment)