From f15ec47c6c74506db396d15bc55e4d7d2696b103 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 19 Dec 2019 10:19:41 -0500 Subject: [PATCH 1/3] Introduced unit tests for definition files --- .gitignore | 2 + requirements.txt | 3 + tests/schema.json | 423 ++++++++++++++++++++++++++++++++++++++ tests/test_definitions.py | 30 +++ vendors/Testing/Test.yaml | 67 ++++++ 5 files changed, 525 insertions(+) create mode 100644 .gitignore create mode 100644 requirements.txt create mode 100644 tests/schema.json create mode 100644 tests/test_definitions.py create mode 100644 vendors/Testing/Test.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..99ec4282e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.pyc +.idea diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..08458aaa7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +jsonschema==3.2.0 +pytest==5.3.2 +PyYAML==5.2 diff --git a/tests/schema.json b/tests/schema.json new file mode 100644 index 000000000..fc6733aa4 --- /dev/null +++ b/tests/schema.json @@ -0,0 +1,423 @@ +{ + + "type": "object", + "properties": { + "manufacturer": { + "type": "string" + }, + "model": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "part_number": { + "type": "string" + }, + "u_height": { + "type": "integer" + }, + "is_full_depth": { + "type": "boolean" + }, + "console-ports": { + "type": "array", + "items": { + "$ref": "#/definitions/console-port" + } + }, + "console-server-ports": { + "type": "array", + "items": { + "$ref": "#/definitions/console-server-port" + } + }, + "power-ports": { + "type": "array", + "items": { + "$ref": "#/definitions/power-port" + } + }, + "power-outlets": { + "type": "array", + "items": { + "$ref": "#/definitions/power-outlet" + } + }, + "interfaces": { + "type": "array", + "items": { + "$ref": "#/definitions/interface" + } + }, + "front-ports": { + "type": "array", + "items": { + "$ref": "#/definitions/front-port" + } + }, + "rear-ports": { + "type": "array", + "items": { + "$ref": "#/definitions/rear-port" + } + }, + "device-bays": { + "type": "array", + "items": { + "$ref": "#/definitions/device-bay" + } + } + }, + "required": ["manufacturer", "model", "slug"], + + "definitions": { + + "console-port": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "de-9", + "db-25", + "rj-45", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "other" + ] + } + }, + "required": ["name", "type"] + }, + + "console-server-port": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "de-9", + "db-25", + "rj-45", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "other" + ] + } + }, + "required": ["name", "type"] + }, + + "power-port": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "iec-60320-c6", + "iec-60320-c8", + "iec-60320-c14", + "iec-60320-c16", + "iec-60320-c20", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "nema-5-15p", + "nema-5-20p", + "nema-5-30p", + "nema-5-50p", + "nema-6-15p", + "nema-6-20p", + "nema-6-30p", + "nema-6-50p", + "nema-l5-15p", + "nema-l5-20p", + "nema-l5-30p", + "nema-l5-50p", + "nema-l6-20p", + "nema-l6-30p", + "nema-l6-50p", + "ita-e", + "ita-f", + "ita-ef", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o" + ] + }, + "maximum_draw": { + "type": "integer" + }, + "allocated_draw": { + "type": "integer" + } + }, + "required": ["name", "type"] + }, + + "power-outlet": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "iec-60320-c5", + "iec-60320-c7", + "iec-60320-c13", + "iec-60320-c15", + "iec-60320-c19", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "nema-5-15r", + "nema-5-20r", + "nema-5-30r", + "nema-5-50r", + "nema-6-15r", + "nema-6-20r", + "nema-6-30r", + "nema-6-50r", + "nema-l5-15r", + "nema-l5-20r", + "nema-l5-30r", + "nema-l5-50r", + "nema-l6-20r", + "nema-l6-30r", + "nema-l6-50r", + "ita-e", + "ita-f", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o" + ] + }, + "power_port": { + "type": "string" + }, + "feed_leg": { + "type": "string", + "enum": ["A", "B", "C"] + } + }, + "required": ["name", "type"] + }, + + "interface": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "virtual", + "lag", + "100base-tx", + "1000base-t", + "2.5gbase-t", + "5gbase-t", + "10gbase-t", + "10gbase-cx4", + "1000base-x-gbic", + "1000base-x-sfp", + "10gbase-x-sfpp", + "10gbase-x-xfp", + "10gbase-x-xenpak", + "10gbase-x-x2", + "25gbase-x-sfp28", + "40gbase-x-qsfpp", + "50gbase-x-sfp28", + "100gbase-x-cfp", + "100gbase-x-cfp2", + "200gbase-x-cfp2", + "100gbase-x-cfp4", + "100gbase-x-cpak", + "100gbase-x-qsfp28", + "200gbase-x-qsfp56", + "400gbase-x-qsfpdd", + "400gbase-x-osfp", + "ieee802.11a", + "ieee802.11g", + "ieee802.11n", + "ieee802.11ac", + "ieee802.11ad", + "gsm", + "cdma", + "lte", + "sonet-oc3", + "sonet-oc12", + "sonet-oc48", + "sonet-oc192", + "sonet-oc768", + "sonet-oc1920", + "sonet-oc3840", + "1gfc-sfp", + "2gfc-sfp", + "4gfc-sfp", + "8gfc-sfpp", + "16gfc-sfpp", + "32gfc-sfp28", + "128gfc-sfp28", + "inifiband-sdr", + "inifiband-ddr", + "inifiband-qdr", + "inifiband-fdr10", + "inifiband-fdr", + "inifiband-edr", + "inifiband-hdr", + "inifiband-ndr", + "inifiband-xdr", + "t1", + "e1", + "t3", + "e3", + "cisco-stackwise", + "cisco-stackwise-plus", + "cisco-flexstack", + "cisco-flexstack-plus", + "juniper-vcp", + "extreme-summitstack", + "extreme-summitstack-128", + "extreme-summitstack-256", + "extreme-summitstack-512" + ] + }, + "mgmt_only": { + "type": "boolean" + } + }, + "required": ["name", "type"] + }, + + "front-port": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "8p8c", + "110-punch", + "bnc", + "fc", + "lc", + "lc-apc", + "lsh", + "lsh-apc", + "mpo", + "mtrj", + "sc", + "sc-apc", + "st" + ] + }, + "rear_port": { + "type": "string" + }, + "rear_port_position": { + "type": "integer" + } + }, + "required": ["name", "type", "rear_port"] + }, + + "rear-port": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "8p8c", + "110-punch", + "bnc", + "fc", + "lc", + "lc-apc", + "lsh", + "lsh-apc", + "mpo", + "mtrj", + "sc", + "sc-apc", + "st" + ] + }, + "positions": { + "type": "integer" + } + }, + "required": ["name", "type"] + }, + + "device-bay": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"] + } + + } + +} diff --git a/tests/test_definitions.py b/tests/test_definitions.py new file mode 100644 index 000000000..e37f0e3d8 --- /dev/null +++ b/tests/test_definitions.py @@ -0,0 +1,30 @@ +import glob +import json +import pytest +import yaml +from jsonschema import validate + + +def _get_definition_files(): + """ + Return a list of all definition files. + """ + return [f for f in glob.glob("vendors/**/*.yaml", recursive=True)] + + +# Initialize schema +with open("tests/schema.json") as schema_file: + schema = json.loads(schema_file.read()) + + +@pytest.mark.parametrize("file_path", _get_definition_files()) +def test_definition(file_path): + """ + Validate DeviceType definitions using the provided JSON schema. + """ + # Read file + with open(file_path) as definition_file: + definition = yaml.load(definition_file.read(), Loader=yaml.SafeLoader) + + # Run validation + validate(definition, schema=schema) diff --git a/vendors/Testing/Test.yaml b/vendors/Testing/Test.yaml new file mode 100644 index 000000000..7349a3c1c --- /dev/null +++ b/vendors/Testing/Test.yaml @@ -0,0 +1,67 @@ +manufacturer: Generic +model: TEST-1000 +slug: test-1000 +u_height: 2 +console-ports: + - name: Console Port 1 + type: de-9 + - name: Console Port 2 + type: de-9 + - name: Console Port 3 + type: de-9 +console-server-ports: + - name: Console Server Port 1 + type: rj-45 + - name: Console Server Port 2 + type: rj-45 + - name: Console Server Port 3 + type: rj-45 +power-ports: + - name: Power Port 1 + type: iec-60320-c14 + - name: Power Port 2 + type: iec-60320-c14 + - name: Power Port 3 + type: iec-60320-c14 +power-outlets: + - name: Power Outlet 1 + type: iec-60320-c13 + power_port: Power Port 1 + feed_leg: A + - name: Power Outlet 2 + type: iec-60320-c13 + power_port: Power Port 1 + feed_leg: A + - name: Power Outlet 3 + type: iec-60320-c13 + power_port: Power Port 1 + feed_leg: A +interfaces: + - name: Interface 1 + type: 1000base-t + mgmt_only: true + - name: Interface 2 + type: 1000base-t + - name: Interface 3 + type: 1000base-t +rear-ports: + - name: Rear Port 1 + type: 8p8c + - name: Rear Port 2 + type: 8p8c + - name: Rear Port 3 + type: 8p8c +front-ports: + - name: Front Port 1 + type: 8p8c + rear_port: Rear Port 1 + - name: Front Port 2 + type: 8p8c + rear_port: Rear Port 2 + - name: Front Port 3 + type: 8p8c + rear_port: Rear Port 3 +device-bays: + - name: Device Bay 1 + - name: Device Bay 2 + - name: Device Bay 3 From 268d8cc43cf500a8f969be1906587bce4faa3cb1 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 19 Dec 2019 11:08:51 -0500 Subject: [PATCH 2/3] Employ more elegant error reporting in tests --- tests/test_definitions.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_definitions.py b/tests/test_definitions.py index e37f0e3d8..f66f2bd26 100644 --- a/tests/test_definitions.py +++ b/tests/test_definitions.py @@ -3,6 +3,7 @@ import json import pytest import yaml from jsonschema import validate +from jsonschema.exceptions import ValidationError def _get_definition_files(): @@ -27,4 +28,7 @@ def test_definition(file_path): definition = yaml.load(definition_file.read(), Loader=yaml.SafeLoader) # Run validation - validate(definition, schema=schema) + try: + validate(definition, schema=schema) + except ValidationError as e: + pytest.fail(f"{file_path} failed validation: {e}") From 3e36dc4c4ccc5769a744fdbc4dcd1f7523ebadcc Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 19 Dec 2019 11:14:27 -0500 Subject: [PATCH 3/3] Add a simple environment test for sanity checking --- tests/test_definitions.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_definitions.py b/tests/test_definitions.py index f66f2bd26..c227a1ac4 100644 --- a/tests/test_definitions.py +++ b/tests/test_definitions.py @@ -18,6 +18,17 @@ with open("tests/schema.json") as schema_file: schema = json.loads(schema_file.read()) +def test_environment(): + """ + Run basic sanity checks on the environment to ensure tests are running correctly. + """ + # Validate that definition files exist + assert _get_definition_files(), "No definition files found!" + + # Validate that the schema exists + assert schema, "Schema definition is empty!" + + @pytest.mark.parametrize("file_path", _get_definition_files()) def test_definition(file_path): """