Skip to content

Commit ec901cf

Browse files
authored
Merge pull request #4683 from OAI/main
dev: update from main
2 parents bda91e7 + 0f9f16e commit ec901cf

File tree

3 files changed

+69
-68
lines changed

3 files changed

+69
-68
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"yargs": "^18.0.0"
2828
},
2929
"devDependencies": {
30-
"@hyperjump/json-schema": "^1.15.0",
30+
"@hyperjump/json-schema": "^1.15.1",
3131
"c8": "^10.1.3",
3232
"markdownlint-cli2": "^0.18.1",
3333
"vitest": "^3.2.3",

scripts/schema-test-coverage.mjs

Lines changed: 64 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,13 @@ import { readdir, readFile } from "node:fs/promises";
22
import YAML from "yaml";
33
import { join } from "node:path";
44
import { argv } from "node:process";
5-
import "@hyperjump/json-schema/draft-2020-12";
5+
import { validate } from "@hyperjump/json-schema/draft-2020-12";
66
import "@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 { EvaluationPlugin } from "@hyperjump/json-schema/experimental"
11+
* @import { Json } from "@hyperjump/json-pointer"
1912
*/
2013

2114
import contentTypeParser from "content-type";
@@ -36,6 +29,41 @@ addMediaTypePlugin("application/schema+yaml", {
3629
fileMatcher: (path) => path.endsWith(".yaml"),
3730
});
3831

32+
/** @implements EvaluationPlugin */
33+
class TestCoveragePlugin {
34+
constructor() {
35+
/** @type Set<string> */
36+
this.visitedLocations = new Set();
37+
}
38+
39+
beforeSchema(_schemaUri, _instance, context) {
40+
if (this.allLocations) {
41+
return;
42+
}
43+
44+
/** @type Set<string> */
45+
this.allLocations = [];
46+
47+
for (const schemaLocation in context.ast) {
48+
if (schemaLocation === "metaData") {
49+
continue;
50+
}
51+
52+
if (Array.isArray(context.ast[schemaLocation])) {
53+
for (const keyword of context.ast[schemaLocation]) {
54+
if (Array.isArray(keyword)) {
55+
this.allLocations.push(keyword[1]);
56+
}
57+
}
58+
}
59+
}
60+
}
61+
62+
beforeKeyword([, schemaUri]) {
63+
this.visitedLocations.add(schemaUri);
64+
}
65+
}
66+
3967
/** @type (testDirectory: string) => AsyncGenerator<[string,Json]> */
4068
const tests = async function* (testDirectory) {
4169
for (const file of await readdir(testDirectory, {
@@ -53,70 +81,43 @@ const tests = async function* (testDirectory) {
5381
}
5482
};
5583

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);
84+
/**
85+
* @typedef {{
86+
* allLocations: string[];
87+
* visitedLocations: Set<string>;
88+
* }} Coverage
89+
*/
6090

61-
const result = interpret(compiled, instance, BASIC);
91+
/** @type (schemaUri: string, testDirectory: string) => Promise<Coverage> */
92+
const runTests = async (schemaUri, testDirectory) => {
93+
const testCoveragePlugin = new TestCoveragePlugin();
94+
const validateOpenApi = await validate(schemaUri);
95+
96+
for await (const [name, test] of tests(testDirectory)) {
97+
const result = validateOpenApi(test, {
98+
outputFormat: BASIC,
99+
plugins: [testCoveragePlugin],
100+
});
62101

63102
if (!result.valid) {
64103
console.log("Failed:", name, result.errors);
65104
}
66105
}
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-
}
86106

87-
return locations;
107+
return {
108+
allLocations: testCoveragePlugin.allLocations ?? new Set(),
109+
visitedLocations: testCoveragePlugin.visitedLocations
110+
};
88111
};
89112

90113
///////////////////////////////////////////////////////////////////////////////
91114

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(
115+
const { allLocations, visitedLocations } = await runTests(argv[2], argv[3]);
116+
const notCovered = allLocations.filter(
116117
(location) => !visitedLocations.has(location),
117118
);
118119
if (notCovered.length > 0) {
119-
console.log("NOT Covered:", notCovered.length, "of", allKeywords.length);
120+
console.log("NOT Covered:", notCovered.length, "of", allLocations.length);
120121
const maxNotCovered = 20;
121122
const firstNotCovered = notCovered.slice(0, maxNotCovered);
122123
if (notCovered.length > maxNotCovered) firstNotCovered.push("...");
@@ -127,6 +128,6 @@ console.log(
127128
"Covered:",
128129
visitedLocations.size,
129130
"of",
130-
allKeywords.length,
131-
"(" + Math.floor((visitedLocations.size / allKeywords.length) * 100) + "%)",
131+
allLocations.length,
132+
"(" + Math.floor((visitedLocations.size / allLocations.length) * 100) + "%)",
132133
);

0 commit comments

Comments
 (0)