Skip to content

Commit 3ed1960

Browse files
authored
fix: check standard http response headers after specified headers (#29)
1 parent d857079 commit 3ed1960

File tree

6 files changed

+92
-50
lines changed

6 files changed

+92
-50
lines changed

src/__tests__/fixtures/response-header/baseline.json

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"mockDetails": {
66
"interactionDescription": "should warn about missing standard response headers",
77
"interactionState": "[none]",
8-
"location": "[root].interactions[1].response.headers.age",
8+
"location": "[root].interactions[2].response.headers.age",
99
"value": "60"
1010
},
1111
"specDetails": {
@@ -19,6 +19,11 @@
1919
"schema": {
2020
"type": "number"
2121
}
22+
},
23+
"access-control-allow-origin": {
24+
"schema": {
25+
"type": "string"
26+
}
2227
}
2328
},
2429
"content": {
@@ -36,7 +41,7 @@
3641
"mockDetails": {
3742
"interactionDescription": "should error on incompatible response headers",
3843
"interactionState": "[none]",
39-
"location": "[root].interactions[2].response.headers.specified",
44+
"location": "[root].interactions[3].response.headers.specified",
4045
"value": "abc"
4146
},
4247
"specDetails": {
@@ -57,7 +62,7 @@
5762
"mockDetails": {
5863
"interactionDescription": "should error on unspecified response headers",
5964
"interactionState": "[none]",
60-
"location": "[root].interactions[3].response.headers.unspecified",
65+
"location": "[root].interactions[4].response.headers.unspecified",
6166
"value": "foo"
6267
},
6368
"specDetails": {
@@ -71,6 +76,11 @@
7176
"schema": {
7277
"type": "number"
7378
}
79+
},
80+
"access-control-allow-origin": {
81+
"schema": {
82+
"type": "string"
83+
}
7484
}
7585
},
7686
"content": {
@@ -88,7 +98,7 @@
8898
"mockDetails": {
8999
"interactionDescription": "should error on unknown response content type",
90100
"interactionState": "[none]",
91-
"location": "[root].interactions[4].response.headers.Content-Type",
101+
"location": "[root].interactions[5].response.headers.Content-Type",
92102
"value": "text/html"
93103
},
94104
"specDetails": {

src/__tests__/fixtures/response-header/oas.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ paths:
1212
specified:
1313
schema:
1414
type: number
15+
access-control-allow-origin:
16+
schema:
17+
type: string
1518
content:
1619
application/json:
1720
schema: {}

src/__tests__/fixtures/response-header/pact.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,21 @@
1616
}
1717
}
1818
},
19+
{
20+
"description": "should pass about specified standard http response header",
21+
"providerState": "",
22+
"request": {
23+
"method": "GET",
24+
"path": "/path"
25+
},
26+
"response": {
27+
"status": 200,
28+
"headers": {
29+
"Access-Control-Allow-Origin": "*",
30+
"Content-Type": "application/json"
31+
}
32+
}
33+
},
1934
{
2035
"description": "should warn about missing standard response headers",
2136
"providerState": "",

src/__tests__/fixtures/response-header/results.json

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"mockDetails": {
66
"interactionDescription": "should warn about missing standard response headers",
77
"interactionState": "[none]",
8-
"location": "[root].interactions[1].response.headers.age"
8+
"location": "[root].interactions[2].response.headers.age"
99
},
1010
"specDetails": {
1111
"location": "[root].paths./path.get",
@@ -20,6 +20,11 @@
2020
"schema": {
2121
"type": "number"
2222
}
23+
},
24+
"access-control-allow-origin": {
25+
"schema": {
26+
"type": "string"
27+
}
2328
}
2429
},
2530
"content": {
@@ -39,7 +44,7 @@
3944
"mockDetails": {
4045
"interactionDescription": "should error on incompatible response headers",
4146
"interactionState": "[none]",
42-
"location": "[root].interactions[2].response.headers.specified",
47+
"location": "[root].interactions[3].response.headers.specified",
4348
"value": "abc"
4449
},
4550
"specDetails": {
@@ -56,7 +61,7 @@
5661
"mockDetails": {
5762
"interactionDescription": "should error on unspecified response headers",
5863
"interactionState": "[none]",
59-
"location": "[root].interactions[3].response.headers.unspecified",
64+
"location": "[root].interactions[4].response.headers.unspecified",
6065
"value": "foo"
6166
},
6267
"specDetails": {
@@ -68,6 +73,11 @@
6873
"schema": {
6974
"type": "number"
7075
}
76+
},
77+
"access-control-allow-origin": {
78+
"schema": {
79+
"type": "string"
80+
}
7181
}
7282
}
7383
},
@@ -79,7 +89,7 @@
7989
"mockDetails": {
8090
"interactionDescription": "should error on unknown response content type",
8191
"interactionState": "[none]",
82-
"location": "[root].interactions[4].response.headers.content-type",
92+
"location": "[root].interactions[5].response.headers.content-type",
8393
"value": "text/html"
8494
},
8595
"specDetails": {

src/compare/requestHeader.ts

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -280,22 +280,8 @@ export function* compareReqHeader(
280280
}
281281
}
282282

283-
// standard headers
284-
// ----------------
285-
for (const headerName of standardHttpRequestHeaders) {
286-
if (
287-
!(operation.parameters || []).find(
288-
(p: OpenAPIV3.ParameterObject) =>
289-
p.in === "header" &&
290-
p.name.toLowerCase() === headerName.toLowerCase(),
291-
)
292-
) {
293-
requestHeaders.delete(headerName);
294-
}
295-
}
296-
297-
// other headers
298-
// -------------
283+
// specified headers
284+
// -----------------
299285
for (const [parameterIndex, parameter] of (
300286
operation.parameters || []
301287
).entries()) {
@@ -362,6 +348,22 @@ export function* compareReqHeader(
362348
requestHeaders.delete(dereferencedParameter.name);
363349
}
364350

351+
// standard headers
352+
// ----------------
353+
for (const headerName of standardHttpRequestHeaders) {
354+
if (
355+
!(operation.parameters || []).find(
356+
(p: OpenAPIV3.ParameterObject) =>
357+
p.in === "header" &&
358+
p.name.toLowerCase() === headerName.toLowerCase(),
359+
)
360+
) {
361+
requestHeaders.delete(headerName);
362+
}
363+
}
364+
365+
// remaining headers
366+
// -----------------
365367
if (isValidRequest(interaction)) {
366368
for (const [headerName, headerValue] of requestHeaders.entries()) {
367369
yield {

src/compare/responseHeader.ts

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -97,32 +97,8 @@ export function* compareResHeader(
9797
}
9898
responseHeaders.delete("content-type");
9999

100-
// standard headers
101-
// ----------------
102-
for (const headerName of standardHttpResponseHeaders) {
103-
if (responseHeaders.has(headerName)) {
104-
yield {
105-
code: "response.header.undefined",
106-
message: `Standard http response header is not defined in the spec file: ${headerName}`,
107-
mockDetails: {
108-
...baseMockDetails(interaction),
109-
location: `[root].interactions[${index}].response.headers.${headerName}`,
110-
value: get(interaction, "request.headers"),
111-
},
112-
specDetails: {
113-
location: `[root].paths.${path}.${method}`,
114-
pathMethod: method,
115-
pathName: path,
116-
value: operation,
117-
},
118-
type: "warning",
119-
};
120-
}
121-
responseHeaders.delete(headerName);
122-
}
123-
124-
// other headers
125-
// -------------
100+
// specified headers
101+
// -----------------
126102
const headers =
127103
dereferenceOas(operation.responses[interaction.response.status] || {}, oas)
128104
?.headers || {};
@@ -161,6 +137,32 @@ export function* compareResHeader(
161137
responseHeaders.delete(headerName);
162138
}
163139

140+
// standard headers
141+
// ----------------
142+
for (const headerName of standardHttpResponseHeaders) {
143+
if (responseHeaders.has(headerName)) {
144+
yield {
145+
code: "response.header.undefined",
146+
message: `Standard http response header is not defined in the spec file: ${headerName}`,
147+
mockDetails: {
148+
...baseMockDetails(interaction),
149+
location: `[root].interactions[${index}].response.headers.${headerName}`,
150+
value: get(interaction, "request.headers"),
151+
},
152+
specDetails: {
153+
location: `[root].paths.${path}.${method}`,
154+
pathMethod: method,
155+
pathName: path,
156+
value: operation,
157+
},
158+
type: "warning",
159+
};
160+
}
161+
responseHeaders.delete(headerName);
162+
}
163+
164+
// remaining headers
165+
// -----------------
164166
for (const [headerName, headerValue] of responseHeaders.entries()) {
165167
yield {
166168
code: "response.header.unknown",

0 commit comments

Comments
 (0)