1
+ import { readFileSync } from "node:fs" ;
1
2
import { readdir , readFile } from "node:fs/promises" ;
2
3
import YAML from "yaml" ;
3
4
import { join } from "node:path" ;
4
5
import { 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" ;
6
7
import "@hyperjump/json-schema/draft-04" ;
7
- import { BASIC } from "@hyperjump/json-schema/experimental" ;
8
+ import { BASIC , addKeyword , defineVocabulary } from "@hyperjump/json-schema/experimental" ;
8
9
9
10
/**
10
11
* @import { EvaluationPlugin } from "@hyperjump/json-schema/experimental"
@@ -45,7 +46,14 @@ class TestCoveragePlugin {
45
46
this . allLocations = [ ] ;
46
47
47
48
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
+ ) {
49
57
continue ;
50
58
}
51
59
@@ -110,6 +118,68 @@ const runTests = async (schemaUri, testDirectory) => {
110
118
} ;
111
119
} ;
112
120
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
+
113
183
///////////////////////////////////////////////////////////////////////////////
114
184
115
185
const { allLocations, visitedLocations } = await runTests ( argv [ 2 ] , argv [ 3 ] ) ;
@@ -122,16 +192,13 @@ if (notCovered.length > 0) {
122
192
const firstNotCovered = notCovered . slice ( 0 , maxNotCovered ) ;
123
193
if ( notCovered . length > maxNotCovered ) firstNotCovered . push ( "..." ) ;
124
194
console . log ( firstNotCovered ) ;
195
+ process . exitCode = 1 ;
125
196
}
126
197
127
198
console . log (
128
199
"Covered:" ,
129
- visitedLocations . size ,
200
+ ( allLocations . length - notCovered . length ) ,
130
201
"of" ,
131
202
allLocations . length ,
132
- "(" + Math . floor ( ( visitedLocations . size / allLocations . length ) * 100 ) + "%)" ,
203
+ "(" + Math . floor ( ( ( allLocations . length - notCovered . length ) / allLocations . length ) * 100 ) + "%)" ,
133
204
) ;
134
-
135
- if ( visitedLocations . size != allLocations . length ) {
136
- process . exitCode = 1 ;
137
- }
0 commit comments