Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

schemaValidation - Improved OpenAPI to JSON schema conversion #644

Merged
merged 3 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 41 additions & 41 deletions src/__snapshots__/Portman.test.ts.snap

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions src/application/__snapshots__/TestSuite.test.ts.snap

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -38633,7 +38633,7 @@ pm.test(\\"[POST]::/crm/leads - Response has JSON Body\\", function () {
});
",
"// Response Validation
const schema = {\\"properties\\":{\\"status_code\\":{\\"type\\":\\"number\\",\\"description\\":\\"HTTP status code\\",\\"example\\":402},\\"error\\":{\\"type\\":\\"string\\",\\"description\\":\\"Contains an explanation of the status_code as defined in HTTP/1.1 standard (RFC 7231)\\",\\"example\\":\\"Payment Required\\"},\\"typeName\\":{\\"type\\":\\"string\\",\\"description\\":\\"The type of error returned\\",\\"example\\":\\"RequestLimitError\\"},\\"message\\":{\\"type\\":\\"string\\",\\"description\\":\\"A human-readable message providing more details about the error.\\",\\"example\\":\\"Request Limit Reached\\"},\\"detail\\":{\\"type\\":\\"string\\",\\"description\\":\\"Contains parameter or domain specific information related to the error and why it occured.\\",\\"example\\":\\"You have reached your limit of 2000\\"},\\"ref\\":{\\"type\\":\\"string\\",\\"description\\":\\"Link to documentation of error type\\",\\"example\\":\\"https://developers.apideck.com/errors#requestlimiterror\\"}}}
const schema = {\\"properties\\":{\\"status_code\\":{\\"type\\":\\"number\\",\\"description\\":\\"HTTP status code\\"},\\"error\\":{\\"type\\":\\"string\\",\\"description\\":\\"Contains an explanation of the status_code as defined in HTTP/1.1 standard (RFC 7231)\\"},\\"typeName\\":{\\"type\\":\\"string\\",\\"description\\":\\"The type of error returned\\"},\\"message\\":{\\"type\\":\\"string\\",\\"description\\":\\"A human-readable message providing more details about the error.\\"},\\"detail\\":{\\"type\\":\\"string\\",\\"description\\":\\"Contains parameter or domain specific information related to the error and why it occured.\\"},\\"ref\\":{\\"type\\":\\"string\\",\\"description\\":\\"Link to documentation of error type\\"}}}

// Validate if response matches JSON schema
pm.test(\\"[POST]::/crm/leads - Schema is valid\\", function() {
Expand Down Expand Up @@ -40169,7 +40169,7 @@ pm.test(\\"[POST]::/crm/leads - Response has JSON Body\\", function () {
});
",
"// Response Validation
const schema = {\\"properties\\":{\\"status_code\\":{\\"type\\":\\"number\\",\\"description\\":\\"HTTP status code\\",\\"example\\":400},\\"error\\":{\\"type\\":\\"string\\",\\"description\\":\\"Contains an explanation of the status_code as defined in HTTP/1.1 standard (RFC 7231)\\",\\"example\\":\\"Bad Request\\"},\\"typeName\\":{\\"type\\":\\"string\\",\\"description\\":\\"The type of error returned\\",\\"example\\":\\"RequestHeadersValidationError\\"},\\"message\\":{\\"type\\":\\"string\\",\\"description\\":\\"A human-readable message providing more details about the error.\\",\\"example\\":\\"Invalid Params\\"},\\"detail\\":{\\"anyOf\\":[{\\"type\\":\\"string\\",\\"example\\":\\"Missing property foobar\\"},{\\"type\\":\\"object\\",\\"example\\":{\\"missing\\":[[{\\"foobar\\":\\"required\\"}]]}}],\\"description\\":\\"Contains parameter or domain specific information related to the error and why it occured.\\"},\\"ref\\":{\\"type\\":\\"string\\",\\"description\\":\\"Link to documentation of error type\\",\\"example\\":\\"https://developers.apideck.com/errors#requestbodyvalidationerror\\"}}}
const schema = {\\"properties\\":{\\"status_code\\":{\\"type\\":\\"number\\",\\"description\\":\\"HTTP status code\\"},\\"error\\":{\\"type\\":\\"string\\",\\"description\\":\\"Contains an explanation of the status_code as defined in HTTP/1.1 standard (RFC 7231)\\"},\\"typeName\\":{\\"type\\":\\"string\\",\\"description\\":\\"The type of error returned\\"},\\"message\\":{\\"type\\":\\"string\\",\\"description\\":\\"A human-readable message providing more details about the error.\\"},\\"detail\\":{\\"anyOf\\":[{\\"type\\":\\"string\\"},{\\"type\\":\\"object\\"}],\\"description\\":\\"Contains parameter or domain specific information related to the error and why it occured.\\"},\\"ref\\":{\\"type\\":\\"string\\",\\"description\\":\\"Link to documentation of error type\\"}}}

// Validate if response matches JSON schema
pm.test(\\"[POST]::/crm/leads - Schema is valid\\", function() {
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

138 changes: 127 additions & 11 deletions src/application/tests/testResponseJsonSchema.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { OpenAPIV3 } from 'openapi-types'
import { getOasMappedOperation } from '../../../__tests__/testUtils/getOasMappedOperation'
import { getPostmanMappedOperation } from '../../../__tests__/testUtils/getPostmanMappedOperation'
import { testResponseJsonSchema } from '../../application'
import { convertUnsupportedJsonSchemaProperties, testResponseJsonSchema } from '../../application'
import { OasMappedOperation } from '../../oas'
import { PostmanMappedOperation } from '../../postman'
import { ContractTestConfig, GlobalConfig } from '../../types'
Expand All @@ -16,7 +16,8 @@ describe('testResponseJsonSchema', () => {
})

it('should add test for valid json schema', async () => {
const schema = (oasOperation.schema?.responses?.['200'] as OpenAPIV3.ResponseObject)?.content
const response = (oasOperation.schema?.responses?.['200'] as OpenAPIV3.ResponseObject)?.content
const schema = response?.['application/json'].schema
pmOperation = testResponseJsonSchema(
{ enabled: true } as ContractTestConfig,
schema,
Expand All @@ -42,7 +43,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should skip test for invalid json schema', async () => {
it('should skip test for json schema with $ref', async () => {
const schema = {
type: 'object',
properties: {
Expand All @@ -62,6 +63,32 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should skip test for invalid json schema', async () => {
const schema = {
type: 'array',
maxItems: 100,
items: {
typess: ['object', 'null'],
required: ['id', 'name'],
properties: {
id: { type: 'integer', format: 'int64' },
name: { type: 'string' },
tag: { type: 'string' }
},
type: [null, 'null']
}
}

pmOperation = testResponseJsonSchema(
{} as ContractTestConfig,
schema,
pmOperation,
oasOperation
)
const pmTest = pmOperation.getTests()
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should add extra unknown formats', async () => {
const schema = (oasOperation.schema?.responses?.['200'] as OpenAPIV3.ResponseObject)?.content
pmOperation = testResponseJsonSchema(
Expand All @@ -75,7 +102,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove minItems on items levels, for valid json schema', async () => {
it.skip('should remove minItems on items levels, for valid json schema', async () => {
const schema = {
type: 'array',
items: {
Expand Down Expand Up @@ -109,7 +136,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove minItems on items levels, for types array/null, for valid json schema', async () => {
it.skip('should remove minItems on items levels, for types array/null, for valid json schema', async () => {
const schema = {
type: ['array', 'null'],
items: {
Expand Down Expand Up @@ -143,7 +170,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove maxItems on items levels, for valid json schema', async () => {
it.skip('should remove maxItems on items levels, for valid json schema', async () => {
const schema = {
type: 'array',
items: {
Expand Down Expand Up @@ -177,7 +204,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove maxItems on items levels, for types array/null, for valid json schema', async () => {
it.skip('should remove maxItems on items levels, for types array/null, for valid json schema', async () => {
const schema = {
type: ['array', 'null'],
items: {
Expand Down Expand Up @@ -211,7 +238,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove minItems on nested levels, for type array, for valid json schema', async () => {
it.skip('should remove minItems on nested levels, for type array, for valid json schema', async () => {
const schema = {
type: 'object',
properties: {
Expand Down Expand Up @@ -263,7 +290,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove minItems on nested levels, for types array/null, for valid json schema', async () => {
it.skip('should remove minItems on nested levels, for types array/null, for valid json schema', async () => {
const schema = {
type: 'object',
properties: {
Expand Down Expand Up @@ -315,7 +342,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove maxItems on nested levels, for type array, for valid json schema', async () => {
it.skip('should remove maxItems on nested levels, for type array, for valid json schema', async () => {
const schema = {
type: 'object',
properties: {
Expand Down Expand Up @@ -367,7 +394,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove maxItems on nested levels, for types array/null, for valid json schema', async () => {
it.skip('should remove maxItems on nested levels, for types array/null, for valid json schema', async () => {
const schema = {
type: 'object',
properties: {
Expand Down Expand Up @@ -522,4 +549,93 @@ describe('testResponseJsonSchema', () => {
const pmTest = pmOperation.getTests()
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should convert OpenAPI schema to valid json schema', async () => {
const schema = {
type: 'array',
maxItems: 100,
minItems: 2,
items: {
types: ['object', 'null'],
nullable: true,
required: ['id', 'name'],
properties: {
id: {
type: 'integer',
format: 'int64',
nullable: true
},
name: {
type: 'string'
},
tag: {
type: 'string',
example: 'sample-tag'
},
description: {
type: 'string',
deprecated: true
},
metadata: {
type: 'object',
properties: {
createdBy: {
type: 'string'
},
createdAt: {
type: 'string',
format: 'date-time'
}
},
discriminator: 'type'
}
},
maxItems: 2,
minItems: 1
}
}

const result = convertUnsupportedJsonSchemaProperties(schema)
expect(result).toEqual({
type: 'array',
maxItems: 100, // Root level maxItems should be kept
minItems: 2,
items: {
type: ['object', 'null'], // 'types' converted to 'type' and 'nullable' handled correctly
required: ['id', 'name'],
properties: {
id: {
type: ['integer', 'null'], // Nullable 'id' should have 'null' added to type
format: 'int64'
},
name: {
type: 'string'
},
tag: {
type: 'string'
// 'example' should be removed as it's OpenAPI-specific
},
description: {
type: 'string'
// 'deprecated' should be removed as it's OpenAPI-specific
},
metadata: {
type: 'object',
properties: {
createdBy: {
type: 'string'
},
createdAt: {
type: 'string',
format: 'date-time'
}
}
// 'discriminator' should be removed as it's OpenAPI-specific
}
},
maxItems: 2,
minItems: 1
}
})
})
})
Loading
Loading