@@ -4,6 +4,12 @@ vi.mock('@redocly/openapi-core', async (importOriginal) => {
44 ...actual ,
55 bundle : vi . fn ( ) ,
66 detectSpec : vi . fn ( ) ,
7+ BaseResolver : vi . fn ( ) ,
8+ resolveDocument : vi . fn ( ) ,
9+ normalizeTypes : vi . fn ( ) ,
10+ getTypes : vi . fn ( ) ,
11+ normalizeVisitors : vi . fn ( ) ,
12+ walkDocument : vi . fn ( ) ,
713 logger : { output : vi . fn ( ) , info : vi . fn ( ) , error : vi . fn ( ) } ,
814 } ;
915} ) ;
@@ -13,67 +19,107 @@ vi.mock('../../../utils/miscellaneous.js', () => ({
1319 printExecutionTime : vi . fn ( ) ,
1420} ) ) ;
1521
16- import { bundle , detectSpec , logger } from '@redocly/openapi-core' ;
22+ vi . mock ( '../collectors/document-metrics.js' , async ( importOriginal ) => {
23+ const actual = ( await importOriginal ( ) ) as any ;
24+ return {
25+ ...actual ,
26+ createScoreAccumulator : vi . fn ( ) ,
27+ createScoreVisitor : vi . fn ( ) ,
28+ } ;
29+ } ) ;
30+
31+ import {
32+ bundle ,
33+ detectSpec ,
34+ logger ,
35+ normalizeTypes ,
36+ resolveDocument ,
37+ normalizeVisitors ,
38+ walkDocument ,
39+ } from '@redocly/openapi-core' ;
1740
1841import { getFallbackApisOrExit } from '../../../utils/miscellaneous.js' ;
42+ import {
43+ createScoreAccumulator ,
44+ createScoreVisitor ,
45+ type ScoreAccumulator ,
46+ } from '../collectors/document-metrics.js' ;
1947import { handleScore , type ScoreArgv } from '../index.js' ;
48+ import type { OperationMetrics } from '../types.js' ;
2049
2150const mockedBundle = vi . mocked ( bundle ) ;
2251const mockedDetectSpec = vi . mocked ( detectSpec ) ;
2352const mockedGetFallback = vi . mocked ( getFallbackApisOrExit ) ;
2453const mockOutput = vi . mocked ( logger . output ) ;
25- const mockInfo = vi . mocked ( logger . info ) ;
2654const mockError = vi . mocked ( logger . error ) ;
27-
28- function makeMinimalDoc ( overrides : Record < string , any > = { } ) {
55+ const mockedCreateAccumulator = vi . mocked ( createScoreAccumulator ) ;
56+ const mockedCreateVisitor = vi . mocked ( createScoreVisitor ) ;
57+ const mockedNormalizeTypes = vi . mocked ( normalizeTypes ) ;
58+ const mockedResolveDocument = vi . mocked ( resolveDocument ) ;
59+ const mockedNormalizeVisitors = vi . mocked ( normalizeVisitors ) ;
60+ const mockedWalkDocument = vi . mocked ( walkDocument ) ;
61+
62+ function makeTestMetrics ( overrides : Partial < OperationMetrics > = { } ) : OperationMetrics {
2963 return {
30- openapi : '3.0.0' ,
31- info : { title : 'Test' , version : '1.0.0' } ,
32- paths : {
33- '/items' : {
34- get : {
35- operationId : 'listItems' ,
36- description : 'List items' ,
37- responses : {
38- '200' : {
39- description : 'OK' ,
40- content : {
41- 'application/json' : {
42- schema : { type : 'object' , properties : { id : { type : 'string' } } } ,
43- example : { id : '1' } ,
44- } ,
45- } ,
46- } ,
47- } ,
48- } ,
49- } ,
50- } ,
64+ path : '/items' ,
65+ method : 'get' ,
66+ operationId : 'listItems' ,
67+ parameterCount : 0 ,
68+ requiredParameterCount : 0 ,
69+ paramsWithDescription : 0 ,
70+ requestBodyPresent : false ,
71+ topLevelWritableFieldCount : 0 ,
72+ maxRequestSchemaDepth : 0 ,
73+ maxResponseSchemaDepth : 1 ,
74+ polymorphismCount : 0 ,
75+ anyOfCount : 0 ,
76+ hasDiscriminator : false ,
77+ propertyCount : 1 ,
78+ operationDescriptionPresent : true ,
79+ schemaPropertiesWithDescription : 0 ,
80+ totalSchemaProperties : 1 ,
81+ constraintCount : 0 ,
82+ requestExamplePresent : false ,
83+ responseExamplePresent : true ,
84+ structuredErrorResponseCount : 0 ,
85+ totalErrorResponses : 0 ,
86+ ambiguousIdentifierCount : 0 ,
87+ refsUsed : new Set ( ) ,
5188 ...overrides ,
5289 } ;
5390}
5491
92+ function makeAccumulator ( ops : Map < string , OperationMetrics > = new Map ( ) ) : ScoreAccumulator {
93+ return { operations : ops , currentPath : '' , pathLevelParams : [ ] } ;
94+ }
95+
5596function createArgs ( overrides : Partial < ScoreArgv > = { } ) {
5697 return {
5798 argv : { format : 'stylish' as const , ...overrides } as ScoreArgv ,
58- config : { } as any ,
99+ config : { extendTypes : ( types : any ) => types , resolve : { } } as any ,
59100 collectSpecData : vi . fn ( ) ,
60101 } ;
61102}
62103
63104describe ( 'handleScore' , ( ) => {
64105 beforeEach ( ( ) => {
65106 mockOutput . mockClear ( ) ;
66- mockInfo . mockClear ( ) ;
67107 mockError . mockClear ( ) ;
68108 mockedGetFallback . mockResolvedValue ( [ { path : 'test.yaml' } ] as any ) ;
109+ mockedBundle . mockResolvedValue ( { bundle : { parsed : { } } } as any ) ;
110+ mockedDetectSpec . mockReturnValue ( 'oas3_0' ) ;
111+ mockedNormalizeTypes . mockReturnValue ( { Root : { } } as any ) ;
112+ mockedResolveDocument . mockResolvedValue ( new Map ( ) as any ) ;
113+ mockedNormalizeVisitors . mockReturnValue ( [ ] as any ) ;
114+ mockedWalkDocument . mockImplementation ( ( ) => { } ) ;
115+ mockedCreateVisitor . mockReturnValue ( { } as any ) ;
116+ mockedCreateAccumulator . mockReturnValue (
117+ makeAccumulator ( new Map ( [ [ 'listItems' , makeTestMetrics ( ) ] ] ) )
118+ ) ;
69119 process . exitCode = undefined ;
70120 } ) ;
71121
72122 it ( 'should produce stylish output for a valid oas3 document' , async ( ) => {
73- const doc = makeMinimalDoc ( ) ;
74- mockedBundle . mockResolvedValue ( { bundle : { parsed : doc } } as any ) ;
75- mockedDetectSpec . mockReturnValue ( 'oas3_0' ) ;
76-
77123 await handleScore ( createArgs ( ) ) ;
78124
79125 const output = mockOutput . mock . calls . map ( ( [ s ] : [ string ] ) => s ) . join ( '' ) ;
@@ -82,10 +128,6 @@ describe('handleScore', () => {
82128 } ) ;
83129
84130 it ( 'should produce JSON output when format is json' , async ( ) => {
85- const doc = makeMinimalDoc ( ) ;
86- mockedBundle . mockResolvedValue ( { bundle : { parsed : doc } } as any ) ;
87- mockedDetectSpec . mockReturnValue ( 'oas3_0' ) ;
88-
89131 await handleScore ( createArgs ( { format : 'json' } ) ) ;
90132
91133 const output = mockOutput . mock . calls [ 0 ] [ 0 ] ;
@@ -96,7 +138,6 @@ describe('handleScore', () => {
96138 } ) ;
97139
98140 it ( 'should reject non-oas3 documents' , async ( ) => {
99- mockedBundle . mockResolvedValue ( { bundle : { parsed : { swagger : '2.0' } } } as any ) ;
100141 mockedDetectSpec . mockReturnValue ( 'oas2' ) ;
101142
102143 await handleScore ( createArgs ( ) ) ;
@@ -108,7 +149,7 @@ describe('handleScore', () => {
108149 } ) ;
109150
110151 it ( 'should call collectSpecData' , async ( ) => {
111- const doc = makeMinimalDoc ( ) ;
152+ const doc = { openapi : '3.0.0' } ;
112153 mockedBundle . mockResolvedValue ( { bundle : { parsed : doc } } as any ) ;
113154 mockedDetectSpec . mockReturnValue ( 'oas3_1' ) ;
114155
@@ -119,9 +160,7 @@ describe('handleScore', () => {
119160 } ) ;
120161
121162 it ( 'should handle document with no operations' , async ( ) => {
122- const doc = makeMinimalDoc ( { paths : { } } ) ;
123- mockedBundle . mockResolvedValue ( { bundle : { parsed : doc } } as any ) ;
124- mockedDetectSpec . mockReturnValue ( 'oas3_0' ) ;
163+ mockedCreateAccumulator . mockReturnValue ( makeAccumulator ( ) ) ;
125164
126165 await handleScore ( createArgs ( ) ) ;
127166
@@ -130,34 +169,23 @@ describe('handleScore', () => {
130169 } ) ;
131170
132171 it ( 'should handle document with multiple operations' , async ( ) => {
133- const doc = makeMinimalDoc ( {
134- paths : {
135- '/items' : {
136- get : {
137- operationId : 'listItems' ,
138- description : 'List items' ,
139- responses : { '200' : { description : 'OK' } } ,
140- } ,
141- post : {
142- operationId : 'createItem' ,
143- description : 'Create item' ,
144- requestBody : {
145- content : {
146- 'application/json' : {
147- schema : { type : 'object' , properties : { name : { type : 'string' } } } ,
148- } ,
149- } ,
150- } ,
151- responses : {
152- '201' : { description : 'Created' } ,
153- '400' : { description : 'Bad Request' } ,
154- } ,
155- } ,
156- } ,
157- } ,
158- } ) ;
159- mockedBundle . mockResolvedValue ( { bundle : { parsed : doc } } as any ) ;
160- mockedDetectSpec . mockReturnValue ( 'oas3_0' ) ;
172+ mockedCreateAccumulator . mockReturnValue (
173+ makeAccumulator (
174+ new Map ( [
175+ [ 'listItems' , makeTestMetrics ( ) ] ,
176+ [
177+ 'createItem' ,
178+ makeTestMetrics ( {
179+ path : '/items' ,
180+ method : 'post' ,
181+ operationId : 'createItem' ,
182+ requestBodyPresent : true ,
183+ operationDescriptionPresent : true ,
184+ } ) ,
185+ ] ,
186+ ] )
187+ )
188+ ) ;
161189
162190 await handleScore ( createArgs ( { format : 'json' } ) ) ;
163191
@@ -168,25 +196,23 @@ describe('handleScore', () => {
168196 } ) ;
169197
170198 it ( 'should include hotspots in output' , async ( ) => {
171- const doc = makeMinimalDoc ( {
172- paths : {
173- '/complex' : {
174- post : {
175- operationId : 'complexOp' ,
176- parameters : Array . from ( { length : 10 } , ( _ , i ) => ( {
177- name : `param${ i } ` ,
178- in : 'query' ,
179- } ) ) ,
180- requestBody : {
181- content : { 'application/json' : { schema : { type : 'object' } } } ,
182- } ,
183- responses : { '200' : { description : 'OK' } } ,
184- } ,
185- } ,
186- } ,
187- } ) ;
188- mockedBundle . mockResolvedValue ( { bundle : { parsed : doc } } as any ) ;
189- mockedDetectSpec . mockReturnValue ( 'oas3_0' ) ;
199+ mockedCreateAccumulator . mockReturnValue (
200+ makeAccumulator (
201+ new Map ( [
202+ [
203+ 'complexOp' ,
204+ makeTestMetrics ( {
205+ path : '/complex' ,
206+ method : 'post' ,
207+ operationId : 'complexOp' ,
208+ parameterCount : 10 ,
209+ requestBodyPresent : true ,
210+ operationDescriptionPresent : false ,
211+ } ) ,
212+ ] ,
213+ ] )
214+ )
215+ ) ;
190216
191217 await handleScore ( createArgs ( ) ) ;
192218
0 commit comments