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