@redocly/openapi-core 1.0.0-rc.3 → 1.0.1

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.
Files changed (275) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/__tests__/utils.ts +88 -0
  3. package/lib/config/all.js +0 -1
  4. package/lib/config/minimal.js +0 -1
  5. package/lib/config/recommended.js +0 -1
  6. package/package.json +1 -1
  7. package/src/__tests__/__snapshots__/bundle.test.ts.snap +437 -0
  8. package/src/__tests__/bundle.test.ts +236 -0
  9. package/src/__tests__/codeframes.test.ts +530 -0
  10. package/src/__tests__/fixtures/.redocly.lint-ignore.yaml +5 -0
  11. package/src/__tests__/fixtures/extension.js +24 -0
  12. package/src/__tests__/fixtures/refs/definitions.yaml +3 -0
  13. package/src/__tests__/fixtures/refs/examples.yaml +8 -0
  14. package/src/__tests__/fixtures/refs/external-request-body.yaml +13 -0
  15. package/src/__tests__/fixtures/refs/externalref.yaml +35 -0
  16. package/src/__tests__/fixtures/refs/hosted.yaml +35 -0
  17. package/src/__tests__/fixtures/refs/openapi-with-external-refs-conflicting-names.yaml +21 -0
  18. package/src/__tests__/fixtures/refs/openapi-with-external-refs.yaml +33 -0
  19. package/src/__tests__/fixtures/refs/openapi-with-url-refs.yaml +18 -0
  20. package/src/__tests__/fixtures/refs/param-b.yaml +1 -0
  21. package/src/__tests__/fixtures/refs/param-c.yaml +1 -0
  22. package/src/__tests__/fixtures/refs/rename.yaml +1 -0
  23. package/src/__tests__/fixtures/refs/requestBody.yaml +9 -0
  24. package/src/__tests__/fixtures/refs/schema-a.yaml +1 -0
  25. package/src/__tests__/fixtures/refs/simple.yaml +1 -0
  26. package/src/__tests__/fixtures/refs/vendor.schema.yaml +20 -0
  27. package/src/__tests__/fixtures/resolve/External.yaml +10 -0
  28. package/src/__tests__/fixtures/resolve/External2.yaml +4 -0
  29. package/src/__tests__/fixtures/resolve/description.md +3 -0
  30. package/src/__tests__/fixtures/resolve/externalInfo.yaml +4 -0
  31. package/src/__tests__/fixtures/resolve/externalLicense.yaml +1 -0
  32. package/src/__tests__/fixtures/resolve/openapi-with-back.yaml +13 -0
  33. package/src/__tests__/fixtures/resolve/openapi-with-md-description.yaml +5 -0
  34. package/src/__tests__/fixtures/resolve/openapi.yaml +28 -0
  35. package/src/__tests__/fixtures/resolve/schemas/type-a.yaml +10 -0
  36. package/src/__tests__/fixtures/resolve/schemas/type-b.yaml +6 -0
  37. package/src/__tests__/fixtures/resolve/transitive/a.yaml +1 -0
  38. package/src/__tests__/fixtures/resolve/transitive/components.yaml +5 -0
  39. package/src/__tests__/fixtures/resolve/transitive/schemas.yaml +3 -0
  40. package/src/__tests__/format.test.ts +76 -0
  41. package/src/__tests__/js-yaml.test.ts +73 -0
  42. package/src/__tests__/lint.test.ts +392 -0
  43. package/src/__tests__/logger-browser.test.ts +53 -0
  44. package/src/__tests__/logger.test.ts +47 -0
  45. package/src/__tests__/login.test.ts +17 -0
  46. package/src/__tests__/normalizeVisitors.test.ts +151 -0
  47. package/src/__tests__/output-browser.test.ts +18 -0
  48. package/src/__tests__/output.test.ts +15 -0
  49. package/src/__tests__/ref-utils.test.ts +120 -0
  50. package/src/__tests__/resolve-http.test.ts +77 -0
  51. package/src/__tests__/resolve.test.ts +431 -0
  52. package/src/__tests__/utils-browser.test.ts +11 -0
  53. package/src/__tests__/utils.test.ts +144 -0
  54. package/src/__tests__/walk.test.ts +1545 -0
  55. package/src/benchmark/benches/lint-with-many-rules.bench.ts +35 -0
  56. package/src/benchmark/benches/lint-with-nested-rule.bench.ts +39 -0
  57. package/src/benchmark/benches/lint-with-no-rules.bench.ts +20 -0
  58. package/src/benchmark/benches/lint-with-top-level-rule-report.bench.ts +35 -0
  59. package/src/benchmark/benches/lint-with-top-level-rule.bench.ts +32 -0
  60. package/src/benchmark/benches/rebilly.yaml +32275 -0
  61. package/src/benchmark/benches/recommended-oas3.bench.ts +22 -0
  62. package/src/benchmark/benches/resolve-with-no-external.bench.ts +23 -0
  63. package/src/benchmark/benchmark.js +311 -0
  64. package/src/benchmark/colors.js +29 -0
  65. package/src/benchmark/fork.js +83 -0
  66. package/src/benchmark/utils.ts +36 -0
  67. package/src/bundle.ts +417 -0
  68. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +164 -0
  69. package/src/config/__tests__/__snapshots__/config.test.ts.snap +144 -0
  70. package/src/config/__tests__/config-resolvers.test.ts +491 -0
  71. package/src/config/__tests__/config.test.ts +312 -0
  72. package/src/config/__tests__/fixtures/ingore-file.ts +8 -0
  73. package/src/config/__tests__/fixtures/load-redocly.yaml +2 -0
  74. package/src/config/__tests__/fixtures/plugin-config.yaml +2 -0
  75. package/src/config/__tests__/fixtures/plugin.js +56 -0
  76. package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +11 -0
  77. package/src/config/__tests__/fixtures/resolve-config/api/plugin.js +69 -0
  78. package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +7 -0
  79. package/src/config/__tests__/fixtures/resolve-config/local-config-with-custom-function.yaml +17 -0
  80. package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +18 -0
  81. package/src/config/__tests__/fixtures/resolve-config/local-config-with-wrong-custom-function.yaml +15 -0
  82. package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +9 -0
  83. package/src/config/__tests__/fixtures/resolve-config/plugin.js +80 -0
  84. package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +3 -0
  85. package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +4 -0
  86. package/src/config/__tests__/load.test.ts +167 -0
  87. package/src/config/__tests__/resolve-plugins.test.ts +27 -0
  88. package/src/config/__tests__/utils.test.ts +204 -0
  89. package/src/config/all.ts +74 -0
  90. package/src/config/builtIn.ts +37 -0
  91. package/src/config/config-resolvers.ts +474 -0
  92. package/src/config/config.ts +332 -0
  93. package/src/config/index.ts +7 -0
  94. package/src/config/load.ts +144 -0
  95. package/src/config/minimal.ts +61 -0
  96. package/src/config/recommended.ts +61 -0
  97. package/src/config/rules.ts +54 -0
  98. package/src/config/types.ts +231 -0
  99. package/src/config/utils.ts +349 -0
  100. package/src/decorators/__tests__/filter-in.test.ts +310 -0
  101. package/src/decorators/__tests__/filter-out.test.ts +335 -0
  102. package/src/decorators/__tests__/media-type-examples-override.test.ts +665 -0
  103. package/src/decorators/__tests__/remove-x-internal.test.ts +316 -0
  104. package/src/decorators/__tests__/resources/request.yaml +3 -0
  105. package/src/decorators/__tests__/resources/response.yaml +3 -0
  106. package/src/decorators/common/filters/filter-helper.ts +72 -0
  107. package/src/decorators/common/filters/filter-in.ts +18 -0
  108. package/src/decorators/common/filters/filter-out.ts +18 -0
  109. package/src/decorators/common/info-description-override.ts +24 -0
  110. package/src/decorators/common/info-override.ts +15 -0
  111. package/src/decorators/common/media-type-examples-override.ts +79 -0
  112. package/src/decorators/common/operation-description-override.ts +30 -0
  113. package/src/decorators/common/registry-dependencies.ts +25 -0
  114. package/src/decorators/common/remove-x-internal.ts +59 -0
  115. package/src/decorators/common/tag-description-override.ts +25 -0
  116. package/src/decorators/oas2/index.ts +20 -0
  117. package/src/decorators/oas3/index.ts +22 -0
  118. package/src/env.ts +5 -0
  119. package/src/format/codeframes.ts +216 -0
  120. package/src/format/format.ts +375 -0
  121. package/src/index.ts +71 -0
  122. package/src/js-yaml/index.ts +14 -0
  123. package/src/lint.ts +148 -0
  124. package/src/logger.ts +34 -0
  125. package/src/oas-types.ts +57 -0
  126. package/src/output.ts +7 -0
  127. package/src/redocly/__tests__/redocly-client.test.ts +146 -0
  128. package/src/redocly/index.ts +187 -0
  129. package/src/redocly/redocly-client-types.ts +10 -0
  130. package/src/redocly/registry-api-types.ts +32 -0
  131. package/src/redocly/registry-api.ts +150 -0
  132. package/src/ref-utils.ts +85 -0
  133. package/src/resolve.ts +417 -0
  134. package/src/rules/__tests__/fixtures/code-sample.php +9 -0
  135. package/src/rules/__tests__/fixtures/invalid-yaml.yaml +1 -0
  136. package/src/rules/__tests__/fixtures/ref.yaml +1 -0
  137. package/src/rules/__tests__/no-unresolved-refs.test.ts +257 -0
  138. package/src/rules/__tests__/utils.test.ts +160 -0
  139. package/src/rules/ajv.ts +102 -0
  140. package/src/rules/common/__tests__/info-license.test.ts +62 -0
  141. package/src/rules/common/__tests__/license-url.test.ts +63 -0
  142. package/src/rules/common/__tests__/no-ambiguous-paths.test.ts +96 -0
  143. package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +210 -0
  144. package/src/rules/common/__tests__/no-identical-paths.test.ts +58 -0
  145. package/src/rules/common/__tests__/no-path-trailing-slash.test.ts +85 -0
  146. package/src/rules/common/__tests__/operation-2xx-response.test.ts +192 -0
  147. package/src/rules/common/__tests__/operation-4xx-response.test.ts +231 -0
  148. package/src/rules/common/__tests__/operation-operationId-unique.test.ts +76 -0
  149. package/src/rules/common/__tests__/operation-operationId-url-safe.test.ts +45 -0
  150. package/src/rules/common/__tests__/operation-parameters-unique.test.ts +167 -0
  151. package/src/rules/common/__tests__/operation-singular-tag.test.ts +72 -0
  152. package/src/rules/common/__tests__/path-http-verbs-order.test.ts +95 -0
  153. package/src/rules/common/__tests__/path-not-include-query.test.ts +64 -0
  154. package/src/rules/common/__tests__/path-params-defined.test.ts +202 -0
  155. package/src/rules/common/__tests__/paths-kebab-case.test.ts +108 -0
  156. package/src/rules/common/__tests__/scalar-property-missing-example.test.ts +264 -0
  157. package/src/rules/common/__tests__/security-defined.test.ts +175 -0
  158. package/src/rules/common/__tests__/spec-strict-refs.test.ts +69 -0
  159. package/src/rules/common/__tests__/spec.test.ts +610 -0
  160. package/src/rules/common/__tests__/tag-description.test.ts +65 -0
  161. package/src/rules/common/__tests__/tags-alphabetical.test.ts +64 -0
  162. package/src/rules/common/assertions/__tests__/asserts.test.ts +869 -0
  163. package/src/rules/common/assertions/__tests__/index.test.ts +100 -0
  164. package/src/rules/common/assertions/__tests__/utils.test.ts +236 -0
  165. package/src/rules/common/assertions/asserts.ts +357 -0
  166. package/src/rules/common/assertions/index.ts +53 -0
  167. package/src/rules/common/assertions/utils.ts +331 -0
  168. package/src/rules/common/info-contact.ts +15 -0
  169. package/src/rules/common/info-license-url.ts +10 -0
  170. package/src/rules/common/info-license.ts +15 -0
  171. package/src/rules/common/no-ambiguous-paths.ts +50 -0
  172. package/src/rules/common/no-enum-type-mismatch.ts +52 -0
  173. package/src/rules/common/no-http-verbs-in-paths.ts +36 -0
  174. package/src/rules/common/no-identical-paths.ts +24 -0
  175. package/src/rules/common/no-invalid-parameter-examples.ts +36 -0
  176. package/src/rules/common/no-invalid-schema-examples.ts +27 -0
  177. package/src/rules/common/no-path-trailing-slash.ts +15 -0
  178. package/src/rules/common/operation-2xx-response.ts +24 -0
  179. package/src/rules/common/operation-4xx-response.ts +24 -0
  180. package/src/rules/common/operation-description.ts +13 -0
  181. package/src/rules/common/operation-operationId-unique.ts +21 -0
  182. package/src/rules/common/operation-operationId-url-safe.ts +19 -0
  183. package/src/rules/common/operation-operationId.ts +17 -0
  184. package/src/rules/common/operation-parameters-unique.ts +48 -0
  185. package/src/rules/common/operation-singular-tag.ts +17 -0
  186. package/src/rules/common/operation-summary.ts +13 -0
  187. package/src/rules/common/operation-tag-defined.ts +26 -0
  188. package/src/rules/common/parameter-description.ts +22 -0
  189. package/src/rules/common/path-declaration-must-exist.ts +15 -0
  190. package/src/rules/common/path-excludes-patterns.ts +23 -0
  191. package/src/rules/common/path-http-verbs-order.ts +30 -0
  192. package/src/rules/common/path-not-include-query.ts +17 -0
  193. package/src/rules/common/path-params-defined.ts +65 -0
  194. package/src/rules/common/path-segment-plural.ts +31 -0
  195. package/src/rules/common/paths-kebab-case.ts +19 -0
  196. package/src/rules/common/required-string-property-missing-min-length.ts +44 -0
  197. package/src/rules/common/response-contains-header.ts +35 -0
  198. package/src/rules/common/scalar-property-missing-example.ts +58 -0
  199. package/src/rules/common/security-defined.ts +65 -0
  200. package/src/rules/common/spec-strict-refs.ts +30 -0
  201. package/src/rules/common/spec.ts +175 -0
  202. package/src/rules/common/tag-description.ts +10 -0
  203. package/src/rules/common/tags-alphabetical.ts +20 -0
  204. package/src/rules/no-unresolved-refs.ts +51 -0
  205. package/src/rules/oas2/__tests__/boolean-parameter-prefixes.test.ts +110 -0
  206. package/src/rules/oas2/__tests__/response-contains-header.test.ts +174 -0
  207. package/src/rules/oas2/__tests__/response-contains-property.test.ts +155 -0
  208. package/src/rules/oas2/__tests__/spec/fixtures/description.md +1 -0
  209. package/src/rules/oas2/__tests__/spec/info.test.ts +355 -0
  210. package/src/rules/oas2/__tests__/spec/operation.test.ts +123 -0
  211. package/src/rules/oas2/__tests__/spec/paths.test.ts +245 -0
  212. package/src/rules/oas2/__tests__/spec/referenceableScalars.test.ts +35 -0
  213. package/src/rules/oas2/__tests__/spec/utils.ts +32 -0
  214. package/src/rules/oas2/boolean-parameter-prefixes.ts +26 -0
  215. package/src/rules/oas2/index.ts +91 -0
  216. package/src/rules/oas2/remove-unused-components.ts +81 -0
  217. package/src/rules/oas2/request-mime-type.ts +16 -0
  218. package/src/rules/oas2/response-contains-property.ts +36 -0
  219. package/src/rules/oas2/response-mime-type.ts +16 -0
  220. package/src/rules/oas3/__tests__/boolean-parameter-prefixes.test.ts +111 -0
  221. package/src/rules/oas3/__tests__/component-name-unique.test.ts +823 -0
  222. package/src/rules/oas3/__tests__/fixtures/common.yaml +11 -0
  223. package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +205 -0
  224. package/src/rules/oas3/__tests__/no-example-value-and-externalValue.test.ts +65 -0
  225. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +473 -0
  226. package/src/rules/oas3/__tests__/no-server-example.com.test.ts +60 -0
  227. package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +79 -0
  228. package/src/rules/oas3/__tests__/no-unused-components.test.ts +131 -0
  229. package/src/rules/oas3/__tests__/operation-4xx-problem-details-rfc7807.test.ts +145 -0
  230. package/src/rules/oas3/__tests__/response-contains-header.test.ts +389 -0
  231. package/src/rules/oas3/__tests__/response-contains-property.test.ts +403 -0
  232. package/src/rules/oas3/__tests__/spec/callbacks.test.ts +41 -0
  233. package/src/rules/oas3/__tests__/spec/fixtures/description.md +1 -0
  234. package/src/rules/oas3/__tests__/spec/info.test.ts +391 -0
  235. package/src/rules/oas3/__tests__/spec/operation.test.ts +253 -0
  236. package/src/rules/oas3/__tests__/spec/paths.test.ts +284 -0
  237. package/src/rules/oas3/__tests__/spec/referenceableScalars.test.ts +77 -0
  238. package/src/rules/oas3/__tests__/spec/servers.test.ts +505 -0
  239. package/src/rules/oas3/__tests__/spec/spec.test.ts +298 -0
  240. package/src/rules/oas3/__tests__/spec/utils.ts +32 -0
  241. package/src/rules/oas3/__tests__/spec-components-invalid-map-name.test.ts +276 -0
  242. package/src/rules/oas3/__tests__/utils/lint-document-for-test.ts +23 -0
  243. package/src/rules/oas3/boolean-parameter-prefixes.ts +28 -0
  244. package/src/rules/oas3/component-name-unique.ts +158 -0
  245. package/src/rules/oas3/index.ts +113 -0
  246. package/src/rules/oas3/no-empty-servers.ts +22 -0
  247. package/src/rules/oas3/no-example-value-and-externalValue.ts +14 -0
  248. package/src/rules/oas3/no-invalid-media-type-examples.ts +49 -0
  249. package/src/rules/oas3/no-server-example.com.ts +14 -0
  250. package/src/rules/oas3/no-server-trailing-slash.ts +15 -0
  251. package/src/rules/oas3/no-server-variables-empty-enum.ts +66 -0
  252. package/src/rules/oas3/no-undefined-server-variable.ts +30 -0
  253. package/src/rules/oas3/no-unused-components.ts +75 -0
  254. package/src/rules/oas3/operation-4xx-problem-details-rfc7807.ts +35 -0
  255. package/src/rules/oas3/remove-unused-components.ts +95 -0
  256. package/src/rules/oas3/request-mime-type.ts +30 -0
  257. package/src/rules/oas3/response-contains-property.ts +38 -0
  258. package/src/rules/oas3/response-mime-type.ts +30 -0
  259. package/src/rules/oas3/spec-components-invalid-map-name.ts +69 -0
  260. package/src/rules/other/stats.ts +73 -0
  261. package/src/rules/utils.ts +193 -0
  262. package/src/types/config-external-schemas.ts +917 -0
  263. package/src/types/index.ts +149 -0
  264. package/src/types/oas2.ts +478 -0
  265. package/src/types/oas3.ts +597 -0
  266. package/src/types/oas3_1.ts +258 -0
  267. package/src/types/redocly-yaml.ts +1040 -0
  268. package/src/typings/common.ts +17 -0
  269. package/src/typings/openapi.ts +298 -0
  270. package/src/typings/swagger.ts +236 -0
  271. package/src/utils.ts +276 -0
  272. package/src/visitors.ts +491 -0
  273. package/src/walk.ts +439 -0
  274. package/tsconfig.json +8 -0
  275. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,392 @@
1
+ import * as path from 'path';
2
+ import { outdent } from 'outdent';
3
+
4
+ import { lintFromString, lintConfig, lintDocument } from '../lint';
5
+ import { BaseResolver } from '../resolve';
6
+ import { loadConfig } from '../config/load';
7
+ import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../__tests__/utils';
8
+ import { detectOpenAPI } from '../oas-types';
9
+
10
+ describe('lint', () => {
11
+ it('lintFromString should work', async () => {
12
+ const results = await lintFromString({
13
+ absoluteRef: '/test/spec.yaml',
14
+ source: outdent`
15
+ openapi: 3.0.0
16
+ info:
17
+ title: Test API
18
+ version: "1.0"
19
+ description: Test
20
+ license: Fail
21
+
22
+ servers:
23
+ - url: http://example.com
24
+ paths: {}
25
+ `,
26
+ config: await loadConfig(),
27
+ });
28
+
29
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
30
+ Array [
31
+ Object {
32
+ "from": undefined,
33
+ "location": Array [
34
+ Object {
35
+ "pointer": "#/info/license",
36
+ "reportOnKey": false,
37
+ "source": "/test/spec.yaml",
38
+ },
39
+ ],
40
+ "message": "Expected type \`License\` (object) but got \`string\`",
41
+ "ruleId": "spec",
42
+ "severity": "error",
43
+ "suggest": Array [],
44
+ },
45
+ ]
46
+ `);
47
+ });
48
+
49
+ it('lintConfig should work', async () => {
50
+ const document = parseYamlToDocument(
51
+ outdent`
52
+ apis: error string
53
+ plugins:
54
+ - './local-plugin.js'
55
+ extends:
56
+ - recommended
57
+ - local/all
58
+ rules:
59
+ operation-2xx-response: warn
60
+ no-invalid-media-type-examples: error
61
+ path-http-verbs-order: error
62
+ boolean-parameter-prefixes: off
63
+ rule/operation-summary-length:
64
+ subject:
65
+ type: Operation
66
+ property: summary
67
+ message: Operation summary should start with an active verb
68
+ assertions:
69
+ local/checkWordsCount:
70
+ min: 3
71
+ theme:
72
+ openapi:
73
+ showConsole: true
74
+ layout:
75
+ scope: section
76
+ routingStrategy: browser
77
+ theme:
78
+ rightPanel:
79
+ backgroundColor: '#263238'
80
+ links:
81
+ color: '#6CC496'
82
+ theme:
83
+ openapi:
84
+ showConsole: true
85
+ layout:
86
+ scope: section
87
+ routingStrategy: browser
88
+ theme:
89
+ rightPanel:
90
+ backgroundColor: '#263238'
91
+ links:
92
+ color: '#6CC496'
93
+ `,
94
+ ''
95
+ );
96
+ const results = await lintConfig({ document });
97
+
98
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
99
+ Array [
100
+ Object {
101
+ "from": undefined,
102
+ "location": Array [
103
+ Object {
104
+ "pointer": "#/eme",
105
+ "reportOnKey": true,
106
+ "source": "",
107
+ },
108
+ ],
109
+ "message": "Property \`eme\` is not expected here.",
110
+ "ruleId": "configuration spec",
111
+ "severity": "error",
112
+ "suggest": Array [
113
+ "theme",
114
+ "seo",
115
+ "sso",
116
+ ],
117
+ },
118
+ Object {
119
+ "from": undefined,
120
+ "location": Array [
121
+ Object {
122
+ "pointer": "#/openapi",
123
+ "reportOnKey": true,
124
+ "source": "",
125
+ },
126
+ ],
127
+ "message": "Property \`openapi\` is not expected here.",
128
+ "ruleId": "configuration spec",
129
+ "severity": "error",
130
+ "suggest": Array [],
131
+ },
132
+ Object {
133
+ "from": undefined,
134
+ "location": Array [
135
+ Object {
136
+ "pointer": "#/apis",
137
+ "reportOnKey": false,
138
+ "source": "",
139
+ },
140
+ ],
141
+ "message": "Expected type \`ConfigApis\` (object) but got \`string\`",
142
+ "ruleId": "configuration spec",
143
+ "severity": "error",
144
+ "suggest": Array [],
145
+ },
146
+ ]
147
+ `);
148
+ });
149
+
150
+ it('lintConfig should detect wrong fields and suggest correct ones', async () => {
151
+ const document = parseYamlToDocument(
152
+ outdent`
153
+ api:
154
+ name@version:
155
+ root: ./file.yaml
156
+ rules:
157
+ operation-2xx-response: warn
158
+ `,
159
+ ''
160
+ );
161
+ const results = await lintConfig({ document });
162
+
163
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
164
+ Array [
165
+ Object {
166
+ "from": undefined,
167
+ "location": Array [
168
+ Object {
169
+ "pointer": "#/api",
170
+ "reportOnKey": true,
171
+ "source": "",
172
+ },
173
+ ],
174
+ "message": "Property \`api\` is not expected here.",
175
+ "ruleId": "configuration spec",
176
+ "severity": "error",
177
+ "suggest": Array [
178
+ "apis",
179
+ "seo",
180
+ "sso",
181
+ ],
182
+ },
183
+ ]
184
+ `);
185
+ });
186
+
187
+ it('lintConfig should work with legacy fields - referenceDocs', async () => {
188
+ const document = parseYamlToDocument(
189
+ outdent`
190
+ apis:
191
+ entry:
192
+ root: ./file.yaml
193
+ rules:
194
+ operation-2xx-response: warn
195
+ referenceDocs:
196
+ showConsole: true
197
+ `,
198
+ ''
199
+ );
200
+ const results = await lintConfig({ document });
201
+
202
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
203
+ Array [
204
+ Object {
205
+ "from": undefined,
206
+ "location": Array [
207
+ Object {
208
+ "pointer": "#/referenceDocs",
209
+ "reportOnKey": true,
210
+ "source": "",
211
+ },
212
+ ],
213
+ "message": "Property \`referenceDocs\` is not expected here.",
214
+ "ruleId": "configuration spec",
215
+ "severity": "error",
216
+ "suggest": Array [],
217
+ },
218
+ ]
219
+ `);
220
+ });
221
+
222
+ it("'plugins' shouldn't be allowed in 'apis'", async () => {
223
+ const document = parseYamlToDocument(
224
+ outdent`
225
+ apis:
226
+ main:
227
+ root: ./main.yaml
228
+ plugins:
229
+ - './local-plugin.js'
230
+ plugins:
231
+ - './local-plugin.js'
232
+ `,
233
+ ''
234
+ );
235
+ const results = await lintConfig({ document });
236
+
237
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
238
+ Array [
239
+ Object {
240
+ "from": undefined,
241
+ "location": Array [
242
+ Object {
243
+ "pointer": "#/apis/main/plugins",
244
+ "reportOnKey": true,
245
+ "source": "",
246
+ },
247
+ ],
248
+ "message": "Property \`plugins\` is not expected here.",
249
+ "ruleId": "configuration spec",
250
+ "severity": "error",
251
+ "suggest": Array [],
252
+ },
253
+ ]
254
+ `);
255
+ });
256
+
257
+ it("'const' can have any type", async () => {
258
+ const document = parseYamlToDocument(
259
+ outdent`
260
+ openapi: "3.1.0"
261
+ info:
262
+ version: 1.0.0
263
+ title: Swagger Petstore
264
+ description: Information about Petstore
265
+ license:
266
+ name: MIT
267
+ url: https://opensource.org/licenses/MIT
268
+ servers:
269
+ - url: http://petstore.swagger.io/v1
270
+ paths:
271
+ /pets:
272
+ get:
273
+ summary: List all pets
274
+ operationId: listPets
275
+ tags:
276
+ - pets
277
+ responses:
278
+ 200:
279
+ description: An paged array of pets
280
+ content:
281
+ application/json:
282
+ schema:
283
+ type: string
284
+ const: ABC
285
+ `,
286
+ 'foobar.yaml'
287
+ );
288
+
289
+ const results = await lintDocument({
290
+ externalRefResolver: new BaseResolver(),
291
+ document,
292
+ config: await makeConfig({ spec: 'error' }),
293
+ });
294
+
295
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
296
+ });
297
+
298
+ it('detect OpenAPI should throw an error when version is not string', () => {
299
+ const testDocument = parseYamlToDocument(
300
+ outdent`
301
+ openapi: 3.0
302
+ `,
303
+ ''
304
+ );
305
+ expect(() => detectOpenAPI(testDocument.parsed)).toThrow(
306
+ `Invalid OpenAPI version: should be a string but got "number"`
307
+ );
308
+ });
309
+
310
+ it("spec rule shouldn't throw an error for named callback", async () => {
311
+ const document = parseYamlToDocument(
312
+ outdent`
313
+ openapi: 3.1.0
314
+ info:
315
+ title: Callback test
316
+ version: 'alpha'
317
+ components:
318
+ callbacks:
319
+ resultCallback:
320
+ '{$url}':
321
+ post:
322
+ requestBody:
323
+ description: Callback payload
324
+ content:
325
+ 'application/json':
326
+ schema:
327
+ type: object
328
+ properties:
329
+ test:
330
+ type: string
331
+ responses:
332
+ '200':
333
+ description: callback successfully processed
334
+ `,
335
+ 'foobar.yaml'
336
+ );
337
+
338
+ const results = await lintDocument({
339
+ externalRefResolver: new BaseResolver(),
340
+ document,
341
+ config: await makeConfig({ spec: 'error' }),
342
+ });
343
+
344
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
345
+ });
346
+
347
+ it('should ignore error because ignore file passed', async () => {
348
+ const absoluteRef = path.join(__dirname, 'fixtures/openapi.yaml');
349
+ const document = parseYamlToDocument(
350
+ outdent`
351
+ openapi: 3.0.0
352
+ info:
353
+ version: 1.0.0
354
+ title: Example OpenAPI 3 definition.
355
+ description: Information about API
356
+ license:
357
+ name: MIT
358
+ url: 'https://opensource.org/licenses/MIT'
359
+ servers:
360
+ - url: 'https://redocly.com/v1'
361
+ paths:
362
+ '/pets/{petId}':
363
+ post:
364
+ responses:
365
+ '201':
366
+ summary: Exist
367
+ description: example description
368
+ `,
369
+ absoluteRef
370
+ );
371
+
372
+ const configFilePath = path.join(__dirname, 'fixtures');
373
+
374
+ const result = await lintDocument({
375
+ externalRefResolver: new BaseResolver(),
376
+ document,
377
+ config: await makeConfig({ 'operation-operationId': 'error' }, undefined, configFilePath),
378
+ });
379
+ expect(result).toHaveLength(1);
380
+ expect(result).toMatchObject([
381
+ {
382
+ ignored: true,
383
+ location: [{ pointer: '#/paths/~1pets~1{petId}/post/operationId' }],
384
+ message: 'Operation object should contain `operationId` field.',
385
+ ruleId: 'operation-operationId',
386
+ severity: 'error',
387
+ },
388
+ ]);
389
+ expect(result[0]).toHaveProperty('ignored', true);
390
+ expect(result[0]).toHaveProperty('ruleId', 'operation-operationId');
391
+ });
392
+ });
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import * as colorette from 'colorette';
6
+ import { logger, colorize } from '../logger';
7
+
8
+ describe('Logger in Browser', () => {
9
+ it('should call "console.error"', () => {
10
+ const error = jest.spyOn(console, 'error').mockImplementation();
11
+
12
+ logger.error('error');
13
+
14
+ expect(error).toBeCalledTimes(1);
15
+ expect(error).toBeCalledWith('error');
16
+
17
+ error.mockRestore();
18
+ });
19
+
20
+ it('should call "console.log"', () => {
21
+ const log = jest.spyOn(console, 'log').mockImplementation();
22
+
23
+ logger.info('info');
24
+
25
+ expect(log).toBeCalledTimes(1);
26
+ expect(log).toBeCalledWith('info');
27
+
28
+ log.mockRestore();
29
+ });
30
+
31
+ it('should call "console.warn"', () => {
32
+ const warn = jest.spyOn(console, 'warn').mockImplementation();
33
+
34
+ logger.warn('warn');
35
+
36
+ expect(warn).toBeCalledTimes(1);
37
+ expect(warn).toBeCalledWith('warn');
38
+
39
+ warn.mockRestore();
40
+ });
41
+ });
42
+
43
+ describe('colorize in Browser', () => {
44
+ it('should not call original colorette lib', () => {
45
+ const color = 'cyan';
46
+ const spyingCyan = jest.spyOn(colorette, color);
47
+
48
+ const colorized = colorize.cyan(color);
49
+
50
+ expect(spyingCyan).not.toBeCalled();
51
+ expect(colorized).toEqual(color);
52
+ });
53
+ });
@@ -0,0 +1,47 @@
1
+ import * as colorette from 'colorette';
2
+ import { logger, colorize } from '../logger';
3
+
4
+ describe('Logger in nodejs', () => {
5
+ let spyingStderr: jest.SpyInstance;
6
+
7
+ beforeEach(() => {
8
+ spyingStderr = jest.spyOn(process.stderr, 'write').mockImplementation();
9
+ });
10
+
11
+ afterEach(() => {
12
+ spyingStderr.mockRestore();
13
+ });
14
+
15
+ it('should call "process.stderr.write" for error severity', () => {
16
+ logger.error('error');
17
+
18
+ expect(spyingStderr).toBeCalledTimes(1);
19
+ expect(spyingStderr).toBeCalledWith(colorette.red('error'));
20
+ });
21
+
22
+ it('should call "process.stderr.write" for warn severity', () => {
23
+ logger.warn('warn');
24
+
25
+ expect(spyingStderr).toBeCalledTimes(1);
26
+ expect(spyingStderr).toBeCalledWith(colorette.yellow('warn'));
27
+ });
28
+
29
+ it('should call "process.stderr.write" for info severity', () => {
30
+ logger.info('info');
31
+
32
+ expect(spyingStderr).toBeCalledTimes(1);
33
+ expect(spyingStderr).toBeCalledWith('info');
34
+ });
35
+ });
36
+
37
+ describe('colorize in nodejs', () => {
38
+ it('should call original colorette lib', () => {
39
+ const color = 'cyan';
40
+ const spyingCyan = jest.spyOn(colorette, color);
41
+
42
+ const colorized = colorize.cyan(color);
43
+
44
+ expect(spyingCyan).toBeCalledWith(color);
45
+ expect(colorized).toEqual(colorette[color](color));
46
+ });
47
+ });
@@ -0,0 +1,17 @@
1
+ import { RedoclyClient } from '../redocly';
2
+
3
+ describe.skip('login', () => {
4
+ it('should call login with setAccessTokens function', async () => {
5
+ const client = new RedoclyClient();
6
+ Object.defineProperty(client, 'registryApi', {
7
+ value: {
8
+ setAccessTokens: jest.fn(),
9
+ authStatus: jest.fn(() => true),
10
+ },
11
+ writable: true,
12
+ configurable: true,
13
+ });
14
+ await client.login('token'); // TODO: bug with rewrite local config file
15
+ expect(client.registryApi.setAccessTokens).toHaveBeenCalled();
16
+ });
17
+ });
@@ -0,0 +1,151 @@
1
+ import { normalizeVisitors, VisitorLevelContext } from '../visitors';
2
+ import { Oas3RuleSet } from '../oas-types';
3
+ import { Oas3Types } from '../types/oas3';
4
+ import { normalizeTypes } from '../types';
5
+
6
+ describe('Normalize visitors', () => {
7
+ it('should work correctly for single rule', () => {
8
+ const schemaEnter = () => undefined;
9
+
10
+ const ruleset: Oas3RuleSet[] = [
11
+ {
12
+ test: () => {
13
+ return {
14
+ Schema: schemaEnter,
15
+ };
16
+ },
17
+ },
18
+ ];
19
+
20
+ const visitors = ruleset.flatMap((ruleset) =>
21
+ Object.keys(ruleset).map((ruleId) => ({
22
+ ruleId,
23
+ severity: 'error' as 'error',
24
+ visitor: ruleset[ruleId]({}),
25
+ }))
26
+ );
27
+
28
+ const normalized = normalizeVisitors(visitors, normalizeTypes(Oas3Types));
29
+ expect(normalized).toBeDefined();
30
+ expect(normalized.Schema.enter).toHaveLength(1);
31
+ expect(normalized.Schema.enter[0].visit).toEqual(schemaEnter);
32
+ expect(normalized.Schema.enter[0].context.parent).toEqual(null);
33
+ const { type, ...contextWithoutType } = normalized.Schema.enter[0]
34
+ .context as VisitorLevelContext;
35
+
36
+ expect(contextWithoutType).toEqual({
37
+ activatedOn: null,
38
+ parent: null,
39
+ isSkippedLevel: false,
40
+ });
41
+
42
+ expect(type.name).toEqual('Schema');
43
+ });
44
+
45
+ it('should work for nested rule', () => {
46
+ const infoEnter = () => undefined;
47
+ const infoLeave = () => undefined;
48
+ const contactEnter = () => undefined;
49
+
50
+ const ruleset: Oas3RuleSet[] = [
51
+ {
52
+ test: () => {
53
+ return {
54
+ Info: {
55
+ enter: infoEnter,
56
+ leave: infoLeave,
57
+ Contact: contactEnter,
58
+ },
59
+ };
60
+ },
61
+ },
62
+ ];
63
+
64
+ const visitors = ruleset.flatMap((ruleset) =>
65
+ Object.keys(ruleset).map((ruleId) => ({
66
+ ruleId,
67
+ severity: 'error' as 'error',
68
+ visitor: ruleset[ruleId]({}),
69
+ }))
70
+ );
71
+
72
+ const normalized = normalizeVisitors(visitors, normalizeTypes(Oas3Types));
73
+ expect(normalized).toBeDefined();
74
+ expect(normalized.Info.enter).toHaveLength(1);
75
+
76
+ expect(normalized.Info.enter[0].context).toStrictEqual(normalized.Info.leave[0].context);
77
+
78
+ expect(normalized.Info.enter[0].visit).toEqual(infoEnter);
79
+ expect(normalized.Contact.enter[0].visit).toEqual(contactEnter);
80
+
81
+ expect(normalized.Contact.enter[0].context.parent).toEqual(normalized.Info.enter[0].context);
82
+
83
+ expect(normalized.Info.leave).toHaveLength(1);
84
+ expect(normalized.Info.leave[0].visit).toEqual(infoLeave);
85
+ });
86
+
87
+ it('should normalize with weak intermittent types', () => {
88
+ const contactEnter = () => undefined;
89
+
90
+ const ruleset: Oas3RuleSet[] = [
91
+ {
92
+ test: () => {
93
+ return {
94
+ PathItem: {
95
+ Parameter: contactEnter,
96
+ },
97
+ };
98
+ },
99
+ },
100
+ ];
101
+
102
+ const visitors = ruleset.flatMap((ruleset) =>
103
+ Object.keys(ruleset).map((ruleId) => ({
104
+ ruleId,
105
+ severity: 'error' as 'error',
106
+ visitor: ruleset[ruleId]({}),
107
+ }))
108
+ );
109
+
110
+ const normalized = normalizeVisitors(visitors, normalizeTypes(Oas3Types));
111
+ expect(normalized).toBeDefined();
112
+ expect(normalized.PathItem.enter).toHaveLength(1);
113
+ expect(normalized.Operation.enter).toHaveLength(1);
114
+ expect(normalized.Parameter.enter).toHaveLength(1);
115
+ expect(normalized.ParameterList.enter).toHaveLength(2);
116
+ });
117
+
118
+ it('should order deeper visitors first', () => {
119
+ const pathParam = () => undefined;
120
+ const operationParam = () => undefined;
121
+
122
+ const ruleset: Oas3RuleSet[] = [
123
+ {
124
+ test: () => {
125
+ return {
126
+ PathItem: {
127
+ Parameter: pathParam,
128
+ Operation: {
129
+ Parameter: operationParam,
130
+ },
131
+ },
132
+ };
133
+ },
134
+ },
135
+ ];
136
+
137
+ const visitors = ruleset.flatMap((ruleset) =>
138
+ Object.keys(ruleset).map((ruleId) => ({
139
+ ruleId,
140
+ severity: 'error' as 'error',
141
+ visitor: ruleset[ruleId]({}),
142
+ }))
143
+ );
144
+
145
+ const normalized = normalizeVisitors(visitors, normalizeTypes(Oas3Types));
146
+ expect(normalized).toBeDefined();
147
+ expect(normalized.Parameter.enter).toHaveLength(2);
148
+ expect(normalized.Parameter.enter[0].visit).toStrictEqual(operationParam);
149
+ expect(normalized.Parameter.enter[1].visit).toStrictEqual(pathParam);
150
+ });
151
+ });
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import { output } from '../output';
6
+
7
+ describe('output', () => {
8
+ it('should ignore all parsable data in browser', () => {
9
+ const spyingStdout = jest.spyOn(process.stdout, 'write').mockImplementation();
10
+ const data = '{ "errors" : [] }';
11
+
12
+ output.write(data);
13
+
14
+ expect(spyingStdout).not.toBeCalled();
15
+
16
+ spyingStdout.mockRestore();
17
+ });
18
+ });
@@ -0,0 +1,15 @@
1
+ import { output } from '../output';
2
+
3
+ describe('output', () => {
4
+ it('should write all parsable data to stdout', () => {
5
+ const spyingStdout = jest.spyOn(process.stdout, 'write').mockImplementation();
6
+ const data = '{ "errors" : [] }';
7
+
8
+ output.write(data);
9
+
10
+ expect(spyingStdout).toBeCalledTimes(1);
11
+ expect(spyingStdout).toBeCalledWith(data);
12
+
13
+ spyingStdout.mockRestore();
14
+ });
15
+ });