@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/cli.cjs
CHANGED
|
@@ -4177,7 +4177,7 @@ function useColor() {
|
|
|
4177
4177
|
|
|
4178
4178
|
const program = new Command();
|
|
4179
4179
|
|
|
4180
|
-
var version = "2.0
|
|
4180
|
+
var version = "2.2.0";
|
|
4181
4181
|
var packageJson = {
|
|
4182
4182
|
version: version};
|
|
4183
4183
|
|
|
@@ -10808,14 +10808,199 @@ function* compareMessageHeaders(ajv, message, content, interactionContext, metad
|
|
|
10808
10808
|
}
|
|
10809
10809
|
}
|
|
10810
10810
|
|
|
10811
|
-
const
|
|
10811
|
+
const PARAMETER_SEPARATOR = ";";
|
|
10812
|
+
const WILDCARD = "*";
|
|
10813
|
+
const TYPE_SUBTYPE_SEPARATOR = "/";
|
|
10814
|
+
const EXTENSION_SEPARATOR = "+";
|
|
10815
|
+
const quality = (a) => {
|
|
10816
|
+
const match = /.*;q=([01].?\d*)/.exec(a);
|
|
10817
|
+
return match ? parseFloat(match[1]) : 1.0;
|
|
10818
|
+
};
|
|
10819
|
+
const byQuality = (a, b) => {
|
|
10820
|
+
return quality(b) - quality(a);
|
|
10821
|
+
};
|
|
10822
|
+
const ignoreCasing = (t) => t.toLowerCase();
|
|
10823
|
+
const ignoreWhitespace = (t) => t.replace(/\s+/g, "");
|
|
10824
|
+
const ignoreParameters = (t) => t.split(PARAMETER_SEPARATOR)[0];
|
|
10825
|
+
const removeQuality = (t) => t.replace(/;q=[01].?\d*/, "");
|
|
10826
|
+
const type$a = (t) => t.split(TYPE_SUBTYPE_SEPARATOR)[0];
|
|
10827
|
+
const subtype = (t) => {
|
|
10828
|
+
const [_maintype, subtype] = t.split(TYPE_SUBTYPE_SEPARATOR);
|
|
10829
|
+
return subtype?.split(EXTENSION_SEPARATOR).pop();
|
|
10830
|
+
};
|
|
10831
|
+
function findMatchingType(requestType, responseTypes) {
|
|
10832
|
+
// exact match
|
|
10833
|
+
let accept = requestType
|
|
10834
|
+
.split(",")
|
|
10835
|
+
.map(ignoreWhitespace)
|
|
10836
|
+
.map(ignoreCasing)
|
|
10837
|
+
.sort(byQuality)
|
|
10838
|
+
.map(removeQuality);
|
|
10839
|
+
let available = responseTypes.map(ignoreWhitespace).map(ignoreCasing);
|
|
10840
|
+
for (const a of accept) {
|
|
10841
|
+
const matchExactly = (t) => t === a;
|
|
10842
|
+
const index = available.findIndex(matchExactly);
|
|
10843
|
+
if (index >= 0) {
|
|
10844
|
+
return responseTypes[index];
|
|
10845
|
+
}
|
|
10846
|
+
}
|
|
10847
|
+
// ignore additional parameters
|
|
10848
|
+
accept = accept.map(ignoreParameters);
|
|
10849
|
+
available = available.map(ignoreParameters);
|
|
10850
|
+
for (const a of accept) {
|
|
10851
|
+
const matchExactly = (t) => t === a;
|
|
10852
|
+
const index = available.findIndex(matchExactly);
|
|
10853
|
+
if (index >= 0) {
|
|
10854
|
+
return responseTypes[index];
|
|
10855
|
+
}
|
|
10856
|
+
}
|
|
10857
|
+
// ignore vendor extensions
|
|
10858
|
+
for (const a of accept) {
|
|
10859
|
+
const matchTypesAndSubtypes = (t) => type$a(t) === type$a(a) && subtype(t) === subtype(a);
|
|
10860
|
+
const index = available.findIndex(matchTypesAndSubtypes);
|
|
10861
|
+
if (index >= 0) {
|
|
10862
|
+
return responseTypes[index];
|
|
10863
|
+
}
|
|
10864
|
+
}
|
|
10865
|
+
// wildcards in responseTypes
|
|
10866
|
+
for (const a of accept) {
|
|
10867
|
+
const matchSubtype = (t) => subtype(t) === WILDCARD && type$a(t) === type$a(a);
|
|
10868
|
+
const index = available.findIndex(matchSubtype);
|
|
10869
|
+
if (index >= 0) {
|
|
10870
|
+
return responseTypes[index];
|
|
10871
|
+
}
|
|
10872
|
+
}
|
|
10873
|
+
if (available.includes(`${WILDCARD}/${WILDCARD}`)) {
|
|
10874
|
+
return `${WILDCARD}/${WILDCARD}`;
|
|
10875
|
+
}
|
|
10876
|
+
// wildcards in requestTypes
|
|
10877
|
+
for (const a of accept) {
|
|
10878
|
+
const matchSubtype = (t) => subtype(a) === WILDCARD && type$a(t) === type$a(a);
|
|
10879
|
+
const index = available.findIndex(matchSubtype);
|
|
10880
|
+
if (index >= 0) {
|
|
10881
|
+
return responseTypes[index];
|
|
10882
|
+
}
|
|
10883
|
+
}
|
|
10884
|
+
if (accept.includes(`${WILDCARD}/${WILDCARD}`)) {
|
|
10885
|
+
return responseTypes[0];
|
|
10886
|
+
}
|
|
10887
|
+
return undefined;
|
|
10888
|
+
}
|
|
10889
|
+
const getByContentType = (object = {}, contentType) => {
|
|
10890
|
+
const key = findMatchingType(contentType, Object.keys(object));
|
|
10891
|
+
return object[key];
|
|
10892
|
+
};
|
|
10893
|
+
const standardHttpRequestHeaders = [
|
|
10894
|
+
"accept",
|
|
10895
|
+
"accept-charset",
|
|
10896
|
+
"accept-datetime",
|
|
10897
|
+
"accept-encoding",
|
|
10898
|
+
"accept-language",
|
|
10899
|
+
"authorization",
|
|
10900
|
+
"cache-control",
|
|
10901
|
+
"connection",
|
|
10902
|
+
"content-length",
|
|
10903
|
+
"content-md5",
|
|
10904
|
+
"content-type",
|
|
10905
|
+
"cookie",
|
|
10906
|
+
"date",
|
|
10907
|
+
"expect",
|
|
10908
|
+
"forwarded",
|
|
10909
|
+
"from",
|
|
10910
|
+
"host",
|
|
10911
|
+
"if-match",
|
|
10912
|
+
"if-modified-since",
|
|
10913
|
+
"if-none-match",
|
|
10914
|
+
"if-range",
|
|
10915
|
+
"if-unmodified-since",
|
|
10916
|
+
"max-forwards",
|
|
10917
|
+
"origin",
|
|
10918
|
+
"pragma",
|
|
10919
|
+
"proxy-authorization",
|
|
10920
|
+
"range",
|
|
10921
|
+
"referer",
|
|
10922
|
+
"te",
|
|
10923
|
+
"upgrade",
|
|
10924
|
+
"user-agent",
|
|
10925
|
+
"via",
|
|
10926
|
+
"warning",
|
|
10927
|
+
];
|
|
10928
|
+
const standardHttpResponseHeaders = [
|
|
10929
|
+
"access-control-allow-origin",
|
|
10930
|
+
"accept-patch",
|
|
10931
|
+
"accept-ranges",
|
|
10932
|
+
"age",
|
|
10933
|
+
"allow",
|
|
10934
|
+
"alt-svc",
|
|
10935
|
+
"cache-control",
|
|
10936
|
+
"connection",
|
|
10937
|
+
"content-disposition",
|
|
10938
|
+
"content-encoding",
|
|
10939
|
+
"content-language",
|
|
10940
|
+
"content-length",
|
|
10941
|
+
"content-location",
|
|
10942
|
+
"content-md5",
|
|
10943
|
+
"content-range",
|
|
10944
|
+
"date",
|
|
10945
|
+
"etag",
|
|
10946
|
+
"expires",
|
|
10947
|
+
"last-modified",
|
|
10948
|
+
"link",
|
|
10949
|
+
"location",
|
|
10950
|
+
"p3p",
|
|
10951
|
+
"pragma",
|
|
10952
|
+
"proxy-authenticate",
|
|
10953
|
+
"public-key-pins",
|
|
10954
|
+
"refresh",
|
|
10955
|
+
"retry-after",
|
|
10956
|
+
"server",
|
|
10957
|
+
"set-cookie",
|
|
10958
|
+
"status",
|
|
10959
|
+
"strict-transport-security",
|
|
10960
|
+
"trailer",
|
|
10961
|
+
"transfer-encoding",
|
|
10962
|
+
"tsv",
|
|
10963
|
+
"upgrade",
|
|
10964
|
+
"vary",
|
|
10965
|
+
"via",
|
|
10966
|
+
"warning",
|
|
10967
|
+
"www-authenticate",
|
|
10968
|
+
"x-frame-options",
|
|
10969
|
+
];
|
|
10970
|
+
|
|
10971
|
+
const VALIDATABLE_CONTENT_TYPES = [
|
|
10972
|
+
"application/json",
|
|
10973
|
+
"application/x-www-form-urlencoded",
|
|
10974
|
+
"multipart/form-data",
|
|
10975
|
+
];
|
|
10976
|
+
const bodyValidationStatus = (contentType, body, validatableTypes = [...VALIDATABLE_CONTENT_TYPES]) => {
|
|
10812
10977
|
if (typeof contentType !== "string")
|
|
10813
|
-
return
|
|
10814
|
-
|
|
10815
|
-
|
|
10978
|
+
return "skip";
|
|
10979
|
+
if (findMatchingType(contentType, validatableTypes))
|
|
10980
|
+
return "validate";
|
|
10981
|
+
return body !== undefined ? "warn" : "skip";
|
|
10816
10982
|
};
|
|
10983
|
+
|
|
10817
10984
|
function* compareMessagePayload(ajv, message, content, interactionContext, contentLocation, messagePath, direction) {
|
|
10818
|
-
|
|
10985
|
+
const status = bodyValidationStatus(content.contentType, content.payload);
|
|
10986
|
+
if (status === "warn") {
|
|
10987
|
+
yield {
|
|
10988
|
+
code: "message.payload.unvalidatable",
|
|
10989
|
+
message: `Body with content type '${content.contentType}' is not supported by the spec comparator`,
|
|
10990
|
+
mockDetails: {
|
|
10991
|
+
...baseMockDetails(interactionContext),
|
|
10992
|
+
location: contentLocation,
|
|
10993
|
+
value: content.payload,
|
|
10994
|
+
},
|
|
10995
|
+
specDetails: {
|
|
10996
|
+
location: messagePath,
|
|
10997
|
+
value: undefined,
|
|
10998
|
+
},
|
|
10999
|
+
type: "warning",
|
|
11000
|
+
};
|
|
11001
|
+
return;
|
|
11002
|
+
}
|
|
11003
|
+
if (status === "skip")
|
|
10819
11004
|
return;
|
|
10820
11005
|
if (!message.payload) {
|
|
10821
11006
|
if (content.payload !== undefined) {
|
|
@@ -11278,16 +11463,16 @@ function requireMultipart () {
|
|
|
11278
11463
|
var multipartExports = requireMultipart();
|
|
11279
11464
|
var multipart = /*@__PURE__*/getDefaultExportFromCjs(multipartExports);
|
|
11280
11465
|
|
|
11281
|
-
var type$
|
|
11466
|
+
var type$9;
|
|
11282
11467
|
var hasRequiredType;
|
|
11283
11468
|
|
|
11284
11469
|
function requireType () {
|
|
11285
|
-
if (hasRequiredType) return type$
|
|
11470
|
+
if (hasRequiredType) return type$9;
|
|
11286
11471
|
hasRequiredType = 1;
|
|
11287
11472
|
|
|
11288
11473
|
/** @type {import('./type')} */
|
|
11289
|
-
type$
|
|
11290
|
-
return type$
|
|
11474
|
+
type$9 = TypeError;
|
|
11475
|
+
return type$9;
|
|
11291
11476
|
}
|
|
11292
11477
|
|
|
11293
11478
|
var util_inspect;
|
|
@@ -14409,166 +14594,6 @@ var qs = /*@__PURE__*/getDefaultExportFromCjs(libExports);
|
|
|
14409
14594
|
|
|
14410
14595
|
const isValidRequest = (interaction) => interaction.response.status < 400;
|
|
14411
14596
|
|
|
14412
|
-
const PARAMETER_SEPARATOR = ";";
|
|
14413
|
-
const WILDCARD = "*";
|
|
14414
|
-
const TYPE_SUBTYPE_SEPARATOR = "/";
|
|
14415
|
-
const EXTENSION_SEPARATOR = "+";
|
|
14416
|
-
const quality = (a) => {
|
|
14417
|
-
const match = /.*;q=([01].?\d*)/.exec(a);
|
|
14418
|
-
return match ? parseFloat(match[1]) : 1.0;
|
|
14419
|
-
};
|
|
14420
|
-
const byQuality = (a, b) => {
|
|
14421
|
-
return quality(b) - quality(a);
|
|
14422
|
-
};
|
|
14423
|
-
const ignoreCasing = (t) => t.toLowerCase();
|
|
14424
|
-
const ignoreWhitespace = (t) => t.replace(/\s+/g, "");
|
|
14425
|
-
const ignoreParameters = (t) => t.split(PARAMETER_SEPARATOR)[0];
|
|
14426
|
-
const removeQuality = (t) => t.replace(/;q=[01].?\d*/, "");
|
|
14427
|
-
const type$9 = (t) => t.split(TYPE_SUBTYPE_SEPARATOR)[0];
|
|
14428
|
-
const subtype = (t) => {
|
|
14429
|
-
const [_maintype, subtype] = t.split(TYPE_SUBTYPE_SEPARATOR);
|
|
14430
|
-
return subtype?.split(EXTENSION_SEPARATOR).pop();
|
|
14431
|
-
};
|
|
14432
|
-
function findMatchingType(requestType, responseTypes) {
|
|
14433
|
-
// exact match
|
|
14434
|
-
let accept = requestType
|
|
14435
|
-
.split(",")
|
|
14436
|
-
.map(ignoreWhitespace)
|
|
14437
|
-
.map(ignoreCasing)
|
|
14438
|
-
.sort(byQuality)
|
|
14439
|
-
.map(removeQuality);
|
|
14440
|
-
let available = responseTypes.map(ignoreWhitespace).map(ignoreCasing);
|
|
14441
|
-
for (const a of accept) {
|
|
14442
|
-
const matchExactly = (t) => t === a;
|
|
14443
|
-
const index = available.findIndex(matchExactly);
|
|
14444
|
-
if (index >= 0) {
|
|
14445
|
-
return responseTypes[index];
|
|
14446
|
-
}
|
|
14447
|
-
}
|
|
14448
|
-
// ignore additional parameters
|
|
14449
|
-
accept = accept.map(ignoreParameters);
|
|
14450
|
-
available = available.map(ignoreParameters);
|
|
14451
|
-
for (const a of accept) {
|
|
14452
|
-
const matchExactly = (t) => t === a;
|
|
14453
|
-
const index = available.findIndex(matchExactly);
|
|
14454
|
-
if (index >= 0) {
|
|
14455
|
-
return responseTypes[index];
|
|
14456
|
-
}
|
|
14457
|
-
}
|
|
14458
|
-
// ignore vendor extensions
|
|
14459
|
-
for (const a of accept) {
|
|
14460
|
-
const matchTypesAndSubtypes = (t) => type$9(t) === type$9(a) && subtype(t) === subtype(a);
|
|
14461
|
-
const index = available.findIndex(matchTypesAndSubtypes);
|
|
14462
|
-
if (index >= 0) {
|
|
14463
|
-
return responseTypes[index];
|
|
14464
|
-
}
|
|
14465
|
-
}
|
|
14466
|
-
// wildcards in responseTypes
|
|
14467
|
-
for (const a of accept) {
|
|
14468
|
-
const matchSubtype = (t) => subtype(t) === WILDCARD && type$9(t) === type$9(a);
|
|
14469
|
-
const index = available.findIndex(matchSubtype);
|
|
14470
|
-
if (index >= 0) {
|
|
14471
|
-
return responseTypes[index];
|
|
14472
|
-
}
|
|
14473
|
-
}
|
|
14474
|
-
if (available.includes(`${WILDCARD}/${WILDCARD}`)) {
|
|
14475
|
-
return `${WILDCARD}/${WILDCARD}`;
|
|
14476
|
-
}
|
|
14477
|
-
// wildcards in requestTypes
|
|
14478
|
-
for (const a of accept) {
|
|
14479
|
-
const matchSubtype = (t) => subtype(a) === WILDCARD && type$9(t) === type$9(a);
|
|
14480
|
-
const index = available.findIndex(matchSubtype);
|
|
14481
|
-
if (index >= 0) {
|
|
14482
|
-
return responseTypes[index];
|
|
14483
|
-
}
|
|
14484
|
-
}
|
|
14485
|
-
if (accept.includes(`${WILDCARD}/${WILDCARD}`)) {
|
|
14486
|
-
return responseTypes[0];
|
|
14487
|
-
}
|
|
14488
|
-
return undefined;
|
|
14489
|
-
}
|
|
14490
|
-
const getByContentType = (object = {}, contentType) => {
|
|
14491
|
-
const key = findMatchingType(contentType, Object.keys(object));
|
|
14492
|
-
return object[key];
|
|
14493
|
-
};
|
|
14494
|
-
const standardHttpRequestHeaders = [
|
|
14495
|
-
"accept",
|
|
14496
|
-
"accept-charset",
|
|
14497
|
-
"accept-datetime",
|
|
14498
|
-
"accept-encoding",
|
|
14499
|
-
"accept-language",
|
|
14500
|
-
"authorization",
|
|
14501
|
-
"cache-control",
|
|
14502
|
-
"connection",
|
|
14503
|
-
"content-length",
|
|
14504
|
-
"content-md5",
|
|
14505
|
-
"content-type",
|
|
14506
|
-
"cookie",
|
|
14507
|
-
"date",
|
|
14508
|
-
"expect",
|
|
14509
|
-
"forwarded",
|
|
14510
|
-
"from",
|
|
14511
|
-
"host",
|
|
14512
|
-
"if-match",
|
|
14513
|
-
"if-modified-since",
|
|
14514
|
-
"if-none-match",
|
|
14515
|
-
"if-range",
|
|
14516
|
-
"if-unmodified-since",
|
|
14517
|
-
"max-forwards",
|
|
14518
|
-
"origin",
|
|
14519
|
-
"pragma",
|
|
14520
|
-
"proxy-authorization",
|
|
14521
|
-
"range",
|
|
14522
|
-
"referer",
|
|
14523
|
-
"te",
|
|
14524
|
-
"upgrade",
|
|
14525
|
-
"user-agent",
|
|
14526
|
-
"via",
|
|
14527
|
-
"warning",
|
|
14528
|
-
];
|
|
14529
|
-
const standardHttpResponseHeaders = [
|
|
14530
|
-
"access-control-allow-origin",
|
|
14531
|
-
"accept-patch",
|
|
14532
|
-
"accept-ranges",
|
|
14533
|
-
"age",
|
|
14534
|
-
"allow",
|
|
14535
|
-
"alt-svc",
|
|
14536
|
-
"cache-control",
|
|
14537
|
-
"connection",
|
|
14538
|
-
"content-disposition",
|
|
14539
|
-
"content-encoding",
|
|
14540
|
-
"content-language",
|
|
14541
|
-
"content-length",
|
|
14542
|
-
"content-location",
|
|
14543
|
-
"content-md5",
|
|
14544
|
-
"content-range",
|
|
14545
|
-
"date",
|
|
14546
|
-
"etag",
|
|
14547
|
-
"expires",
|
|
14548
|
-
"last-modified",
|
|
14549
|
-
"link",
|
|
14550
|
-
"location",
|
|
14551
|
-
"p3p",
|
|
14552
|
-
"pragma",
|
|
14553
|
-
"proxy-authenticate",
|
|
14554
|
-
"public-key-pins",
|
|
14555
|
-
"refresh",
|
|
14556
|
-
"retry-after",
|
|
14557
|
-
"server",
|
|
14558
|
-
"set-cookie",
|
|
14559
|
-
"status",
|
|
14560
|
-
"strict-transport-security",
|
|
14561
|
-
"trailer",
|
|
14562
|
-
"transfer-encoding",
|
|
14563
|
-
"tsv",
|
|
14564
|
-
"upgrade",
|
|
14565
|
-
"vary",
|
|
14566
|
-
"via",
|
|
14567
|
-
"warning",
|
|
14568
|
-
"www-authenticate",
|
|
14569
|
-
"x-frame-options",
|
|
14570
|
-
];
|
|
14571
|
-
|
|
14572
14597
|
const parseBody = (body, contentType, legacyParser) => {
|
|
14573
14598
|
if (contentType.includes("application/x-www-form-urlencoded") &&
|
|
14574
14599
|
typeof body === "string") {
|
|
@@ -14592,13 +14617,6 @@ const parseBody = (body, contentType, legacyParser) => {
|
|
|
14592
14617
|
}
|
|
14593
14618
|
return body;
|
|
14594
14619
|
};
|
|
14595
|
-
const canValidate$1 = (contentType, disableMultipartFormdata) => {
|
|
14596
|
-
return !!findMatchingType(contentType, [
|
|
14597
|
-
"application/json",
|
|
14598
|
-
"application/x-www-form-urlencoded",
|
|
14599
|
-
disableMultipartFormdata ? "" : "multipart/form-data",
|
|
14600
|
-
].filter(Boolean));
|
|
14601
|
-
};
|
|
14602
14620
|
const DEFAULT_CONTENT_TYPE$1 = "application/json";
|
|
14603
14621
|
function* compareReqBody(ajv, route, interaction, index, config) {
|
|
14604
14622
|
const { method, oas, operation, path } = route.store;
|
|
@@ -14618,8 +14636,30 @@ function* compareReqBody(ajv, route, interaction, index, config) {
|
|
|
14618
14636
|
if (!required && body === undefined) {
|
|
14619
14637
|
return;
|
|
14620
14638
|
}
|
|
14639
|
+
const bodyStatus = bodyValidationStatus(contentType, body, VALIDATABLE_CONTENT_TYPES.filter((t) => t !== "multipart/form-data" ||
|
|
14640
|
+
!config.get("disable-multipart-formdata")));
|
|
14641
|
+
if (bodyStatus === "skip")
|
|
14642
|
+
return;
|
|
14643
|
+
if (bodyStatus === "warn" && isValidRequest(interaction)) {
|
|
14644
|
+
yield {
|
|
14645
|
+
code: "request.body.unvalidatable",
|
|
14646
|
+
message: `Body with content type '${contentType}' is not supported by the spec comparator`,
|
|
14647
|
+
mockDetails: {
|
|
14648
|
+
...baseMockDetails(interaction),
|
|
14649
|
+
location: `[root].interactions[${index}].request.body`,
|
|
14650
|
+
value: body,
|
|
14651
|
+
},
|
|
14652
|
+
specDetails: {
|
|
14653
|
+
location: `[root].paths.${path}.${method}.requestBody.content`,
|
|
14654
|
+
pathMethod: method,
|
|
14655
|
+
pathName: path,
|
|
14656
|
+
value: get$1(operation, "requestBody.content"),
|
|
14657
|
+
},
|
|
14658
|
+
type: "warning",
|
|
14659
|
+
};
|
|
14660
|
+
return;
|
|
14661
|
+
}
|
|
14621
14662
|
if (schema &&
|
|
14622
|
-
canValidate$1(contentType, config.get("disable-multipart-formdata")) &&
|
|
14623
14663
|
isValidRequest(interaction) &&
|
|
14624
14664
|
(config.get("no-validate-request-body-unless-application-json")
|
|
14625
14665
|
? !!findMatchingType("application/json", availableRequestContentTypes)
|
|
@@ -15258,9 +15298,6 @@ function* compareReqSecurity(ajv, route, interaction, index, config) {
|
|
|
15258
15298
|
|
|
15259
15299
|
const patternedStatus = (status) => `${Math.floor(status / 100)}XX`;
|
|
15260
15300
|
|
|
15261
|
-
const canValidate = (contentType = "") => {
|
|
15262
|
-
return !!findMatchingType(contentType, ["application/json"]);
|
|
15263
|
-
};
|
|
15264
15301
|
const DEFAULT_CONTENT_TYPE = "application/json";
|
|
15265
15302
|
function* compareResBody(ajv, route, interaction, index, config) {
|
|
15266
15303
|
const { method, oas, operation, path } = route.store;
|
|
@@ -15315,14 +15352,15 @@ function* compareResBody(ajv, route, interaction, index, config) {
|
|
|
15315
15352
|
type: "warning",
|
|
15316
15353
|
};
|
|
15317
15354
|
}
|
|
15318
|
-
|
|
15355
|
+
const bodyStatus = bodyValidationStatus(contentType, value);
|
|
15356
|
+
if (bodyStatus === "warn") {
|
|
15319
15357
|
yield {
|
|
15320
|
-
code: "response.body.
|
|
15321
|
-
message:
|
|
15358
|
+
code: "response.body.unvalidatable",
|
|
15359
|
+
message: `Body with content type '${contentType}' is not supported by the spec comparator`,
|
|
15322
15360
|
mockDetails: {
|
|
15323
15361
|
...baseMockDetails(interaction),
|
|
15324
15362
|
location: `[root].interactions[${index}].response.body`,
|
|
15325
|
-
value
|
|
15363
|
+
value,
|
|
15326
15364
|
},
|
|
15327
15365
|
specDetails: {
|
|
15328
15366
|
location: `[root].paths.${path}.${method}.responses.${status}.content`,
|
|
@@ -15330,32 +15368,52 @@ function* compareResBody(ajv, route, interaction, index, config) {
|
|
|
15330
15368
|
pathName: path,
|
|
15331
15369
|
value: response.content,
|
|
15332
15370
|
},
|
|
15333
|
-
type: "
|
|
15371
|
+
type: "warning",
|
|
15334
15372
|
};
|
|
15335
15373
|
}
|
|
15336
|
-
if (
|
|
15337
|
-
|
|
15338
|
-
|
|
15339
|
-
|
|
15340
|
-
|
|
15341
|
-
|
|
15342
|
-
|
|
15343
|
-
|
|
15344
|
-
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
|
|
15348
|
-
|
|
15349
|
-
|
|
15350
|
-
|
|
15351
|
-
|
|
15352
|
-
|
|
15353
|
-
|
|
15354
|
-
|
|
15355
|
-
|
|
15356
|
-
|
|
15357
|
-
|
|
15358
|
-
|
|
15374
|
+
else if (bodyStatus === "validate") {
|
|
15375
|
+
if (value && !schema) {
|
|
15376
|
+
yield {
|
|
15377
|
+
code: "response.body.unknown",
|
|
15378
|
+
message: "No matching schema found for response body",
|
|
15379
|
+
mockDetails: {
|
|
15380
|
+
...baseMockDetails(interaction),
|
|
15381
|
+
location: `[root].interactions[${index}].response.body`,
|
|
15382
|
+
value: value,
|
|
15383
|
+
},
|
|
15384
|
+
specDetails: {
|
|
15385
|
+
location: `[root].paths.${path}.${method}.responses.${status}.content`,
|
|
15386
|
+
pathMethod: method,
|
|
15387
|
+
pathName: path,
|
|
15388
|
+
value: response.content,
|
|
15389
|
+
},
|
|
15390
|
+
type: "error",
|
|
15391
|
+
};
|
|
15392
|
+
}
|
|
15393
|
+
if (value && schema) {
|
|
15394
|
+
const schemaId = `[root].paths.${path}.${method}.responses.${status}.content.${contentType}`;
|
|
15395
|
+
const validate = getValidateFunction(ajv, schemaId, () => transformReceivedSchema(minimumSchema(schema, oas), config.get("no-transform-non-nullable-response-schema")));
|
|
15396
|
+
if (!validate(value)) {
|
|
15397
|
+
for (const error of validate.errors) {
|
|
15398
|
+
yield {
|
|
15399
|
+
code: "response.body.incompatible",
|
|
15400
|
+
message: `Response body is incompatible with the response body schema in the spec file: ${formatMessage(error)}`,
|
|
15401
|
+
mockDetails: {
|
|
15402
|
+
...baseMockDetails(interaction),
|
|
15403
|
+
location: `[root].interactions[${index}].response.body${formatInstancePath(error)}`,
|
|
15404
|
+
value: error.instancePath
|
|
15405
|
+
? get$1(value, splitPath(error.instancePath))
|
|
15406
|
+
: value,
|
|
15407
|
+
},
|
|
15408
|
+
specDetails: {
|
|
15409
|
+
location: `${schemaId}.schema.${formatSchemaPath(error)}`,
|
|
15410
|
+
pathMethod: method,
|
|
15411
|
+
pathName: path,
|
|
15412
|
+
value: get$1(validate.schema, splitPath(error.schemaPath)),
|
|
15413
|
+
},
|
|
15414
|
+
type: "error",
|
|
15415
|
+
};
|
|
15416
|
+
}
|
|
15359
15417
|
}
|
|
15360
15418
|
}
|
|
15361
15419
|
}
|
|
@@ -15551,11 +15609,17 @@ function* compareHttpInteraction(ajvCoerce, ajvNocoerce, router, interaction, in
|
|
|
15551
15609
|
return;
|
|
15552
15610
|
}
|
|
15553
15611
|
yield* compareReqSecurity(ajvCoerce, route, interaction, index, config);
|
|
15554
|
-
|
|
15612
|
+
const reqHeaderResults = Array.from(compareReqHeader(ajvCoerce, route, interaction, index, config));
|
|
15613
|
+
yield* reqHeaderResults;
|
|
15555
15614
|
yield* compareReqQuery(ajvCoerce, route, interaction, index, config);
|
|
15556
|
-
|
|
15557
|
-
|
|
15558
|
-
|
|
15615
|
+
if (!reqHeaderResults.some((r) => r.code === "request.content-type.incompatible")) {
|
|
15616
|
+
yield* compareReqBody(ajvNocoerce, route, interaction, index, config);
|
|
15617
|
+
}
|
|
15618
|
+
const resHeaderResults = Array.from(compareResHeader(ajvCoerce, route, interaction, index));
|
|
15619
|
+
yield* resHeaderResults;
|
|
15620
|
+
if (!resHeaderResults.some((r) => r.code === "response.content-type.incompatible")) {
|
|
15621
|
+
yield* compareResBody(ajvNocoerce, route, interaction, index, config);
|
|
15622
|
+
}
|
|
15559
15623
|
}
|
|
15560
15624
|
|
|
15561
15625
|
const isSwagger2 = (oas) => Object.hasOwn(oas, "swagger") &&
|