Merge pull request #481 from postmanlabs/fix479/valueTypes

values like <integer> in validation
This commit is contained in:
Vishal Shingala
2022-03-14 21:06:54 +05:30
committed by GitHub
19 changed files with 1790 additions and 95 deletions

View File

@@ -1,4 +1,6 @@
const { formatDataPath } = require('../common/schemaUtilsCommon');
const { formatDataPath,
formatSchemaPathFromAJVErrorToConvertToDataPath,
isTypeValue } = require('../common/schemaUtilsCommon');
var _ = require('lodash');
const IGNORED_KEYWORDS = ['propertyNames', 'const', 'additionalItems', 'dependencies'],
@@ -97,6 +99,14 @@ function validateSchema (schema, valueToUse, options = {}, jsonSchemaDialect) {
isPmVariable(dataPath === '' ? valueToUse : _.get(valueToUse, dataPath))) {
return false;
}
if (validationError.keyword === 'type' || validationError.keyword === 'format') {
let schemaDataPath = formatDataPath(formatSchemaPathFromAJVErrorToConvertToDataPath(validationError.schemaPath)),
schemaToUse = schemaDataPath ? _.get(schema, schemaDataPath) : schema,
valueToValidate = dataPath ? _.get(valueToUse, dataPath) : valueToUse;
return !isTypeValue(valueToValidate, schemaToUse);
}
return true;
});
@@ -110,5 +120,6 @@ module.exports = {
validateSchema,
getLocalDraft,
getAjvValidator,
getDraftToUse
getDraftToUse,
isTypeValue
};

View File

@@ -2,7 +2,207 @@
* This file contains util functions that are common between versions
*/
const parse = require('../parse.js');
const parse = require('../parse.js'),
_ = require('lodash'),
typesMap = {
integer: {
int32: '<integer>',
int64: '<long>'
},
number: {
float: '<float>',
double: '<double>'
},
string: {
byte: '<byte>',
binary: '<binary>',
date: '<date>',
'date-time': '<dateTime>',
password: '<password>'
},
boolean: '<boolean>',
array: '<array>',
object: '<object>'
},
/* eslint-disable arrow-body-style */
schemaTypeToJsValidator = {
'string': (d) => typeof d === 'string',
'number': (d) => !isNaN(d),
'integer': (d) => !isNaN(d) && Number.isInteger(Number(d)),
'boolean': (d) => _.isBoolean(d) || d === 'true' || d === 'false',
'array': (d) => Array.isArray(d),
'object': (d) => typeof d === 'object' && !Array.isArray(d)
};
/**
* Remove the # character from the beginning of a schema path
* @param {string} schemaPath - a defined schemaPath
* @returns {string} - the schema path with # removed
*/
function removeSharpAndSlashFromFirstPosition(schemaPath) {
return schemaPath[0] === '#' ? schemaPath.slice(2) : schemaPath;
}
/**
* Remove the defined word from the last position of a schema path
* @param {string} schemaPath - a defined schemaPath
* @param {string} word - word to remove
* @returns {string} - the schema path with type removed
*/
function removeWordFromLastPosition(schemaPath, word) {
let splittedDataPath = schemaPath.split('/');
if (splittedDataPath[splittedDataPath.length - 1] === word) {
splittedDataPath.splice(-1);
}
return splittedDataPath.join('/');
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
*
* @param {string} value - Value to check for
* @param {string} type - The type in the schemna
* @returns {Boolean} the value is the representation of its type
*/
function compareType(value, type) {
return value === '<' + type + '>';
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} types - The types in the schemna
* @returns {Boolean} the value is the representation of its type
*/
function isTypeValueArrayCheck(value, types) {
return types.find((type) => {
return compareType(value, type);
}) !== undefined;
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} types - The types in the schemna
* @returns {Boolean} the value is the representation of its type
*/
function checkValueOnlyTypes(value, types) {
return Array.isArray(types) ? isTypeValueArrayCheck(value, types) : compareType(value, types);
}
/**
* Checks if value is postman variable or not
*
* @param {string} type - type to look for
* @param {string} format - format from schema
* @returns {Boolean} postman variable or not
*/
function getDefaultFromTypeAndFormat(type, format) {
return typesMap[type][format];
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} types - The types in the schema
* @param {*} format - format from the schema
* @returns {Boolean} the value is the representation of its type
*/
function checkValueTypesAndFormat(value, types, format) {
let typesNotInMapp = [],
typesArray = Array.isArray(types) ? types : [types],
found = typesArray.find((type) => {
let defaultValue;
if (typesMap.hasOwnProperty(type)) {
defaultValue = getDefaultFromTypeAndFormat(type, format);
// in case the format is a custom format (email, hostname etc.)
// https://swagger.io/docs/specification/data-models/data-types/#string
if (!defaultValue && format) {
defaultValue = '<' + format + '>';
}
}
else {
typesNotInMapp.push(type);
}
return defaultValue === value;
});
if (found) {
return true;
}
found = typesNotInMapp.find((type) => {
let defaultValue;
defaultValue = '<' + type + (format ? ('-' + format) : '') + '>';
return defaultValue === value;
});
return found !== undefined;
}
/**
* Checks if value is equal to the defined default
* Works in array types
* @param {string} value - Value to check for
* @param {*} definedDefault - The defined default value of the schema
* @returns {Boolean} wheter value is equal to the defined or not
*/
function checkValueEqualsDefault(value, definedDefault) {
return value === definedDefault;
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} schema - The schema portion used in validation
* @returns {Boolean} the value is the representation of its type
*/
function isTypeValue(value, schema) {
if (schema.hasOwnProperty('type') && schema.hasOwnProperty('default')) {
const isDefault = checkValueEqualsDefault(value, schema.default);
if (isDefault) {
return true;
}
}
if (schema.hasOwnProperty('type') && !schema.hasOwnProperty('format')) {
return checkValueOnlyTypes(value, schema.type);
}
if (schema.hasOwnProperty('type') && schema.hasOwnProperty('format')) {
return checkValueTypesAndFormat(value, schema.type, schema.format);
}
}
/**
* Checks if value is correcta according to schema
* If the value should be numeric, it tries to convert and then validate
* also validates if the value is a correct representation in the form of
* <long> or <integer> etc for integers format 32 or format 64
* @param {string} value - Value to check for
* @param {*} schema - The schema portion used in validation
* @returns {Boolean} the value is the representation of its type
*/
function checkIsCorrectType(value, schema) {
if (schema.hasOwnProperty('type') &&
typeof schemaTypeToJsValidator[schema.type] === 'function') {
const isCorrectType = schemaTypeToJsValidator[schema.type](value);
if (isCorrectType) {
return true;
}
}
return isTypeValue(value, schema);
}
module.exports = {
@@ -54,15 +254,21 @@ module.exports = {
isANumber = (value) => {
return !isNaN(value);
},
formattedElements = splittedDataPath.map((element) => {
formattedElements = splittedDataPath.map((element, index) => {
if (element !== '' && isANumber(element)) {
return `[${element}]`;
}
if (element === '' || element[0] === '.') {
return element;
}
if (index === 0 && !initialDotIfExist) {
return `${element}`;
}
return `.${element}`;
}),
formattedDataPath = formattedElements.join('');
return `${initialDotIfExist}${formattedDataPath}`;
return `${formattedDataPath}`;
},
handleExclusiveMaximum: function(schema, max) {
@@ -101,5 +307,43 @@ module.exports = {
}
}
return min;
},
/**
* Removes initial "#/" from a schema path and the last "/type" segment
* @param {string} schemaPath - The OAS 3.x specification specified in either YAML or JSON
* @returns {string} - The schemaPath with initial #/ and last "/type" removed
*/
formatSchemaPathFromAJVErrorToConvertToDataPath: function (schemaPath) {
return removeWordFromLastPosition(removeWordFromLastPosition(removeSharpAndSlashFromFirstPosition(schemaPath),
'type'), 'format');
},
typesMap,
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} schema - The schema portion used in validation
* @returns {Boolean} the value is the representation of its type
*/
isTypeValue,
/**
* Checks if value is correcta according to schema
* If the value should be numeric, it tries to convert and then validate
* also validates if the value is a correct representation in the form of
* <long> or <integer> etc for integers format 32 or format 64
* @param {string} value - Value to check for
* @param {*} schema - The schema portion used in validation
* @returns {Boolean} the value is the representation of its type
*/
checkIsCorrectType,
isKnownType: function(schema) {
return typeof schemaTypeToJsValidator[schema.type] === 'function';
}
};

View File

@@ -1,24 +1,9 @@
const _ = require('lodash'),
mergeAllOf = require('json-schema-merge-allof'),
type = {
integer: {
int32: '<integer>',
int64: '<long>'
},
number: {
float: '<float>',
double: '<double>'
},
string: {
byte: '<byte>',
binary: '<binary>',
date: '<date>',
'date-time': '<dateTime>',
password: '<password>'
},
boolean: '<boolean>',
array: '<array>',
object: '<object>'
{ typesMap } = require('./common/schemaUtilsCommon'),
PARAMETER_SOURCE = {
REQUEST: 'REQUEST',
RESPONSE: 'RESPONSE'
},
SCHEMA_TYPES = {
array: 'array',
@@ -28,10 +13,6 @@ const _ = require('lodash'),
object: 'object',
string: 'string'
},
PARAMETER_SOURCE = {
REQUEST: 'REQUEST',
RESPONSE: 'RESPONSE'
},
// All formats supported by both ajv and json-schema-faker
SUPPORTED_FORMATS = [
'date', 'time', 'date-time',
@@ -41,7 +22,10 @@ const _ = require('lodash'),
'ipv4', 'ipv6',
'regex',
'uuid',
'json-pointer'
'json-pointer',
'int64',
'float',
'double'
],
DEFAULT_SCHEMA_UTILS = require('./30XUtils/schemaUtils30X');
@@ -281,7 +265,7 @@ module.exports = {
// Override deefault value to appropriate type representation for parameter resolution to schema
if (resolveFor === 'CONVERSION' && resolveTo === 'schema') {
schema.default = type.object;
schema.default = typesMap.object;
}
}
else if (concreteUtils.compareTypes(schema.type, SCHEMA_TYPES.array) && schema.items) {
@@ -321,8 +305,8 @@ module.exports = {
if (!schema.hasOwnProperty('format')) {
schema.default = '<' + schema.type + '>';
}
else if (type.hasOwnProperty(schema.type)) {
schema.default = type[schema.type][schema.format];
else if (typesMap.hasOwnProperty(schema.type)) {
schema.default = typesMap[schema.type][schema.format];
// in case the format is a custom format (email, hostname etc.)
// https://swagger.io/docs/specification/data-models/data-types/#string

View File

@@ -3,7 +3,7 @@
* utils.js contains other util functions
*/
const { formatDataPath } = require('./common/schemaUtilsCommon.js'),
const { formatDataPath, checkIsCorrectType, isKnownType } = require('./common/schemaUtilsCommon.js'),
{ getConcreteSchemaUtils } = require('./common/versionUtils.js'),
async = require('async'),
sdk = require('postman-collection'),
@@ -85,15 +85,6 @@ const { formatDataPath } = require('./common/schemaUtilsCommon.js'),
'authorization'
],
/* eslint-disable arrow-body-style */
schemaTypeToJsValidator = {
'string': (d) => typeof d === 'string',
'number': (d) => !isNaN(d),
'integer': (d) => !isNaN(d) && Number.isInteger(Number(d)),
'boolean': (d) => _.isBoolean(d) || d === 'true' || d === 'false',
'array': (d) => Array.isArray(d),
'object': (d) => typeof d === 'object' && !Array.isArray(d)
},
crypto = require('crypto'),
DEFAULT_SCHEMA_UTILS = require('./30XUtils/schemaUtils30X');
/* eslint-enable */
@@ -3105,7 +3096,7 @@ module.exports = {
}
// When processing a reference, schema.type could also be undefined
else if (schema && schema.type) {
if (typeof schemaTypeToJsValidator[schema.type] === 'function') {
if (isKnownType(schema)) {
let isCorrectType;
// Treat unresolved postman collection/environment variable as correct type
@@ -3113,7 +3104,7 @@ module.exports = {
isCorrectType = true;
}
else {
isCorrectType = schemaTypeToJsValidator[schema.type](valueToUse);
isCorrectType = checkIsCorrectType(valueToUse, schema);
}
if (!isCorrectType) {

View File

@@ -0,0 +1,32 @@
openapi: "3.1.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: http://petstore.swagger.io/v1
paths:
/pets/anyOf:
post:
summary: issue 479
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/example"
responses:
default:
description: ok
components:
schemas:
example:
type:
- object
properties:
id:
type:
- integer
hasPet:
type:
- boolean

View File

@@ -0,0 +1,105 @@
{
"item": [
{
"id": "c3a2f66d-f91e-45ec-9116-4ce926acb630",
"name": "pets",
"item": [
{
"id": "25758366-55ed-4370-90fd-1e1be8536b0b",
"name": "issue 479",
"request": {
"name": "composite schema with anyOf keyword",
"description": {},
"url": {
"path": [
"pets",
"anyOf"
],
"host": [
"{{baseUrl}}"
],
"query": [],
"variable": []
},
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"method": "POST",
"auth": null,
"body": {
"mode": "raw",
"raw": "{ \"id\": \"<integer>\", \"hasPet\": \"<boolean>\" }",
"options": {
"raw": {
"language": "json"
}
}
}
},
"response": [
{
"id": "f4f6ddf5-d259-45b7-89fe-086cc22a6549",
"name": "ok",
"originalRequest": {
"url": {
"path": [
"pets",
"anyOf"
],
"host": [
"{{baseUrl}}"
],
"query": [],
"variable": []
},
"method": "POST",
"body": {
"mode": "raw",
"raw": "{ \"id\": \"<integer>\", \"hasPet\": \"<boolean>\" }",
"options": {
"raw": {
"language": "json"
}
}
}
},
"status": "Internal Server Error",
"code": 500,
"header": [
{
"key": "Content-Type",
"value": "text/plain"
}
],
"body": "",
"cookie": [],
"_postman_previewlanguage": "text"
}
],
"event": []
}
],
"event": []
}
],
"event": [],
"variable": [
{
"type": "string",
"value": "http://petstore.swagger.io/v1",
"key": "baseUrl"
}
],
"info": {
"_postman_id": "b498d8c3-e6e5-4a72-92de-e5c455786a8e",
"name": "Swagger Petstore",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"description": {
"content": "",
"type": "text/plain"
}
}
}

View File

@@ -0,0 +1,117 @@
openapi: "3.1.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: https://postman-echo.com/get
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a pet
operationId: createPets
tags:
- pets
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
responses:
'201':
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
format: date
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string`

View File

@@ -113,6 +113,12 @@ components:
items:
type:
- string
user:
type: object
properties:
entityId:
type: string
maxLength: 5
isFavorite:
type:
- boolean

View File

@@ -0,0 +1,97 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: http://petstore.swagger.io/v1
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
- name: header-1
in: header
required: false
schema:
type: integer
format: int64
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: integer
format: int64
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
/pets2/{pathVar}:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: pathVar
in: path
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
components:
schemas:
Pet:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string

View File

@@ -0,0 +1,108 @@
openapi: 3.1.0
info:
version: 1
title: test for types
servers:
- url: 'http://localhost:3000'
paths:
/user:
post:
summary: 'Sample endpoint: Returns details about a particular user'
operationId: listUser
tags:
- user
parameters:
- name: id
in: query
description: ID of the user
required: true
schema:
type:
- integer
format: int32
requestBody:
content:
application/json:
schema:
type:
- object
properties:
data:
type:
- array
items:
$ref: '#/components/schemas/Entity'
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
components:
schemas:
Entity:
type:
- object
title: Entity
description: A single and unique entity linked to a user
properties:
entityId:
type:
- string
user:
type: object
properties:
id:
type: integer
format: int64
age:
type: integer
format: int32
created_at:
type: string
format: date-time
isFavorite:
type: integer
format: int32
needThis:
type:
- string
Pet:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
created_at:
type: string
format: date-time
birthday:
type: string
format: date
floatField:
type: number
format: float
doubleField:
type: number
format: double
content:
format: byte
type: string
file:
type: string
format: binary
root_pass:
type: string
format: password
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"

View File

@@ -37,7 +37,7 @@
},
"body": {
"mode": "raw",
"raw": "{\n \"data\": [\n {\n \"entityId\": \"5e5792b234d88e12b8511b92\",\n \"accountNumber\": \"1YNykgIi3T2NDeElON0IqcPOpPI\",\n \"entityName\": \"Farmer Freddy's Veg\",\n \"entityPhone\": \"+4420832132132\",\n \"incType\": \"sole\",\n \"companyNumber\": 10000,\n \"needThisNot\": \"hello\",\n \"website\": \"https://farmer-freddy.null\",\n \"turnover\": 10000,\n \"description\": \"def\",\n \"status\": \"tradingAccepted\",\n \"wants\": [\n \"carpentry\",\n \"beer\",\n \"beer\"\n ],\n \"isFavorite\": true\n }\n ],\n \"meta\": {\n \"notNeeded\": 1,\n \"numberOfResults\": 1,\n \"totalPages\": 1\n }\n}"
"raw": "{\n \"data\": [\n {\n \"entityId\": \"5e5792b234d88e12b8511b92\",\n \"accountNumber\": \"1YNykgIi3T2NDeElON0IqcPOpPI\",\n \"entityName\": \"Farmer Freddy's Veg\",\n \"entityPhone\": \"+4420832132132\",\n \"incType\": \"sole\",\n \"companyNumber\": 10000,\n \"needThisNot\": \"hello\",\n \"website\": \"https://farmer-freddy.null\",\n \"turnover\": 10000,\n \"description\": \"def\",\n \"status\": \"tradingAccepted\",\n \"wants\": [\n \"carpentry\",\n \"beer\",\n \"beer\"\n ],\n \"user\": {\"entityId\": \"5e5792b234d88e12b8511b92\"},\n \"isFavorite\": true\n }\n ],\n \"meta\": {\n \"notNeeded\": 1,\n \"numberOfResults\": 1,\n \"totalPages\": 1\n }\n}"
}
},
"response": [

View File

@@ -94,6 +94,12 @@ components:
uniqueItems: true
items:
type: string
user:
type: object
properties:
entityId:
type: string
maxLength: 5
isFavorite:
type: boolean
needThis:

View File

@@ -0,0 +1,375 @@
{
"item": [
{
"id": "ec09078b-8861-4367-9e9e-22f8c3d31f82",
"name": "pets",
"description": {
"content": "",
"type": "text/plain"
},
"item": [
{
"id": "2c8762c9-54bc-4981-8d2c-09b71fadcebc",
"name": "List all pets",
"request": {
"name": "List all pets",
"description": {},
"url": {
"path": [
"pets"
],
"host": [
"{{baseUrl}}"
],
"query": [
{
"disabled": false,
"key": "limit",
"value": "82526723",
"description": "How many items to return at one time (max 100)"
}
],
"variable": []
},
"header": [
{
"key": "Accept",
"value": "application/json"
}
],
"method": "GET",
"auth": null
},
"response": [
{
"id": "2d0c2488-0d26-4029-9294-fbc5c9b8363f",
"name": "A paged array of pets",
"originalRequest": {
"url": {
"path": [
"pets"
],
"host": [
"{{baseUrl}}"
],
"query": [
{
"key": "limit",
"value": "82526723"
}
],
"variable": []
},
"method": "GET",
"body": {}
},
"status": "OK",
"code": 200,
"header": [
{
"disabled": false,
"description": "A link to the next page of responses",
"key": "x-next",
"value": "in nulla Lorem ex"
},
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": "[\n {\n \"id\": 41014273,\n \"name\": \"dolor eu minim in\",\n \"tag\": \"urn:uuid:94b326e4-ed11-bd40-c87d-1a1524b6bc2f\"\n },\n {\n \"id\": -74291521,\n \"name\": \"pariatur dolor e\",\n \"tag\": \"urn:uuid:092c825a-7a1d-1adc-71da-b9dbe24c2da6\"\n }\n]",
"cookie": [],
"_postman_previewlanguage": "json"
},
{
"id": "eb93bc02-1380-433a-9a6e-acd8875a8334",
"name": "unexpected error",
"originalRequest": {
"url": {
"path": [
"pets"
],
"host": [
"{{baseUrl}}"
],
"query": [
{
"key": "limit",
"value": "82526723"
}
],
"variable": []
},
"method": "GET",
"body": {}
},
"status": "Internal Server Error",
"code": 500,
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": "{\n \"code\": -57418069,\n \"message\": \"non quis proident\"\n}",
"cookie": [],
"_postman_previewlanguage": "json"
}
],
"event": []
},
{
"id": "87611012-e59c-40e1-90f2-24a50fbbff1d",
"name": "Create a pet",
"request": {
"name": "Create a pet",
"description": {},
"url": {
"path": [
"pets"
],
"host": [
"{{baseUrl}}"
],
"query": [],
"variable": []
},
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Accept",
"value": "application/json"
}
],
"method": "POST",
"auth": null,
"body": {
"mode": "raw",
"raw": "{\n \"id\": -56104459,\n \"name\": \"dolore incididunt pariatur occaecat\",\n \"tag\": \"9e6b59af-239e-b38b-74a7-f3110c87227e\"\n}",
"options": {
"raw": {
"language": "json"
}
}
}
},
"response": [
{
"id": "813809b9-3290-43e3-8c43-f939db815d2c",
"name": "Null response",
"originalRequest": {
"url": {
"path": [
"pets"
],
"host": [
"{{baseUrl}}"
],
"query": [],
"variable": []
},
"method": "POST",
"body": {
"mode": "raw",
"raw": "{\n \"id\": -56104459,\n \"name\": \"dolore incididunt pariatur occaecat\",\n \"tag\": \"9e6b59af-239e-b38b-74a7-f3110c87227e\"\n}",
"options": {
"raw": {
"language": "json"
}
}
}
},
"status": "Created",
"code": 201,
"header": [
{
"key": "Content-Type",
"value": "text/plain"
}
],
"body": "",
"cookie": [],
"_postman_previewlanguage": "text"
},
{
"id": "436ebb0e-5c7a-4cba-883e-f36089ccd5e2",
"name": "unexpected error",
"originalRequest": {
"url": {
"path": [
"pets"
],
"host": [
"{{baseUrl}}"
],
"query": [],
"variable": []
},
"method": "POST",
"body": {
"mode": "raw",
"raw": "{\n \"id\": -56104459,\n \"name\": \"dolore incididunt pariatur occaecat\",\n \"tag\": \"9e6b59af-239e-b38b-74a7-f3110c87227e\"\n}",
"options": {
"raw": {
"language": "json"
}
}
}
},
"status": "Internal Server Error",
"code": 500,
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": "{\n \"code\": -57418069,\n \"message\": \"non quis proident\"\n}",
"cookie": [],
"_postman_previewlanguage": "json"
}
],
"event": [],
"protocolProfileBehavior": {
"disableBodyPruning": true
}
},
{
"id": "ab623305-0ccf-4edc-9210-c3ed68d4e1dc",
"name": "Info for a specific pet",
"request": {
"name": "Info for a specific pet",
"description": {},
"url": {
"path": [
"pets",
":petId"
],
"host": [
"{{baseUrl}}"
],
"query": [],
"variable": [
{
"disabled": false,
"type": "any",
"value": "in nulla Lorem ex",
"key": "petId",
"description": "(Required) The id of the pet to retrieve"
}
]
},
"header": [
{
"key": "Accept",
"value": "application/json"
}
],
"method": "GET",
"auth": null
},
"response": [
{
"id": "6de63e43-7e59-4713-8893-53bdbddeddef",
"name": "Expected response to a valid request",
"originalRequest": {
"url": {
"path": [
"pets",
":petId"
],
"host": [
"{{baseUrl}}"
],
"query": [],
"variable": [
{
"disabled": false,
"type": "any",
"value": "in nulla Lorem ex",
"key": "petId",
"description": "(Required) The id of the pet to retrieve"
}
]
},
"method": "GET",
"body": {}
},
"status": "OK",
"code": 200,
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": "{\n \"id\": -56104459,\n \"name\": \"dolore incididunt pariatur occaecat\",\n \"tag\": \"9e6b59af-239e-b38b-74a7-f3110c87227e\"\n}",
"cookie": [],
"_postman_previewlanguage": "json"
},
{
"id": "d221e68f-af12-4042-935e-ae2709ef52e7",
"name": "unexpected error",
"originalRequest": {
"url": {
"path": [
"pets",
":petId"
],
"host": [
"{{baseUrl}}"
],
"query": [],
"variable": [
{
"disabled": false,
"type": "any",
"value": "in nulla Lorem ex",
"key": "petId",
"description": "(Required) The id of the pet to retrieve"
}
]
},
"method": "GET",
"body": {}
},
"status": "Internal Server Error",
"code": 500,
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": "{\n \"code\": -57418069,\n \"message\": \"non quis proident\"\n}",
"cookie": [],
"_postman_previewlanguage": "json"
}
],
"event": []
}
],
"event": []
}
],
"event": [],
"variable": [
{
"type": "string",
"value": "https://postman-echo.com/get",
"key": "baseUrl"
}
],
"info": {
"_postman_id": "5baba942-df02-45da-9e3d-540db4667bc4",
"name": "Swagger Petstore",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"description": {
"content": "",
"type": "text/plain"
}
}
}

View File

@@ -0,0 +1,118 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: https://postman-echo.com/get
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a pet
operationId: createPets
tags:
- pets
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
responses:
'201':
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
format: date
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string`

View File

@@ -132,13 +132,13 @@ describe('DEREF FUNCTION TESTS ', function() {
expect(output).to.deep.include({ type: 'object',
required: ['id'],
properties: { id: { default: '<long>', type: 'integer' } } });
properties: { id: { default: '<long>', format: 'int64', type: 'integer' } } });
expect(output_validation).to.deep.include({ anyOf: [
{ type: 'object',
required: ['id'],
description: 'Schema 2',
properties: { id: { type: 'integer' } }
properties: { id: { format: 'int64', type: 'integer' } }
}, {
type: 'object',
properties: { emailField: { type: 'string', format: 'email' } }
@@ -147,7 +147,7 @@ describe('DEREF FUNCTION TESTS ', function() {
expect(output_withdot).to.deep.include({ type: 'object',
required: ['id'],
properties: { id: { default: '<long>', type: 'integer' } } });
properties: { id: { default: '<long>', format: 'int64', type: 'integer' } } });
expect(output_customFormat).to.deep.include({ type: 'object',
properties: { emailField: { default: '<email>', format: 'email', type: 'string' } } });
@@ -156,7 +156,7 @@ describe('DEREF FUNCTION TESTS ', function() {
type: 'object',
description: 'Schema 2',
properties: {
id: { default: '<long>', type: 'integer' },
id: { default: '<long>', format: 'int64', type: 'integer' },
test_prop: { default: '<string>', type: 'string' }
}
});
@@ -201,9 +201,6 @@ describe('DEREF FUNCTION TESTS ', function() {
],
nonSupportedFormats = [
{ type: 'integer', format: 'int32' },
{ type: 'integer', format: 'int64' },
{ type: 'number', format: 'float' },
{ type: 'number', format: 'double' },
{ type: 'string', format: 'byte' },
{ type: 'string', format: 'binary' },
{ type: 'string', format: 'password' },

View File

@@ -1,5 +1,7 @@
const schemaUtilsCommon = require('../../lib/common/schemaUtilsCommon');
const { formatDataPath } = require('../../lib/common/schemaUtilsCommon'),
const schemaUtilsCommon = require('../../lib/common/schemaUtilsCommon'),
{ formatDataPath,
formatSchemaPathFromAJVErrorToConvertToDataPath,
isTypeValue } = require('../../lib/common/schemaUtilsCommon'),
expect = require('chai').expect;
describe('formatData method', function() {
@@ -31,6 +33,14 @@ describe('formatData method', function() {
expect(formattedDataPath).to.be.equal(expectedDataPath);
});
it('Should return "properties.automatic.items.properties.configs.items"' +
' when input is "#/properties/automatic/items/properties/configs/items/type"', function() {
const input = 'properties/automatic/items/properties/configs/items',
expectedDataPath = 'properties.automatic.items.properties.configs.items',
formattedDataPath = formatDataPath(input);
expect(formattedDataPath).to.be.equal(expectedDataPath);
});
});
describe('handleExclusiveMaximum method', function() {
@@ -149,4 +159,139 @@ describe('handleExclusiveMinimum method', function() {
});
});
describe('formatSchemaPathFromAJVErrorToConvertToDataPath method', function () {
it('should return properties/automatic/items/properties/configs/items ' +
'when entry is #/properties/automatic/items/properties/configs/items/type', function () {
const result =
formatSchemaPathFromAJVErrorToConvertToDataPath('#/properties/automatic/items/properties/configs/items/type');
expect(result).to.equal('properties/automatic/items/properties/configs/items');
});
});
describe('isTypeValue method', function () {
it('should return true when value is <integer> and type is integer', function () {
const result = isTypeValue('<integer>', {
type: [
'integer'
]
});
expect(result).to.be.true;
});
it('should return true when input is <long> type integer and format int64', function () {
const result = isTypeValue('<long>', {
format: 'int64',
type: ['integer']
});
expect(result).to.be.true;
});
it('should return true when value is <uuid> type is string format is uuid', function () {
const result = isTypeValue('<uuid>', {
format: 'uuid',
type: ['string']
});
expect(result).to.be.true;
});
it('should return true when value is <otherType> type is otherType and there is not format', function () {
const result = isTypeValue('<otherType>', {
type: ['otherType']
});
expect(result).to.be.true;
});
it('should return true value is <otherType-otherFormat> type is otherType and format is otherFormat', function () {
const result = isTypeValue('<otherType-otherFormat>', {
format: 'otherFormat',
type: ['otherType']
});
expect(result).to.be.true;
});
it('should return false when value is <integer> and type is boolean', function () {
const result = isTypeValue('<integer>', {
type: ['boolean']
});
expect(result).to.be.false;
});
it('should return true when value is <string> and type is string', function () {
const result = isTypeValue('<string>', {
type: ['string']
});
expect(result).to.be.true;
});
it('should return true when value is <integer> and type is ["boolean", "integer"]', function () {
const result = isTypeValue('<integer>', {
type: ['boolean', 'integer']
});
expect(result).to.be.true;
});
it('should return true when value is <integer> and type is integer not array', function () {
const result = isTypeValue('<integer>', {
type: 'integer'
});
expect(result).to.be.true;
});
it('should return true when value is <integer> and type is integer not array format int64', function () {
const result = isTypeValue('<long>', {
format: 'int64',
type: 'integer'
});
expect(result).to.be.true;
});
it('should return true when value is <long> and type is integer, format not present default is <long>', function () {
const result = isTypeValue('<long>', {
default: '<long>',
type: 'integer'
});
expect(result).to.be.true;
});
it('should return false when value is <long> and type is integer,' +
' there is no format default is <nlong>', function () {
const result = isTypeValue('<nlong>', {
default: '<long>',
type: 'integer'
});
expect(result).to.be.false;
});
it('should return true when value is <dateTime> and type is string,' +
' and format is date-time', function () {
const result = isTypeValue('<dateTime>', { type: 'string', format: 'date-time' });
expect(result).to.be.true;
});
it('should return true when value is <date> and type is string,' +
' and format is date', function () {
const result = isTypeValue('<date>', { type: 'string', format: 'date' });
expect(result).to.be.true;
});
it('should return true when value is <float> and type is number,' +
' and format is float', function () {
const result = isTypeValue('<float>', { type: 'number', format: 'float' });
expect(result).to.be.true;
});
it('should return true when value is <double> and type is number,' +
' and format is double', function () {
const result = isTypeValue('<double>', { type: 'number', format: 'double' });
expect(result).to.be.true;
});
it('should return true when value is <double> and type is number,' +
' and format is double', function () {
const result = isTypeValue('<double>', { type: 'number', format: 'double' });
expect(result).to.be.true;
});
});

View File

@@ -63,6 +63,197 @@ function getFoldersByVersion(folder30Path, folder31Path) {
}];
}
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'],
@@ -391,21 +582,22 @@ describe('The Validation option', function () {
{ detailedBlobValidation: true }),
historyRequest = [],
resultObj,
violatedKeywords = [
'data.items.minProperties',
'data.items.required',
'data.items.properties.entityId.maxLength',
'data.items.properties.accountNumber.minLength',
'data.items.properties.entityName.format',
'data.items.properties.incType.enum',
'data.items.properties.companyNumber.exclusiveMinimum',
'data.items.properties.website.type',
'data.items.properties.turnover.multipleOf',
'data.items.properties.description.pattern',
'data.items.properties.wants.uniqueItems',
'meta.maxProperties',
'meta.additionalProperties'
];
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) => {
@@ -416,7 +608,8 @@ describe('The Validation option', function () {
_.forEach(resultObj.mismatches, (mismatch) => {
// remove starting string '$.paths[/user].post.requestBody.content[application/json].schema.properties.'
let localJsonPath = mismatch.schemaJsonPath.slice(76);
expect(_.includes(violatedKeywords, localJsonPath)).to.eql(true);
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)] = '';
@@ -506,6 +699,13 @@ describe('VALIDATE FUNCTION TESTS ', function () {
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) => {
@@ -749,7 +949,7 @@ describe('VALIDATE FUNCTION TESTS ', function () {
ignoreUnresolvedVariables: true,
validateMetadata: true,
suggestAvailableFixes: true,
detailedBlobValidation: false
detailedBlobValidation: true
},
schemaPack = new Converter.SchemaPack({ type: 'string', data: primitiveDataTypeBodySpec }, options);
@@ -762,11 +962,13 @@ describe('VALIDATE FUNCTION TESTS ', function () {
// 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[_.keys(resultObj.responses)[0]];
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();
});
});
@@ -961,6 +1163,36 @@ describe('VALIDATE FUNCTION TESTS ', function () {
});
});
});
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 () {

View File

@@ -145,6 +145,27 @@ describe('Openapi 3.1 schema pack validateTransactions', function() {
});
}
it('Fix for GITHUB#479: Should accept values like <integer> in validation', function() {
const collectionSource = path.join(__dirname, OPENAPI_31_COLLECTIONS + '/479col.json'),
collectionData = fs.readFileSync(collectionSource, 'utf8'),
schemaSource = path.join(__dirname, OPENAPI_31_COLLECTIONS + '/479.yaml'),
schemaData = fs.readFileSync(schemaSource, 'utf8'),
validator = new SchemaPack({
type: 'string',
data: schemaData
});
let transactions = [];
getAllTransactions(JSON.parse(collectionData), transactions);
validator.validateTransaction(transactions, (err, result) => {
let requestIds = Object.keys(result.requests);
expect(err).to.be.null;
requestIds.forEach((requestId) => {
expect(result.requests[requestId].endpoints[0].matched).to.be.true;
});
});
});
it('Should not generate any mismatch with a correct file', function() {
const collectionSource = path.join(__dirname, OPENAPI_31_COLLECTIONS + '/compositeSchemaCollection.json'),
collectionData = fs.readFileSync(collectionSource, 'utf8'),

View File

@@ -302,6 +302,112 @@ describe('validateSchema', function () {
result = validateSchemaAJVDraft04(null, valueToUse);
expect(result.filteredValidationError).to.be.undefined;
});
it('Fix for GITHUB#479: should validate as correct input <integer> for type integer', function () {
const schema = {
type: 'object',
properties: {
id: {
type: [
'integer',
'boolean'
],
examples: [
111111
]
},
hasPet: {
type: [
'boolean'
]
}
}
},
valueToUse = {
'id': '<integer>',
'hasPet': '<boolean>'
},
result = validateSchema(schema, valueToUse);
expect(result).to.be.empty;
});
it('Fix for GITHUB#479: should validate as incorrect input <boolean> for type integer', function () {
const schema = {
type: 'object',
properties: {
id: {
type: [
'integer'
],
examples: [
111111
]
},
hasPet: {
type: [
'boolean'
]
}
}
},
valueToUse = {
'id': '<boolean>',
'hasPet': '<boolean>'
},
result = validateSchema(schema, valueToUse);
expect(result[0].instancePath).equal('/id');
});
it('Fix for GITHUB#479: should validate as correct input <long> for type integer format int64', function () {
const schema = {
type: 'object',
properties: {
id: {
type: [
'integer'
],
format: 'int64'
},
hasPet: {
type: [
'boolean'
]
}
}
},
valueToUse = {
'id': '<long>',
'hasPet': '<boolean>'
},
result = validateSchema(schema, valueToUse);
expect(result).to.be.empty;
});
it('Fix for GITHUB#479: should validate as correct input <long> for type integer boolean format int64', function () {
const schema = {
type: 'object',
properties: {
id: {
type: [
'integer',
'boolean'
],
format: 'int64'
},
hasPet: {
type: [
'boolean'
]
}
}
},
valueToUse = {
'id': '<long>',
'hasPet': '<boolean>'
},
result = validateSchema(schema, valueToUse);
expect(result).to.be.empty;
});
});
describe('getDraftToUse', function() {