@@ -2,20 +2,14 @@ import { readdir, readFile } from "node:fs/promises";
22import YAML from "yaml" ;
33import { join } from "node:path" ;
44import { argv } from "node:process" ;
5- import "@hyperjump/json-schema/draft-2020-12" ;
5+ import { validate } from "@hyperjump/json-schema/draft-2020-12" ;
66import "@hyperjump/json-schema/draft-04" ;
7- import {
8- compile ,
9- getSchema ,
10- interpret ,
11- Validation ,
12- BASIC ,
13- } from "@hyperjump/json-schema/experimental" ;
14- import * as Instance from "@hyperjump/json-schema/instance/experimental" ;
7+ import { BASIC } from "@hyperjump/json-schema/experimental" ;
158
169/**
17- * @import { AST } from "@hyperjump/json-schema/experimental"
18- * @import { Json } from "@hyperjump/json-schema"
10+ * @import { SchemaObject } from "@hyperjump/json-schema/draft-2020-12"
11+ * @import { EvaluationPlugin } from "@hyperjump/json-schema/experimental"
12+ * @import { Json } from "@hyperjump/json-pointer"
1913 */
2014
2115import contentTypeParser from "content-type" ;
@@ -30,12 +24,48 @@ addMediaTypePlugin("application/schema+yaml", {
3024 const contextDialectId =
3125 contentType . parameters . schema ?? contentType . parameters . profile ;
3226
27+ /** @type SchemaObject */
3328 const foo = YAML . parse ( await response . text ( ) ) ;
3429 return buildSchemaDocument ( foo , response . url , contextDialectId ) ;
3530 } ,
3631 fileMatcher : ( path ) => path . endsWith ( ".yaml" ) ,
3732} ) ;
3833
34+ /** @implements EvaluationPlugin */
35+ class TestCoveragePlugin {
36+ constructor ( ) {
37+ /** @type Set<string> */
38+ this . visitedLocations = new Set ( ) ;
39+ }
40+
41+ beforeSchema ( _schemaUri , _instance , context ) {
42+ if ( this . allLocations ) {
43+ return ;
44+ }
45+
46+ /** @type Set<string> */
47+ this . allLocations = [ ] ;
48+
49+ for ( const schemaLocation in context . ast ) {
50+ if ( schemaLocation === "metaData" ) {
51+ continue ;
52+ }
53+
54+ if ( Array . isArray ( context . ast [ schemaLocation ] ) ) {
55+ for ( const keyword of context . ast [ schemaLocation ] ) {
56+ if ( Array . isArray ( keyword ) ) {
57+ this . allLocations . push ( keyword [ 1 ] ) ;
58+ }
59+ }
60+ }
61+ }
62+ }
63+
64+ beforeKeyword ( [ , schemaUri ] ) {
65+ this . visitedLocations . add ( schemaUri ) ;
66+ }
67+ }
68+
3969/** @type (testDirectory: string) => AsyncGenerator<[string,Json]> */
4070const tests = async function * ( testDirectory ) {
4171 for ( const file of await readdir ( testDirectory , {
@@ -53,70 +83,43 @@ const tests = async function* (testDirectory) {
5383 }
5484} ;
5585
56- /** @type (testDirectory: string) => Promise<void> */
57- const runTests = async ( testDirectory ) => {
58- for await ( const [ name , test ] of tests ( testDirectory ) ) {
59- const instance = Instance . fromJs ( test ) ;
86+ /**
87+ * @typedef {{
88+ * allLocations: string[];
89+ * visitedLocations: Set<string>;
90+ * }} Coverage
91+ */
6092
61- const result = interpret ( compiled , instance , BASIC ) ;
93+ /** @type (schemaUri: string, testDirectory: string) => Promise<Coverage> */
94+ const runTests = async ( schemaUri , testDirectory ) => {
95+ const testCoveragePlugin = new TestCoveragePlugin ( ) ;
96+ const validateOpenApi = await validate ( schemaUri ) ;
97+
98+ for await ( const [ name , test ] of tests ( testDirectory ) ) {
99+ const result = validateOpenApi ( test , {
100+ outputFormat : BASIC ,
101+ plugins : [ testCoveragePlugin ] ,
102+ } ) ;
62103
63104 if ( ! result . valid ) {
64105 console . log ( "Failed:" , name , result . errors ) ;
65106 }
66107 }
67- } ;
68-
69- /** @type (ast: AST) => string[] */
70- const keywordLocations = ( ast ) => {
71- /** @type string[] */
72- const locations = [ ] ;
73- for ( const schemaLocation in ast ) {
74- if ( schemaLocation === "metaData" ) {
75- continue ;
76- }
77-
78- if ( Array . isArray ( ast [ schemaLocation ] ) ) {
79- for ( const keyword of ast [ schemaLocation ] ) {
80- if ( Array . isArray ( keyword ) ) {
81- locations . push ( keyword [ 1 ] ) ;
82- }
83- }
84- }
85- }
86108
87- return locations ;
109+ return {
110+ allLocations : testCoveragePlugin . allLocations ?? new Set ( ) ,
111+ visitedLocations : testCoveragePlugin . visitedLocations
112+ } ;
88113} ;
89114
90115///////////////////////////////////////////////////////////////////////////////
91116
92- const schema = await getSchema ( argv [ 2 ] ) ;
93- const compiled = await compile ( schema ) ;
94-
95- /** @type Set<string> */
96- const visitedLocations = new Set ( ) ;
97- const baseInterpret = Validation . interpret ;
98- Validation . interpret = ( url , instance , context ) => {
99- if ( Array . isArray ( context . ast [ url ] ) ) {
100- for ( const keywordNode of context . ast [ url ] ) {
101- if ( Array . isArray ( keywordNode ) ) {
102- visitedLocations . add ( keywordNode [ 1 ] ) ;
103- }
104- }
105- }
106- return baseInterpret ( url , instance , context ) ;
107- } ;
108-
109- await runTests ( argv [ 3 ] ) ;
110- Validation . interpret = baseInterpret ;
111-
112- // console.log("Covered:", visitedLocations);
113-
114- const allKeywords = keywordLocations ( compiled . ast ) ;
115- const notCovered = allKeywords . filter (
117+ const { allLocations, visitedLocations } = await runTests ( argv [ 2 ] , argv [ 3 ] ) ;
118+ const notCovered = allLocations . filter (
116119 ( location ) => ! visitedLocations . has ( location ) ,
117120) ;
118121if ( notCovered . length > 0 ) {
119- console . log ( "NOT Covered:" , notCovered . length , "of" , allKeywords . length ) ;
122+ console . log ( "NOT Covered:" , notCovered . length , "of" , allLocations . length ) ;
120123 const maxNotCovered = 20 ;
121124 const firstNotCovered = notCovered . slice ( 0 , maxNotCovered ) ;
122125 if ( notCovered . length > maxNotCovered ) firstNotCovered . push ( "..." ) ;
@@ -127,6 +130,6 @@ console.log(
127130 "Covered:" ,
128131 visitedLocations . size ,
129132 "of" ,
130- allKeywords . length ,
131- "(" + Math . floor ( ( visitedLocations . size / allKeywords . length ) * 100 ) + "%)" ,
133+ allLocations . length ,
134+ "(" + Math . floor ( ( visitedLocations . size / allLocations . length ) * 100 ) + "%)"
132135) ;
0 commit comments