@tetsujs/openapi 0.5.2 → 0.6.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/src/testing.ts CHANGED
@@ -59,6 +59,9 @@ export interface AssertDescribedOptions {
59
59
  * Headers the status must declare when the response carries them —
60
60
  * `retry-after`. Only those named: a response carries headers no
61
61
  * document describes, `content-type` and `x-request-id` among them.
62
+ *
63
+ * The other way needs no list: a header the status declares required —
64
+ * a `location`, a `set-cookie` — is one the response must carry.
62
65
  */
63
66
  readonly headers?: readonly string[];
64
67
  }
@@ -124,8 +127,10 @@ async function problemsOf(
124
127
  * The path template a requested path belongs to: the one whose segments
125
128
  * match it, `{param}` matching any one segment. Of several, the one with
126
129
  * the most literal segments, as the router prefers `/users/me` to
127
- * `/users/{id}`; a template ending in a parameter also stands for a
128
- * wildcard, which matches the rest of the path.
130
+ * `/users/{id}`. A template ending in `{wildcard}` — the name the
131
+ * generator gives a trailing `*` — matches the rest of the path; any other
132
+ * matches as many segments as it has, so a mistyped path is no operation
133
+ * rather than the one it happens to start like.
129
134
  */
130
135
  function templateOf(
131
136
  document: OpenApiDocument,
@@ -138,7 +143,7 @@ function templateOf(
138
143
  for (const template of Object.keys(document.paths)) {
139
144
  const parts = template.split("/").filter(Boolean);
140
145
  const last = parts.at(-1) ?? "";
141
- const rest = isParameter(last) && segments.length > parts.length;
146
+ const rest = last === "{wildcard}" && segments.length > parts.length;
142
147
 
143
148
  if (parts.length !== segments.length && !rest) {
144
149
  continue;
@@ -185,7 +190,11 @@ async function bodyProblems(
185
190
  const mediaTypes = Object.keys(content);
186
191
 
187
192
  if (text === "") {
188
- return [];
193
+ return mediaTypes.length === 0
194
+ ? []
195
+ : [
196
+ `an empty body, where its ${status} describes ${mediaTypes.join(", ")}`,
197
+ ];
189
198
  }
190
199
 
191
200
  if (mediaTypes.length === 0) {
@@ -421,7 +430,7 @@ function headerProblems(
421
430
  name.toLowerCase(),
422
431
  );
423
432
 
424
- return names
433
+ const undeclared = names
425
434
  .filter(
426
435
  (name) =>
427
436
  response.headers.has(name) && !declared.includes(name.toLowerCase()),
@@ -429,6 +438,12 @@ function headerProblems(
429
438
  .map(
430
439
  (name) => `the header "${name}", which its ${status} does not declare`,
431
440
  );
441
+
442
+ const missing = Object.entries(described.headers ?? {})
443
+ .filter(([name, header]) => header.required && !response.headers.has(name))
444
+ .map(([name]) => `no header "${name}", which its ${status} requires`);
445
+
446
+ return [...undeclared, ...missing];
432
447
  }
433
448
 
434
449
  /**