@pactflow/openapi-pact-comparator 2.0.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +277 -213
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +276 -212
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +276 -212
- package/dist/index.mjs.map +1 -1
- package/dist/src/compare/utils/body.d.ts +3 -0
- package/dist/src/results/index.d.ts +1 -1
- package/package.json +4 -5
package/dist/index.cjs
CHANGED
|
@@ -4252,14 +4252,199 @@ function* compareMessageHeaders(ajv, message, content, interactionContext, metad
|
|
|
4252
4252
|
}
|
|
4253
4253
|
}
|
|
4254
4254
|
|
|
4255
|
-
const
|
|
4255
|
+
const PARAMETER_SEPARATOR = ";";
|
|
4256
|
+
const WILDCARD = "*";
|
|
4257
|
+
const TYPE_SUBTYPE_SEPARATOR = "/";
|
|
4258
|
+
const EXTENSION_SEPARATOR = "+";
|
|
4259
|
+
const quality = (a) => {
|
|
4260
|
+
const match = /.*;q=([01].?\d*)/.exec(a);
|
|
4261
|
+
return match ? parseFloat(match[1]) : 1.0;
|
|
4262
|
+
};
|
|
4263
|
+
const byQuality = (a, b) => {
|
|
4264
|
+
return quality(b) - quality(a);
|
|
4265
|
+
};
|
|
4266
|
+
const ignoreCasing = (t) => t.toLowerCase();
|
|
4267
|
+
const ignoreWhitespace = (t) => t.replace(/\s+/g, "");
|
|
4268
|
+
const ignoreParameters = (t) => t.split(PARAMETER_SEPARATOR)[0];
|
|
4269
|
+
const removeQuality = (t) => t.replace(/;q=[01].?\d*/, "");
|
|
4270
|
+
const type$a = (t) => t.split(TYPE_SUBTYPE_SEPARATOR)[0];
|
|
4271
|
+
const subtype = (t) => {
|
|
4272
|
+
const [_maintype, subtype] = t.split(TYPE_SUBTYPE_SEPARATOR);
|
|
4273
|
+
return subtype?.split(EXTENSION_SEPARATOR).pop();
|
|
4274
|
+
};
|
|
4275
|
+
function findMatchingType(requestType, responseTypes) {
|
|
4276
|
+
// exact match
|
|
4277
|
+
let accept = requestType
|
|
4278
|
+
.split(",")
|
|
4279
|
+
.map(ignoreWhitespace)
|
|
4280
|
+
.map(ignoreCasing)
|
|
4281
|
+
.sort(byQuality)
|
|
4282
|
+
.map(removeQuality);
|
|
4283
|
+
let available = responseTypes.map(ignoreWhitespace).map(ignoreCasing);
|
|
4284
|
+
for (const a of accept) {
|
|
4285
|
+
const matchExactly = (t) => t === a;
|
|
4286
|
+
const index = available.findIndex(matchExactly);
|
|
4287
|
+
if (index >= 0) {
|
|
4288
|
+
return responseTypes[index];
|
|
4289
|
+
}
|
|
4290
|
+
}
|
|
4291
|
+
// ignore additional parameters
|
|
4292
|
+
accept = accept.map(ignoreParameters);
|
|
4293
|
+
available = available.map(ignoreParameters);
|
|
4294
|
+
for (const a of accept) {
|
|
4295
|
+
const matchExactly = (t) => t === a;
|
|
4296
|
+
const index = available.findIndex(matchExactly);
|
|
4297
|
+
if (index >= 0) {
|
|
4298
|
+
return responseTypes[index];
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
// ignore vendor extensions
|
|
4302
|
+
for (const a of accept) {
|
|
4303
|
+
const matchTypesAndSubtypes = (t) => type$a(t) === type$a(a) && subtype(t) === subtype(a);
|
|
4304
|
+
const index = available.findIndex(matchTypesAndSubtypes);
|
|
4305
|
+
if (index >= 0) {
|
|
4306
|
+
return responseTypes[index];
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
// wildcards in responseTypes
|
|
4310
|
+
for (const a of accept) {
|
|
4311
|
+
const matchSubtype = (t) => subtype(t) === WILDCARD && type$a(t) === type$a(a);
|
|
4312
|
+
const index = available.findIndex(matchSubtype);
|
|
4313
|
+
if (index >= 0) {
|
|
4314
|
+
return responseTypes[index];
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
if (available.includes(`${WILDCARD}/${WILDCARD}`)) {
|
|
4318
|
+
return `${WILDCARD}/${WILDCARD}`;
|
|
4319
|
+
}
|
|
4320
|
+
// wildcards in requestTypes
|
|
4321
|
+
for (const a of accept) {
|
|
4322
|
+
const matchSubtype = (t) => subtype(a) === WILDCARD && type$a(t) === type$a(a);
|
|
4323
|
+
const index = available.findIndex(matchSubtype);
|
|
4324
|
+
if (index >= 0) {
|
|
4325
|
+
return responseTypes[index];
|
|
4326
|
+
}
|
|
4327
|
+
}
|
|
4328
|
+
if (accept.includes(`${WILDCARD}/${WILDCARD}`)) {
|
|
4329
|
+
return responseTypes[0];
|
|
4330
|
+
}
|
|
4331
|
+
return undefined;
|
|
4332
|
+
}
|
|
4333
|
+
const getByContentType = (object = {}, contentType) => {
|
|
4334
|
+
const key = findMatchingType(contentType, Object.keys(object));
|
|
4335
|
+
return object[key];
|
|
4336
|
+
};
|
|
4337
|
+
const standardHttpRequestHeaders = [
|
|
4338
|
+
"accept",
|
|
4339
|
+
"accept-charset",
|
|
4340
|
+
"accept-datetime",
|
|
4341
|
+
"accept-encoding",
|
|
4342
|
+
"accept-language",
|
|
4343
|
+
"authorization",
|
|
4344
|
+
"cache-control",
|
|
4345
|
+
"connection",
|
|
4346
|
+
"content-length",
|
|
4347
|
+
"content-md5",
|
|
4348
|
+
"content-type",
|
|
4349
|
+
"cookie",
|
|
4350
|
+
"date",
|
|
4351
|
+
"expect",
|
|
4352
|
+
"forwarded",
|
|
4353
|
+
"from",
|
|
4354
|
+
"host",
|
|
4355
|
+
"if-match",
|
|
4356
|
+
"if-modified-since",
|
|
4357
|
+
"if-none-match",
|
|
4358
|
+
"if-range",
|
|
4359
|
+
"if-unmodified-since",
|
|
4360
|
+
"max-forwards",
|
|
4361
|
+
"origin",
|
|
4362
|
+
"pragma",
|
|
4363
|
+
"proxy-authorization",
|
|
4364
|
+
"range",
|
|
4365
|
+
"referer",
|
|
4366
|
+
"te",
|
|
4367
|
+
"upgrade",
|
|
4368
|
+
"user-agent",
|
|
4369
|
+
"via",
|
|
4370
|
+
"warning",
|
|
4371
|
+
];
|
|
4372
|
+
const standardHttpResponseHeaders = [
|
|
4373
|
+
"access-control-allow-origin",
|
|
4374
|
+
"accept-patch",
|
|
4375
|
+
"accept-ranges",
|
|
4376
|
+
"age",
|
|
4377
|
+
"allow",
|
|
4378
|
+
"alt-svc",
|
|
4379
|
+
"cache-control",
|
|
4380
|
+
"connection",
|
|
4381
|
+
"content-disposition",
|
|
4382
|
+
"content-encoding",
|
|
4383
|
+
"content-language",
|
|
4384
|
+
"content-length",
|
|
4385
|
+
"content-location",
|
|
4386
|
+
"content-md5",
|
|
4387
|
+
"content-range",
|
|
4388
|
+
"date",
|
|
4389
|
+
"etag",
|
|
4390
|
+
"expires",
|
|
4391
|
+
"last-modified",
|
|
4392
|
+
"link",
|
|
4393
|
+
"location",
|
|
4394
|
+
"p3p",
|
|
4395
|
+
"pragma",
|
|
4396
|
+
"proxy-authenticate",
|
|
4397
|
+
"public-key-pins",
|
|
4398
|
+
"refresh",
|
|
4399
|
+
"retry-after",
|
|
4400
|
+
"server",
|
|
4401
|
+
"set-cookie",
|
|
4402
|
+
"status",
|
|
4403
|
+
"strict-transport-security",
|
|
4404
|
+
"trailer",
|
|
4405
|
+
"transfer-encoding",
|
|
4406
|
+
"tsv",
|
|
4407
|
+
"upgrade",
|
|
4408
|
+
"vary",
|
|
4409
|
+
"via",
|
|
4410
|
+
"warning",
|
|
4411
|
+
"www-authenticate",
|
|
4412
|
+
"x-frame-options",
|
|
4413
|
+
];
|
|
4414
|
+
|
|
4415
|
+
const VALIDATABLE_CONTENT_TYPES = [
|
|
4416
|
+
"application/json",
|
|
4417
|
+
"application/x-www-form-urlencoded",
|
|
4418
|
+
"multipart/form-data",
|
|
4419
|
+
];
|
|
4420
|
+
const bodyValidationStatus = (contentType, body, validatableTypes = [...VALIDATABLE_CONTENT_TYPES]) => {
|
|
4256
4421
|
if (typeof contentType !== "string")
|
|
4257
|
-
return
|
|
4258
|
-
|
|
4259
|
-
|
|
4422
|
+
return "skip";
|
|
4423
|
+
if (findMatchingType(contentType, validatableTypes))
|
|
4424
|
+
return "validate";
|
|
4425
|
+
return body !== undefined ? "warn" : "skip";
|
|
4260
4426
|
};
|
|
4427
|
+
|
|
4261
4428
|
function* compareMessagePayload(ajv, message, content, interactionContext, contentLocation, messagePath, direction) {
|
|
4262
|
-
|
|
4429
|
+
const status = bodyValidationStatus(content.contentType, content.payload);
|
|
4430
|
+
if (status === "warn") {
|
|
4431
|
+
yield {
|
|
4432
|
+
code: "message.payload.unvalidatable",
|
|
4433
|
+
message: `Body with content type '${content.contentType}' is not supported by the spec comparator`,
|
|
4434
|
+
mockDetails: {
|
|
4435
|
+
...baseMockDetails(interactionContext),
|
|
4436
|
+
location: contentLocation,
|
|
4437
|
+
value: content.payload,
|
|
4438
|
+
},
|
|
4439
|
+
specDetails: {
|
|
4440
|
+
location: messagePath,
|
|
4441
|
+
value: undefined,
|
|
4442
|
+
},
|
|
4443
|
+
type: "warning",
|
|
4444
|
+
};
|
|
4445
|
+
return;
|
|
4446
|
+
}
|
|
4447
|
+
if (status === "skip")
|
|
4263
4448
|
return;
|
|
4264
4449
|
if (!message.payload) {
|
|
4265
4450
|
if (content.payload !== undefined) {
|
|
@@ -4722,16 +4907,16 @@ function requireMultipart () {
|
|
|
4722
4907
|
var multipartExports = requireMultipart();
|
|
4723
4908
|
var multipart = /*@__PURE__*/getDefaultExportFromCjs(multipartExports);
|
|
4724
4909
|
|
|
4725
|
-
var type$
|
|
4910
|
+
var type$9;
|
|
4726
4911
|
var hasRequiredType;
|
|
4727
4912
|
|
|
4728
4913
|
function requireType () {
|
|
4729
|
-
if (hasRequiredType) return type$
|
|
4914
|
+
if (hasRequiredType) return type$9;
|
|
4730
4915
|
hasRequiredType = 1;
|
|
4731
4916
|
|
|
4732
4917
|
/** @type {import('./type')} */
|
|
4733
|
-
type$
|
|
4734
|
-
return type$
|
|
4918
|
+
type$9 = TypeError;
|
|
4919
|
+
return type$9;
|
|
4735
4920
|
}
|
|
4736
4921
|
|
|
4737
4922
|
var util_inspect;
|
|
@@ -7853,166 +8038,6 @@ var qs = /*@__PURE__*/getDefaultExportFromCjs(libExports);
|
|
|
7853
8038
|
|
|
7854
8039
|
const isValidRequest = (interaction) => interaction.response.status < 400;
|
|
7855
8040
|
|
|
7856
|
-
const PARAMETER_SEPARATOR = ";";
|
|
7857
|
-
const WILDCARD = "*";
|
|
7858
|
-
const TYPE_SUBTYPE_SEPARATOR = "/";
|
|
7859
|
-
const EXTENSION_SEPARATOR = "+";
|
|
7860
|
-
const quality = (a) => {
|
|
7861
|
-
const match = /.*;q=([01].?\d*)/.exec(a);
|
|
7862
|
-
return match ? parseFloat(match[1]) : 1.0;
|
|
7863
|
-
};
|
|
7864
|
-
const byQuality = (a, b) => {
|
|
7865
|
-
return quality(b) - quality(a);
|
|
7866
|
-
};
|
|
7867
|
-
const ignoreCasing = (t) => t.toLowerCase();
|
|
7868
|
-
const ignoreWhitespace = (t) => t.replace(/\s+/g, "");
|
|
7869
|
-
const ignoreParameters = (t) => t.split(PARAMETER_SEPARATOR)[0];
|
|
7870
|
-
const removeQuality = (t) => t.replace(/;q=[01].?\d*/, "");
|
|
7871
|
-
const type$9 = (t) => t.split(TYPE_SUBTYPE_SEPARATOR)[0];
|
|
7872
|
-
const subtype = (t) => {
|
|
7873
|
-
const [_maintype, subtype] = t.split(TYPE_SUBTYPE_SEPARATOR);
|
|
7874
|
-
return subtype?.split(EXTENSION_SEPARATOR).pop();
|
|
7875
|
-
};
|
|
7876
|
-
function findMatchingType(requestType, responseTypes) {
|
|
7877
|
-
// exact match
|
|
7878
|
-
let accept = requestType
|
|
7879
|
-
.split(",")
|
|
7880
|
-
.map(ignoreWhitespace)
|
|
7881
|
-
.map(ignoreCasing)
|
|
7882
|
-
.sort(byQuality)
|
|
7883
|
-
.map(removeQuality);
|
|
7884
|
-
let available = responseTypes.map(ignoreWhitespace).map(ignoreCasing);
|
|
7885
|
-
for (const a of accept) {
|
|
7886
|
-
const matchExactly = (t) => t === a;
|
|
7887
|
-
const index = available.findIndex(matchExactly);
|
|
7888
|
-
if (index >= 0) {
|
|
7889
|
-
return responseTypes[index];
|
|
7890
|
-
}
|
|
7891
|
-
}
|
|
7892
|
-
// ignore additional parameters
|
|
7893
|
-
accept = accept.map(ignoreParameters);
|
|
7894
|
-
available = available.map(ignoreParameters);
|
|
7895
|
-
for (const a of accept) {
|
|
7896
|
-
const matchExactly = (t) => t === a;
|
|
7897
|
-
const index = available.findIndex(matchExactly);
|
|
7898
|
-
if (index >= 0) {
|
|
7899
|
-
return responseTypes[index];
|
|
7900
|
-
}
|
|
7901
|
-
}
|
|
7902
|
-
// ignore vendor extensions
|
|
7903
|
-
for (const a of accept) {
|
|
7904
|
-
const matchTypesAndSubtypes = (t) => type$9(t) === type$9(a) && subtype(t) === subtype(a);
|
|
7905
|
-
const index = available.findIndex(matchTypesAndSubtypes);
|
|
7906
|
-
if (index >= 0) {
|
|
7907
|
-
return responseTypes[index];
|
|
7908
|
-
}
|
|
7909
|
-
}
|
|
7910
|
-
// wildcards in responseTypes
|
|
7911
|
-
for (const a of accept) {
|
|
7912
|
-
const matchSubtype = (t) => subtype(t) === WILDCARD && type$9(t) === type$9(a);
|
|
7913
|
-
const index = available.findIndex(matchSubtype);
|
|
7914
|
-
if (index >= 0) {
|
|
7915
|
-
return responseTypes[index];
|
|
7916
|
-
}
|
|
7917
|
-
}
|
|
7918
|
-
if (available.includes(`${WILDCARD}/${WILDCARD}`)) {
|
|
7919
|
-
return `${WILDCARD}/${WILDCARD}`;
|
|
7920
|
-
}
|
|
7921
|
-
// wildcards in requestTypes
|
|
7922
|
-
for (const a of accept) {
|
|
7923
|
-
const matchSubtype = (t) => subtype(a) === WILDCARD && type$9(t) === type$9(a);
|
|
7924
|
-
const index = available.findIndex(matchSubtype);
|
|
7925
|
-
if (index >= 0) {
|
|
7926
|
-
return responseTypes[index];
|
|
7927
|
-
}
|
|
7928
|
-
}
|
|
7929
|
-
if (accept.includes(`${WILDCARD}/${WILDCARD}`)) {
|
|
7930
|
-
return responseTypes[0];
|
|
7931
|
-
}
|
|
7932
|
-
return undefined;
|
|
7933
|
-
}
|
|
7934
|
-
const getByContentType = (object = {}, contentType) => {
|
|
7935
|
-
const key = findMatchingType(contentType, Object.keys(object));
|
|
7936
|
-
return object[key];
|
|
7937
|
-
};
|
|
7938
|
-
const standardHttpRequestHeaders = [
|
|
7939
|
-
"accept",
|
|
7940
|
-
"accept-charset",
|
|
7941
|
-
"accept-datetime",
|
|
7942
|
-
"accept-encoding",
|
|
7943
|
-
"accept-language",
|
|
7944
|
-
"authorization",
|
|
7945
|
-
"cache-control",
|
|
7946
|
-
"connection",
|
|
7947
|
-
"content-length",
|
|
7948
|
-
"content-md5",
|
|
7949
|
-
"content-type",
|
|
7950
|
-
"cookie",
|
|
7951
|
-
"date",
|
|
7952
|
-
"expect",
|
|
7953
|
-
"forwarded",
|
|
7954
|
-
"from",
|
|
7955
|
-
"host",
|
|
7956
|
-
"if-match",
|
|
7957
|
-
"if-modified-since",
|
|
7958
|
-
"if-none-match",
|
|
7959
|
-
"if-range",
|
|
7960
|
-
"if-unmodified-since",
|
|
7961
|
-
"max-forwards",
|
|
7962
|
-
"origin",
|
|
7963
|
-
"pragma",
|
|
7964
|
-
"proxy-authorization",
|
|
7965
|
-
"range",
|
|
7966
|
-
"referer",
|
|
7967
|
-
"te",
|
|
7968
|
-
"upgrade",
|
|
7969
|
-
"user-agent",
|
|
7970
|
-
"via",
|
|
7971
|
-
"warning",
|
|
7972
|
-
];
|
|
7973
|
-
const standardHttpResponseHeaders = [
|
|
7974
|
-
"access-control-allow-origin",
|
|
7975
|
-
"accept-patch",
|
|
7976
|
-
"accept-ranges",
|
|
7977
|
-
"age",
|
|
7978
|
-
"allow",
|
|
7979
|
-
"alt-svc",
|
|
7980
|
-
"cache-control",
|
|
7981
|
-
"connection",
|
|
7982
|
-
"content-disposition",
|
|
7983
|
-
"content-encoding",
|
|
7984
|
-
"content-language",
|
|
7985
|
-
"content-length",
|
|
7986
|
-
"content-location",
|
|
7987
|
-
"content-md5",
|
|
7988
|
-
"content-range",
|
|
7989
|
-
"date",
|
|
7990
|
-
"etag",
|
|
7991
|
-
"expires",
|
|
7992
|
-
"last-modified",
|
|
7993
|
-
"link",
|
|
7994
|
-
"location",
|
|
7995
|
-
"p3p",
|
|
7996
|
-
"pragma",
|
|
7997
|
-
"proxy-authenticate",
|
|
7998
|
-
"public-key-pins",
|
|
7999
|
-
"refresh",
|
|
8000
|
-
"retry-after",
|
|
8001
|
-
"server",
|
|
8002
|
-
"set-cookie",
|
|
8003
|
-
"status",
|
|
8004
|
-
"strict-transport-security",
|
|
8005
|
-
"trailer",
|
|
8006
|
-
"transfer-encoding",
|
|
8007
|
-
"tsv",
|
|
8008
|
-
"upgrade",
|
|
8009
|
-
"vary",
|
|
8010
|
-
"via",
|
|
8011
|
-
"warning",
|
|
8012
|
-
"www-authenticate",
|
|
8013
|
-
"x-frame-options",
|
|
8014
|
-
];
|
|
8015
|
-
|
|
8016
8041
|
const parseBody = (body, contentType, legacyParser) => {
|
|
8017
8042
|
if (contentType.includes("application/x-www-form-urlencoded") &&
|
|
8018
8043
|
typeof body === "string") {
|
|
@@ -8036,13 +8061,6 @@ const parseBody = (body, contentType, legacyParser) => {
|
|
|
8036
8061
|
}
|
|
8037
8062
|
return body;
|
|
8038
8063
|
};
|
|
8039
|
-
const canValidate$1 = (contentType, disableMultipartFormdata) => {
|
|
8040
|
-
return !!findMatchingType(contentType, [
|
|
8041
|
-
"application/json",
|
|
8042
|
-
"application/x-www-form-urlencoded",
|
|
8043
|
-
disableMultipartFormdata ? "" : "multipart/form-data",
|
|
8044
|
-
].filter(Boolean));
|
|
8045
|
-
};
|
|
8046
8064
|
const DEFAULT_CONTENT_TYPE$1 = "application/json";
|
|
8047
8065
|
function* compareReqBody(ajv, route, interaction, index, config) {
|
|
8048
8066
|
const { method, oas, operation, path } = route.store;
|
|
@@ -8062,8 +8080,30 @@ function* compareReqBody(ajv, route, interaction, index, config) {
|
|
|
8062
8080
|
if (!required && body === undefined) {
|
|
8063
8081
|
return;
|
|
8064
8082
|
}
|
|
8083
|
+
const bodyStatus = bodyValidationStatus(contentType, body, VALIDATABLE_CONTENT_TYPES.filter((t) => t !== "multipart/form-data" ||
|
|
8084
|
+
!config.get("disable-multipart-formdata")));
|
|
8085
|
+
if (bodyStatus === "skip")
|
|
8086
|
+
return;
|
|
8087
|
+
if (bodyStatus === "warn" && isValidRequest(interaction)) {
|
|
8088
|
+
yield {
|
|
8089
|
+
code: "request.body.unvalidatable",
|
|
8090
|
+
message: `Body with content type '${contentType}' is not supported by the spec comparator`,
|
|
8091
|
+
mockDetails: {
|
|
8092
|
+
...baseMockDetails(interaction),
|
|
8093
|
+
location: `[root].interactions[${index}].request.body`,
|
|
8094
|
+
value: body,
|
|
8095
|
+
},
|
|
8096
|
+
specDetails: {
|
|
8097
|
+
location: `[root].paths.${path}.${method}.requestBody.content`,
|
|
8098
|
+
pathMethod: method,
|
|
8099
|
+
pathName: path,
|
|
8100
|
+
value: get$1(operation, "requestBody.content"),
|
|
8101
|
+
},
|
|
8102
|
+
type: "warning",
|
|
8103
|
+
};
|
|
8104
|
+
return;
|
|
8105
|
+
}
|
|
8065
8106
|
if (schema &&
|
|
8066
|
-
canValidate$1(contentType, config.get("disable-multipart-formdata")) &&
|
|
8067
8107
|
isValidRequest(interaction) &&
|
|
8068
8108
|
(config.get("no-validate-request-body-unless-application-json")
|
|
8069
8109
|
? !!findMatchingType("application/json", availableRequestContentTypes)
|
|
@@ -8702,9 +8742,6 @@ function* compareReqSecurity(ajv, route, interaction, index, config) {
|
|
|
8702
8742
|
|
|
8703
8743
|
const patternedStatus = (status) => `${Math.floor(status / 100)}XX`;
|
|
8704
8744
|
|
|
8705
|
-
const canValidate = (contentType = "") => {
|
|
8706
|
-
return !!findMatchingType(contentType, ["application/json"]);
|
|
8707
|
-
};
|
|
8708
8745
|
const DEFAULT_CONTENT_TYPE = "application/json";
|
|
8709
8746
|
function* compareResBody(ajv, route, interaction, index, config) {
|
|
8710
8747
|
const { method, oas, operation, path } = route.store;
|
|
@@ -8759,14 +8796,15 @@ function* compareResBody(ajv, route, interaction, index, config) {
|
|
|
8759
8796
|
type: "warning",
|
|
8760
8797
|
};
|
|
8761
8798
|
}
|
|
8762
|
-
|
|
8799
|
+
const bodyStatus = bodyValidationStatus(contentType, value);
|
|
8800
|
+
if (bodyStatus === "warn") {
|
|
8763
8801
|
yield {
|
|
8764
|
-
code: "response.body.
|
|
8765
|
-
message:
|
|
8802
|
+
code: "response.body.unvalidatable",
|
|
8803
|
+
message: `Body with content type '${contentType}' is not supported by the spec comparator`,
|
|
8766
8804
|
mockDetails: {
|
|
8767
8805
|
...baseMockDetails(interaction),
|
|
8768
8806
|
location: `[root].interactions[${index}].response.body`,
|
|
8769
|
-
value
|
|
8807
|
+
value,
|
|
8770
8808
|
},
|
|
8771
8809
|
specDetails: {
|
|
8772
8810
|
location: `[root].paths.${path}.${method}.responses.${status}.content`,
|
|
@@ -8774,32 +8812,52 @@ function* compareResBody(ajv, route, interaction, index, config) {
|
|
|
8774
8812
|
pathName: path,
|
|
8775
8813
|
value: response.content,
|
|
8776
8814
|
},
|
|
8777
|
-
type: "
|
|
8815
|
+
type: "warning",
|
|
8778
8816
|
};
|
|
8779
8817
|
}
|
|
8780
|
-
if (
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8818
|
+
else if (bodyStatus === "validate") {
|
|
8819
|
+
if (value && !schema) {
|
|
8820
|
+
yield {
|
|
8821
|
+
code: "response.body.unknown",
|
|
8822
|
+
message: "No matching schema found for response body",
|
|
8823
|
+
mockDetails: {
|
|
8824
|
+
...baseMockDetails(interaction),
|
|
8825
|
+
location: `[root].interactions[${index}].response.body`,
|
|
8826
|
+
value: value,
|
|
8827
|
+
},
|
|
8828
|
+
specDetails: {
|
|
8829
|
+
location: `[root].paths.${path}.${method}.responses.${status}.content`,
|
|
8830
|
+
pathMethod: method,
|
|
8831
|
+
pathName: path,
|
|
8832
|
+
value: response.content,
|
|
8833
|
+
},
|
|
8834
|
+
type: "error",
|
|
8835
|
+
};
|
|
8836
|
+
}
|
|
8837
|
+
if (value && schema) {
|
|
8838
|
+
const schemaId = `[root].paths.${path}.${method}.responses.${status}.content.${contentType}`;
|
|
8839
|
+
const validate = getValidateFunction(ajv, schemaId, () => transformReceivedSchema(minimumSchema(schema, oas), config.get("no-transform-non-nullable-response-schema")));
|
|
8840
|
+
if (!validate(value)) {
|
|
8841
|
+
for (const error of validate.errors) {
|
|
8842
|
+
yield {
|
|
8843
|
+
code: "response.body.incompatible",
|
|
8844
|
+
message: `Response body is incompatible with the response body schema in the spec file: ${formatMessage(error)}`,
|
|
8845
|
+
mockDetails: {
|
|
8846
|
+
...baseMockDetails(interaction),
|
|
8847
|
+
location: `[root].interactions[${index}].response.body${formatInstancePath(error)}`,
|
|
8848
|
+
value: error.instancePath
|
|
8849
|
+
? get$1(value, splitPath(error.instancePath))
|
|
8850
|
+
: value,
|
|
8851
|
+
},
|
|
8852
|
+
specDetails: {
|
|
8853
|
+
location: `${schemaId}.schema.${formatSchemaPath(error)}`,
|
|
8854
|
+
pathMethod: method,
|
|
8855
|
+
pathName: path,
|
|
8856
|
+
value: get$1(validate.schema, splitPath(error.schemaPath)),
|
|
8857
|
+
},
|
|
8858
|
+
type: "error",
|
|
8859
|
+
};
|
|
8860
|
+
}
|
|
8803
8861
|
}
|
|
8804
8862
|
}
|
|
8805
8863
|
}
|
|
@@ -8995,11 +9053,17 @@ function* compareHttpInteraction(ajvCoerce, ajvNocoerce, router, interaction, in
|
|
|
8995
9053
|
return;
|
|
8996
9054
|
}
|
|
8997
9055
|
yield* compareReqSecurity(ajvCoerce, route, interaction, index, config);
|
|
8998
|
-
|
|
9056
|
+
const reqHeaderResults = Array.from(compareReqHeader(ajvCoerce, route, interaction, index, config));
|
|
9057
|
+
yield* reqHeaderResults;
|
|
8999
9058
|
yield* compareReqQuery(ajvCoerce, route, interaction, index, config);
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9059
|
+
if (!reqHeaderResults.some((r) => r.code === "request.content-type.incompatible")) {
|
|
9060
|
+
yield* compareReqBody(ajvNocoerce, route, interaction, index, config);
|
|
9061
|
+
}
|
|
9062
|
+
const resHeaderResults = Array.from(compareResHeader(ajvCoerce, route, interaction, index));
|
|
9063
|
+
yield* resHeaderResults;
|
|
9064
|
+
if (!resHeaderResults.some((r) => r.code === "response.content-type.incompatible")) {
|
|
9065
|
+
yield* compareResBody(ajvNocoerce, route, interaction, index, config);
|
|
9066
|
+
}
|
|
9003
9067
|
}
|
|
9004
9068
|
|
|
9005
9069
|
const isSwagger2 = (oas) => Object.hasOwn(oas, "swagger") &&
|