1+ import { readFileSync } from "node:fs" ;
12import { readdir , readFile } from "node:fs/promises" ;
23import YAML from "yaml" ;
34import { join } from "node:path" ;
45import { argv } from "node:process" ;
5- import { validate } from "@hyperjump/json-schema/draft-2020-12" ;
6+ import { registerSchema , validate } from "@hyperjump/json-schema/draft-2020-12" ;
67import "@hyperjump/json-schema/draft-04" ;
7- import { BASIC } from "@hyperjump/json-schema/experimental" ;
8+ import { BASIC , addKeyword , defineVocabulary } from "@hyperjump/json-schema/experimental" ;
89
910/**
1011 * @import { EvaluationPlugin } from "@hyperjump/json-schema/experimental"
@@ -45,7 +46,14 @@ class TestCoveragePlugin {
4546 this . allLocations = [ ] ;
4647
4748 for ( const schemaLocation in context . ast ) {
48- if ( schemaLocation === "metaData" ) {
49+ if (
50+ schemaLocation === "metaData" ||
51+ // Do not require coverage of standard JSON Schema
52+ schemaLocation . includes ( "json-schema.org" ) ||
53+ // Do not require coverage of default $dynamicAnchor
54+ // schemas, as they are not expected to be reached
55+ schemaLocation . endsWith ( "/schema/WORK-IN-PROGRESS#/$defs/schema" )
56+ ) {
4957 continue ;
5058 }
5159
@@ -110,6 +118,68 @@ const runTests = async (schemaUri, testDirectory) => {
110118 } ;
111119} ;
112120
121+ addKeyword ( {
122+ id : "https://spec.openapis.org/oas/schema/vocab/keyword/discriminator" ,
123+ interpret : ( discriminator , instance , context ) => {
124+ return true ;
125+ } ,
126+ /* discriminator is not exactly an annotation, but it's not allowed
127+ * to change the validation outcome (hence returing true from interopret())
128+ * and for our purposes of testing, this is sufficient.
129+ */
130+ annotation : ( discriminator ) => {
131+ return discriminator ;
132+ } ,
133+ } ) ;
134+
135+ addKeyword ( {
136+ id : "https://spec.openapis.org/oas/schema/vocab/keyword/example" ,
137+ interpret : ( example , instance , context ) => {
138+ return true ;
139+ } ,
140+ annotation : ( example ) => {
141+ return example ;
142+ } ,
143+ } ) ;
144+
145+ addKeyword ( {
146+ id : "https://spec.openapis.org/oas/schema/vocab/keyword/externalDocs" ,
147+ interpret : ( externalDocs , instance , context ) => {
148+ return true ;
149+ } ,
150+ annotation : ( externalDocs ) => {
151+ return externalDocs ;
152+ } ,
153+ } ) ;
154+
155+ addKeyword ( {
156+ id : "https://spec.openapis.org/oas/schema/vocab/keyword/xml" ,
157+ interpret : ( xml , instance , context ) => {
158+ return true ;
159+ } ,
160+ annotation : ( xml ) => {
161+ return xml ;
162+ } ,
163+ } ) ;
164+
165+ defineVocabulary (
166+ "https://spec.openapis.org/oas/3.2/vocab/base" ,
167+ {
168+ "discriminator" : "https://spec.openapis.org/oas/schema/vocab/keyword/discriminator" ,
169+ "example" : "https://spec.openapis.org/oas/schema/vocab/keyword/example" ,
170+ "externalDocs" : "https://spec.openapis.org/oas/schema/vocab/keyword/externalDocs" ,
171+ "xml" : "https://spec.openapis.org/oas/schema/vocab/keyword/xml" ,
172+ } ,
173+ ) ;
174+
175+ const parseYamlFromFile = ( filePath ) => {
176+ const schemaYaml = readFileSync ( filePath , "utf8" ) ;
177+ return YAML . parse ( schemaYaml , { prettyErrors : true } ) ;
178+ } ;
179+ registerSchema ( parseYamlFromFile ( "./src/schemas/validation/meta.yaml" ) ) ;
180+ registerSchema ( parseYamlFromFile ( "./src/schemas/validation/dialect.yaml" ) ) ;
181+ registerSchema ( parseYamlFromFile ( "./src/schemas/validation/schema.yaml" ) ) ;
182+
113183///////////////////////////////////////////////////////////////////////////////
114184
115185const { allLocations, visitedLocations } = await runTests ( argv [ 2 ] , argv [ 3 ] ) ;
@@ -122,16 +192,13 @@ if (notCovered.length > 0) {
122192 const firstNotCovered = notCovered . slice ( 0 , maxNotCovered ) ;
123193 if ( notCovered . length > maxNotCovered ) firstNotCovered . push ( "..." ) ;
124194 console . log ( firstNotCovered ) ;
195+ process . exitCode = 1 ;
125196}
126197
127198console . log (
128199 "Covered:" ,
129- visitedLocations . size ,
200+ ( allLocations . length - notCovered . length ) ,
130201 "of" ,
131202 allLocations . length ,
132- "(" + Math . floor ( ( visitedLocations . size / allLocations . length ) * 100 ) + "%)" ,
203+ "(" + Math . floor ( ( ( allLocations . length - notCovered . length ) / allLocations . length ) * 100 ) + "%)" ,
133204) ;
134-
135- if ( visitedLocations . size != allLocations . length ) {
136- process . exitCode = 1 ;
137- }
0 commit comments