diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..79c7b061 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,31 @@ +# https://editorconfig.org/ + +root = true + +[*] +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf +charset = utf-8 + +[*.md] +#inside code block, indentation could be anything +indent_size = unset + +[*.py] +indent_size = 4 +# 88 is the default for black formatter +# 79 is PEP8's recommendation +# 119 is django's recommendation +max_line_length = 88 + +# The JSON files contain newlines inconsistently +[*.json] +insert_final_newline = unset + +# Makefiles always use tabs for indentation +[Makefile] +indent_style = tab +indent_size = 4 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a009dfa8..f4d14173 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -59,11 +59,11 @@ jobs: cache: npm cache-dependency-path: tools/package-lock.json - name: Install dependencies - run: | - cd tools - npm install -g --no-fund ajv-cli ajv-formats + run: npm ci --no-fund + working-directory: tools - name: Run Validate - run: ./tools/validate.sh + run: npm run validate + working-directory: tools spellcheck: name: Spellcheck (en_US) diff --git a/schemas/pipelinerunfinished.json b/schemas/pipelinerunfinished.json index 433eb691..35d8b4da 100644 --- a/schemas/pipelinerunfinished.json +++ b/schemas/pipelinerunfinished.json @@ -41,7 +41,7 @@ }, "additionalProperties": false, "type": "object", - "required": ["version", "id", "source", "type", "timestamp"] + "required": ["specversion", "id", "source", "type", "timestamp"] }, "subject": { "properties": { diff --git a/schemas/testcaserunfinished.json b/schemas/testcaserunfinished.json index 02712d03..00303aac 100644 --- a/schemas/testcaserunfinished.json +++ b/schemas/testcaserunfinished.json @@ -40,7 +40,7 @@ }, "additionalProperties": false, "type": "object", - "required": ["version", "id", "source", "type", "timestamp"] + "required": ["specversion", "id", "source", "type", "timestamp"] }, "subject": { "properties": { diff --git a/schemas/testsuiterunfinished.json b/schemas/testsuiterunfinished.json index 044c1f82..3725cad2 100644 --- a/schemas/testsuiterunfinished.json +++ b/schemas/testsuiterunfinished.json @@ -40,7 +40,7 @@ }, "additionalProperties": false, "type": "object", - "required": ["version", "id", "source", "type", "timestamp"] + "required": ["specversion", "id", "source", "type", "timestamp"] }, "subject": { "properties": { diff --git a/tools/package-lock.json b/tools/package-lock.json index 7de78a84..c7855118 100644 --- a/tools/package-lock.json +++ b/tools/package-lock.json @@ -6,8 +6,30 @@ "": { "name": "cdevents-tools", "dependencies": { - "ajv-cli": "^5.0.0", - "ajv-formats": "^3.0.1" + "ajv": "^8.17", + "ajv-formats": "^3.0", + "glob": "^13.0" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" } }, "node_modules/ajv": { @@ -26,32 +48,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-cli": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ajv-cli/-/ajv-cli-5.0.0.tgz", - "integrity": "sha512-LY4m6dUv44HTyhV+u2z5uX4EhPYTM38Iv1jdgDJJJCyOOuqB8KtZEGjPZ2T+sh5ZIJrXUfgErYx/j3gLd3+PlQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0", - "fast-json-patch": "^2.0.0", - "glob": "^7.1.0", - "js-yaml": "^3.14.0", - "json-schema-migrate": "^2.0.0", - "json5": "^2.1.3", - "minimist": "^1.2.0" - }, - "bin": { - "ajv": "dist/index.js" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } - } - }, "node_modules/ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -69,74 +65,12 @@ } } }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, - "node_modules/fast-json-patch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-2.2.1.tgz", - "integrity": "sha512-4j5uBaTnsYAV5ebkidvxiLUYOwjQ+JSFljeqfTxCrH9bDmlCQaOJFS84oDJ2rAXZq2yskmk3ORfoP9DCwqFNig==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^2.0.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/fast-json-patch/node_modules/fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", - "license": "MIT" - }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -153,127 +87,76 @@ ], "license": "BSD-3-Clause" }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "license": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" }, "engines": { - "node": "*" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-schema-migrate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-2.0.0.tgz", - "integrity": "sha512-r38SVTtojDRp4eD6WsCqiE0eNDt4v1WalBXb9cyZYw9ai5cGtBwzRNWjHzJl38w6TxFkXAIA7h+fyX3tnrAFhQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - } - }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, + "node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=6" + "node": "20 || >=22" } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", + "node": "20 || >=22" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "license": "ISC", - "dependencies": { - "wrappy": "1" + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, "engines": { - "node": ">=0.10.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/require-from-string": { @@ -284,18 +167,6 @@ "engines": { "node": ">=0.10.0" } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause" - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" } } } diff --git a/tools/package.json b/tools/package.json index 9036f659..3904ae9a 100644 --- a/tools/package.json +++ b/tools/package.json @@ -1,7 +1,11 @@ { "name": "cdevents-tools", + "scripts": { + "validate": "node validate.js" + }, "dependencies": { - "ajv-cli": "^5.0.0", - "ajv-formats": "^3.0.1" + "ajv": "^8.17", + "ajv-formats": "^3.0", + "glob": "^13.0" } } diff --git a/tools/validate.js b/tools/validate.js new file mode 100644 index 00000000..fdb9a028 --- /dev/null +++ b/tools/validate.js @@ -0,0 +1,219 @@ +#!/usr/bin/env node + +// Copyright 2023 The CDEvents Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { readFileSync, unlinkSync } from "node:fs"; +import { basename, dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import Ajv2020 from "ajv/dist/2020.js"; +import addFormats from "ajv-formats"; +import { globSync } from "glob"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +// Folders +const ROOT = join(__dirname, ".."); +const EXAMPLES_FOLDER = join(ROOT, "conformance"); +const SCHEMAS_FOLDER = join(ROOT, "schemas"); +const EMBEDDED_LINKS_SCHEMAS_PATTERN = join( + ROOT, + "schemas/links/embedded*.json", +); + +// Helper function to load JSON file +function loadJSON(filepath) { + try { + return JSON.parse(readFileSync(filepath, "utf8")); + } catch (error) { + console.error(`Error loading ${filepath}: ${error.message}`); + throw error; + } +} + +// Helper function to create ajv instance with options +function createAjv() { + const ajv = new Ajv2020({ + strict: false, + validateFormats: true, + allErrors: true, + }); + addFormats(ajv); + return ajv; +} + +// Load embedded links schemas +function loadEmbeddedLinksSchemas() { + const schemas = []; + const files = globSync(EMBEDDED_LINKS_SCHEMAS_PATTERN); + for (const file of files) { + schemas.push(loadJSON(file)); + } + return schemas; +} + +// Test schema compilation +function testSchemas() { + console.log("==> Testing Schema Files"); + + let schemaFailed = 0; + const embeddedLinksSchemas = loadEmbeddedLinksSchemas(); + + // Test event schemas + const eventSchemaFiles = globSync(join(SCHEMAS_FOLDER, "*.json")); + let numSchemas = eventSchemaFiles.length; + + for (const schemaFile of eventSchemaFiles) { + try { + const ajv = createAjv(); + // Add embedded links schemas as references + for (const embeddedSchema of embeddedLinksSchemas) { + ajv.addSchema(embeddedSchema); + } + const schema = loadJSON(schemaFile); + ajv.compile(schema); + } catch (error) { + console.error(`Failed to compile ${schemaFile}: ${error.message}`); + schemaFailed++; + } + } + + // Test links schemas + const linkSchemaFiles = globSync(join(SCHEMAS_FOLDER, "links/link*.json")); + numSchemas += linkSchemaFiles.length; + + for (const schemaFile of linkSchemaFiles) { + try { + const ajv = createAjv(); + const schema = loadJSON(schemaFile); + ajv.compile(schema); + } catch (error) { + console.error(`Failed to compile ${schemaFile}: ${error.message}`); + schemaFailed++; + } + } + + // Test custom schema + numSchemas += 1; + try { + const ajv = createAjv(); + for (const embeddedSchema of embeddedLinksSchemas) { + ajv.addSchema(embeddedSchema); + } + const customSchema = loadJSON(join(ROOT, "custom/schema.json")); + ajv.compile(customSchema); + } catch (error) { + console.error(`Failed to compile custom schema: ${error.message}`); + schemaFailed++; + } + + console.log( + `${numSchemas - schemaFailed} out of ${numSchemas} schemas are valid`, + ); + + return schemaFailed; +} + +// Test conformance examples +function testConformance() { + console.log("\n==> Testing Conformance Files"); + + let exampleFailed = 0; + const embeddedLinksSchemas = loadEmbeddedLinksSchemas(); + + const exampleFiles = globSync(join(EXAMPLES_FOLDER, "*.json")); + let numExamples = exampleFiles.length; + + for (const exampleFile of exampleFiles) { + const exampleFileName = basename(exampleFile); + const subjectPredicate = basename(exampleFileName, ".json"); + const parts = subjectPredicate.split("_"); + const subject = parts[0]; + const predicate = parts[1]; + const schemaFile = join(SCHEMAS_FOLDER, `${subject}${predicate}.json`); + + process.stdout.write(`${subject} ${predicate}: `); + + try { + const ajv = createAjv(); + // Add embedded links schemas as references + for (const embeddedSchema of embeddedLinksSchemas) { + ajv.addSchema(embeddedSchema); + } + const schema = loadJSON(schemaFile); + const example = loadJSON(exampleFile); + const validate = ajv.compile(schema); + const valid = validate(example); + + if (!valid) { + console.log("invalid"); + console.error(validate.errors); + exampleFailed++; + } else { + console.log("valid"); + } + } catch (error) { + console.log("failed"); + console.error(`Error: ${error.message}`); + exampleFailed++; + } + } + + // Test custom example + numExamples += 1; + try { + const ajv = createAjv(); + for (const embeddedSchema of embeddedLinksSchemas) { + ajv.addSchema(embeddedSchema); + } + const customSchema = loadJSON(join(ROOT, "custom/schema.json")); + const customExample = loadJSON(join(ROOT, "custom/conformance.json")); + const validate = ajv.compile(customSchema); + const valid = validate(customExample); + + if (!valid) { + console.error("Custom example validation failed"); + console.error(validate.errors); + exampleFailed++; + } + } catch (error) { + console.error(`Custom example validation error: ${error.message}`); + exampleFailed++; + } + + // Cleanup local schemas + const localSchemaFiles = globSync(join(ROOT, "schemas/**/*.local")); + for (const file of localSchemaFiles) { + unlinkSync(file); + } + + console.log( + `${numExamples - exampleFailed} out of ${numExamples} examples are valid`, + ); + + return exampleFailed; +} + +// Main execution +try { + const schemaFailed = testSchemas(); + const exampleFailed = testConformance(); + + const totalFailed = schemaFailed + exampleFailed; + process.exit(totalFailed); +} catch (error) { + console.error(`Fatal error: ${error.message}`); + process.exit(1); +} diff --git a/tools/validate.sh b/tools/validate.sh deleted file mode 100755 index 83ce8e2c..00000000 --- a/tools/validate.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2023 The CDEvents Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# The script requires the packages from requirements-dev.txt - -function usage() { - cat < Testing Schema Files" - -schema_failed=0 -# Test event schemas. Embedded links schemas are tested through this too. -num_schemas=$(find "${SCHEMAS_FOLDER}" -type f -name '*json' -maxdepth 1 | wc -l | awk '{ print $1 }') -while read -r schema; do - ajv compile ${JSON_VALIDATOR_OPTIONS} -r "${EMBEDDED_LINKS_SCHEMAS}" -s "${schema}" || schema_failed=$(( schema_failed + 1 )) -done <<<$(find "${SCHEMAS_FOLDER}" -type f -name '*.json' -maxdepth 1) -# Test links schemas -num_schemas=$(( num_schemas + $(find "${SCHEMAS_FOLDER}/links" -type f -name 'link*json' -maxdepth 1 | wc -l | awk '{ print $1 }') )) -while read -r schema; do - ajv compile ${JSON_VALIDATOR_OPTIONS} -s "${schema}" || schema_failed=$(( schema_failed + 1 )) -done <<<$(find "${SCHEMAS_FOLDER}/links" -type f -name 'link*json' -maxdepth 1) -# Test custom schema -num_schemas=$(( num_schemas + 1 )) -ajv compile ${JSON_VALIDATOR_OPTIONS} -r "${EMBEDDED_LINKS_SCHEMAS}" -s "${ROOT}/custom/schema.json" || schema_failed=$(( schema_failed + 1 )) - -echo "$(( num_schemas - schema_failed )) out of ${num_schemas} schemas are valid" - -# Loop over all conformance files -# - conformance are subject_predicate.json -# - schemas are subjectpredicate.json -echo -e "\n==> Testing Conformance Files" -example_failed=0 -num_examples=$(find "${EXAMPLES_FOLDER}" -type f -name '*json' | wc -l | awk '{ print $1 }') -find "${EXAMPLES_FOLDER}" -type f -name '*.json' | while read -r example; do - EXAMPLE_FILE=$(basename "${example}") - SUBJECT_PREDICATE=$(basename "${EXAMPLE_FILE}" .json) - splitArray=(${SUBJECT_PREDICATE//_/ }) - SUBJECT=${splitArray[0]} - PREDICATE=${splitArray[1]} - SCHEMA_FILE=${SCHEMAS_FOLDER}/${SUBJECT}${PREDICATE}.json - printf "%s %s: " "${SUBJECT}" "${PREDICATE}" - ajv validate ${JSON_VALIDATOR_OPTIONS} -r "${EMBEDDED_LINKS_SCHEMAS}" -s "${SCHEMA_FILE}" -d "${example}" || example_failed=$(( example_failed + 1 )) -done -# Test custom example -num_examples=$(( num_examples + 1 )) -ajv validate ${JSON_VALIDATOR_OPTIONS} -r "${EMBEDDED_LINKS_SCHEMAS}" -s "${ROOT}/custom/schema.json" -d "${ROOT}/custom/conformance.json" || example_failed=$(( example_failed + 1 )) - -# Cleanup local schemas -find "${ROOT}/schemas" -name '*.local' -exec rm {} + - -echo "$(( num_examples - example_failed )) out of ${num_examples} examples are valid" - -exit "$(( schema_failed + example_failed ))"