Repository: codekie/openapi-examples-validator
Pull request #63 (2020-04-27 12:52:08)

fix: fix statistics for `schemasWithExamples`


Patch:
diff --git a/src/index.js b/src/index.js
index c47a312..9d51b19 100644
--- a/src/index.js
+++ b/src/index.js
@@ -18,10 +18,12 @@ const
 
 // CONSTANTS
 
-const FILE_EXTENSIONS__YAML = [
-    'yaml',
-    'yml'
-];
+const SYM__INTERNAL = Symbol('internal'),
+    PROP__SCHEMAS_WITH_EXAMPLES = 'schemasWithExamples',
+    FILE_EXTENSIONS__YAML = [
+        'yaml',
+        'yml'
+    ];
 
 // STATICS
 
@@ -151,13 +153,18 @@ async function validateExamplesByMap(filePathSchema, globMapExternalExamples, {
         matchingFilePathsMapping++;
         responses.push(
             _validate(
-                Object.keys(mapExternalExamples),
-                statistics => _handleExamplesByMapValidation(openapiSpec, mapExternalExamples, statistics, {
-                    cwdToMappingFile,
-                    dirPathMapExternalExamples: path.dirname(filePathMapExternalExamples)
-                }).map((/** @type ApplicationError */ error) => Object.assign(error, {
-                    mapFilePath: filePathMapExternalExamples
-                }))
+                statistics => {
+                    return _handleExamplesByMapValidation(
+                        openapiSpec, mapExternalExamples, statistics, {
+                            cwdToMappingFile,
+                            dirPathMapExternalExamples: path.dirname(filePathMapExternalExamples)
+                        }
+                    ).map(
+                        (/** @type ApplicationError */ error) => Object.assign(error, {
+                            mapFilePath: filePathMapExternalExamples
+                        })
+                    );
+                }
             )
         );
     }
@@ -191,7 +198,6 @@ async function validateExample(filePathSchema, pathSchema, filePathExample) {
         return createValidationResponse({ errors: [ApplicationError.create(err)] });
     }
     return _validate(
-        [pathSchema],
         statistics => _validateExample({
             createValidator: _initValidatorFactory(openapiSpec),
             schema,
@@ -231,15 +237,14 @@ function _isFileTypeYaml(filePath) {
 /**
  * Top-level validator. Prepares common values, required for the validation, then calles the validator and prepares
  * the result for the output.
- * @param {Array.<String>}      schemaPaths             JSON-paths to the schemas
  * @param {ValidationHandler}   validationHandler       The handler which performs the validation. It will receive the
  *                                                      statistics-object as argument and has to return an Array of
  *                                                      errors (or an empty Array, when all examples are valid)
  * @returns {ValidationResponse}
  * @private
  */
-function _validate(schemaPaths, validationHandler) {
-    const statistics = _initStatistics({ schemaPaths }),
+function _validate(validationHandler) {
+    const statistics = _initStatistics(),
         errors = validationHandler(statistics);
     return createValidationResponse({ errors, statistics });
 }
@@ -308,6 +313,14 @@ function _mergeValidationResponses(response1, response2) {
         errors: response1.errors.concat(response2.errors),
         statistics: _.entries(response1.statistics)
             .reduce((res, [key, val]) => {
+                if (PROP__SCHEMAS_WITH_EXAMPLES === key) {
+                    const schemasWithExample = response2.statistics[SYM__INTERNAL][PROP__SCHEMAS_WITH_EXAMPLES]
+                        .values();
+                    for (let schema of schemasWithExample) {
+                        res[SYM__INTERNAL][PROP__SCHEMAS_WITH_EXAMPLES].add(schema);
+                    }
+                    return res;
+                }
                 res[key] = val + response2.statistics[key];
                 return res;
             }, _initStatistics())
@@ -361,7 +374,6 @@ function _validateExamplesPaths({ impl }, pathsExamples, openapiSpec) {
     }
     // Start validation
     const schemaPaths = Object.keys(validationMap);
-    validationResult.statistics.schemasWithExamples = schemaPaths.length;
     schemaPaths.forEach(pathSchema => {
         _validateSchema({ openapiSpec, createValidator, pathSchema, validationMap, statistics,
             validationResult });
@@ -403,16 +415,22 @@ function _validateSchema({ openapiSpec, createValidator, pathSchema, validationM
 
 /**
  * Creates a container-object for the validation statistics.
- * @param {Array.<String>}  [schemaPaths=[]]    JSON-paths schemas
  * @returns {ValidationStatistics}
  * @private
  */
-function _initStatistics({ schemaPaths=[] } = {}) {
-    return {
-        schemasWithExamples: schemaPaths.length,
+function _initStatistics() {
+    const statistics = {
+        [SYM__INTERNAL]: {
+            [PROP__SCHEMAS_WITH_EXAMPLES]: new Set()
+        },
         examplesTotal: 0,
         examplesWithoutSchema: 0
     };
+    Object.defineProperty(statistics, PROP__SCHEMAS_WITH_EXAMPLES, {
+        enumerable: true,
+        get: () => statistics[SYM__INTERNAL][PROP__SCHEMAS_WITH_EXAMPLES].size
+    });
+    return statistics;
 }
 
 /**
@@ -451,10 +469,10 @@ function _validateExample({ createValidator, schema, example, statistics, filePa
     statistics.examplesTotal++;
     // No schema, no validation (Examples without schema are considered valid)
     if (!schema) {
-        statistics.schemasWithExamples--;
         statistics.examplesWithoutSchema++;
         return errors;
     }
+    statistics[SYM__INTERNAL][PROP__SCHEMAS_WITH_EXAMPLES].add(schema);
     const validate = compileValidate(createValidator(), schema);
     if (validate(example)) { return errors; }
     return errors.concat(...validate.errors.map(ApplicationError.create))


Test patch:
diff --git a/test/data/v2/map-external-examples-relative-invalid.json b/test/data/v2/map-external-examples-relative-invalid.json
new file mode 100644
index 0000000..2a0410d
--- /dev/null
+++ b/test/data/v2/map-external-examples-relative-invalid.json
@@ -0,0 +1,7 @@
+{
+  "$.paths./.get.responses.200.schema": [
+    "external-examples-invalid-missing-link.json",
+    "external-examples-valid-example1.json"
+  ],
+  "$.paths./.get.responses.300.schema": "./external-examples-valid-example1.json"
+}
diff --git a/test/data/v2/output-map-external-examples.js b/test/data/v2/output-map-external-examples.js
index 2853631..d0aab21 100644
--- a/test/data/v2/output-map-external-examples.js
+++ b/test/data/v2/output-map-external-examples.js
@@ -3,7 +3,7 @@ module.exports = {
     text: `Validating with mapping file
 Schemas with examples found: 2
 Examples without schema found: 0
-Total examples found: 0
+Total examples found: 3
 Matching mapping files found: 1
 
 Errors found.
diff --git a/test/specs/cli.js b/test/specs/cli.js
index 4c36e7f..23f5439 100644
--- a/test/specs/cli.js
+++ b/test/specs/cli.js
@@ -1,6 +1,6 @@
 const VERSION = require('../../package').version,
-    { text: textHelp } = require('../data/output-help'),
-    { text: textMapExternalExamples } = require('../data/v2/output-map-external-examples'),
+    { text: _textHelp } = require('../data/output-help'),
+    { text: _textMapExternalExamples } = require('../data/v2/output-map-external-examples'),
     exec = require('child_process').exec,
     {
         getPathOfTestData
@@ -23,7 +23,7 @@ describe('CLI-module', function() {
     describe('help', function() {
         it('should show the right text', function(done) {
             exec(`${ CMD__RUN } --help`, (err, stdout, stderr) => {
-                stdout.should.equal(textHelp);
+                stdout.should.equal(_textHelp);
                 stderr.should.equal('');
                 done();
             });
@@ -31,49 +31,61 @@ describe('CLI-module', function() {
     });
     describe('file mappings and invalid examples', function() {
         it('should work with the source code', function(done) {
-            exec(`${ CMD__RUN } -m ${ getPathOfTestData('v2/map-external-examples') } `
-                + `-c ${ getPathOfTestData('v2/external-examples-schema') }`, (err, stdout, stderr) => {
-                stdout.should.equal(textMapExternalExamples);
-                stderr.should.not.equal('');
-                done();
-            });
+            const pathMapExamples = getPathOfTestData('v2/map-external-examples-relative-invalid'),
+                pathSchema = getPathOfTestData('v2/external-examples-schema');
+            exec(`${ CMD__RUN } -m ${ pathMapExamples } -c ${ pathSchema }`,
+                (err, stdout, stderr) => {
+                    stdout.should.equal(_textMapExternalExamples);
+                    stderr.should.not.equal('');
+                    done();
+                }
+            );
         });
         it('should work with the built code', function(done) {
-            exec(`${ CMD__RUN_BUILT } -m ${ getPathOfTestData('v2/map-external-examples') } `
-                + `-c ${ getPathOfTestData('v2/external-examples-schema') }`, (err, stdout, stderr) => {
-                stdout.should.equal(textMapExternalExamples);
-                stderr.should.not.equal('');
-                done();
-            });
+            const pathMapExamples = getPathOfTestData('v2/map-external-examples-relative-invalid'),
+                pathSchema = getPathOfTestData('v2/external-examples-schema');
+            exec(`${ CMD__RUN_BUILT } -m ${ pathMapExamples } -c ${ pathSchema }`,
+                (err, stdout, stderr) => {
+                    stdout.should.equal(_textMapExternalExamples);
+                    stderr.should.not.equal('');
+                    done();
+                }
+            );
         });
     });
     describe('with valid examples', function() {
         it('should write to stdout, but not into stderr', function(done) {
-            exec(`${ CMD__RUN } ${ getPathOfTestData('v2/simple-example') }`, (err, stdout, stderr) => {
-                stdout.should.not.equal('');
-                stderr.should.equal('');
-                done();
-            });
+            exec(`${ CMD__RUN } ${ getPathOfTestData('v2/simple-example') }`,
+                (err, stdout, stderr) => {
+                    stdout.should.not.equal('');
+                    stderr.should.equal('');
+                    done();
+                }
+            );
         });
     });
     describe('with invalid examples', function() {
         it('should write to stdout and stderr', function(done) {
-            exec(`${ CMD__RUN } ${ getPathOfTestData('v2/multiple-errors') }`, (err, stdout, stderr) => {
-                stdout.should.not.equal('');
-                stderr.should.not.equal('');
-                done();
-            });
+            exec(`${ CMD__RUN } ${ getPathOfTestData('v2/multiple-errors') }`,
+                (err, stdout, stderr) => {
+                    stdout.should.not.equal('');
+                    stderr.should.not.equal('');
+                    done();
+                }
+            );
         });
     });
     describe('single external example', function() {
         it('should have no error', function(done) {
-            exec(`${ CMD__RUN } -s ${ JSON_PATH__SCHEMA } `
-                + `-e ${ getPathOfTestData('v2/external-examples-valid-example1') } `
-                + `${ getPathOfTestData('v2/external-examples-schema') }`, (err, stdout, stderr) => {
-                stdout.should.not.equal('');
-                stderr.should.equal('');
-                done();
-            });
+            const pathMapExamples = getPathOfTestData('v2/external-examples-valid-example1'),
+                pathSchema = getPathOfTestData('v2/external-examples-schema');
+            exec(`${ CMD__RUN } -s ${ JSON_PATH__SCHEMA } -e ${ pathMapExamples } ${ pathSchema }`,
+                (err, stdout, stderr) => {
+                    stdout.should.not.equal('');
+                    stderr.should.equal('');
+                    done();
+                }
+            );
         });
     });
 });
diff --git a/test/specs/impl/v2/index.js b/test/specs/impl/v2/index.js
index 342e310..65f0f80 100644
--- a/test/specs/impl/v2/index.js
+++ b/test/specs/impl/v2/index.js
@@ -326,7 +326,7 @@ describe('Main-module, for v2 should', () => {
         it('should collect the statistics over all mapping-files', async() => {
             (await validateExamplesByMap(FILE_PATH__EXTERNAL_EXAMPLES_SCHEMA, FILE_PATH__EXTERNAL_EXAMPLES_GLOB))
                 .statistics.should.deep.equal({
-                    schemasWithExamples: 4,
+                    schemasWithExamples: 2,
                     examplesWithoutSchema: 0,
                     examplesTotal: 7,
                     matchingFilePathsMapping: 2