From 83e5783e37f28bbe0a59ad8bdeeba1bb167c97c6 Mon Sep 17 00:00:00 2001 From: Dan Jones <danjon@noc.ac.uk> Date: Fri, 23 Aug 2024 10:27:26 +0100 Subject: [PATCH] ci: run both js and python tests in ci - use discover to find python unittests - choose filename for output compiled schema - use python+node container for JS tests - compile ignored test schema before running all tests --- .gitignore | 1 + .gitlab-ci.yml | 1 + CHANGELOG.md | 4 +++ README.md | 16 +++++++++--- docker/docker-compose-test.yaml | 2 +- generate_schema_config.py | 4 +-- gitlab/test-js.yml | 13 ++++++++++ tests-js/docker/Dockerfile | 11 +++++++- tests-js/soar-examples.test.js | 2 +- tests/test_schemas.py | 45 +++++++++++++++++++++++++-------- 10 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 gitlab/test-js.yml diff --git a/.gitignore b/.gitignore index 41e951a..fc272ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ __pycache__/ *.pyc +tests/test_swagger.json \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 513b309..d4e696e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,7 @@ include: - project: communications-backbone-system/backbone-infrastructure-config ref: master file: gitlab/all.yml + - local: /gitlab/test-js.yml variables: DOCKER_IMAGE_NAME: backbone-message-format diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ca1898..c1eda55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Refactor schema script to remove invalid definitions object automatically - Refactor generate_schema_config script to output file on -f flag +- Use discover to find all unit tests +- Run tests against current format definitions +- Test that formats match saved schema +- Run python and javascript tests in CI ## [v0.1.0] - 2023-03-24 diff --git a/README.md b/README.md index 1588222..081c9d8 100644 --- a/README.md +++ b/README.md @@ -78,14 +78,22 @@ python3 generate_schema_config.py -f 2. Commit any changes to the `backbone-message-format/project/<project_name>/swagger.json` ## Run Tests + +We have found slightly different behaviour between the OpenAPI validators in python and javascript. + +By running tests in both languages we should protect against messages that pass validation in +one client but fail in another. + Run both tests below: -1. Test 1 + +1. Test 1 - Use python validators ``` -python3 -m unittest tests/test_schemas.py +python3 -m unittest discover ``` -2. Test 2 +2. Test 2 - Use javascript validators ``` -cd tests-js/docker; docker compose up --build +# Compile schema and run javascript validation tests in docker +python3 test-js.py ``` ## Quick Links diff --git a/docker/docker-compose-test.yaml b/docker/docker-compose-test.yaml index 2461af3..6caad2f 100644 --- a/docker/docker-compose-test.yaml +++ b/docker/docker-compose-test.yaml @@ -12,4 +12,4 @@ services: - PYTHONDONTWRITEBYTECODE=1 volumes: - ../:/app - command: "python3 -m unittest tests/test_schemas.py" \ No newline at end of file + command: "python3 -m unittest discover" diff --git a/generate_schema_config.py b/generate_schema_config.py index 07e9fdf..fa58bb1 100644 --- a/generate_schema_config.py +++ b/generate_schema_config.py @@ -28,7 +28,6 @@ FLASK_PORT = os.getenv("FLASK_PORT", 5000) # Switch on debug mode if env var is truthy FLASK_DEBUG = os.getenv("FLASK_DEBUG", "False").lower() in ("true", "1", "t") - swagger_config = { "openapi": "3.0.2", "swagger_ui": True, @@ -214,6 +213,7 @@ def get_options(): help="Save output to schema file", default=False, ) + parser.add_argument("filename", nargs="?", default="project/soar/swagger.json") args = parser.parse_args() config = vars(args) # If no flag is specified default to running the flask server @@ -228,7 +228,7 @@ if __name__ == "__main__": # Output compiled schema if config.get("output_file"): - write_schema(swagger_config, "project/soar/swagger.json") + write_schema(swagger_config, config.get("filename")) # Run flask app if config.get("run_flask"): diff --git a/gitlab/test-js.yml b/gitlab/test-js.yml new file mode 100644 index 0000000..d773334 --- /dev/null +++ b/gitlab/test-js.yml @@ -0,0 +1,13 @@ +test-js: + stage: test + script: + - cd tests-js/docker + - docker compose up --build + tags: + - shell + rules: + - if: '$CI_SKIP_TESTS == "1"' + when: never + - if: '$CI_IGNORE_TESTS == "1"' + allow_failure: true + - when: on_success diff --git a/tests-js/docker/Dockerfile b/tests-js/docker/Dockerfile index 8e06b59..27076ba 100644 --- a/tests-js/docker/Dockerfile +++ b/tests-js/docker/Dockerfile @@ -1,15 +1,24 @@ -FROM node:18.7.0-alpine +FROM python:3.9.16-alpine + +# Add node for running JS validator tests +RUN apk add --update nodejs npm WORKDIR /app/tests-js COPY tests-js/package.json /app/tests-js/package.json +RUN npm install -g yarn + RUN yarn install WORKDIR /app COPY . /app +# compile test schema from message +RUN pip install -r requirements.txt +RUN python generate_schema_config.py -f tests/test_swagger.json + WORKDIR /app/tests-js CMD [ 'yarn', 'test' ] diff --git a/tests-js/soar-examples.test.js b/tests-js/soar-examples.test.js index 22abf84..56141d1 100644 --- a/tests-js/soar-examples.test.js +++ b/tests-js/soar-examples.test.js @@ -3,7 +3,7 @@ const Validator = require('swagger-model-validator'); const OpenAPISchemaValidator = require('openapi-schema-validator').default; const getSchema = () => { - const schema = require(`${__dirname}/../project/soar/swagger.json`); + const schema = require(`${__dirname}/../tests/test_swagger.json`); return schema; }; diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 2f9d465..421c48b 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -5,12 +5,28 @@ from jsonschema.validators import RefResolver import unittest import json import os +from generate_schema_config import write_schema, swagger_config -SCHEMA_DIR = "project/soar/swagger.json" MOCK_DATA_DIR = "examples/" +class SchemaTestCase(unittest.TestCase): + def load_schema(self, file_path=None): + if file_path: + schema_path = file_path + else: + schema_path = os.getenv("SCHEMA_PATH") + schema, _ = read_from_filename(schema_path) + return schema + + @classmethod + def setUpClass(cls): + test_schema_path = "tests/test_swagger.json" + os.environ["SCHEMA_PATH"] = test_schema_path + write_schema(swagger_config, test_schema_path) + + class SchemaError(Exception): """ Test config specs of swagger.json for projects @@ -19,26 +35,37 @@ class SchemaError(Exception): def __init__(self, exception_type, message): self.type = exception_type self.message = message - super().__init__(self.message) + super().__init__(message) -class TestSpecs(unittest.TestCase): +class TestCompiledSoarSpec(SchemaTestCase): + """ + Test the saved version of the compiled schema has been updated + """ + + def test_compiled_spec_matches_definitions(self): + print("TEST: definitions match project/soar/swagger.json") + derived_schema = self.load_schema() + saved_schema = self.load_schema("project/soar/swagger.json") + self.assertEqual(saved_schema, derived_schema) + + +class TestSpecs(SchemaTestCase): def test_swagger_specs(self): """ Test specs (swagger.json generated from generate_schema_config.py) """ - schema, spec_url = read_from_filename("tests/fixtures/swagger.json") + print("TEST: compiled schema matches openapi v3 spec") + schema = self.load_schema() self.assertIsNone(openapi_v30_spec_validator.validate(schema)) -class TestAllMessageExamples(unittest.TestCase): +class TestAllMessageExamples(SchemaTestCase): def test_schema_specs(self): """ Test specs (swagger.json generated from generate_schema_config.py) """ - schema_ref = open(SCHEMA_DIR) - schema = json.load(schema_ref) - schema_ref.close() + schema = self.load_schema() partner_dir_list = os.listdir(MOCK_DATA_DIR) @@ -70,8 +97,6 @@ class TestAllMessageExamples(unittest.TestCase): f.close() print("Done.") - schema_ref.close() - if __name__ == "__main__": unittest.main() -- GitLab