From f9219ad805894e413021028093cdfdb65eb8e278 Mon Sep 17 00:00:00 2001 From: Voon Siong Wong Date: Tue, 10 Oct 2023 09:16:51 +1100 Subject: [PATCH] fix: handle type: "object" in schema composition (#47) --- .../validate-parsed-mock-response-body.ts | 8 +++- test/unit/schema-transformation.spec.ts | 48 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 test/unit/schema-transformation.spec.ts diff --git a/lib/swagger-mock-validator/validate-spec-and-mock/validate-parsed-mock-response-body.ts b/lib/swagger-mock-validator/validate-spec-and-mock/validate-parsed-mock-response-body.ts index 3c71c18..4b30e35 100644 --- a/lib/swagger-mock-validator/validate-spec-and-mock/validate-parsed-mock-response-body.ts +++ b/lib/swagger-mock-validator/validate-spec-and-mock/validate-parsed-mock-response-body.ts @@ -7,7 +7,7 @@ import { ValidateOptions } from '../types'; import { isTypesOfJson } from './content-negotiation'; import { validateJson } from './validate-json'; -const transformSchema = ( +export const transformSchema = ( schema: ParsedSpecJsonSchema, opts: Pick ): ParsedSpecJsonSchema => { @@ -18,7 +18,11 @@ const transformSchema = ( if (!opts.additionalPropertiesInResponse) { traverseJsonSchema(modifiedSchema, (mutableSchema) => { if ( - (typeof mutableSchema.additionalProperties === 'undefined' || mutableSchema.additionalProperties === true) && + (typeof mutableSchema.additionalProperties === 'undefined' || + mutableSchema.additionalProperties === true) && + !mutableSchema.oneOf && + !mutableSchema.allOf && + !mutableSchema.anyOf && mutableSchema.type && mutableSchema.type === 'object' ) { diff --git a/test/unit/schema-transformation.spec.ts b/test/unit/schema-transformation.spec.ts new file mode 100644 index 0000000..9ae63cd --- /dev/null +++ b/test/unit/schema-transformation.spec.ts @@ -0,0 +1,48 @@ +import { transformSchema } from '../../lib/swagger-mock-validator/validate-spec-and-mock/validate-parsed-mock-response-body'; + +// defaults +const options = { + additionalPropertiesInResponse: false, + requiredPropertiesInResponse: false, +}; + +const transformedAdditionalProps = (schema) => transformSchema(schema, options).additionalProperties; +const transformedRequired = (schema) => transformSchema(schema, options).required; + +describe('response transformation', () => { + // a provider must provide a superset of what the consumer asks for + // additionalProperties expected in pact response are disallowed + describe('additionalProperties', () => { + it('is prevented in objects', () => { + expect(transformedAdditionalProps({ type: 'object' })).toBeFalse(); + }); + + it('is forced to be false', () => { + expect(transformedAdditionalProps({ type: 'object', additionalProperties: true })).toBeFalse(); + }); + + it('allows schema composition', () => { + expect(transformedAdditionalProps({ type: 'object', oneOf: [] })).toBeUndefined(); + expect(transformedAdditionalProps({ type: 'object', allOf: [] })).toBeUndefined(); + expect(transformedAdditionalProps({ type: 'object', anyOf: [] })).toBeUndefined(); + }); + }); + + // a consumer may only use a subset of the provider *response* + // any field marked as required in OAS, should be considered optional for pact testing + describe('required properties', () => { + it('makes properties optional', () => { + expect( + transformedRequired({ + type: 'object', + required: ['foo'], + properties: { + foo: { + type: 'string', + }, + }, + }) + ).toBeUndefined(); + }); + }); +});