Best JavaScript code snippet using pact-foundation-pact
openapi3.spec.ts
Source:openapi3.spec.ts
...57 name: null58 };59 const pactFile = pactBuilder60 .withInteraction(defaultInteractionBuilder61 .withRequestHeader('Content-Type', 'application/json')62 .withRequestBody(pactRequestBody))63 .build();64 const operationBuilder = openApi3OperationBuilder65 .withRequestBody(openApi3RequestBodyBuilder66 .withContent(openApi3ContentBuilder67 .withJsonContent(openApi3SchemaBuilder68 .withTypeObject()69 .withOptionalProperty('name', openApi3SchemaBuilder70 .withTypeNumber()71 .withNullable(true))))72 );73 const specFile = openApi3Builder74 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operationBuilder))75 .build();76 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);77 expect(result).toContainNoWarningsOrErrors();78 });79 it('should fail with a null request body when nullable is explicitly set to false', async () => {80 const pactRequestBody = {81 name: null82 };83 const pactFile = pactBuilder84 .withInteraction(defaultInteractionBuilder85 .withRequestHeader('Content-Type', 'application/json')86 .withRequestBody(pactRequestBody))87 .build();88 const operationBuilder = openApi3OperationBuilder89 .withRequestBody(openApi3RequestBodyBuilder90 .withContent(openApi3ContentBuilder91 .withJsonContent(openApi3SchemaBuilder92 .withTypeObject()93 .withOptionalProperty('name', openApi3SchemaBuilder94 .withTypeNumber()95 .withNullable(false))))96 );97 const specFile = openApi3Builder98 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operationBuilder))99 .build();100 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);101 expect(result).toContainErrors([{102 code: 'request.body.incompatible',103 message: 'Request body is incompatible with the request body schema in the spec file: ' +104 'should be number',105 mockDetails: {106 interactionDescription: defaultInteractionDescription,107 interactionState: '[none]',108 location: '[root].interactions[0].request.body.name',109 mockFile: 'pact.json',110 value: null111 },112 source: 'spec-mock-validation',113 specDetails: {114 location: `[root].paths.${defaultPath}.get.requestBody.content.application/` +115 'json.schema.properties.name.type',116 pathMethod: 'get',117 pathName: defaultPath,118 specFile: 'spec.json',119 value: 'number'120 },121 type: 'error'122 }]);123 });124 it('should support the nullable schema keyword in an object property without type', async () => {125 const pactRequestBody = {126 name: null127 };128 const pactFile = pactBuilder129 .withInteraction(defaultInteractionBuilder130 .withRequestHeader('Content-Type', 'application/json')131 .withRequestBody(pactRequestBody))132 .build();133 const operationBuilder = openApi3OperationBuilder134 .withRequestBody(openApi3RequestBodyBuilder135 .withContent(openApi3ContentBuilder136 .withJsonContent(openApi3SchemaBuilder137 .withTypeObject()138 .withOptionalProperty('name', openApi3SchemaBuilder139 .withNullable(true))))140 );141 const specFile = openApi3Builder142 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operationBuilder))143 .build();144 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);145 expect(result).toContainNoWarningsOrErrors();146 });147 describe('nullable and schemas with swagger custom formats', async () => {148 const whenValidatingNullMockPropertyAgainstSpecPropertySchema = (149 specPropertySchema: OpenApi3SchemaBuilder150 ) => {151 const pactRequestBody = {152 name: null153 };154 const pactFile = pactBuilder155 .withInteraction(defaultInteractionBuilder156 .withRequestHeader('Content-Type', 'application/json')157 .withRequestBody(pactRequestBody))158 .build();159 const operationBuilder = openApi3OperationBuilder160 .withRequestBody(openApi3RequestBodyBuilder161 .withContent(openApi3ContentBuilder162 .withJsonContent(openApi3SchemaBuilder163 .withTypeObject()164 .withOptionalProperty('name', specPropertySchema)))165 );166 const specFile = openApi3Builder167 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operationBuilder))168 .build();169 return swaggerMockValidatorLoader.invoke(specFile, pactFile);170 };171 it('should support nullable for an integer schema with int32 format', async () => {172 const result = await whenValidatingNullMockPropertyAgainstSpecPropertySchema(173 openApi3SchemaBuilder174 .withFormatInt32()175 .withTypeInteger()176 .withNullable(true)177 );178 expect(result).toContainNoWarningsOrErrors();179 });180 it('should support nullable for an integer schema with int64 format', async () => {181 const result = await whenValidatingNullMockPropertyAgainstSpecPropertySchema(182 openApi3SchemaBuilder183 .withFormatInt64()184 .withTypeInteger()185 .withNullable(true)186 );187 expect(result).toContainNoWarningsOrErrors();188 });189 it('should support nullable for an number schema with float format', async () => {190 const result = await whenValidatingNullMockPropertyAgainstSpecPropertySchema(191 openApi3SchemaBuilder192 .withFormatFloat()193 .withTypeNumber()194 .withNullable(true)195 );196 expect(result).toContainNoWarningsOrErrors();197 });198 it('should support nullable for an number schema with double format', async () => {199 const result = await whenValidatingNullMockPropertyAgainstSpecPropertySchema(200 openApi3SchemaBuilder201 .withFormatDouble()202 .withTypeNumber()203 .withNullable(true)204 );205 expect(result).toContainNoWarningsOrErrors();206 });207 });208 describe('any named string and schemas with swagger custom formats', async () => {209 const whenValidatingNullMockPropertyAgainstSpecPropertySchema = (210 specPropertySchema: OpenApi3SchemaBuilder211 ) => {212 const pactRequestBody = {213 name: null214 };215 const pactFile = pactBuilder216 .withInteraction(defaultInteractionBuilder217 .withRequestHeader('Content-Type', 'application/json')218 .withRequestBody(pactRequestBody))219 .build();220 const operationBuilder = openApi3OperationBuilder221 .withRequestBody(openApi3RequestBodyBuilder222 .withContent(openApi3ContentBuilder223 .withJsonContent(openApi3SchemaBuilder224 .withTypeObject()225 .withOptionalProperty('S2S-Token', specPropertySchema)))226 );227 const specFile = openApi3Builder228 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operationBuilder))229 .build();230 return swaggerMockValidatorLoader.invoke(specFile, pactFile);231 };232 it('should support any named format parameter', async () => {233 const result = await whenValidatingNullMockPropertyAgainstSpecPropertySchema(234 openApi3SchemaBuilder235 .withFormatAny('S2S-Token').withTypeString()236 );237 expect(result).toContainNoWarningsOrErrors();238 });239 });240 });241 describe('paths', () => {242 it('should return an error when the pact request path does not match an openapi3 path', async () => {243 const pactFile = pactBuilder244 .withInteraction(defaultInteractionBuilder245 .withRequestPath('/does/not/exist'))246 .build();247 const specFile = openApi3Builder248 .withPath('/does/exist', openApi3PathItemBuilder)249 .build();250 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);251 expect(result).toContainErrors([{252 code: 'request.path-or-method.unknown',253 message: 'Path or method not defined in spec file: GET /does/not/exist',254 mockDetails: {255 interactionDescription: defaultInteractionDescription,256 interactionState: '[none]',257 location: '[root].interactions[0].request.path',258 mockFile: 'pact.json',259 value: '/does/not/exist'260 },261 source: 'spec-mock-validation',262 specDetails: {263 location: '[root].paths',264 pathMethod: null,265 pathName: null,266 specFile: 'spec.json',267 value: {268 '/does/exist': openApi3PathItemBuilder.build()269 }270 },271 type: 'error'272 }]);273 });274 });275 describe('operations', () => {276 it('should return an error when the pact request method does not match an openapi3 method', async () => {277 const pactFile = pactBuilder278 .withInteraction(defaultInteractionBuilder279 .withRequestMethodPost())280 .build();281 const pathItem = openApi3PathItemBuilder.withGetOperation(openApi3OperationBuilder);282 const specFile = openApi3Builder283 .withPath(defaultPath, pathItem)284 .build();285 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);286 expect(result).toContainErrors([{287 code: 'request.path-or-method.unknown',288 message: `Path or method not defined in spec file: POST ${defaultPath}`,289 mockDetails: {290 interactionDescription: defaultInteractionDescription,291 interactionState: '[none]',292 location: '[root].interactions[0].request.path',293 mockFile: 'pact.json',294 value: defaultPath295 },296 source: 'spec-mock-validation',297 specDetails: {298 location: '[root].paths',299 pathMethod: null,300 pathName: null,301 specFile: 'spec.json',302 value: {303 '/does/exist': pathItem.build()304 }305 },306 type: 'error'307 }]);308 });309 it('should not try to parse non-method properties as operations', async () => {310 const pactFile = pactBuilder311 .withInteraction(defaultInteractionBuilder312 .withRequestMethodGet())313 .build();314 const specFile = openApi3Builder315 .withPath(defaultPath, openApi3PathItemBuilder316 .withGetOperation(openApi3OperationBuilder)317 .withDescription('description is not a method'))318 .build();319 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);320 expect(result).toContainNoWarningsOrErrors();321 });322 });323 describe('consumes', () => {324 it('should return error when request content-type does not comply with request body media type', async () => {325 const pactFile = pactBuilder326 .withInteraction(defaultInteractionBuilder327 .withRequestHeader('Content-Type', 'application/json'))328 .build();329 const specFile = openApi3Builder330 .withPath(defaultPath, openApi3PathItemBuilder331 .withGetOperation(openApi3OperationBuilder332 .withRequestBody(openApi3RequestBodyBuilder333 .withContent(openApi3ContentBuilder334 .withMimeTypeContent('image/png', openApi3SchemaBuilder)335 .withMimeTypeContent('text/html', openApi3SchemaBuilder))336 )337 )338 )339 .build();340 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);341 expect(result).toContainErrors([{342 code: 'request.content-type.incompatible',343 message: 'Request Content-Type header is incompatible with the mime-types the spec accepts to consume',344 mockDetails: {345 interactionDescription: defaultInteractionDescription,346 interactionState: '[none]',347 location: '[root].interactions[0].request.headers.Content-Type',348 mockFile: 'pact.json',349 value: 'application/json'350 },351 source: 'spec-mock-validation',352 specDetails: {353 location: `[root].paths.${defaultPath}.get.requestBody.content`,354 pathMethod: 'get',355 pathName: defaultPath,356 specFile: 'spec.json',357 value: ['image/png', 'text/html']358 },359 type: 'error'360 }]);361 });362 it('should return warning when request content-type is defined and openapi3 requestBody is not', async () => {363 const pactFile = pactBuilder364 .withInteraction(defaultInteractionBuilder365 .withRequestHeader('Content-Type', 'application/json'))366 .build();367 const specFile = openApi3Builder368 .withPath(defaultPath, openApi3PathItemBuilder369 .withGetOperation(openApi3OperationBuilder))370 .build();371 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);372 expect(result).toContainNoErrors();373 expect(result).toContainWarnings([{374 code: 'request.content-type.unknown',375 message: 'Request content-type header is defined but the spec does not specify any mime-types ' +376 'to consume',377 mockDetails: {378 interactionDescription: defaultInteractionDescription,379 interactionState: '[none]',380 location: '[root].interactions[0].request.headers.Content-Type',381 mockFile: 'pact.json',382 value: 'application/json'383 },384 source: 'spec-mock-validation',385 specDetails: {386 location: `[root].paths.${defaultPath}.get`,387 pathMethod: 'get',388 pathName: defaultPath,389 specFile: 'spec.json',390 value: openApi3OperationBuilder.build()391 },392 type: 'warning'393 }]);394 });395 });396 describe('request body', () => {397 it('should return an error when the pact request body does not match the openapi3 request body', async () => {398 const pactFile = pactBuilder399 .withInteraction(defaultInteractionBuilder400 .withRequestBody('not-a-number'))401 .build();402 const specFile = openApi3Builder403 .withPath(defaultPath, openApi3PathItemBuilder404 .withGetOperation(openApi3OperationBuilder405 .withRequestBody(openApi3RequestBodyBuilder406 .withContent(openApi3ContentBuilder407 .withJsonContent(openApi3SchemaBuilder.withTypeNumber()))408 )409 )410 )411 .build();412 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);413 expect(result).toContainErrors([{414 code: 'request.body.incompatible',415 message: 'Request body is incompatible with the request body schema in the spec file: ' +416 'should be number',417 mockDetails: {418 interactionDescription: defaultInteractionDescription,419 interactionState: '[none]',420 location: '[root].interactions[0].request.body',421 mockFile: 'pact.json',422 value: 'not-a-number'423 },424 source: 'spec-mock-validation',425 specDetails: {426 location: `[root].paths.${defaultPath}.get.requestBody.content.application/json.schema.type`,427 pathMethod: 'get',428 pathName: defaultPath,429 specFile: 'spec.json',430 value: 'number'431 },432 type: 'error'433 }]);434 });435 it('should pass when request body has an unsupported mimetype (unsupported feature)', async () => {436 const pactFile = pactBuilder437 .withInteraction(defaultInteractionBuilder438 .withRequestHeader('Content-Type', 'application/xml')439 .withRequestBody(1))440 .build();441 const operationBuilder = openApi3OperationBuilder442 .withRequestBody(openApi3RequestBodyBuilder443 .withContent(openApi3ContentBuilder.withXmlContent(openApi3SchemaBuilder.withTypeNumber()))444 );445 const specFile = openApi3Builder446 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operationBuilder))447 .build();448 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);449 expect(result).toContainNoWarningsOrErrors();450 });451 it('should return error if pact not compatible with application/json request with charset', async () => {452 const pactFile = pactBuilder453 .withInteraction(defaultInteractionBuilder454 .withRequestHeader('content-type', 'application/json; charset=UTF-8')455 .withRequestBody(true))456 .build();457 const operationBuilder = openApi3OperationBuilder458 .withRequestBody(openApi3RequestBodyBuilder459 .withContent(openApi3ContentBuilder460 .withMimeTypeContent('application/json;charset=utf-8', openApi3SchemaBuilder.withTypeNumber())))461 .withResponse(200, openApi3ResponseBuilder);462 const specFile = openApi3Builder463 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operationBuilder))464 .build();465 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);466 expect(result).toContainErrors([{467 code: 'request.body.incompatible',468 message: 'Request body is incompatible with the request body schema in the spec file: ' +469 'should be number',470 mockDetails: {471 interactionDescription: defaultInteractionDescription,472 interactionState: '[none]',473 location: '[root].interactions[0].request.body',474 mockFile: 'pact.json',475 value: true476 },477 source: 'spec-mock-validation',478 specDetails: {479 location: `[root].paths.${defaultPath}.get.requestBody.content.` +480 'application/json;charset=utf-8.schema.type',481 pathMethod: 'get',482 pathName: defaultPath,483 specFile: 'spec.json',484 value: 'number'485 },486 type: 'error'487 }]);488 });489 it('should return an error when pact has no request body but an openapi3 requires a body', async () => {490 const pactFile = pactBuilder491 .withInteraction(defaultInteractionBuilder)492 .build();493 const operationBuilder = openApi3OperationBuilder494 .withRequestBody(openApi3RequestBodyBuilder495 .withRequiredBody()496 .withContent(openApi3ContentBuilder497 .withJsonContent(openApi3SchemaBuilder.withTypeNumber()))498 );499 const specFile = openApi3Builder500 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operationBuilder))501 .build();502 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);503 expect(result).toContainErrors([{504 code: 'request.body.incompatible',505 message: 'Request body is incompatible with the request body schema in the spec file: ' +506 'should be number',507 mockDetails: {508 interactionDescription: defaultInteractionDescription,509 interactionState: '[none]',510 location: '[root].interactions[0].request.body',511 mockFile: 'pact.json',512 value: undefined513 },514 source: 'spec-mock-validation',515 specDetails: {516 location: `[root].paths.${defaultPath}.get.requestBody.content.application/json.schema.type`,517 pathMethod: 'get',518 pathName: defaultPath,519 specFile: 'spec.json',520 value: 'number'521 },522 type: 'error'523 }]);524 });525 it('should pass when there is no pact request body and an optional schema', async () => {526 const pactFile = pactBuilder527 .withInteraction(defaultInteractionBuilder)528 .build();529 const operationBuilder = openApi3OperationBuilder530 .withRequestBody(openApi3RequestBodyBuilder531 .withContent(openApi3ContentBuilder532 .withJsonContent(openApi3SchemaBuilder.withTypeNumber()))533 );534 const specFile = openApi3Builder535 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operationBuilder))536 .build();537 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);538 expect(result).toContainNoWarningsOrErrors();539 });540 it('should return error when request body is not compatible with spec with circular references', async () => {541 const pactRequestBody = {542 child: {id: 'not-a-number'},543 id: 1544 };545 const pactFile = pactBuilder546 .withInteraction(defaultInteractionBuilder547 .withRequestBody(pactRequestBody))548 .build();549 const specFile = openApi3Builder550 .withRequestBodyComponent('aRequestBody', openApi3RequestBodyBuilder551 .withContent(openApi3ContentBuilder552 .withJsonContent(openApi3SchemaBuilder.withReference('#/components/schemas/circles'))))553 .withSchemaComponent('circles', openApi3SchemaBuilder554 .withTypeObject()555 .withOptionalProperty('id', openApi3SchemaBuilder.withTypeNumber())556 .withOptionalProperty('child', openApi3SchemaBuilder.withReference('#/components/schemas/circles'))557 )558 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(559 openApi3OperationBuilder.withRequestBodyRef('#/components/requestBodies/aRequestBody')))560 .build();561 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);562 expect(result).toContainErrors([{563 code: 'request.body.incompatible',564 message:565 'Request body is incompatible with the request body schema in the spec file: should be number',566 mockDetails: {567 interactionDescription: defaultInteractionDescription,568 interactionState: '[none]',569 location: '[root].interactions[0].request.body.child.id',570 mockFile: 'pact.json',571 value: 'not-a-number'572 },573 source: 'spec-mock-validation',574 specDetails: {575 location: `[root].paths.${defaultPath}.get.requestBody.content.` +576 'application/json.schema.properties.id.type',577 pathMethod: 'get',578 pathName: defaultPath,579 specFile: 'spec.json',580 value: undefined581 },582 type: 'error'583 }]);584 });585 it('should pass when pact request body matches oneOf request bodies in openapi3', async () => {586 const pactFile = pactBuilder587 .withInteraction(defaultInteractionBuilder588 .withRequestHeader('Content-Type', 'application/json')589 .withRequestBody(1))590 .build();591 const operationBuilder = openApi3OperationBuilder592 .withRequestBody(openApi3RequestBodyBuilder593 .withContent(openApi3ContentBuilder594 .withJsonContent(openApi3SchemaBuilder595 .withOneOf([596 openApi3SchemaBuilder.withTypeNumber(),597 openApi3SchemaBuilder.withTypeObject()598 ]))599 )600 );601 const specFile = openApi3Builder602 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operationBuilder))603 .build();604 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);605 expect(result).toContainNoWarningsOrErrors();606 });607 it('should support de-referencing an object containing a reference pointing to another reference', async () => {608 const pactFile = pactBuilder609 .withInteraction(defaultInteractionBuilder610 .withRequestHeader('content-type', 'application/json')611 .withRequestBody({aNumber: 1}))612 .build();613 const specFile = openApi3Builder614 .withPath(defaultPath, openApi3PathItemBuilder615 .withGetOperation(openApi3OperationBuilder616 .withRequestBodyRef('#/components/requestBodies/aRequestBody')))617 .withRequestBodyComponentRef('aRequestBody', '#/components/requestBodies/anotherRequestBody')618 .withRequestBodyComponent('anotherRequestBody', openApi3RequestBodyBuilder619 .withContent(openApi3ContentBuilder.withJsonContentRef('#/components/schemas/anSchema')))620 .withSchemaComponent('anSchema', openApi3SchemaBuilder621 .withTypeObject()622 .withOptionalProperty('aNumber', openApi3SchemaBuilder.withTypeNumber())623 .withOptionalProperty('circle', openApi3SchemaBuilder624 .withReference('#/components/schemas/anSchema')))625 .build();626 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);627 expect(result).toContainNoWarningsOrErrors();628 });629 });630 describe('path parameters', () => {631 it('should not fail when the pact file matches a parameter defined at the path-item level', async () => {632 const pactFile = pactBuilder.withInteraction(interactionBuilder.withRequestPath('/users/1')).build();633 const specFile = openApi3Builder634 .withPath('/users/{userId}', openApi3PathItemBuilder635 .withGetOperation(openApi3OperationBuilder)636 .withParameter(openApi3PathParameterBuilder637 .withName('userId')638 .withSchema(openApi3SchemaBuilder.withTypeNumber())))639 .build();640 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);641 expect(result).toContainNoWarningsOrErrors();642 });643 it('should use the operation parameters when there are duplicate parameter definitions', async () => {644 const pactFile = pactBuilder.withInteraction(interactionBuilder.withRequestPath('/users/1')).build();645 const specFile = openApi3Builder646 .withPath('/users/{userId}', openApi3PathItemBuilder647 .withGetOperation(openApi3OperationBuilder648 .withParameter(openApi3PathParameterBuilder649 .withName('userId')650 .withSchema(openApi3SchemaBuilder.withTypeNumber())))651 .withParameter(openApi3PathParameterBuilder652 .withName('userId')653 .withSchema(openApi3SchemaBuilder.withTypeBoolean())))654 .build();655 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);656 expect(result).toContainNoWarningsOrErrors();657 });658 it('should return error if pact path segment value does not match the spec path parameter schema', async () => {659 const pactFile = pactBuilder660 .withInteraction(661 defaultInteractionBuilder662 .withRequestPath('/user/not-a-number/info'))663 .build();664 const specPathItem = openApi3PathItemBuilder665 .withGetOperation(openApi3OperationBuilder666 .withParameter(openApi3PathParameterBuilder667 .withName('userId')668 .withSchema(openApi3SchemaBuilder.withTypeNumber())));669 const specFile = openApi3Builder670 .withPath('/user/{userId}/info', specPathItem)671 .build();672 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);673 expect(result).toContainErrors([{674 code: 'request.path-or-method.unknown',675 message: 'Path or method not defined in spec file: GET /user/not-a-number/info',676 mockDetails: {677 interactionDescription: defaultInteractionDescription,678 interactionState: '[none]',679 location: '[root].interactions[0].request.path',680 mockFile: 'pact.json',681 value: '/user/not-a-number/info'682 },683 source: 'spec-mock-validation',684 specDetails: {685 location: '[root].paths',686 pathMethod: null,687 pathName: null,688 specFile: 'spec.json',689 value: {'/user/{userId}/info': specPathItem.build()}690 },691 type: 'error'692 }]);693 });694 });695 describe('request query', () => {696 it('should pass when the pact file matches a parameter defined at the path-item level', async () => {697 const pactFile = pactBuilder698 .withInteraction(defaultInteractionBuilder699 .withRequestQuery('id=1'))700 .build();701 const specFile = openApi3Builder702 .withPath(defaultPath, openApi3PathItemBuilder703 .withGetOperation(openApi3OperationBuilder)704 .withParameter(openApi3QueryParameterBuilder705 .withName('id')706 .withSchema(openApi3SchemaBuilder.withTypeNumber())))707 .build();708 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);709 expect(result).toContainNoWarningsOrErrors();710 });711 it('should return the error when the pact request query does not match the spec', async () => {712 const pactFile = pactBuilder713 .withInteraction(714 defaultInteractionBuilder715 .withRequestQuery('name=not-a-number'))716 .build();717 const queryParameter = openApi3QueryParameterBuilder718 .withSchemaRef('#/components/schemas/queryParamSchema')719 .withName('name')720 .withRequired();721 const operation = openApi3OperationBuilder.withParameterRef('#/components/parameters/name');722 const specFile = openApi3Builder723 .withSchemaComponent('queryParamSchema', openApi3SchemaBuilder.withTypeNumber())724 .withPath(defaultPath, openApi3PathItemBuilder725 .withGetOperation(operation))726 .withParameterComponent(queryParameter)727 .build();728 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);729 expect(result).toContainErrors([{730 code: 'request.query.incompatible',731 message: 'Value is incompatible with the parameter defined in the spec file: should be number',732 mockDetails: {733 interactionDescription: defaultInteractionDescription,734 interactionState: '[none]',735 location: '[root].interactions[0].request.query.name',736 mockFile: 'pact.json',737 value: 'not-a-number'738 },739 source: 'spec-mock-validation',740 specDetails: {741 location: `[root].paths.${defaultPath}.get.parameters[0]`,742 pathMethod: 'get',743 pathName: defaultPath,744 specFile: 'spec.json',745 value: {in: 'query', name: 'name', required: true, schema: {type: 'number'}}746 },747 type: 'error'748 }]);749 });750 it('should pass when the spec defines a parameter with a content property (unsupported feature)', async () => {751 const pactFile = pactBuilder752 .withInteraction(753 defaultInteractionBuilder754 .withRequestQuery('name=not-a-number'))755 .build();756 const specFile = openApi3Builder757 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(758 openApi3OperationBuilder.withParameter(759 openApi3QueryParameterBuilder760 .withName('name')761 .withRequired()762 .withContent(openApi3ContentBuilder.withJsonContent(openApi3SchemaBuilder.withTypeObject()))763 )))764 .build();765 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);766 expect(result).toContainNoWarningsOrErrors();767 });768 it('should pass when the pact request query is missing but parameter is not required', async () => {769 const pactFile = pactBuilder770 .withInteraction(defaultInteractionBuilder)771 .build();772 const specFile = openApi3Builder773 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(774 openApi3OperationBuilder.withParameter(775 openApi3QueryParameterBuilder776 .withName('name')777 )))778 .build();779 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);780 expect(result).toContainNoWarningsOrErrors();781 });782 it('should pass when the pact query does not match a schema of type object (unsupported feature)', async () => {783 const pactFile = pactBuilder784 .withInteraction(785 defaultInteractionBuilder786 .withRequestQuery('name=not-an-object'))787 .build();788 const specFile = openApi3Builder789 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(790 openApi3OperationBuilder.withParameter(791 openApi3QueryParameterBuilder792 .withName('name')793 .withRequired()794 .withSchema(openApi3SchemaBuilder795 .withTypeObject())796 )797 ))798 .build();799 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);800 expect(result).toContainNoWarningsOrErrors();801 });802 it('should pass when the pact query does not match a schema of type array (unsupported feature)', async () => {803 const pactFile = pactBuilder804 .withInteraction(805 defaultInteractionBuilder806 .withRequestQuery('name=not-an-array'))807 .build();808 const specFile = openApi3Builder809 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(810 openApi3OperationBuilder.withParameter(811 openApi3QueryParameterBuilder812 .withName('name')813 .withRequired()814 .withSchema(openApi3SchemaBuilder815 .withTypeArray()816 .withItems(openApi3SchemaBuilder.withTypeBoolean())817 )818 )819 ))820 .build();821 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);822 expect(result).toContainNoWarningsOrErrors();823 });824 it('should not fail when the pact query does not match a parameter of different "in" value', async () => {825 const pactFile = pactBuilder826 .withInteraction(827 defaultInteractionBuilder828 .withRequestQuery('name=not-a-number')829 )830 .build();831 const headerParameter = openApi3HeaderParameterBuilder832 .withName('name')833 .withSchema(openApi3SchemaBuilder.withTypeNumber());834 const specFile = openApi3Builder835 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(836 openApi3OperationBuilder.withParameter(headerParameter)))837 .build();838 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);839 expect(result).toContainNoErrors();840 });841 });842 describe('request headers', () => {843 it('should not fail when the pact file matches a parameter defined at the path-item level', async () => {844 const pactFile = pactBuilder845 .withInteraction(defaultInteractionBuilder846 .withRequestHeader('x-number-required', '1'))847 .build();848 const specFile = openApi3Builder849 .withPath(defaultPath, openApi3PathItemBuilder850 .withGetOperation(openApi3OperationBuilder)851 .withParameter(openApi3HeaderParameterBuilder852 .withName('x-number-required')853 .withSchema(openApi3SchemaBuilder.withTypeNumber())))854 .build();855 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);856 expect(result).toContainNoWarningsOrErrors();857 });858 it('should return the error when the pact request header does not match the spec header', async () => {859 const pactFile = pactBuilder860 .withInteraction(861 defaultInteractionBuilder862 .withRequestHeader('header-name', 'not-a-number')863 )864 .build();865 const headerParameter = openApi3HeaderParameterBuilder866 .withName('header-name')867 .withSchema(openApi3SchemaBuilder.withTypeNumber());868 const specFile = openApi3Builder869 .withPath(defaultPath, openApi3PathItemBuilder870 .withGetOperation(openApi3OperationBuilder871 .withParameter(headerParameter)))872 .build();873 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);874 expect(result).toContainErrors([{875 code: 'request.header.incompatible',876 message: 'Value is incompatible with the parameter defined in the spec file: should be number',877 mockDetails: {878 interactionDescription: defaultInteractionDescription,879 interactionState: '[none]',880 location: '[root].interactions[0].request.headers.header-name',881 mockFile: 'pact.json',882 value: 'not-a-number'883 },884 source: 'spec-mock-validation',885 specDetails: {886 location: `[root].paths.${defaultPath}.get.parameters[0]`,887 pathMethod: 'get',888 pathName: defaultPath,889 specFile: 'spec.json',890 value: headerParameter.build()891 },892 type: 'error'893 }]);894 });895 });896 describe('response status', () => {897 it('should return error when pact mocks response status is not defined in openapi3', async () => {898 const pactFile = pactBuilder899 .withInteraction(defaultInteractionBuilder900 .withResponseStatus(202))901 .build();902 const operation = openApi3OperationBuilder903 .withResponseRef(200, '#/components/responses/aResponse');904 const specFile = openApi3Builder905 .withResponseComponent('aResponse', openApi3ResponseBuilder)906 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operation))907 .build();908 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);909 expect(result).toContainErrors([{910 code: 'response.status.unknown',911 message: 'Response status code not defined in spec file: 202',912 mockDetails: {913 interactionDescription: defaultInteractionDescription,914 interactionState: '[none]',915 location: '[root].interactions[0].response.status',916 mockFile: 'pact.json',917 value: 202918 },919 source: 'spec-mock-validation',920 specDetails: {921 location: `[root].paths.${defaultPath}.get.responses`,922 pathMethod: 'get',923 pathName: defaultPath,924 specFile: 'spec.json',925 value: {200: openApi3ResponseBuilder.build()}926 },927 type: 'error'928 }]);929 });930 });931 describe('produces', () => {932 it('should return the error when the pact request accept header does not match the spec', async () => {933 const pactFile = pactBuilder934 .withInteraction(defaultInteractionBuilder935 .withRequestHeader('Accept', 'application/json'))936 .build();937 const specFile = openApi3Builder938 .withPath(defaultPath, openApi3PathItemBuilder939 .withGetOperation(openApi3OperationBuilder940 .withResponse(200, openApi3ResponseBuilder941 .withContent(openApi3ContentBuilder.withXmlContent(openApi3SchemaBuilder)))942 )943 )944 .build();945 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);946 expect(result).toContainErrors([{947 code: 'request.accept.incompatible',948 message: 'Request Accept header is incompatible with the mime-types the spec defines to produce',949 mockDetails: {950 interactionDescription: defaultInteractionDescription,951 interactionState: '[none]',952 location: '[root].interactions[0].request.headers.Accept',953 mockFile: 'pact.json',954 value: 'application/json'955 },956 source: 'spec-mock-validation',957 specDetails: {958 location: `[root].paths.${defaultPath}.get`,959 pathMethod: 'get',960 pathName: defaultPath,961 specFile: 'spec.json',962 value: ['application/xml']963 },964 type: 'error'965 }]);966 });967 it('should return warning if pact accept header defined but no mime types declared in responses', async () => {968 const pactFile = pactBuilder969 .withInteraction(defaultInteractionBuilder.withRequestHeader('Accept', 'application/json'))970 .build();971 const responseStatusWithNoContent = openApi3ResponseBuilder972 const specFile = openApi3Builder973 .withPath(defaultPath, openApi3PathItemBuilder974 .withGetOperation(openApi3OperationBuilder975 .withResponse(200, responseStatusWithNoContent)976 .withResponse(204, responseStatusWithNoContent)977 )978 )979 .build();980 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);981 expect(result).toContainWarnings([{982 code: 'request.accept.unknown',983 message: 'Request Accept header is defined but the spec does not specify any mime-types to produce',984 mockDetails: {985 interactionDescription: defaultInteractionDescription,986 interactionState: '[none]',987 location: '[root].interactions[0].request.headers.Accept',988 mockFile: 'pact.json',989 value: 'application/json'990 },991 source: 'spec-mock-validation',992 specDetails: {993 location: `[root].paths.${defaultPath}.get`,994 pathMethod: 'get',995 pathName: defaultPath,996 specFile: 'spec.json',997 value: specFile.paths[defaultPath].get998 },999 type: 'warning'1000 }]);1001 });1002 it('should pass if pact request accept header matches mime type of any response in the spec', async () => {1003 const pactFile = pactBuilder1004 .withInteraction(defaultInteractionBuilder1005 .withRequestHeader('Accept', '*/*')1006 .withResponseStatus(204))1007 .build();1008 const responseStatusWithContentMatchingAcceptHeader = openApi3ResponseBuilder1009 .withContent(openApi3ContentBuilder.withJsonContent(openApi3SchemaBuilder.withTypeObject()))1010 const responseStatusWithNoContent = openApi3ResponseBuilder1011 const specFile = openApi3Builder1012 .withPath(defaultPath, openApi3PathItemBuilder1013 .withGetOperation(openApi3OperationBuilder1014 .withResponse(404, responseStatusWithContentMatchingAcceptHeader)1015 .withResponse(204, responseStatusWithNoContent)1016 )1017 )1018 .build();1019 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);1020 expect(result).toContainNoWarningsOrErrors();1021 });1022 });1023 describe('response bodies', () => {1024 it('should return error when the pact response body does not match the openapi3 response body', async () => {1025 const pactResponseBody = {1026 child: {id: 'not-a-number'},1027 id: 11028 };1029 const pactFile = pactBuilder1030 .withInteraction(defaultInteractionBuilder1031 .withResponseBody(pactResponseBody))1032 .build();1033 const specFile = openApi3Builder1034 .withResponseComponent('aResponse', openApi3ResponseBuilder1035 .withContent(1036 openApi3ContentBuilder1037 .withJsonContent(1038 openApi3SchemaBuilder.withReference('#/components/schemas/circles'))))1039 .withSchemaComponent('circles', openApi3SchemaBuilder1040 .withTypeObject()1041 .withOptionalProperty('id', openApi3SchemaBuilder.withTypeNumber())1042 .withOptionalProperty('child', openApi3SchemaBuilder.withReference('#/components/schemas/circles'))1043 )1044 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(1045 openApi3OperationBuilder.withResponseRef(200, '#/components/responses/aResponse')))1046 .build();1047 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);1048 expect(result).toContainErrors([{1049 code: 'response.body.incompatible',1050 message: 'Response body is incompatible with the response body schema in the spec file: ' +1051 'should be number',1052 mockDetails: {1053 interactionDescription: defaultInteractionDescription,1054 interactionState: '[none]',1055 location: '[root].interactions[0].response.body.child.id',1056 mockFile: 'pact.json',1057 value: 'not-a-number'1058 },1059 source: 'spec-mock-validation',1060 specDetails: {1061 location:1062 `[root].paths.${defaultPath}.get.responses.200.content.` +1063 'application/json.schema.properties.id.type',1064 pathMethod: 'get',1065 pathName: defaultPath,1066 specFile: 'spec.json',1067 value: undefined1068 },1069 type: 'error'1070 }]);1071 });1072 it('should pass when response body has an unsupported mime type (unsupported feature)', async () => {1073 const pactFile = pactBuilder1074 .withInteraction(defaultInteractionBuilder1075 .withResponseHeader('Content-Type', 'application/xml')1076 .withResponseBody(1))1077 .build();1078 const operationBuilder = openApi3OperationBuilder1079 .withResponse(200, openApi3ResponseBuilder1080 .withContent(1081 openApi3ContentBuilder.withXmlContent(openApi3SchemaBuilder.withTypeNumber()))1082 );1083 const specFile = openApi3Builder1084 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operationBuilder))1085 .build();1086 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);1087 expect(result).toContainNoWarningsOrErrors();1088 });1089 it('should return error if pact not compatible with application/json response with charset', async () => {1090 const pactFile = pactBuilder1091 .withInteraction(defaultInteractionBuilder1092 .withRequestHeader('accept', 'application/json; charset=UTF-8')1093 .withResponseBody(true))1094 .build();1095 const operationBuilder = openApi3OperationBuilder1096 .withResponse(200, openApi3ResponseBuilder1097 .withContent(openApi3ContentBuilder1098 .withMimeTypeContent('application/json;charset=utf-8', openApi3SchemaBuilder.withTypeNumber()))1099 );1100 const specFile = openApi3Builder1101 .withPath(defaultPath, openApi3PathItemBuilder.withGetOperation(operationBuilder))1102 .build();1103 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);1104 expect(result).toContainErrors([{1105 code: 'response.body.incompatible',1106 message: 'Response body is incompatible with the response body schema in the spec file: ' +1107 'should be number',1108 mockDetails: {1109 interactionDescription: defaultInteractionDescription,1110 interactionState: '[none]',1111 location: '[root].interactions[0].response.body',1112 mockFile: 'pact.json',1113 value: true1114 },1115 source: 'spec-mock-validation',1116 specDetails: {1117 location:1118 `[root].paths.${defaultPath}.get.responses.200.content.` +1119 'application/json;charset=utf-8.schema.type',1120 pathMethod: 'get',1121 pathName: defaultPath,1122 specFile: 'spec.json',1123 value: 'number'1124 },1125 type: 'error'1126 }]);1127 });1128 });1129 describe('response headers', () => {1130 it('should return the error when the pact response header does not match the spec', async () => {1131 const pactFile = pactBuilder1132 .withInteraction(1133 defaultInteractionBuilder1134 .withResponseHeader('x-custom-header', 'not-a-number'))1135 .build();1136 const specFile = openApi3Builder1137 .withSchemaComponent('aResponseHeader', openApi3SchemaBuilder1138 .withTypeNumber())1139 .withHeaderComponent('aHeaderComponent', openApi3ResponseHeaderBuilder1140 .withSchemaRef('#/components/schemas/aResponseHeader'))1141 .withPath(defaultPath, openApi3PathItemBuilder1142 .withGetOperation(openApi3OperationBuilder1143 .withResponse(200, openApi3ResponseBuilder1144 .withHeaderRef('x-custom-header', '#/components/headers/aHeaderComponent'))))1145 .build();1146 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);1147 expect(result).toContainErrors([{1148 code: 'response.header.incompatible',1149 message: 'Value is incompatible with the parameter defined in the spec file: should be number',1150 mockDetails: {1151 interactionDescription: defaultInteractionDescription,1152 interactionState: '[none]',1153 location: '[root].interactions[0].response.headers.x-custom-header',1154 mockFile: 'pact.json',1155 value: 'not-a-number'1156 },1157 source: 'spec-mock-validation',1158 specDetails: {1159 location: `[root].paths.${defaultPath}.get.responses.200.headers.x-custom-header`,1160 pathMethod: 'get',1161 pathName: defaultPath,1162 specFile: 'spec.json',1163 value: {schema: {type: 'number'}}1164 },1165 type: 'error'1166 }]);1167 });1168 it('should pass when the pact header does not match a schema of type array (unsupported feature)', async () => {1169 const pactFile = pactBuilder1170 .withInteraction(1171 defaultInteractionBuilder1172 .withResponseHeader('x-custom-header', 'not-an-array'))1173 .build();1174 const specFile = openApi3Builder1175 .withPath(defaultPath, openApi3PathItemBuilder1176 .withGetOperation(openApi3OperationBuilder1177 .withResponse(200, openApi3ResponseBuilder1178 .withHeader('x-custom-header', openApi3ResponseHeaderBuilder1179 .withSchema(openApi3SchemaBuilder1180 .withTypeArray()1181 .withItems(openApi3SchemaBuilder.withTypeBoolean()))))))1182 .build();1183 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);1184 expect(result).toContainNoWarningsOrErrors();1185 });1186 });1187 describe('security definitions', () => {1188 it('should fail when the pact request does not match a global apiKey header security requirement', async () => {1189 const pactFile = pactBuilder.withInteraction(defaultInteractionBuilder).build();1190 const specFile = openApi3Builder1191 .withSecuritySchemeComponent('securityRequirement', openApi3SecuritySchemeBuilder1192 .withTypeApiKeyInHeader('x-custom-auth'))1193 .withSecurityRequirementNamed('securityRequirement')1194 .withPath(defaultPath, openApi3PathItemBuilder1195 .withGetOperation(openApi3OperationBuilder1196 .withResponse(200, openApi3ResponseBuilder)))1197 .build();1198 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);1199 expect(result).toContainErrors([{1200 code: 'request.authorization.missing',1201 message: 'Request Authorization header is missing but is required by the spec file',1202 mockDetails: {1203 interactionDescription: defaultInteractionDescription,1204 interactionState: '[none]',1205 location: '[root].interactions[0]',1206 mockFile: 'pact.json',1207 value: defaultInteractionBuilder.build()1208 },1209 source: 'spec-mock-validation',1210 specDetails: {1211 location: '[root].security[0].securityRequirement',1212 pathMethod: 'get',1213 pathName: defaultPath,1214 specFile: 'spec.json',1215 value: []1216 },1217 type: 'error'1218 }]);1219 });1220 it('should fail when the pact request does not match an operation query security requirement', async () => {1221 const pactFile = pactBuilder.withInteraction(defaultInteractionBuilder).build();1222 const specFile = openApi3Builder1223 .withSecuritySchemeComponent('securityRequirement',1224 openApi3SecuritySchemeBuilder.withTypeApiKeyInQuery('queryParam'))1225 .withPath(defaultPath, openApi3PathItemBuilder1226 .withGetOperation(openApi3OperationBuilder1227 .withSecurityRequirementNamed('securityRequirement')))1228 .build();1229 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);1230 expect(result).toContainErrors([{1231 code: 'request.authorization.missing',1232 message: 'Request Authorization query is missing but is required by the spec file',1233 mockDetails: {1234 interactionDescription: defaultInteractionDescription,1235 interactionState: '[none]',1236 location: '[root].interactions[0]',1237 mockFile: 'pact.json',1238 value: defaultInteractionBuilder.build()1239 },1240 source: 'spec-mock-validation',1241 specDetails: {1242 location: `[root].paths.${defaultPath}.get.security[0].securityRequirement`,1243 pathMethod: 'get',1244 pathName: defaultPath,1245 specFile: 'spec.json',1246 value: []1247 },1248 type: 'error'1249 }]);1250 });1251 it('should pass when the pact request has the required apiKey auth header', async () => {1252 const pactFile = pactBuilder1253 .withInteraction(defaultInteractionBuilder1254 .withRequestHeader('x-api-token', 'Bearer a-token'))1255 .build();1256 const specFile = openApi3Builder1257 .withPath(defaultPath, openApi3PathItemBuilder1258 .withGetOperation(openApi3OperationBuilder.withSecurityRequirementNamed('apiKey')))1259 .withSecuritySchemeComponent('apiKey', openApi3SecuritySchemeBuilder1260 .withTypeApiKeyInHeader('x-api-token'))1261 .build();1262 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);1263 expect(result).toContainNoWarningsOrErrors();1264 });1265 it('should pass when the pact request has the required apiKey auth query', async () => {1266 const pactFile = pactBuilder1267 .withInteraction(defaultInteractionBuilder1268 .withRequestQuery('secretToken=123'))1269 .build();1270 const specFile = openApi3Builder1271 .withPath(defaultPath, openApi3PathItemBuilder1272 .withGetOperation(openApi3OperationBuilder.withSecurityRequirementNamed('apiKey')))1273 .withSecuritySchemeComponent('apiKey', openApi3SecuritySchemeBuilder1274 .withTypeApiKeyInQuery('secretToken'))1275 .build();1276 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);1277 expect(result).toContainNoWarningsOrErrors();1278 });1279 it('should pass when the pact request has the required http auth header', async () => {1280 const pactFile = pactBuilder1281 .withInteraction(defaultInteractionBuilder1282 .withRequestHeader('Authorization', 'Basic user:pwd'))1283 .build();1284 const specFile = openApi3Builder1285 .withPath(defaultPath, openApi3PathItemBuilder1286 .withGetOperation(openApi3OperationBuilder.withSecurityRequirementNamed('basicAuth')))1287 .withSecuritySchemeComponent('basicAuth', openApi3SecuritySchemeBuilder1288 .withTypeBasic())1289 .build();1290 const result = await swaggerMockValidatorLoader.invoke(specFile, pactFile);1291 expect(result).toContainNoWarningsOrErrors();1292 });1293 it('should ignore unsupported security definition types', async () => {1294 const pactFile = pactBuilder.withInteraction(defaultInteractionBuilder).build();1295 const specFile = openApi3Builder1296 .withPath(defaultPath, openApi3PathItemBuilder...
produces.spec.ts
Source:produces.spec.ts
...21 const interaction = defaultInteractionBuilder22 .withResponseStatus(202)23 .withRequestPath('/does/exist')24 .withResponseHeader('Content-Type', 'application/json')25 .withRequestHeader('Accept', 'application/json');26 const pactFile = pactBuilder27 .withInteraction(interaction).build();28 const operation = operationBuilder29 .withResponse(200, responseBuilder)30 .withProduces(['application/json']);31 const swaggerFile = swagger2Builder32 .withPath('/does/exist', pathBuilder.withGetOperation(operation))33 .build();34 const result = await swaggerMockValidatorLoader.invoke(swaggerFile, pactFile);35 expect(result.errors.length).toBe(1);36 expect(result.errors[0].code).toBe('response.status.unknown');37 });38 describe('request accepts', () => {39 const validateRequestAcceptHeader = (swaggerProduces?: string[],40 pactRequestAcceptHeaderValue?: string) => {41 const interaction = pactRequestAcceptHeaderValue42 ? defaultInteractionBuilder.withRequestHeader('Accept', pactRequestAcceptHeaderValue)43 : defaultInteractionBuilder;44 const pactFile = pactBuilder.withInteraction(interaction).build();45 const operation = swaggerProduces46 ? operationBuilder.withProduces(swaggerProduces)47 : operationBuilder;48 const swaggerFile = swagger2Builder49 .withPath('/does/exist', pathBuilder.withGetOperation(operation))50 .build();51 return swaggerMockValidatorLoader.invoke(swaggerFile, pactFile);52 };53 it('should pass when the pact request accept header matches the spec', async () => {54 const result = await validateRequestAcceptHeader(55 ['text/html', 'application/json;charset=utf-8'], 'application/json'56 );57 expect(result).toContainNoWarningsOrErrors();58 });59 it('should pass when the pact request accept header is not defined', async () => {60 const result = await validateRequestAcceptHeader(['application/json']);61 expect(result).toContainNoWarningsOrErrors();62 });63 it('should pass when neither pact request header, nor spec produces is defined', async () => {64 const result = await validateRequestAcceptHeader();65 expect(result).toContainNoWarningsOrErrors();66 });67 it('should pass when the accept header has multiple types and at least one matches the spec', async () => {68 const result = await validateRequestAcceptHeader(69 ['text/html', 'application/json;charset=utf-8'], 'image/png, application/json'70 );71 expect(result).toContainNoWarningsOrErrors();72 });73 it('should return a warning when request accept header is defined and spec produces is not', async () => {74 const result = await validateRequestAcceptHeader(undefined, 'application/json');75 expect(result).toContainNoErrors();76 expect(result).toContainWarnings([{77 code: 'request.accept.unknown',78 message: 'Request Accept header is defined but the spec does not specify any mime-types to produce',79 mockDetails: {80 interactionDescription: 'interaction description',81 interactionState: '[none]',82 location: '[root].interactions[0].request.headers.Accept',83 mockFile: 'pact.json',84 value: 'application/json'85 },86 source: 'spec-mock-validation',87 specDetails: {88 location: '[root].paths./does/exist.get',89 pathMethod: 'get',90 pathName: '/does/exist',91 specFile: 'spec.json',92 value: operationBuilder.build()93 },94 type: 'warning'95 }]);96 });97 it('should return the error when the pact request accept header does not match the spec', async () => {98 const result = await validateRequestAcceptHeader(['application/xml'], 'application/json');99 expect(result.failureReason).toEqual(expectedFailedValidationError);100 expect(result).toContainErrors([{101 code: 'request.accept.incompatible',102 message: 'Request Accept header is incompatible with the mime-types the spec defines to produce',103 mockDetails: {104 interactionDescription: 'interaction description',105 interactionState: '[none]',106 location: '[root].interactions[0].request.headers.Accept',107 mockFile: 'pact.json',108 value: 'application/json'109 },110 source: 'spec-mock-validation',111 specDetails: {112 location: '[root].paths./does/exist.get.produces',113 pathMethod: 'get',114 pathName: '/does/exist',115 specFile: 'spec.json',116 value: ['application/xml']117 },118 type: 'error'119 }]);120 });121 it('should return the error when the request accept header does not match the global spec', async () => {122 const pactFile = pactBuilder123 .withInteraction(defaultInteractionBuilder.withRequestHeader('Accept', 'application/json'))124 .build();125 const swaggerFile = swagger2Builder126 .withPath('/does/exist', pathBuilder.withGetOperation(operationBuilder))127 .withProduces(['application/xml'])128 .build();129 const result = await swaggerMockValidatorLoader.invoke(swaggerFile, pactFile);130 expect(result.failureReason).toEqual(expectedFailedValidationError);131 expect(result).toContainErrors([{132 code: 'request.accept.incompatible',133 message: 'Request Accept header is incompatible with the mime-types the spec defines to produce',134 mockDetails: {135 interactionDescription: 'interaction description',136 interactionState: '[none]',137 location: '[root].interactions[0].request.headers.Accept',138 mockFile: 'pact.json',139 value: 'application/json'140 },141 source: 'spec-mock-validation',142 specDetails: {143 location: '[root].produces',144 pathMethod: 'get',145 pathName: '/does/exist',146 specFile: 'spec.json',147 value: ['application/xml']148 },149 type: 'error'150 }]);151 });152 it('should use the operation produces over the global produces', async () => {153 const pactFile = pactBuilder154 .withInteraction(defaultInteractionBuilder.withRequestHeader('Accept', 'application/json'))155 .build();156 const swaggerFile = swagger2Builder157 .withPath('/does/exist', pathBuilder158 .withGetOperation(operationBuilder.withProduces(['application/xml']))159 )160 .withProduces(['application/json'])161 .build();162 const result = await swaggerMockValidatorLoader.invoke(swaggerFile, pactFile);163 expect(result.failureReason).toEqual(expectedFailedValidationError);164 expect(result).toContainErrors([{165 code: 'request.accept.incompatible',166 message: 'Request Accept header is incompatible with the mime-types the spec defines to produce',167 mockDetails: {168 interactionDescription: 'interaction description',...
index.spec.ts
Source:index.spec.ts
1// @ts-nocheck2import chai from 'chai';3import chaiAsPromised from 'chai-as-promised';4import sinon from 'sinon';5import sinonChai from 'sinon-chai';6import { PactOptions, PactOptionsComplete } from '../dsl/options';7import { Pact } from '.';8import { ConsumerInteraction, ConsumerPact } from '@pact-foundation/pact-core';9chai.use(sinonChai);10chai.use(chaiAsPromised);11const { expect } = chai;12describe('Pact', () => {13 const fullOpts = {14 consumer: 'A',15 provider: 'B',16 port: 1234,17 host: '127.0.0.1',18 ssl: false,19 logLevel: 'info',20 spec: 2,21 cors: false,22 pactfileWriteMode: 'merge',23 } as PactOptionsComplete;24 afterEach(() => {25 sinon.restore();26 });27 describe('#constructor', () => {28 it('throws Error when consumer not provided', () => {29 expect(() => {30 new Pact({ consumer: '', provider: 'provider' });31 }).to.throw(Error, 'You must specify a Consumer for this pact.');32 });33 it('throws Error when provider not provided', () => {34 expect(() => {35 new Pact({ consumer: 'someconsumer', provider: '' });36 }).to.throw(Error, 'You must specify a Provider for this pact.');37 });38 });39 describe('#createOptionsWithDefault', () => {40 const constructorOpts: PactOptions = {41 consumer: 'A',42 provider: 'B',43 };44 it('merges options with sensible defaults', () => {45 const opts = Pact.createOptionsWithDefaults(constructorOpts);46 expect(opts.consumer).to.eq('A');47 expect(opts.provider).to.eq('B');48 expect(opts.cors).to.eq(false);49 expect(opts.host).to.eq('127.0.0.1');50 expect(opts.logLevel).to.eq('info');51 expect(opts.spec).to.eq(2);52 expect(opts.dir).not.to.be.empty;53 expect(opts.log).not.to.be.empty;54 expect(opts.pactfileWriteMode).to.eq('merge');55 expect(opts.ssl).to.eq(false);56 expect(opts.sslcert).to.eq(undefined);57 expect(opts.sslkey).to.eq(undefined);58 });59 });60 describe('#setup', () => {61 describe('when server is properly configured', () => {62 it('updates the mock service configuration', async () => {63 const p: Pact = new Pact(fullOpts);64 await p.setup();65 expect(p.mockService).to.deep.equal({66 baseUrl: 'http://127.0.0.1:1234',67 pactDetails: {68 pactfile_write_mode: 'merge',69 consumer: {70 name: 'A',71 },72 provider: { name: 'B' },73 },74 });75 });76 it('returns the current configuration', () => {77 const p: any = new Pact(fullOpts);78 return expect(p.setup()).to.eventually.include({79 consumer: 'A',80 provider: 'B',81 port: 1234,82 host: '127.0.0.1',83 ssl: false,84 logLevel: 'info',85 spec: 2,86 cors: false,87 pactfileWriteMode: 'merge',88 });89 });90 });91 describe('when a port is given', () => {92 it('checks if the port is available', () => {93 const p: any = new Pact(fullOpts);94 return expect(p.setup())95 .to.eventually.have.property('port')96 .eq(fullOpts.port);97 });98 });99 describe('when no port is given', () => {100 it('finds a free port', () => {101 const opts = {102 ...fullOpts,103 port: undefined,104 };105 const p: any = new Pact(opts);106 return expect(p.setup()).to.eventually.have.property('port').not107 .undefined;108 });109 });110 });111 describe('#addInteraction', () => {112 // This is more of an integration test, as the function has taken on a lot more113 // responsibility previously covered by other functions during the upgrade to114 // the rust core, to ensure the API remains backwards compatible115 it('sets the correct request and response details on the FFI and starts the mock server', () => {116 const p: Pact = new Pact(fullOpts);117 const uponReceiving = sinon.stub().returns(true);118 const given = sinon.stub().returns(true);119 const withRequest = sinon.stub().returns(true);120 const withRequestBody = sinon.stub().returns(true);121 const withRequestHeader = sinon.stub().returns(true);122 const withQuery = sinon.stub().returns(true);123 const withResponseBody = sinon.stub().returns(true);124 const withResponseHeader = sinon.stub().returns(true);125 const withStatus = sinon.stub().returns(true);126 const createMockServer = sinon.stub().returns(1234);127 const pactMock: ConsumerPact = {128 createMockServer,129 };130 const interactionMock: ConsumerInteraction = {131 uponReceiving,132 given,133 withRequest,134 withRequestBody,135 withRequestHeader,136 withQuery,137 withResponseBody,138 withResponseHeader,139 withStatus,140 };141 p.pact = pactMock;142 p.interaction = interactionMock;143 p.mockService = {};144 p.addInteraction({145 state: 'some state',146 uponReceiving: 'some description',147 withRequest: {148 method: 'GET',149 path: '/',150 body: { foo: 'bar' },151 headers: {152 'content-type': 'application/json',153 foo: 'bar',154 },155 query: {156 query: 'string',157 foo: 'bar',158 },159 },160 willRespondWith: {161 status: 200,162 body: { baz: 'bat' },163 headers: {164 'content-type': 'application/hal+json',165 foo: 'bar',166 },167 },168 });169 expect(uponReceiving.calledOnce).to.be.true;170 expect(given.calledOnce).to.be.true;171 expect(withRequest.calledOnce).to.be.true;172 expect(withQuery.calledTwice).to.be.true;173 expect(withRequestHeader.calledTwice).to.be.true;174 expect(withRequestBody.calledOnce).to.be.true;175 expect(withResponseBody.calledOnce).to.be.true;176 expect(withResponseHeader.calledTwice).to.be.true;177 // Pact mock server started178 expect(createMockServer.called).to.be.true;179 });180 });...
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!