Files
fastapi-openapi-to-postman/test/unit/validator.test.js
Luis Tejeda ad78625271 Add support for url with fragment and servers
Add support for url with fragment and serversAdd support for url with fragment and serversAdd support for url with fragment and servers
2022-07-13 14:39:23 -05:00

1351 lines
60 KiB
JavaScript

var expect = require('chai').expect,
Converter = require('../../index.js'),
fs = require('fs'),
path = require('path'),
async = require('async'),
_ = require('lodash'),
schemaUtils = require('../../lib/schemaUtils'),
VALIDATION_DATA_FOLDER_PATH = '../data/validationData',
VALIDATION_DATA_OPTIONS_FOLDER_31_PATH = '../data/31CollectionTransactions/validateOptions',
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH = '../data/31CollectionTransactions/validate30Scenarios',
VALID_OPENAPI_FOLDER_PATH = '../data/valid_openapi';
/**
* Extract all transaction from collection and appends them into array
*
* @param {*} collection - Postman Collection
* @param {*} allRequests - Array to which transactions are appended
* @returns {*} - null
*/
function getAllTransactions (collection, allRequests) {
if (!_.has(collection, 'item') || !_.isArray(collection.item)) {
return;
}
_.forEach(collection.item, (item) => {
if (_.has(item, 'request') || _.has(item, 'response')) {
allRequests.push(item);
}
else {
getAllTransactions(item, allRequests);
}
});
}
/**
* Gets an array of objects with the specification file path and the version
* @param {array} foldersByVersion An array with the path of the folders that contain the validation test files
* @param {*} fileName the name of the file (all scenarios must be in both folders)
* @returns {array} An array with objects that contains the version and the specification file path
*/
function getSpecsPathByVersion(foldersByVersion, fileName) {
return foldersByVersion.map((folderData) => {
return {
path: path.join(__dirname, folderData.path + fileName),
version: folderData.version
};
});
}
/**
* Returns an array with objects with the version and the folder where the files are
* @param {string} folder30Path the path of the 3.0 spec validation files folder
* @param {*} folder31Path the path of the 3.1 spec validation files folder
* @returns {array} An array with objects that contain the version and the corresponding files folder
*/
function getFoldersByVersion(folder30Path, folder31Path) {
return [{
version: '3.0',
path: folder30Path
},
{
version: '3.1',
path: folder31Path
}];
}
describe('Validate with servers', function () {
it('Fix for GITHUB#497: Should identify url with fragment', function () {
const openAPI = path.join(__dirname, VALID_OPENAPI_FOLDER_PATH + '/explicit_server_in_path.json'),
openAPIData = fs.readFileSync(openAPI, 'utf8'),
options = {
requestParametersResolution: 'Example',
exampleParametersResolution: 'Example',
showMissingInSchemaErrors: true,
strictRequestMatching: true,
ignoreUnresolvedVariables: true,
validateMetadata: true,
suggestAvailableFixes: true,
detailedBlobValidation: false
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: openAPIData }, options);
schemaPack.convert((err, conversionResult) => {
expect(err).to.be.null;
expect(conversionResult.result).to.equal(true);
let historyRequest = [];
getAllTransactions(conversionResult.output[0].data, historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
let requestIds = Object.keys(result.requests);
expect(err).to.be.null;
expect(result.missingEndpoints.length).to.eq(0);
requestIds.forEach((requestId) => {
expect(result.requests[requestId].endpoints[0]).to.not.be.undefined;
expect(result.requests[requestId].endpoints[0].matched).to.be.true;
});
});
});
});
});
describe('Validation with different resolution parameters options', function () {
it('Should validate correctly with request and example parameters as Schema', function () {
let fileData = fs.readFileSync(path.join(__dirname, VALID_OPENAPI_FOLDER_PATH,
'/issue#479_2.yaml'), 'utf8'),
expectedRequestBody =
'{"data":[{"entityId":"<string>","user":{"id":"<long>","age":"<integer>","created_at":"<dateTime>"},' +
'"isFavorite":"<integer>","needThis":"<string>"},' +
'{"entityId":"<string>","user":{"id":"<long>","age":"<integer>","created_at":"<dateTime>"},' +
'"isFavorite":"<integer>","needThis":"<string>"}]}',
expectedResponseBody =
'[{"id":"<long>","name":"<string>","tag":"<string>","created_at":"<dateTime>","birthday":"<date>"' +
',"floatField":"<float>","doubleField":"<double>","content":"<byte>","file":"<binary>",' +
'"root_pass":"<password>"},' +
'{"id":"<long>","name":"<string>","tag":"<string>","created_at":"<dateTime>","birthday":"<date>"' +
',"floatField":"<float>","doubleField":"<double>","content":"<byte>","file":"<binary>",' +
'"root_pass":"<password>"}]',
options = {
requestParametersResolution: 'Schema',
exampleParametersResolution: 'Schema',
showMissingInSchemaErrors: true,
strictRequestMatching: true,
ignoreUnresolvedVariables: true,
validateMetadata: true,
suggestAvailableFixes: true,
detailedBlobValidation: true
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: fileData }, options);
schemaPack.convert((err, conversionResult) => {
expect(err).to.be.null;
expect(conversionResult.result).to.equal(true);
let historyRequest = [];
getAllTransactions(conversionResult.output[0].data, historyRequest);
const fixedResponseBody = historyRequest[0].response[0].body.replace(/\s/g, ''),
fixedRequestBody = historyRequest[0].request.body.raw.replace(/\s/g, '');
expect(fixedResponseBody).to.equal(expectedResponseBody);
expect(fixedRequestBody).to.equal(expectedRequestBody);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
let requestIds = Object.keys(result.requests);
expect(err).to.be.null;
requestIds.forEach((requestId) => {
expect(result.requests[requestId].endpoints[0].matched).to.be.true;
const responsesIds = Object.keys(result.requests[requestId].endpoints[0].responses);
responsesIds.forEach((responseId) => {
expect(result.requests[requestId].endpoints[0].responses[responseId].matched).to.be.true;
});
});
});
});
});
it('Should validate correctly with request as schema and example parameters as Example', function () {
let fileData = fs.readFileSync(path.join(__dirname, VALID_OPENAPI_FOLDER_PATH,
'/issue#479_2.yaml'), 'utf8'),
expectedBody =
'{"data":[{"entityId":"<string>","user":{"id":"<long>","age":"<integer>","created_at":"<dateTime>"},' +
'"isFavorite":"<integer>","needThis":"<string>"},' +
'{"entityId":"<string>","user":{"id":"<long>","age":"<integer>","created_at":"<dateTime>"},' +
'"isFavorite":"<integer>","needThis":"<string>"}]}',
options = {
requestParametersResolution: 'Schema',
exampleParametersResolution: 'Example',
showMissingInSchemaErrors: true,
strictRequestMatching: true,
ignoreUnresolvedVariables: true,
validateMetadata: true,
suggestAvailableFixes: true,
detailedBlobValidation: true
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: fileData }, options);
schemaPack.convert((err, conversionResult) => {
expect(err).to.be.null;
expect(conversionResult.result).to.equal(true);
let historyRequest = [];
getAllTransactions(conversionResult.output[0].data, historyRequest);
const fixedBody = historyRequest[0].request.body.raw.replace(/\s/g, '');
expect(fixedBody).to.equal(expectedBody);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
let requestIds = Object.keys(result.requests);
expect(err).to.be.null;
requestIds.forEach((requestId) => {
expect(result.requests[requestId].endpoints[0].matched).to.be.true;
const responsesIds = Object.keys(result.requests[requestId].endpoints[0].responses);
responsesIds.forEach((responseId) => {
expect(result.requests[requestId].endpoints[0].responses[responseId].matched).to.be.true;
});
});
});
});
});
it('Should validate correctly with request as Example and example parameters as Schema', function () {
let fileData = fs.readFileSync(path.join(__dirname, VALID_OPENAPI_FOLDER_PATH,
'/issue#479_2.yaml'), 'utf8'),
expectedResponseBody =
'[{"id":"<long>","name":"<string>","tag":"<string>","created_at":"<dateTime>","birthday":"<date>"' +
',"floatField":"<float>","doubleField":"<double>","content":"<byte>","file":"<binary>",' +
'"root_pass":"<password>"},' +
'{"id":"<long>","name":"<string>","tag":"<string>","created_at":"<dateTime>","birthday":"<date>"' +
',"floatField":"<float>","doubleField":"<double>","content":"<byte>","file":"<binary>",' +
'"root_pass":"<password>"}]',
options = {
requestParametersResolution: 'Example',
exampleParametersResolution: 'Schema',
showMissingInSchemaErrors: true,
strictRequestMatching: true,
ignoreUnresolvedVariables: true,
validateMetadata: true,
suggestAvailableFixes: true,
detailedBlobValidation: true
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: fileData }, options);
schemaPack.convert((err, conversionResult) => {
expect(err).to.be.null;
expect(conversionResult.result).to.equal(true);
let historyRequest = [];
getAllTransactions(conversionResult.output[0].data, historyRequest);
const fixedResponseBody = historyRequest[0].response[0].body.replace(/\s/g, '');
expect(fixedResponseBody).to.equal(expectedResponseBody);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
let requestIds = Object.keys(result.requests);
expect(err).to.be.null;
requestIds.forEach((requestId) => {
expect(result.requests[requestId].endpoints[0].matched).to.be.true;
const responsesIds = Object.keys(result.requests[requestId].endpoints[0].responses);
responsesIds.forEach((responseId) => {
expect(result.requests[requestId].endpoints[0].responses[responseId].matched).to.be.true;
});
});
});
});
});
it('Should validate correctly with request and example parameters as Example', function () {
let fileData = fs.readFileSync(path.join(__dirname, VALID_OPENAPI_FOLDER_PATH,
'/issue#479_2.yaml'), 'utf8'),
options = {
requestParametersResolution: 'Example',
exampleParametersResolution: 'Example',
showMissingInSchemaErrors: true,
strictRequestMatching: true,
ignoreUnresolvedVariables: true,
validateMetadata: true,
suggestAvailableFixes: true,
detailedBlobValidation: true
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: fileData }, options);
schemaPack.convert((err, conversionResult) => {
expect(err).to.be.null;
expect(conversionResult.result).to.equal(true);
let historyRequest = [];
getAllTransactions(conversionResult.output[0].data, historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
let requestIds = Object.keys(result.requests);
expect(err).to.be.null;
requestIds.forEach((requestId) => {
expect(result.requests[requestId].endpoints[0].matched).to.be.true;
const responsesIds = Object.keys(result.requests[requestId].endpoints[0].responses);
responsesIds.forEach((responseId) => {
expect(result.requests[requestId].endpoints[0].responses[responseId].matched).to.be.true;
});
});
});
});
});
});
describe('The validator must validate generated collection from schema against schema itself', function () {
var validOpenapiFolder = fs.readdirSync(path.join(__dirname, VALID_OPENAPI_FOLDER_PATH)),
suggestedFixProps = ['key', 'actualValue', 'suggestedValue'],
checkMismatch = (mismatch) => {
expect(['REQUEST_NAME', 'REQUEST_DESCRIPTION', 'PATHVARIABLE', 'QUERYPARAM', 'HEADER', 'RESPONSE_HEADER',
'BODY', 'RESPONSE_BODY', 'ENDPOINT']).to.include(mismatch.property);
expect(mismatch).to.include.keys('transactionJsonPath');
expect(mismatch).to.include.keys('schemaJsonPath');
expect(mismatch.reason).to.be.a('string');
expect(['MISSING_IN_REQUEST', 'INVALID_TYPE', 'MISSING_IN_SCHEMA', 'INVALID_VALUE', 'INVALID_BODY',
'INVALID_RESPONSE_BODY', 'BODY_SCHEMA_NOT_FOUND', 'MISSING_ENDPOINT']).to.include(mismatch.reasonCode);
};
async.each(validOpenapiFolder, function (file, cb) {
it('correctly for schema: ' + file, function () {
let fileData = fs.readFileSync(path.join(__dirname, VALID_OPENAPI_FOLDER_PATH + '/' + file), 'utf8'),
options = {
requestParametersResolution: 'Example',
exampleParametersResolution: 'Example',
showMissingInSchemaErrors: true,
strictRequestMatching: true,
ignoreUnresolvedVariables: true,
validateMetadata: true,
suggestAvailableFixes: true,
detailedBlobValidation: false
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: fileData }, options);
// Increase timeout for larger schema
this.timeout(30000);
schemaPack.convert((err, conversionResult) => {
expect(err).to.be.null;
expect(conversionResult.result).to.equal(true);
let historyRequest = [];
getAllTransactions(conversionResult.output[0].data, historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
// check for result.requests structure
_.forEach(result.requests, (req) => {
expect(req.requestId).to.be.a('string');
_.forEach(req.endpoints, (endpoint) => {
expect(endpoint.matched).to.be.a('boolean');
expect(endpoint.endpointMatchScore).to.be.a('number');
expect(endpoint.endpoint).to.be.a('string');
expect(endpoint.endpoint).to.be.a('string');
_.forEach(endpoint.mismatches, (mismatch) => {
// console.log(file, ' ----------');
// console.log(JSON.stringify(mismatch, null, 2));
checkMismatch(mismatch);
if (mismatch.suggestedFix) {
expect(mismatch.suggestedFix).to.have.all.keys(suggestedFixProps);
}
});
_.forEach(endpoint.responses, (response) => {
expect(response.id).to.be.a('string');
expect(response.matched).to.be.a('boolean');
_.forEach(response.mismatches, (mismatch) => {
// console.log(file, ' **********');
// console.log(JSON.stringify(mismatch, null, 2));
checkMismatch(mismatch);
if (mismatch.suggestedFix) {
expect(mismatch.suggestedFix).to.have.all.keys(suggestedFixProps);
}
});
});
});
});
// check for result.missingEndpoints structure
_.forEach(result.missingEndpoints, (endpoint) => {
checkMismatch(endpoint);
expect(endpoint.property).to.eql('ENDPOINT');
if (endpoint.suggestedFix) {
expect(endpoint.suggestedFix).to.have.all.keys(suggestedFixProps);
}
});
return cb(null);
});
});
});
});
});
describe('The Validation option', function () {
const strictRequestMatchingSpecs = getSpecsPathByVersion(
getFoldersByVersion(VALIDATION_DATA_FOLDER_PATH, VALIDATION_DATA_OPTIONS_FOLDER_31_PATH),
'/strictRequestMatchingSpec.yaml'
),
strictRequestMatchingCollection = path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/strictRequestMatchingCollection.json'),
ignoreUnresolvedVariablesSpecs = getSpecsPathByVersion(
getFoldersByVersion(VALIDATION_DATA_FOLDER_PATH, VALIDATION_DATA_OPTIONS_FOLDER_31_PATH),
'/ignoreUnresolvedVariablesSpec.yaml'),
ignoreUnresolvedVariablesCollection = path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/ignoreUnresolvedVariablesCollection.json'),
validateMetadataSpecs = getSpecsPathByVersion(
getFoldersByVersion(VALIDATION_DATA_FOLDER_PATH, VALIDATION_DATA_OPTIONS_FOLDER_31_PATH),
'/validateMetadataSpec.yaml'),
validateMetadataCollection = path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/validateMetadataCollection.json'),
suggestAvailableFixesSpecs = getSpecsPathByVersion(
getFoldersByVersion(VALIDATION_DATA_FOLDER_PATH, VALIDATION_DATA_OPTIONS_FOLDER_31_PATH),
'/suggestAvailableFixesSpec.yaml'),
suggestAvailableFixesCollection = path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/suggestAvailableFixesCollection.json'),
detailedBlobValidationSpecs = getSpecsPathByVersion(
getFoldersByVersion(VALIDATION_DATA_FOLDER_PATH, VALIDATION_DATA_OPTIONS_FOLDER_31_PATH),
'/detailedBlobValidationSpec.yaml'),
detailedBlobValidationCollection = path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/detailedBlobValidationCollection.json');
describe('strictRequestMatching ', function () {
strictRequestMatchingSpecs.forEach((specData) => {
it('should strictly match collection request with corresponding schema endpoint/s - version: ' +
specData.version, function (done) {
const schema = fs.readFileSync(specData.path, 'utf8'),
collection = fs.readFileSync(strictRequestMatchingCollection, 'utf8'),
schemaPack = new Converter.SchemaPack({ type: 'string', data: schema },
{ strictRequestMatching: true }),
historyRequest = [];
getAllTransactions(JSON.parse(collection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
/**
Collection request contains
- GET /users/admin/:userId (userId = 12345)
- GET /users/admin/profile
Schema Endpoints contains
- GET /users/admin/profile
- GET /users/admin/{userId}
- GET /admin/{adminId}
For strictRequestMatching = false, both collection request matches with all 3 endpoints from schema,
and no endpoint will be present in missingEndpoints
For strictRequestMatching = true, we have matches as following
*/
// for endpoint "/users/admin/:userId" we should only one match with "/users/admin/{userId}"
expect(result.requests[historyRequest[0].id].endpoints).to.have.lengthOf(1);
// for endpoint "/users/admin/profile" we should have two matches first match with "/users/admin/profile"
// and second with "/users/admin/{userId}" as first match has more fixed matched segments
expect(result.requests[historyRequest[1].id].endpoints).to.have.lengthOf(2);
// endpoint "/admin/{adminId}"" should be present as missing endpoint
expect(result.missingEndpoints).to.have.lengthOf(1);
expect(result.missingEndpoints[0].endpoint).to.eql('GET /admin/{adminId}');
done();
});
});
});
});
describe('ignoreUnresolvedVariables ', function () {
ignoreUnresolvedVariablesSpecs.forEach((specData) => {
it('should ignore all mismatches happening due to collection/environment variables present in request ' +
'- version: ' + specData.version, function (done) {
var schema = fs.readFileSync(specData.path, 'utf8'),
collection = fs.readFileSync(ignoreUnresolvedVariablesCollection, 'utf8'),
schemaPack = new Converter.SchemaPack({ type: 'string', data: schema },
{ ignoreUnresolvedVariables: true }),
historyRequest = [];
getAllTransactions(JSON.parse(collection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
var reqResultObj;
expect(err).to.be.null;
/**
Schema/Collection tested here contains pm variables in all parts checked.
i.e. path variable, query params, headers, req/res body etc
*/
reqResultObj = result.requests[_.keys(result.requests)[0]];
expect(reqResultObj.endpoints).to.have.lengthOf(1);
// checking for no mismatches in req and responses
expect(reqResultObj.endpoints[0].matched).to.be.true;
expect(reqResultObj.endpoints[0].mismatches).to.have.lengthOf(0);
_.forEach(reqResultObj.endpoints[0].responses, (response) => {
expect(response.matched).to.be.true;
expect(response.mismatches).to.have.lengthOf(0);
});
done();
});
});
});
});
describe('validateMetadata ', function () {
validateMetadataSpecs.forEach((specData) => {
let schema = fs.readFileSync(specData.path, 'utf8'),
collection = fs.readFileSync(validateMetadataCollection, 'utf8'),
schemaPack = new Converter.SchemaPack({ type: 'string', data: schema },
{ validateMetadata: true, suggestAvailableFixes: true }),
historyRequest = [],
resultObj1,
resultObj2;
before(function (done) {
getAllTransactions(JSON.parse(collection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
resultObj1 = result.requests[historyRequest[0].id].endpoints[0];
resultObj2 = result.requests[historyRequest[1].id].endpoints[0];
done();
});
});
it('should validate request name and description according to schema - version: ' +
specData.version, function () {
expect(resultObj1.mismatches).to.have.lengthOf(2);
_.forEach(resultObj1.mismatches, (mismatch) => {
// check for suggested value to be one in schema
if (mismatch.property === 'REQUEST_NAME') {
expect(mismatch.reasonCode).to.eql('INVALID_VALUE');
expect(mismatch.reason).to.eql('The request name didn\'t match with specified schema');
expect(mismatch.suggestedFix.suggestedValue).to.eql('List all pets Updated');
}
else if (mismatch.property === 'REQUEST_DESCRIPTION') {
expect(mismatch.reasonCode).to.eql('INVALID_VALUE');
expect(mismatch.reason).to.eql('The request description didn\'t match with specified schema');
expect(mismatch.suggestedFix.suggestedValue).to.eql('Description for GET /pets - List all pets');
}
else {
throw Error('Unhandled mismatch property in test');
}
});
});
it('should handle empty and null request name and description in request - version: ' +
specData.version, function () {
expect(resultObj2.mismatches).to.have.lengthOf(1);
expect(resultObj2.mismatches[0].property).to.eql('REQUEST_DESCRIPTION');
expect(resultObj2.mismatches[0].reasonCode).to.eql('INVALID_VALUE');
expect(resultObj2.mismatches[0].reason).to.eql('The request description didn\'t match with specified schema');
expect(resultObj2.mismatches[0].suggestedFix.actualValue).to.be.null;
expect(resultObj2.mismatches[0].suggestedFix.suggestedValue)
.to.eql('Description for POST /pets - Create a pet');
});
});
});
describe('suggestAvailableFixes ', function () {
suggestAvailableFixesSpecs.forEach((specData) => {
let schema = fs.readFileSync(specData.path, 'utf8'),
collection = fs.readFileSync(suggestAvailableFixesCollection, 'utf8'),
schemaPack = new Converter.SchemaPack({ type: 'string', data: schema },
{ suggestAvailableFixes: true }),
historyRequest = [],
resultObj,
responseResult,
propertyMismatchMap = {};
before(function (done) {
getAllTransactions(JSON.parse(collection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
resultObj = result.requests[historyRequest[0].id].endpoints[0];
responseResult = resultObj.responses[historyRequest[0].response[0].id];
// check for expected mismatches length
expect(resultObj.mismatches).to.have.lengthOf(4);
expect(responseResult.mismatches).to.have.lengthOf(2);
// map all mismatch objects with it's property
_.forEach(_.concat(resultObj.mismatches, responseResult.mismatches), (mismatch) => {
propertyMismatchMap[mismatch.property] = mismatch;
});
done();
});
});
it('should suggest valid available fix for all kind of violated properties - version: ' +
specData.version, function () {
// check for all suggested value to be according to schema
expect(_.isInteger(propertyMismatchMap.PATHVARIABLE.suggestedFix.suggestedValue)).to.eql(true);
expect(propertyMismatchMap.QUERYPARAM.suggestedFix.suggestedValue).to.be.a('number');
expect(propertyMismatchMap.HEADER.suggestedFix.suggestedValue.value).to.be.a('boolean');
expect(propertyMismatchMap.HEADER.suggestedFix.suggestedValue.description)
.to.eql('(Required) Quantity of pets available');
expect(propertyMismatchMap.BODY.suggestedFix.suggestedValue.name).to.be.a('string');
expect(propertyMismatchMap.BODY.suggestedFix.suggestedValue.name.length >= 30).to.eql(true);
expect(propertyMismatchMap.BODY.suggestedFix.suggestedValue.tag).to.be.a('string');
expect(_.includes(['Bulldog', 'Retriever', 'Timberwolf', 'Grizzly', 'Husky'],
propertyMismatchMap.BODY.suggestedFix.suggestedValue.breeds[2])).to.eql(true);
expect(_.isInteger(propertyMismatchMap.RESPONSE_HEADER.suggestedFix.suggestedValue)).to.eql(true);
expect(_.isInteger(propertyMismatchMap.RESPONSE_BODY.suggestedFix.suggestedValue.code)).to.eql(true);
expect(propertyMismatchMap.RESPONSE_BODY.suggestedFix.suggestedValue.code % 7).to.equal(0);
expect(propertyMismatchMap.RESPONSE_BODY.suggestedFix.suggestedValue.message).to.be.a('string');
});
it('should maintain valid properties/items in suggested value - version:' +
specData.version, function () {
expect(propertyMismatchMap.BODY.suggestedFix.suggestedValue.petId).to.eql(
propertyMismatchMap.BODY.suggestedFix.actualValue.petId
);
expect(propertyMismatchMap.BODY.suggestedFix.suggestedValue.breeds[0]).to.eql(
propertyMismatchMap.BODY.suggestedFix.actualValue.breeds[0]
);
expect(propertyMismatchMap.BODY.suggestedFix.suggestedValue.breeds[1]).to.eql(
propertyMismatchMap.BODY.suggestedFix.actualValue.breeds[1]
);
});
});
});
describe('detailedBlobValidation ', function () {
detailedBlobValidationSpecs.forEach((specData) => {
it('should provide detailed mismatches for each schema keyword violation - version:' +
specData.version, function (done) {
var schema = fs.readFileSync(specData.path, 'utf8'),
collection = fs.readFileSync(detailedBlobValidationCollection, 'utf8'),
schemaPack = new Converter.SchemaPack({ type: 'string', data: schema },
{ detailedBlobValidation: true }),
historyRequest = [],
resultObj,
violatedKeywords = {
'data.items.minProperties': '$.request.body.data[0]',
'data.items.required': '$.request.body.data[0]',
'data.items.properties.entityId.maxLength': '$.request.body.data[0].entityId',
'data.items.properties.accountNumber.minLength': '$.request.body.data[0].accountNumber',
'data.items.properties.entityName.format': '$.request.body.data[0].entityName',
'data.items.properties.incType.enum': '$.request.body.data[0].incType',
'data.items.properties.companyNumber.exclusiveMinimum': '$.request.body.data[0].companyNumber',
'data.items.properties.website.type': '$.request.body.data[0].website',
'data.items.properties.turnover.multipleOf': '$.request.body.data[0].turnover',
'data.items.properties.description.pattern': '$.request.body.data[0].description',
'data.items.properties.wants.uniqueItems': '$.request.body.data[0].wants',
'data.items.properties.user.properties.entityId.maxLength': '$.request.body.data[0].user.entityId',
'meta.maxProperties': '$.request.body.meta',
'meta.additionalProperties': '$.request.body.meta'
};
getAllTransactions(JSON.parse(collection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
resultObj = result.requests[historyRequest[0].id].endpoints[0];
// map all mismatch objects with it's property
_.forEach(resultObj.mismatches, (mismatch) => {
// remove starting string '$.paths[/user].post.requestBody.content[application/json].schema.properties.'
let localJsonPath = mismatch.schemaJsonPath.slice(76);
expect(_.includes(_.keys(violatedKeywords), localJsonPath)).to.eql(true);
expect(_.includes(_.values(violatedKeywords), mismatch.transactionJsonPath)).to.eql(true);
// mark matched path as empty to ensure repetition does'n occur
violatedKeywords[_.indexOf(violatedKeywords, localJsonPath)] = '';
});
done();
});
});
});
});
});
describe('VALIDATE FUNCTION TESTS ', function () {
describe('validateTransaction function', function () {
const emptyParameterSpecs = getSpecsPathByVersion(
getFoldersByVersion(
VALIDATION_DATA_FOLDER_PATH,
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH
),
'/emptyParameterSpec.yaml'
),
implicitHeadersSpecs = getSpecsPathByVersion(
getFoldersByVersion(
VALIDATION_DATA_FOLDER_PATH,
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH
),
'/implicitHeaderSpec.yaml'
),
rootKeywordViolationSpecs = getSpecsPathByVersion(
getFoldersByVersion(
VALIDATION_DATA_FOLDER_PATH,
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH
),
'/rootKeywordViolationSpec.yaml'
),
doubleValidationFixSpecs = getSpecsPathByVersion(
getFoldersByVersion(
VALIDATION_DATA_FOLDER_PATH,
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH
),
'/doubleValidationFixSpec.yaml'
),
internalRefsSpecs = getSpecsPathByVersion(
getFoldersByVersion(
VALIDATION_DATA_FOLDER_PATH,
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH
),
'/internalRefsSpec.yaml'
),
differentContentTypesSpecs = getSpecsPathByVersion(
getFoldersByVersion(
VALIDATION_DATA_FOLDER_PATH,
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH
),
'/differentContentTypesSpec.yaml'
),
primitiveDataTypeBodySpecs = getSpecsPathByVersion(
getFoldersByVersion(
VALIDATION_DATA_FOLDER_PATH,
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH
),
'/primitiveDataTypeBodySpec.yaml'
),
multiplePathVarSpecs = getSpecsPathByVersion(
getFoldersByVersion(
VALIDATION_DATA_FOLDER_PATH,
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH
),
'/multiplePathVarSpec.json'
),
nestedObjectParamsSpecs = getSpecsPathByVersion(
getFoldersByVersion(
VALIDATION_DATA_FOLDER_PATH,
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH
),
'/nestedObjectParamsSpec.yaml'
),
queryParamDeepObjectSpecs = getSpecsPathByVersion(
getFoldersByVersion(
VALIDATION_DATA_FOLDER_PATH,
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH
),
'/queryParamDeepObjectSpec.yaml'
),
compositeSchemaSpecs = getSpecsPathByVersion(
getFoldersByVersion(
VALIDATION_DATA_FOLDER_PATH,
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH
),
'/compositeSchemaSpec.yaml'
),
invalidTypeProperty = getSpecsPathByVersion(
getFoldersByVersion(
VALIDATION_DATA_FOLDER_PATH,
VALIDATION_DATA_SCENARIOS_FOLDER_31_PATH
),
'/invalidTypeProperty.yaml'
);
emptyParameterSpecs.forEach((specData) => {
it('Should not fail if spec to validate contains empty parameters - version:' +
specData.version, function (done) {
let emptyParameterSpec = fs.readFileSync(specData.path, 'utf-8'),
emptyParameterCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/emptyParameterCollection.json'), 'utf-8'),
resultObj,
historyRequest = [],
schemaPack = new Converter.SchemaPack({ type: 'string', data: emptyParameterSpec }, {});
getAllTransactions(JSON.parse(emptyParameterCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
// Schema is sample petsore with one of parameter as empty, expect no mismatch / error
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj = result.requests[historyRequest[0].id].endpoints[0];
expect(resultObj.mismatches).to.have.lengthOf(0);
done();
});
});
});
implicitHeadersSpecs.forEach((specData) => {
it('Should correctly handle transactionPath property when Implicit headers are present - version:' +
specData.version, function (done) {
let implicitHeaderSpec = fs.readFileSync(specData.path, 'utf-8'),
implicitHeaderCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/implicitHeaderCollection.json'), 'utf-8'),
resultObj,
historyRequest = [],
schemaPack = new Converter.SchemaPack({ type: 'string', data: implicitHeaderSpec }, {});
getAllTransactions(JSON.parse(implicitHeaderCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj = result.requests[historyRequest[0].id].endpoints[0];
expect(resultObj.mismatches).to.have.lengthOf(1);
/**
header-1 is invalid according to schema, as request contains other 2 implicit headers(Content-Type and Accept)
the mismatch for header-1 should contain correct index as in request.
*/
expect(_.endsWith(resultObj.mismatches[0].transactionJsonPath, '[2].value')).to.eql(true);
_.forEach(resultObj.responses, (response) => {
expect(response.matched).to.be.true;
expect(response.mismatches).to.have.lengthOf(0);
});
done();
});
});
});
rootKeywordViolationSpecs.forEach((specData) => {
it('Should correctly suggest value when violated keyword is at root level - version: ' +
specData.version, function (done) {
let rootKeywordViolationSpec = fs.readFileSync(specData.path, 'utf-8'),
rootKeywordViolationCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/rootKeywordViolationCollection.json'), 'utf-8'),
options = { suggestAvailableFixes: true },
resultObj,
historyRequest = [],
schemaPack = new Converter.SchemaPack({ type: 'string', data: rootKeywordViolationSpec }, options);
getAllTransactions(JSON.parse(rootKeywordViolationCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj = result.requests[historyRequest[0].id].endpoints[0];
expect(resultObj.mismatches).to.have.lengthOf(1);
/**
The spec contains "POST /pet" endpoint with request body as Pet object which requires minimum property of 4
as this property is root (Json path to prop is ''(empty), we expect suggested value to be according to spec)
*/
expect(_.keys(resultObj.mismatches[0].suggestedFix.actualValue)).to.have.lengthOf(3);
expect(_.keys(resultObj.mismatches[0].suggestedFix.suggestedValue)).to.have.lengthOf(4);
done();
});
});
});
doubleValidationFixSpecs.forEach((specData) => {
it('Should correctly suggest value when property is vioalting multiple keywords - version: ' +
specData.version, function (done) {
let doubleValidationFixSpec = fs.readFileSync(specData.path, 'utf-8'),
options = { requestParametersResolution: 'Example', suggestAvailableFixes: true },
resultObj,
schemaPack = new Converter.SchemaPack({ type: 'string', data: doubleValidationFixSpec }, options);
schemaPack.convert((err, conversionResult) => {
expect(err).to.be.null;
expect(conversionResult.result).to.equal(true);
let historyRequest = [];
getAllTransactions(conversionResult.output[0].data, historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj = result.requests[historyRequest[0].id].endpoints[0];
expect(resultObj.mismatches).to.have.lengthOf(1);
/**
The spec contains request body which has name and identifier props as required which is
violated in collection. We expect both props to be present and valid according to schema.
*/
expect(resultObj.mismatches[0].suggestedFix.suggestedValue).to.contain.keys(['name', 'identifier']);
expect(resultObj.mismatches[0].suggestedFix.suggestedValue.name).to.be.a('string');
expect(resultObj.mismatches[0].suggestedFix.suggestedValue.identifier).to.be.a('string');
done();
});
});
});
});
internalRefsSpecs.forEach((specData) => {
it('Should correctly handle internal $ref when present - version: ' +
specData.version, function (done) {
let internalRefsSpec = fs.readFileSync(specData.path, 'utf-8'),
internalRefsCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/internalRefsCollection.json'), 'utf-8'),
resultObj,
options = {
showMissingInSchemaErrors: true,
strictRequestMatching: true,
ignoreUnresolvedVariables: true,
validateMetadata: true,
suggestAvailableFixes: true,
detailedBlobValidation: false
},
historyRequest = [],
schemaPack = new Converter.SchemaPack({ type: 'string', data: internalRefsSpec }, options);
getAllTransactions(JSON.parse(internalRefsCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
_.forEach(historyRequest, (hr) => {
resultObj = result.requests[hr.id].endpoints[0];
// no mismatches should be found when resolved correctly
expect(resultObj.matched).to.be.true;
expect(resultObj.mismatches).to.have.lengthOf(0);
_.forEach(resultObj.responses, (response) => {
expect(response.matched).to.be.true;
expect(response.mismatches).to.have.lengthOf(0);
});
});
done();
});
});
});
differentContentTypesSpecs.forEach((specData) => {
it('Should correctly match and validate content type headers having wildcard characters' +
' with collection req/res body', function (done) {
let differentContentTypesSpec = fs.readFileSync(specData.path, 'utf-8'),
differentContentTypesCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/differentContentTypesCollection.json'), 'utf-8'),
resultObj,
historyRequest = [],
options = {
showMissingInSchemaErrors: true,
suggestAvailableFixes: true
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: differentContentTypesSpec }, options);
getAllTransactions(JSON.parse(differentContentTypesCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj = result.requests[historyRequest[1].id].endpoints[0];
/**
* Both req and res body should have matched content types
*/
expect(resultObj.matched).to.eql(true);
expect(resultObj.mismatches).to.have.lengthOf(0);
expect(resultObj.responses[_.keys(resultObj.responses)[0]].matched).to.eql(true);
expect(resultObj.responses[_.keys(resultObj.responses)[0]].mismatches).to.have.lengthOf(0);
done();
});
});
it('Should correctly match and validate valid json content type with collection req/res body - version:' +
specData.version, function (done) {
let differentContentTypesSpec = fs.readFileSync(specData.path, 'utf-8'),
differentContentTypesCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/differentContentTypesCollection.json'), 'utf-8'),
resultObj,
historyRequest = [],
options = {
suggestAvailableFixes: true
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: differentContentTypesSpec }, options);
getAllTransactions(JSON.parse(differentContentTypesCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj = result.requests[historyRequest[0].id].endpoints[0];
/**
* Both req and res body should match with schema content object and each have one mismatch
*/
expect(resultObj.mismatches).to.have.lengthOf(1);
expect(resultObj.mismatches[0].property).to.equal('BODY');
expect(resultObj.responses[_.keys(resultObj.responses)[0]].mismatches).to.have.lengthOf(1);
expect(resultObj.responses[_.keys(resultObj.responses)[0]].mismatches[0].property).to.equal('RESPONSE_BODY');
done();
});
});
});
primitiveDataTypeBodySpecs.forEach((specData) => {
it('Should be able to validate and suggest correct value for body with primitive data type - version: ' +
specData.version, function (done) {
let primitiveDataTypeBodySpec = fs.readFileSync(specData.path, 'utf-8'),
primitiveDataTypeBodyCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/primitiveDataTypeBodyCollection.json'), 'utf-8'),
resultObj,
responseObj,
historyRequest = [],
options = {
showMissingInSchemaErrors: true,
strictRequestMatching: true,
ignoreUnresolvedVariables: true,
validateMetadata: true,
suggestAvailableFixes: true,
detailedBlobValidation: true
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: primitiveDataTypeBodySpec }, options);
getAllTransactions(JSON.parse(primitiveDataTypeBodyCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
// request body is boolean
resultObj = result.requests[historyRequest[0].id].endpoints[0];
expect(resultObj.mismatches).to.have.lengthOf(0);
const responseId = _.keys(resultObj.responses)[0];
// request body is integer
responseObj = resultObj.responses[responseId];
expect(responseObj.mismatches).to.have.lengthOf(1);
expect(responseObj.mismatches[0].suggestedFix.suggestedValue).to.be.within(5, 10);
expect(responseObj.mismatches[0].transactionJsonPath).to
.equal(`$.responses[${responseId}].body`);
done();
});
});
});
multiplePathVarSpecs.forEach((specData) => {
it('Should correctly validate path variable in collection that are part of URL itself and are ' +
'not present in $request.url.variable', function (done) {
let multiplePathVarSpec = fs.readFileSync(specData.path, 'utf-8'),
multiplePathVarCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/multiplePathVarCollection.json'), 'utf-8'),
resultObj,
historyRequest = [],
options = {
detailedBlobValidation: true,
allowUrlPathVarMatching: true
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: multiplePathVarSpec }, options);
getAllTransactions(JSON.parse(multiplePathVarCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj = result.requests[historyRequest[2].id].endpoints[0];
expect(resultObj.mismatches).to.have.lengthOf(0);
done();
});
});
it('Should correctly validate schema having path with various path variables - version: ' +
specData.version, function (done) {
let multiplePathVarSpec = fs.readFileSync(specData.path, 'utf-8'),
multiplePathVarCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/multiplePathVarCollection.json'), 'utf-8'),
resultObj1,
resultObj2,
historyRequest = [],
options = {
showMissingInSchemaErrors: true,
strictRequestMatching: true,
ignoreUnresolvedVariables: true,
suggestAvailableFixes: true
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: multiplePathVarSpec }, options);
getAllTransactions(JSON.parse(multiplePathVarCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj1 = result.requests[historyRequest[0].id].endpoints[0];
expect(resultObj1.mismatches).to.have.lengthOf(0);
resultObj2 = result.requests[historyRequest[1].id].endpoints[0];
expect(resultObj2.mismatches).to.have.lengthOf(1);
expect(resultObj2.mismatches[0].reasonCode).to.eql('MISSING_IN_REQUEST');
done();
});
});
});
nestedObjectParamsSpecs.forEach((specData) => {
it('Should ignore mismatches for nested objects in parameters - version: ' +
specData.version, function (done) {
let nestedObjectParamsSpec = fs.readFileSync(specData.path, 'utf-8'),
nestedObjectParamsCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/nestedObjectParamsCollection.json'), 'utf-8'),
resultObj,
historyRequest = [],
options = {
showMissingInSchemaErrors: true,
strictRequestMatching: true,
ignoreUnresolvedVariables: true,
suggestAvailableFixes: true
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: nestedObjectParamsSpec }, options);
getAllTransactions(JSON.parse(nestedObjectParamsCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj = result.requests[historyRequest[0].id].endpoints[0];
expect(resultObj.mismatches).to.have.lengthOf(0);
done();
});
});
});
queryParamDeepObjectSpecs.forEach((specData) => {
it('Should be able to validate schema with deepObject style query params against corresponding ' +
'transactions - version: ' + specData.version, function (done) {
let queryParamDeepObjectSpec = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/queryParamDeepObjectSpec.yaml'), 'utf-8'),
queryParamDeepObjectCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/queryParamDeepObjectCollection.json'), 'utf-8'),
resultObj,
historyRequest = [],
schemaPack = new Converter.SchemaPack({ type: 'string', data: queryParamDeepObjectSpec },
{ suggestAvailableFixes: true, showMissingInSchemaErrors: true });
getAllTransactions(JSON.parse(queryParamDeepObjectCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj = result.requests[historyRequest[0].id].endpoints[0];
expect(resultObj.mismatches).to.have.lengthOf(2);
/**
* no mismatches should be found for complex array type params as validation is skipped for them,
* even though corresponding value is of incorrect type
*/
_.forEach(resultObj.mismatches, (mismatch) => {
expect(mismatch.suggestedFix.key).to.not.eql('propArrayComplex[0][prop1ArrayComp]');
});
// for deepObject param "user", child param "user[id]" is of incorrect type
expect(resultObj.mismatches[0].reasonCode).to.eql('INVALID_TYPE');
expect(resultObj.mismatches[0].transactionJsonPath).to.eql('$.request.url.query[0].value');
expect(resultObj.mismatches[0].suggestedFix.actualValue).to.eql('notAnInteger');
expect(resultObj.mismatches[0].suggestedFix.suggestedValue).to.eql(123);
// for deepObject param "user", child param "user[address][country]" is missing in transaction
expect(resultObj.mismatches[1].reasonCode).to.eql('MISSING_IN_REQUEST');
expect(resultObj.mismatches[1].suggestedFix.key).to.eql('user[address][country]');
expect(resultObj.mismatches[1].suggestedFix.actualValue).to.be.null;
expect(resultObj.mismatches[1].suggestedFix.suggestedValue).to.eql({
key: 'user[address][country]',
value: 'India',
description: '(Required) info about user'
});
done();
});
});
});
compositeSchemaSpecs.forEach((specData) => {
it('Should be able to correctly validate composite schemas with anyOf, oneOf and allOf keywords correctly ' +
'against corresponding transactions - version: ' + specData.version, function (done) {
let compositeSchemaSpec = fs.readFileSync(specData.path, 'utf-8'),
compositeSchemaCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/compositeSchemaCollection.json'), 'utf-8'),
resultObjAnyOf,
resultObjOneOf,
resultObjAllOf,
historyRequest = [],
schemaPack = new Converter.SchemaPack({ type: 'string', data: compositeSchemaSpec },
{ suggestAvailableFixes: true, showMissingInSchemaErrors: true });
getAllTransactions(JSON.parse(compositeSchemaCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObjAnyOf = result.requests[historyRequest[0].id].endpoints[0];
resultObjOneOf = result.requests[historyRequest[1].id].endpoints[0];
resultObjAllOf = result.requests[historyRequest[2].id].endpoints[0];
/**
* no mismatches should be found here even though value present in collection
* is only valid as per 2nd element of anyOf keyword here
*/
expect(resultObjAnyOf.mismatches).to.have.lengthOf(0);
/**
* no mismatches should be found here even though key present in collection request body
* is only valid as per 2nd element of oneOf keyword here
*/
expect(resultObjOneOf.mismatches).to.have.lengthOf(0);
//
expect(resultObjAllOf.mismatches).to.have.lengthOf(1);
expect(resultObjAllOf.mismatches[0].reasonCode).to.eql('INVALID_BODY');
expect(resultObjAllOf.mismatches[0].transactionJsonPath).to.eql('$.request.body');
expect(resultObjAllOf.mismatches[0].schemaJsonPath).to
.eql('$.paths[/pets/allOf].post.requestBody.content[application/json].schema');
expect(resultObjAllOf.mismatches[0].suggestedFix.actualValue).to.eql({
objectType: 'not an integer',
objectType2: 'prop named objectType2'
});
expect(resultObjAllOf.mismatches[0].suggestedFix.suggestedValue).to.eql({
objectType: 4321,
objectType2: 'prop named objectType2'
});
done();
});
});
});
invalidTypeProperty.forEach((specData) => {
it('Should correctly suggest value and report transactionJsonPath on a body property with incorrect value ' +
specData.version, function (done) {
let invalidTypePropertySpec = fs.readFileSync(specData.path, 'utf-8'),
invalidTypePropertyCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/invalidTypeProperty.json'), 'utf-8'),
options = { suggestAvailableFixes: true, detailedBlobValidation: true },
resultObj,
historyRequest = [],
schemaPack = new Converter.SchemaPack({ type: 'string', data: invalidTypePropertySpec }, options);
getAllTransactions(JSON.parse(invalidTypePropertyCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj = result.requests[historyRequest[0].id].endpoints[0];
const responseId = _.keys(resultObj.responses)[0],
responseMissmatches = resultObj.responses[responseId].mismatches;
expect(responseMissmatches).to.have.lengthOf(2);
expect(responseMissmatches[0].transactionJsonPath)
.to.equal(`$.responses[${responseId}].body[0].tag`);
expect(responseMissmatches[0].suggestedFix.key).to.equal('tag');
expect(responseMissmatches[1].transactionJsonPath)
.to.equal(`$.responses[${responseId}].body[1].tag`);
expect(responseMissmatches[1].suggestedFix.key).to.equal('tag');
done();
});
});
});
});
describe('getPostmanUrlSuffixSchemaScore function', function () {
it('Should maintain correct order in which path vaiables occur in result', function (done) {
let pmSuffix = ['pets', '123', '456', '789'],
schemaPath = ['pets', '{petId1}', '{petId2}', '{petId3}'],
result;
result = schemaUtils.getPostmanUrlSuffixSchemaScore(pmSuffix, schemaPath, { strictRequestMatching: true });
expect(result.match).to.be.true;
expect(result.pathVars).to.have.lengthOf(3);
expect(result.pathVars[0]).to.deep.equal({ key: 'petId1', value: pmSuffix[1] });
expect(result.pathVars[1]).to.deep.equal({ key: 'petId2', value: pmSuffix[2] });
expect(result.pathVars[2]).to.deep.equal({ key: 'petId3', value: pmSuffix[3] });
done();
});
});
it('Should be able to validate schema with request body of content type "application/x-www-form-urlencoded" ' +
'against transaction with valid UrlEncoded body correctly', function (done) {
let urlencodedBodySpec = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/urlencodedBodySpec.yaml'), 'utf-8'),
urlencodedBodyCollection = fs.readFileSync(path.join(__dirname, VALIDATION_DATA_FOLDER_PATH +
'/urlencodedBodyCollection.json'), 'utf-8'),
resultObj,
historyRequest = [],
schemaPack = new Converter.SchemaPack({ type: 'string', data: urlencodedBodySpec },
{ suggestAvailableFixes: true, showMissingInSchemaErrors: true });
getAllTransactions(JSON.parse(urlencodedBodyCollection), historyRequest);
schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj = result.requests[historyRequest[0].id].endpoints[0];
expect(resultObj.mismatches).to.have.lengthOf(5);
/**
* no mismatches should be found for complex array type params as validation is skipped for them,
* even though corresponding value is of incorrect type
*/
_.forEach(resultObj.mismatches, (mismatch) => {
expect(mismatch.suggestedFix.key).to.not.eql('propArrayComplex[0][prop1ArrayComp]');
});
// for explodable property of type object named "propObjectExplodable",
// second property named "prop2" is incorrect, while property "prop1" is correct
expect(resultObj.mismatches[0].transactionJsonPath).to.eql('$.request.body.urlencoded[1].value');
expect(resultObj.mismatches[0].suggestedFix.actualValue).to.eql('false');
expect(resultObj.mismatches[0].suggestedFix.suggestedValue).to.eql('world');
// for non explodable property of type object, entire property with updated value should be suggested
expect(resultObj.mismatches[1].transactionJsonPath).to.eql('$.request.body.urlencoded[2].value');
expect(resultObj.mismatches[1].suggestedFix.actualValue).to.eql('prop3,hello,prop4,true');
expect(resultObj.mismatches[1].suggestedFix.suggestedValue).to.eql('prop3,hello,prop4,world');
// for type array property named "propArray" second element is incorrect
expect(resultObj.mismatches[2].transactionJsonPath).to.eql('$.request.body.urlencoded[4].value');
expect(resultObj.mismatches[2].suggestedFix.actualValue).to.eql('999');
expect(resultObj.mismatches[2].suggestedFix.suggestedValue).to.eql('exampleString');
// for deepObject property named "propDeepObject" child param "propDeepObject[address][city]" is of incorrect type
expect(resultObj.mismatches[3].transactionJsonPath).to.eql('$.request.body.urlencoded[8].value');
expect(resultObj.mismatches[3].suggestedFix.actualValue).to.eql('123');
expect(resultObj.mismatches[3].suggestedFix.suggestedValue).to.eql('Delhi');
// property named "propMissingInReq" is missing in request
expect(resultObj.mismatches[4].reasonCode).to.eql('MISSING_IN_REQUEST');
expect(resultObj.mismatches[4].suggestedFix.actualValue).to.eql(null);
expect(resultObj.mismatches[4].suggestedFix.suggestedValue.key).to.eql('propMissingInReq');
expect(resultObj.mismatches[4].suggestedFix.suggestedValue.description)
.to.eql('(Required) This property is not available in matched collection.');
done();
});
});
describe('findMatchingRequestFromSchema function', function () {
it('#GITHUB-9396 Should maintain correct order of matched endpoint', function (done) {
let schema = {
paths: {
'/lookups': {
'get': { 'summary': 'Lookup Job Values' }
},
'/{jobid}': {
'get': {
'summary': 'Get Job by ID',
'parameters': [
{
'in': 'path',
'name': 'jobid',
'schema': {
'type': 'string'
},
'required': true,
'description': 'Unique identifier for a job to retrieve.',
'example': '{{jobid}}'
}
]
}
}
}
},
schemaPath = '{{baseUrl}}/{{jobid}}',
result;
result = schemaUtils.findMatchingRequestFromSchema('GET', schemaPath, schema, { strictRequestMatching: true });
expect(result).to.have.lengthOf(2);
expect(result[0].name).to.eql('GET /{jobid}');
expect(result[1].name).to.eql('GET /lookups');
done();
});
});
});