@tsed/schema 8.0.1 → 8.0.2

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 (291) hide show
  1. package/package.json +11 -10
  2. package/src/components/async-api/channelsMapper.ts +81 -0
  3. package/src/components/async-api/generate.ts +24 -0
  4. package/src/components/async-api/messageMapper.ts +62 -0
  5. package/src/components/async-api/payloadMapper.ts +61 -0
  6. package/src/components/async-api/responseMapper.ts +70 -0
  7. package/src/components/default/anyMapper.spec.ts +7 -0
  8. package/src/components/default/anyMapper.ts +33 -0
  9. package/src/components/default/classMapper.ts +55 -0
  10. package/src/components/default/discriminatorMappingMapper.ts +36 -0
  11. package/src/components/default/enumsMapper.spec.ts +64 -0
  12. package/src/components/default/enumsMapper.ts +17 -0
  13. package/src/components/default/genericsMapper.ts +70 -0
  14. package/src/components/default/inheritedClassMapper.ts +24 -0
  15. package/src/components/default/itemMapper.ts +8 -0
  16. package/src/components/default/lazyRefMapper.ts +21 -0
  17. package/src/components/default/mapMapper.ts +25 -0
  18. package/src/components/default/nullableMapper.ts +55 -0
  19. package/src/components/default/objectMapper.ts +36 -0
  20. package/src/components/default/ofMapper.ts +16 -0
  21. package/src/components/default/propertiesMapper.ts +25 -0
  22. package/src/components/default/requiredMapper.ts +69 -0
  23. package/src/components/default/schemaMapper.ts +151 -0
  24. package/src/components/index.ts +36 -0
  25. package/src/components/open-spec/discriminatorMappingMapper.ts +7 -0
  26. package/src/components/open-spec/enumsMapper.ts +20 -0
  27. package/src/components/open-spec/generate.ts +29 -0
  28. package/src/components/open-spec/nullableMapper.ts +51 -0
  29. package/src/components/open-spec/operationInFilesMapper.ts +32 -0
  30. package/src/components/open-spec/operationInParameterMapper.ts +56 -0
  31. package/src/components/open-spec/operationInParametersMapper.ts +9 -0
  32. package/src/components/open-spec/operationInQueryMapper.ts +74 -0
  33. package/src/components/open-spec/operationMapper.ts +65 -0
  34. package/src/components/open-spec/operationMediaMapper.ts +11 -0
  35. package/src/components/open-spec/operationRequestBodyMapper.ts +82 -0
  36. package/src/components/open-spec/operationResponseMapper.ts +26 -0
  37. package/src/components/open-spec/pathsMapper.ts +132 -0
  38. package/src/constants/OperationVerbs.ts +29 -0
  39. package/src/constants/httpStatusMessages.ts +21 -0
  40. package/src/constants/jsonSchemaProperties.ts +17 -0
  41. package/src/decorators/class/children.ts +22 -0
  42. package/src/decorators/class/discriminatorValue.ts +53 -0
  43. package/src/decorators/collections/collectionContains.ts +25 -0
  44. package/src/decorators/collections/collectionOf.spec.ts +327 -0
  45. package/src/decorators/collections/collectionOf.ts +182 -0
  46. package/src/decorators/collections/maxItems.spec.ts +40 -0
  47. package/src/decorators/collections/maxItems.ts +59 -0
  48. package/src/decorators/collections/maxProperties.spec.ts +109 -0
  49. package/src/decorators/collections/maxProperties.ts +82 -0
  50. package/src/decorators/collections/minItems.spec.ts +40 -0
  51. package/src/decorators/collections/minItems.ts +63 -0
  52. package/src/decorators/collections/minProperties.spec.ts +109 -0
  53. package/src/decorators/collections/minProperties.ts +81 -0
  54. package/src/decorators/collections/uniqueItems.spec.ts +28 -0
  55. package/src/decorators/collections/uniqueItems.ts +59 -0
  56. package/src/decorators/common/__snapshots__/groups.spec.ts.snap +321 -0
  57. package/src/decorators/common/additionalProperties.spec.ts +133 -0
  58. package/src/decorators/common/additionalProperties.ts +17 -0
  59. package/src/decorators/common/allOf.spec.ts +276 -0
  60. package/src/decorators/common/allOf.ts +19 -0
  61. package/src/decorators/common/allow.spec.ts +130 -0
  62. package/src/decorators/common/allow.ts +50 -0
  63. package/src/decorators/common/any.spec.ts +109 -0
  64. package/src/decorators/common/any.ts +45 -0
  65. package/src/decorators/common/anyOf.spec.ts +270 -0
  66. package/src/decorators/common/anyOf.ts +23 -0
  67. package/src/decorators/common/const.spec.ts +98 -0
  68. package/src/decorators/common/const.ts +68 -0
  69. package/src/decorators/common/customKey.spec.ts +105 -0
  70. package/src/decorators/common/customKey.ts +39 -0
  71. package/src/decorators/common/default.spec.ts +25 -0
  72. package/src/decorators/common/default.ts +47 -0
  73. package/src/decorators/common/defaultMsg.spec.ts +22 -0
  74. package/src/decorators/common/defaultMsg.ts +38 -0
  75. package/src/decorators/common/description.spec.ts +166 -0
  76. package/src/decorators/common/description.ts +77 -0
  77. package/src/decorators/common/discriminatorKey.ts +14 -0
  78. package/src/decorators/common/enum.spec.ts +305 -0
  79. package/src/decorators/common/enum.ts +111 -0
  80. package/src/decorators/common/errorMsg.spec.ts +57 -0
  81. package/src/decorators/common/errorMsg.ts +14 -0
  82. package/src/decorators/common/example.spec.ts +93 -0
  83. package/src/decorators/common/example.ts +18 -0
  84. package/src/decorators/common/examples.spec.ts +101 -0
  85. package/src/decorators/common/examples.ts +20 -0
  86. package/src/decorators/common/exclusiveMaximum.spec.ts +47 -0
  87. package/src/decorators/common/exclusiveMaximum.ts +75 -0
  88. package/src/decorators/common/exclusiveMinimum.spec.ts +47 -0
  89. package/src/decorators/common/exclusiveMinimum.ts +75 -0
  90. package/src/decorators/common/format.spec.ts +149 -0
  91. package/src/decorators/common/format.ts +486 -0
  92. package/src/decorators/common/forwardGroups.spec.ts +270 -0
  93. package/src/decorators/common/forwardGroups.ts +16 -0
  94. package/src/decorators/common/groups.spec.ts +968 -0
  95. package/src/decorators/common/groups.ts +66 -0
  96. package/src/decorators/common/hidden.spec.ts +52 -0
  97. package/src/decorators/common/hidden.ts +36 -0
  98. package/src/decorators/common/ignore.spec.ts +88 -0
  99. package/src/decorators/common/ignore.ts +66 -0
  100. package/src/decorators/common/integer.spec.ts +110 -0
  101. package/src/decorators/common/integer.ts +69 -0
  102. package/src/decorators/common/jsonEntityFn.spec.ts +16 -0
  103. package/src/decorators/common/jsonEntityFn.ts +33 -0
  104. package/src/decorators/common/maxLength.spec.ts +61 -0
  105. package/src/decorators/common/maxLength.ts +88 -0
  106. package/src/decorators/common/maximum.spec.ts +73 -0
  107. package/src/decorators/common/maximum.ts +146 -0
  108. package/src/decorators/common/minLength.spec.ts +186 -0
  109. package/src/decorators/common/minLength.ts +89 -0
  110. package/src/decorators/common/minimum.spec.ts +74 -0
  111. package/src/decorators/common/minimum.ts +145 -0
  112. package/src/decorators/common/multipleOf.spec.ts +33 -0
  113. package/src/decorators/common/multipleOf.ts +80 -0
  114. package/src/decorators/common/name.spec.ts +56 -0
  115. package/src/decorators/common/name.ts +50 -0
  116. package/src/decorators/common/nullable.spec.ts +818 -0
  117. package/src/decorators/common/nullable.ts +32 -0
  118. package/src/decorators/common/oneOf.spec.ts +301 -0
  119. package/src/decorators/common/oneOf.ts +24 -0
  120. package/src/decorators/common/optional.spec.ts +43 -0
  121. package/src/decorators/common/optional.ts +26 -0
  122. package/src/decorators/common/pattern.spec.ts +27 -0
  123. package/src/decorators/common/pattern.ts +102 -0
  124. package/src/decorators/common/property.ts +24 -0
  125. package/src/decorators/common/readOnly.spec.ts +135 -0
  126. package/src/decorators/common/readOnly.ts +17 -0
  127. package/src/decorators/common/recordOf.spec.ts +41 -0
  128. package/src/decorators/common/recordOf.ts +59 -0
  129. package/src/decorators/common/required.spec.ts +188 -0
  130. package/src/decorators/common/required.ts +50 -0
  131. package/src/decorators/common/requiredGroups.spec.ts +127 -0
  132. package/src/decorators/common/requiredGroups.ts +23 -0
  133. package/src/decorators/common/schema.spec.ts +23 -0
  134. package/src/decorators/common/schema.ts +64 -0
  135. package/src/decorators/common/title.spec.ts +20 -0
  136. package/src/decorators/common/title.ts +45 -0
  137. package/src/decorators/common/typeError.spec.ts +22 -0
  138. package/src/decorators/common/typeError.ts +15 -0
  139. package/src/decorators/common/writeOnly.spec.ts +105 -0
  140. package/src/decorators/common/writeOnly.ts +17 -0
  141. package/src/decorators/config/jsonEntityComponent.ts +16 -0
  142. package/src/decorators/generics/genericOf.spec.ts +246 -0
  143. package/src/decorators/generics/genericOf.ts +112 -0
  144. package/src/decorators/generics/generics.ts +22 -0
  145. package/src/decorators/operations/acceptMime.spec.ts +39 -0
  146. package/src/decorators/operations/acceptMime.ts +25 -0
  147. package/src/decorators/operations/consumes.spec.ts +89 -0
  148. package/src/decorators/operations/consumes.ts +37 -0
  149. package/src/decorators/operations/contentType.spec.ts +40 -0
  150. package/src/decorators/operations/contentType.ts +24 -0
  151. package/src/decorators/operations/deprecated.spec.ts +223 -0
  152. package/src/decorators/operations/deprecated.ts +49 -0
  153. package/src/decorators/operations/header.spec.ts +214 -0
  154. package/src/decorators/operations/header.ts +62 -0
  155. package/src/decorators/operations/in.spec.ts +321 -0
  156. package/src/decorators/operations/in.ts +141 -0
  157. package/src/decorators/operations/inFile.spec.ts +209 -0
  158. package/src/decorators/operations/inFile.ts +55 -0
  159. package/src/decorators/operations/location.spec.ts +51 -0
  160. package/src/decorators/operations/location.ts +26 -0
  161. package/src/decorators/operations/operation.ts +119 -0
  162. package/src/decorators/operations/operationId.spec.ts +43 -0
  163. package/src/decorators/operations/operationId.ts +22 -0
  164. package/src/decorators/operations/operationPath.spec.ts +42 -0
  165. package/src/decorators/operations/operationPath.ts +33 -0
  166. package/src/decorators/operations/partial.spec.ts +249 -0
  167. package/src/decorators/operations/partial.ts +14 -0
  168. package/src/decorators/operations/path.spec.ts +47 -0
  169. package/src/decorators/operations/path.ts +27 -0
  170. package/src/decorators/operations/produces.spec.ts +89 -0
  171. package/src/decorators/operations/produces.ts +44 -0
  172. package/src/decorators/operations/publish.spec.ts +26 -0
  173. package/src/decorators/operations/publish.ts +6 -0
  174. package/src/decorators/operations/redirect.spec.ts +146 -0
  175. package/src/decorators/operations/redirect.ts +82 -0
  176. package/src/decorators/operations/returns.spec.ts +1393 -0
  177. package/src/decorators/operations/returns.ts +662 -0
  178. package/src/decorators/operations/route.spec.ts +211 -0
  179. package/src/decorators/operations/route.ts +154 -0
  180. package/src/decorators/operations/security.spec.ts +162 -0
  181. package/src/decorators/operations/security.ts +83 -0
  182. package/src/decorators/operations/status.spec.ts +582 -0
  183. package/src/decorators/operations/status.ts +130 -0
  184. package/src/decorators/operations/subscribe.spec.ts +32 -0
  185. package/src/decorators/operations/subscribe.ts +6 -0
  186. package/src/decorators/operations/summary.spec.ts +45 -0
  187. package/src/decorators/operations/summary.ts +32 -0
  188. package/src/decorators/operations/tags.spec.ts +113 -0
  189. package/src/decorators/operations/tags.ts +53 -0
  190. package/src/decorators/operations/view.spec.ts +16 -0
  191. package/src/decorators/operations/view.ts +27 -0
  192. package/src/domain/DecoratorContext.ts +116 -0
  193. package/src/domain/JsonAliasMap.ts +19 -0
  194. package/src/domain/JsonClassStore.ts +35 -0
  195. package/src/domain/JsonDiscriminator.spec.ts +65 -0
  196. package/src/domain/JsonDiscriminator.ts +52 -0
  197. package/src/domain/JsonEntityStore.spec.ts +277 -0
  198. package/src/domain/JsonEntityStore.ts +284 -0
  199. package/src/domain/JsonFormatTypes.ts +17 -0
  200. package/src/domain/JsonLazyRef.ts +21 -0
  201. package/src/domain/JsonMap.spec.ts +15 -0
  202. package/src/domain/JsonMap.ts +33 -0
  203. package/src/domain/JsonMedia.ts +24 -0
  204. package/src/domain/JsonMethodStore.spec.ts +301 -0
  205. package/src/domain/JsonMethodStore.ts +212 -0
  206. package/src/domain/JsonOperation.spec.ts +126 -0
  207. package/src/domain/JsonOperation.ts +219 -0
  208. package/src/domain/JsonOperationPathsMap.ts +16 -0
  209. package/src/domain/JsonOperationRoute.spec.ts +109 -0
  210. package/src/domain/JsonOperationRoute.ts +75 -0
  211. package/src/domain/JsonParameter.ts +70 -0
  212. package/src/domain/JsonParameterStore.spec.ts +212 -0
  213. package/src/domain/JsonParameterStore.ts +138 -0
  214. package/src/domain/JsonParameterTypes.spec.ts +12 -0
  215. package/src/domain/JsonParameterTypes.ts +20 -0
  216. package/src/domain/JsonPropertyStore.spec.ts +177 -0
  217. package/src/domain/JsonPropertyStore.ts +92 -0
  218. package/src/domain/JsonRequestBody.ts +47 -0
  219. package/src/domain/JsonResponse.spec.ts +33 -0
  220. package/src/domain/JsonResponse.ts +64 -0
  221. package/src/domain/JsonSchema.spec.ts +1470 -0
  222. package/src/domain/JsonSchema.ts +1049 -0
  223. package/src/domain/SpecTypes.ts +6 -0
  224. package/src/hooks/alterIgnore.ts +25 -0
  225. package/src/hooks/alterOneOf.ts +67 -0
  226. package/src/hooks/alterRequiredGroups.ts +12 -0
  227. package/src/index.ts +185 -0
  228. package/src/interfaces/IgnoreCallback.ts +5 -0
  229. package/src/interfaces/JsonHookContext.ts +18 -0
  230. package/src/interfaces/JsonOpenSpec.ts +7 -0
  231. package/src/interfaces/JsonSchemaOptions.ts +35 -0
  232. package/src/registries/JsonSchemaMapperContainer.ts +52 -0
  233. package/src/registries/enumRegistries.ts +3 -0
  234. package/src/utils/__snapshots__/generateSpec.spec.ts.snap +109 -0
  235. package/src/utils/buildPath.ts +8 -0
  236. package/src/utils/concatParameters.ts +12 -0
  237. package/src/utils/concatPath.spec.ts +23 -0
  238. package/src/utils/concatPath.ts +24 -0
  239. package/src/utils/defineStatusModel.ts +11 -0
  240. package/src/utils/from.spec.ts +243 -0
  241. package/src/utils/from.ts +281 -0
  242. package/src/utils/generateSpec.spec.ts +1262 -0
  243. package/src/utils/generateSpec.ts +54 -0
  244. package/src/utils/generics.ts +83 -0
  245. package/src/utils/getComputedType.spec.ts +45 -0
  246. package/src/utils/getComputedType.ts +39 -0
  247. package/src/utils/getInheritedStores.spec.ts +47 -0
  248. package/src/utils/getInheritedStores.ts +23 -0
  249. package/src/utils/getJsonEntityStore.ts +36 -0
  250. package/src/utils/getJsonPathParameters.spec.ts +126 -0
  251. package/src/utils/getJsonPathParameters.ts +97 -0
  252. package/src/utils/getJsonSchema.spec.ts +695 -0
  253. package/src/utils/getJsonSchema.ts +69 -0
  254. package/src/utils/getJsonType.spec.ts +28 -0
  255. package/src/utils/getJsonType.ts +46 -0
  256. package/src/utils/getOperationsRoutes.spec.ts +284 -0
  257. package/src/utils/getOperationsRoutes.ts +56 -0
  258. package/src/utils/getOperationsStore.spec.ts +47 -0
  259. package/src/utils/getOperationsStores.ts +37 -0
  260. package/src/utils/getPropertiesStores.spec.ts +138 -0
  261. package/src/utils/getPropertiesStores.ts +66 -0
  262. package/src/utils/getSpec.spec.ts +610 -0
  263. package/src/utils/getSpec.ts +117 -0
  264. package/src/utils/getSpecType.spec.ts +16 -0
  265. package/src/utils/getSpecType.ts +19 -0
  266. package/src/utils/isSuccessStatus.spec.ts +22 -0
  267. package/src/utils/isSuccessStatus.ts +2 -0
  268. package/src/utils/mapHeaders.spec.ts +57 -0
  269. package/src/utils/mapHeaders.ts +33 -0
  270. package/src/utils/mapOpenSpec.ts +44 -0
  271. package/src/utils/mapOpenSpec2.ts +50 -0
  272. package/src/utils/mapOpenSpec3.ts +25 -0
  273. package/src/utils/mapOpenSpecInfo.ts +19 -0
  274. package/src/utils/mapOperationOptions.ts +34 -0
  275. package/src/utils/matchGroups.spec.ts +85 -0
  276. package/src/utils/matchGroups.ts +64 -0
  277. package/src/utils/mergeSpec.spec.ts +533 -0
  278. package/src/utils/mergeSpec.ts +54 -0
  279. package/src/utils/operationIdFormatter.spec.ts +11 -0
  280. package/src/utils/operationIdFormatter.ts +49 -0
  281. package/src/utils/ref.ts +65 -0
  282. package/src/utils/removeHiddenOperation.ts +5 -0
  283. package/src/utils/serializeEnumValues.ts +27 -0
  284. package/src/utils/somethingOf.ts +11 -0
  285. package/src/utils/toJsonMapCollection.spec.ts +18 -0
  286. package/src/utils/toJsonMapCollection.ts +12 -0
  287. package/src/utils/toJsonRegex.ts +7 -0
  288. package/src/utils/transformToOS2.spec.ts +177 -0
  289. package/src/utils/transformToOS2.ts +607 -0
  290. package/src/utils/withErrorMsg.spec.ts +16 -0
  291. package/src/utils/withErrorMsg.ts +56 -0
@@ -0,0 +1,1470 @@
1
+ import "../index.js";
2
+
3
+ import {Ajv} from "ajv";
4
+
5
+ import {JsonSchema} from "./JsonSchema.js";
6
+
7
+ describe("JsonSchema", () => {
8
+ describe("extra Props", () => {
9
+ // https://json-schema.org/understanding-json-schema/basics.html
10
+ it("should add extra props", () => {
11
+ const schema = JsonSchema.from({});
12
+ schema.set("extra", "test");
13
+
14
+ expect(schema.isGeneric).toBe(false);
15
+ expect(schema.genericType).toBeUndefined();
16
+ expect(schema.toJSON()).toEqual({
17
+ extra: "test"
18
+ });
19
+ });
20
+ });
21
+ describe("basics", () => {
22
+ // https://json-schema.org/understanding-json-schema/basics.html
23
+ it("should validate {}", () => {
24
+ const schema = JsonSchema.from({}).toObject();
25
+ const validate = new Ajv({strict: true}).compile(schema);
26
+
27
+ expect(schema).toEqual({});
28
+ expect(validate(42)).toBe(true);
29
+ expect(validate("I'm a string")).toBe(true);
30
+ expect(validate({an: ["arbitrarily", "nested"], data: "structure"})).toBe(true);
31
+ });
32
+ });
33
+ describe("String", () => {
34
+ describe("Basics", () => {
35
+ // https://json-schema.org/understanding-json-schema/reference/string.html#string
36
+ it("should create a new jsonSchema", () => {
37
+ const schema = JsonSchema.from({type: String}).toObject();
38
+ const validate = new Ajv({strict: true}).compile(schema);
39
+
40
+ expect(schema).toEqual({
41
+ type: "string"
42
+ });
43
+
44
+ expect(validate("This is a string")).toBe(true);
45
+ expect(validate("Déjà vu")).toBe(true);
46
+ expect(validate("")).toBe(true);
47
+ expect(validate("42")).toBe(true);
48
+ expect(validate(42)).toBe(false);
49
+ });
50
+ });
51
+
52
+ describe("Length", () => {
53
+ // https://json-schema.org/understanding-json-schema/reference/string.html#length
54
+ it("should create a new jsonSchema", () => {
55
+ const schema = JsonSchema.from({type: String}).minLength(2).maxLength(3).toObject();
56
+
57
+ const validate = new Ajv({strict: true}).compile(schema);
58
+
59
+ expect(schema).toEqual({
60
+ type: "string",
61
+ minLength: 2,
62
+ maxLength: 3
63
+ });
64
+
65
+ expect(validate("A")).toBe(false);
66
+ expect(validate("AB")).toBe(true);
67
+ expect(validate("ABC")).toBe(true);
68
+ expect(validate("ABCD")).toBe(false);
69
+ });
70
+ });
71
+
72
+ describe("Regular expression", () => {
73
+ // https://json-schema.org/understanding-json-schema/reference/string.html#regular-expressions
74
+ it("should build json schema", () => {
75
+ const schema = JsonSchema.from({type: String}).pattern(new RegExp("^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$")).toObject();
76
+
77
+ const validate = new Ajv({strict: true}).compile(schema);
78
+
79
+ expect(schema).toEqual({
80
+ type: "string",
81
+ pattern: "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
82
+ });
83
+
84
+ expect(validate("555-1212")).toBe(true);
85
+ expect(validate("(888)555-1212")).toBe(true);
86
+ expect(validate("(888)555-1212 ext. 532")).toBe(false);
87
+ expect(validate("(800)FLOWERS")).toBe(false);
88
+ });
89
+ });
90
+
91
+ describe("Format", () => {
92
+ // https://json-schema.org/understanding-json-schema/reference/string.html#format
93
+ it("should create a new jsonSchema", () => {
94
+ const result = JsonSchema.from({type: Date}).toObject();
95
+
96
+ expect(result).toEqual({
97
+ type: "string"
98
+ });
99
+ });
100
+
101
+ it("should create a new jsonSchema with format", () => {
102
+ const result = JsonSchema.from({type: Date}).format("date-time").toObject();
103
+
104
+ expect(result).toEqual({
105
+ type: "string",
106
+ format: "date-time"
107
+ });
108
+ });
109
+ });
110
+ });
111
+ describe("Number", () => {
112
+ describe("Basics", () => {
113
+ // https://json-schema.org/understanding-json-schema/reference/numeric.html#number
114
+ it("should create a jsonschema for number", () => {
115
+ const schema = JsonSchema.from({type: Number}).toObject();
116
+ const validate = new Ajv({strict: true}).compile(schema);
117
+
118
+ expect(schema).toEqual({
119
+ type: "number"
120
+ });
121
+ expect(validate(42)).toBe(true);
122
+ expect(validate(-1)).toBe(true);
123
+ expect(validate(5.0)).toBe(true);
124
+ expect(validate(2.99792458e8)).toBe(true);
125
+ expect(validate("42")).toBe(false);
126
+ });
127
+ });
128
+
129
+ describe("Integer", () => {
130
+ // https://json-schema.org/understanding-json-schema/reference/numeric.html#integer
131
+ it("should create a jsonschema", () => {
132
+ const schema = JsonSchema.from({type: "integer"}).toObject();
133
+ const validate = new Ajv({strict: true}).compile(schema);
134
+
135
+ expect(schema).toEqual({
136
+ type: "integer",
137
+ multipleOf: 1.0
138
+ });
139
+ expect(validate(42)).toBe(true);
140
+ expect(validate(42.0)).toBe(true);
141
+ expect(validate(3.14156926)).toBe(false);
142
+ });
143
+ });
144
+
145
+ describe("Multiples", () => {
146
+ // https://json-schema.org/understanding-json-schema/reference/numeric.html#multiples
147
+ it("should create a jsonschema", () => {
148
+ const schema = JsonSchema.from({type: Number, multipleOf: 10}).toObject();
149
+ const validate = new Ajv({strict: true}).compile(schema);
150
+
151
+ expect(schema).toEqual({
152
+ type: "number",
153
+ multipleOf: 10
154
+ });
155
+ expect(validate(0)).toBe(true);
156
+ expect(validate(10)).toBe(true);
157
+ expect(validate(20)).toBe(true);
158
+ expect(validate(23)).toBe(false);
159
+ });
160
+ });
161
+
162
+ describe("Ranges", () => {
163
+ // https://json-schema.org/understanding-json-schema/reference/numeric.html#range
164
+ it("should create a jsonschema minimum & exclusiveMaximum", () => {
165
+ const schema = JsonSchema.from({type: Number, minimum: 0, exclusiveMaximum: 100}).toObject();
166
+ const validate = new Ajv({strict: true}).compile(schema);
167
+
168
+ expect(schema).toEqual({
169
+ type: "number",
170
+ minimum: 0,
171
+ exclusiveMaximum: 100
172
+ });
173
+ expect(validate(0)).toBe(true);
174
+ expect(validate(10)).toBe(true);
175
+ expect(validate(99)).toBe(true);
176
+ expect(validate(100)).toBe(false);
177
+ expect(validate(101)).toBe(false);
178
+ });
179
+ it("should create a jsonschema exclusiveMinimum & maximum", () => {
180
+ const schema = JsonSchema.from({type: Number, exclusiveMinimum: 0, maximum: 100}).toObject();
181
+ const validate = new Ajv({strict: true}).compile(schema);
182
+
183
+ expect(schema).toEqual({
184
+ type: "number",
185
+ exclusiveMinimum: 0,
186
+ maximum: 100
187
+ });
188
+ expect(validate(-1)).toBe(false);
189
+ expect(validate(0)).toBe(false);
190
+ expect(validate(1)).toBe(true);
191
+ expect(validate(10)).toBe(true);
192
+ expect(validate(99)).toBe(true);
193
+ expect(validate(100)).toBe(true);
194
+ expect(validate(101)).toBe(false);
195
+ });
196
+ });
197
+ });
198
+ describe("Object", () => {
199
+ describe("basic", () => {
200
+ // https://json-schema.org/understanding-json-schema/reference/object.html#object
201
+ it("should create a new jsonSchema", () => {
202
+ const schema = JsonSchema.from({type: Object}).toObject();
203
+ const validate = new Ajv({strict: true}).compile(schema);
204
+
205
+ expect(schema).toEqual({
206
+ type: "object"
207
+ });
208
+
209
+ expect(
210
+ validate({
211
+ key: "value",
212
+ another_key: "another_value"
213
+ })
214
+ ).toBe(true);
215
+ expect(
216
+ validate({
217
+ Sun: 1.9891e30,
218
+ Jupiter: 1.8986e27,
219
+ Saturn: 5.6846e26,
220
+ Neptune: 10.243e25,
221
+ Uranus: 8.681e25,
222
+ Earth: 5.9736e24,
223
+ Venus: 4.8685e24,
224
+ Mars: 6.4185e23,
225
+ Mercury: 3.3022e23,
226
+ Moon: 7.349e22,
227
+ Pluto: 1.25e22
228
+ })
229
+ ).toBe(true);
230
+ expect(
231
+ validate({
232
+ 0.01: "cm",
233
+ 1: "m",
234
+ 1000: "km"
235
+ })
236
+ ).toBe(true);
237
+ expect(validate("Not an object")).toBe(false);
238
+ expect(validate(["An", "array", "not", "an", "object"])).toBe(false);
239
+ });
240
+ });
241
+ describe("Properties", () => {
242
+ // https://json-schema.org/understanding-json-schema/reference/object.html#properties
243
+ it("should create a valid jsonchema (properties)", () => {
244
+ const schema = JsonSchema.from({
245
+ type: Object,
246
+ properties: {
247
+ number: {type: "number"},
248
+ street_name: {type: "string"},
249
+ street_type: {
250
+ type: "string",
251
+ enum: ["Street", "Avenue", "Boulevard"]
252
+ }
253
+ }
254
+ }).toObject();
255
+
256
+ const validate = new Ajv({strict: true}).compile(schema);
257
+ expect(validate({number: 1600, street_name: "Pennsylvania", street_type: "Avenue"})).toBe(true);
258
+ expect(validate({number: "1600", street_name: "Pennsylvania", street_type: "Avenue"})).toBe(false);
259
+ expect(validate({number: 1600, street_name: "Pennsylvania"})).toBe(true);
260
+ expect(validate({})).toBe(true);
261
+ expect(
262
+ validate({
263
+ number: 1600,
264
+ street_name: "Pennsylvania",
265
+ street_type: "Avenue",
266
+ direction: "NW"
267
+ })
268
+ ).toBe(true);
269
+ });
270
+ it("should create a valid jsonschema (additionalProperties boolean)", () => {
271
+ const schema = JsonSchema.from({
272
+ type: "object",
273
+ properties: {
274
+ number: {type: "number"},
275
+ street_name: {type: "string"},
276
+ street_type: {
277
+ type: "string",
278
+ enum: ["Street", "Avenue", "Boulevard"]
279
+ }
280
+ },
281
+ additionalProperties: false // Unauthorized unknown properties
282
+ }).toObject();
283
+
284
+ expect(schema).toEqual({
285
+ type: "object",
286
+ properties: {
287
+ number: {type: "number"},
288
+ street_name: {type: "string"},
289
+ street_type: {
290
+ type: "string",
291
+ enum: ["Street", "Avenue", "Boulevard"]
292
+ }
293
+ },
294
+ additionalProperties: false // Unauthorized unknown properties
295
+ });
296
+
297
+ const validate = new Ajv({strict: true}).compile(schema);
298
+
299
+ expect(validate({number: 1600, street_name: "Pennsylvania", street_type: "Avenue"})).toBe(true);
300
+ expect(
301
+ validate({
302
+ number: 1600,
303
+ street_name: "Pennsylvania",
304
+ street_type: "Avenue",
305
+ direction: "NW"
306
+ })
307
+ ).toBe(false);
308
+ });
309
+ it("should create a valid jsonchema (additionalProperties schema)", () => {
310
+ const schema = JsonSchema.from({
311
+ type: "object",
312
+ properties: {
313
+ number: {type: "number"},
314
+ street_name: {type: "string"},
315
+ street_type: {
316
+ type: "string",
317
+ enum: ["Street", "Avenue", "Boulevard"]
318
+ }
319
+ },
320
+ additionalProperties: JsonSchema.from({type: "string"})
321
+ }).toObject();
322
+
323
+ const validate = new Ajv({strict: true}).compile(schema);
324
+
325
+ expect(validate({number: 1600, street_name: "Pennsylvania", street_type: "Avenue"})).toBe(true);
326
+ expect(
327
+ validate({
328
+ number: 1600,
329
+ street_name: "Pennsylvania",
330
+ street_type: "Avenue",
331
+ direction: "NW"
332
+ })
333
+ ).toBe(true);
334
+ expect(
335
+ validate({
336
+ number: 1600,
337
+ street_name: "Pennsylvania",
338
+ street_type: "Avenue",
339
+ office_number: 201
340
+ })
341
+ ).toBe(false);
342
+ });
343
+ });
344
+ describe("Required", () => {
345
+ // https://json-schema.org/understanding-json-schema/reference/object.html#required-properties
346
+ it("should create a valid jsonchema (basic)", () => {
347
+ const jsonSchema = JsonSchema.from({
348
+ type: "object",
349
+ properties: {
350
+ name: {type: "string"},
351
+ email: {type: "string"},
352
+ address: {type: "string"},
353
+ telephone: {type: "string"}
354
+ },
355
+ required: ["name", "email", "email"]
356
+ });
357
+
358
+ const schema = jsonSchema.clone().toObject();
359
+
360
+ expect(jsonSchema.isRequired("name")).toBe(true);
361
+ expect(schema).toEqual({
362
+ type: "object",
363
+ properties: {
364
+ name: {type: "string"},
365
+ email: {type: "string"},
366
+ address: {type: "string"},
367
+ telephone: {type: "string"}
368
+ },
369
+ required: ["name", "email"]
370
+ });
371
+
372
+ const validate = new Ajv({strict: true}).compile(schema);
373
+ expect(
374
+ validate({
375
+ name: "William Shakespeare",
376
+ email: "bill@stratford-upon-avon.co.uk"
377
+ })
378
+ ).toBe(true);
379
+ expect(
380
+ validate({
381
+ name: "William Shakespeare",
382
+ email: "bill@stratford-upon-avon.co.uk",
383
+ address: "Henley Street, Stratford-upon-Avon, Warwickshire, England",
384
+ authorship: "in question"
385
+ })
386
+ ).toBe(true);
387
+ expect(
388
+ validate({
389
+ name: "William Shakespeare",
390
+ address: "Henley Street, Stratford-upon-Avon, Warwickshire, England"
391
+ })
392
+ ).toBe(false);
393
+ });
394
+ it("should create a valid jsonchema (default - string)", () => {
395
+ const schema = JsonSchema.from({
396
+ type: "object",
397
+ properties: {
398
+ name: {type: "string", minLength: 1}
399
+ },
400
+ required: ["name"]
401
+ }).toObject();
402
+
403
+ expect(schema).toEqual({
404
+ type: "object",
405
+ properties: {
406
+ name: {type: "string", minLength: 1}
407
+ },
408
+ required: ["name"]
409
+ });
410
+
411
+ const validate = new Ajv({strict: true}).compile(schema);
412
+ expect(validate({name: "William Shakespeare"})).toBe(true);
413
+ expect(validate({name: ""})).toBe(false);
414
+ expect(validate({name: null})).toBe(false);
415
+ expect(validate({name: 0})).toBe(false);
416
+ expect(validate({})).toBe(false);
417
+ });
418
+
419
+ it("should create a valid jsonchema (default - number)", () => {
420
+ const schema = JsonSchema.from({
421
+ type: "object",
422
+ properties: {
423
+ name: {type: "number"}
424
+ },
425
+ required: ["name"]
426
+ }).toObject();
427
+
428
+ expect(schema).toEqual({
429
+ type: "object",
430
+ properties: {
431
+ name: {type: "number"}
432
+ },
433
+ required: ["name"]
434
+ });
435
+
436
+ const validate = new Ajv({strict: true}).compile(schema);
437
+ expect(validate({name: "William Shakespeare"})).toBe(false);
438
+ expect(validate({name: ""})).toBe(false);
439
+ expect(validate({name: null})).toBe(false);
440
+ expect(validate({name: 0})).toBe(true);
441
+ expect(validate({})).toBe(false);
442
+ });
443
+
444
+ it("should create a valid jsonchema (empty string is falsy)", () => {
445
+ const schema = JsonSchema.from({
446
+ type: "object",
447
+ properties: {
448
+ name: {
449
+ type: "string",
450
+ minLength: 1
451
+ }
452
+ },
453
+ required: ["name"]
454
+ }).toObject();
455
+
456
+ expect(schema).toEqual({
457
+ type: "object",
458
+ properties: {
459
+ name: {
460
+ type: "string",
461
+ minLength: 1
462
+ }
463
+ },
464
+ required: ["name"]
465
+ });
466
+
467
+ const validate = new Ajv({strict: true}).compile(schema);
468
+ expect(validate({name: "William Shakespeare"})).toBe(true);
469
+ expect(validate({name: ""})).toBe(false);
470
+ expect(validate({name: null})).toBe(false);
471
+ expect(validate({name: 0})).toBe(false);
472
+ expect(validate({})).toBe(false);
473
+ });
474
+
475
+ it("should create a valid jsonchema (0 is falsy - number)", () => {
476
+ const schema = JsonSchema.from({
477
+ type: "object",
478
+ properties: {
479
+ name: {type: "number", exclusiveMinimum: 0}
480
+ },
481
+ required: ["name"]
482
+ }).toObject();
483
+
484
+ expect(schema).toEqual({
485
+ type: "object",
486
+ properties: {
487
+ name: {type: "number", exclusiveMinimum: 0}
488
+ },
489
+ required: ["name"]
490
+ });
491
+
492
+ const validate = new Ajv({strict: true}).compile(schema);
493
+ expect(validate({name: "William Shakespeare"})).toBe(false);
494
+ expect(validate({name: ""})).toBe(false);
495
+ expect(validate({name: null})).toBe(false);
496
+ expect(validate({name: 0})).toBe(false);
497
+ expect(validate({})).toBe(false);
498
+ });
499
+ });
500
+ describe("Property names", () => {
501
+ // https://json-schema.org/understanding-json-schema/reference/object.html#property-names
502
+ it("should create a valid jsonchema", () => {
503
+ const schema = JsonSchema.from({
504
+ type: "object",
505
+ propertyNames: JsonSchema.from({
506
+ pattern: "^[A-Za-z_][A-Za-z0-9_]*$"
507
+ })
508
+ }).toObject();
509
+
510
+ const validate = new Ajv({strict: true}).compile(schema);
511
+ expect(
512
+ validate({
513
+ _a_proper_token_001: "value"
514
+ })
515
+ ).toBe(true);
516
+ expect(
517
+ validate({
518
+ "001 invalid": "value"
519
+ })
520
+ ).toBe(false);
521
+ });
522
+ });
523
+ describe("Size", () => {
524
+ // https://json-schema.org/understanding-json-schema/reference/object.html#size
525
+ it("should create a valid jsonchema", () => {
526
+ const schema = JsonSchema.from({
527
+ type: "object",
528
+ minProperties: 2,
529
+ maxProperties: 3
530
+ }).toObject();
531
+
532
+ const validate = new Ajv({strict: true}).compile(schema);
533
+ expect(validate({})).toBe(false);
534
+ expect(validate({a: 0})).toBe(false);
535
+ expect(validate({a: 0, b: 1})).toBe(true);
536
+ expect(validate({a: 0, b: 1, c: 2})).toBe(true);
537
+ expect(validate({a: 0, b: 1, c: 2, d: 3})).toBe(false);
538
+ });
539
+ });
540
+ describe("Dependencies", () => {
541
+ // https://json-schema.org/understanding-json-schema/reference/object.html#dependencies
542
+ describe("Property Dependencies", () => {
543
+ it("should create a valid jsonchema", () => {
544
+ const schema = JsonSchema.from({
545
+ type: "object",
546
+ properties: {
547
+ name: {type: "string"},
548
+ credit_card: {type: "number"},
549
+ billing_address: {type: "string"}
550
+ },
551
+ required: ["name"],
552
+ dependencies: {
553
+ credit_card: ["billing_address"]
554
+ }
555
+ }).toObject();
556
+
557
+ expect(schema).toEqual({
558
+ type: "object",
559
+
560
+ properties: {
561
+ name: {type: "string"},
562
+ credit_card: {type: "number"},
563
+ billing_address: {type: "string"}
564
+ },
565
+
566
+ required: ["name"],
567
+
568
+ dependencies: {
569
+ credit_card: ["billing_address"]
570
+ }
571
+ });
572
+
573
+ const validate = new Ajv({strict: true}).compile(schema);
574
+ expect(
575
+ validate({
576
+ name: "John Doe",
577
+ credit_card: 5555555555555555,
578
+ billing_address: "555 Debtor's Lane"
579
+ })
580
+ ).toBe(true);
581
+ expect(
582
+ validate({
583
+ name: "John Doe",
584
+ credit_card: 5555555555555555
585
+ })
586
+ ).toBe(false);
587
+ expect(
588
+ validate({
589
+ name: "John Doe"
590
+ })
591
+ ).toBe(true);
592
+ expect(
593
+ validate({
594
+ name: "John Doe",
595
+ billing_address: "555 Debtor's Lane"
596
+ })
597
+ ).toBe(true);
598
+ });
599
+ it("should create a valid jsonchema (bidirectional dependencies)", () => {
600
+ const schema = JsonSchema.from({
601
+ type: "object",
602
+
603
+ properties: {
604
+ name: {type: "string"},
605
+ credit_card: {type: "number"},
606
+ billing_address: {type: "string"}
607
+ },
608
+
609
+ required: ["name"],
610
+
611
+ dependencies: {
612
+ credit_card: ["billing_address"],
613
+ billing_address: ["credit_card"]
614
+ }
615
+ }).toObject();
616
+
617
+ const validate = new Ajv({strict: true}).compile(schema);
618
+ expect(
619
+ validate({
620
+ name: "John Doe",
621
+ credit_card: 5555555555555555
622
+ })
623
+ ).toBe(false);
624
+ expect(
625
+ validate({
626
+ name: "John Doe",
627
+ billing_address: "555 Debtor's Lane"
628
+ })
629
+ ).toBe(false);
630
+ });
631
+ });
632
+ describe("Schema Dependencies", () => {
633
+ it("should create a valid jsonchema", () => {
634
+ const schema = JsonSchema.from({
635
+ type: "object",
636
+
637
+ properties: {
638
+ name: {type: "string"},
639
+ credit_card: {type: "number"}
640
+ },
641
+
642
+ required: ["name"],
643
+
644
+ dependencies: {
645
+ credit_card: {
646
+ properties: {
647
+ billing_address: {type: "string"}
648
+ },
649
+ required: ["billing_address"]
650
+ }
651
+ }
652
+ }).toObject();
653
+
654
+ const validate = new Ajv({strict: true}).compile(schema);
655
+ expect(
656
+ validate({
657
+ name: "John Doe",
658
+ credit_card: 5555555555555555,
659
+ billing_address: "555 Debtor's Lane"
660
+ })
661
+ ).toBe(true);
662
+ expect(
663
+ validate({
664
+ name: "John Doe",
665
+ credit_card: 5555555555555555
666
+ })
667
+ ).toBe(false);
668
+ expect(
669
+ validate({
670
+ name: "John Doe"
671
+ })
672
+ ).toBe(true);
673
+ expect(
674
+ validate({
675
+ name: "John Doe",
676
+ billing_address: "555 Debtor's Lane"
677
+ })
678
+ ).toBe(true);
679
+ });
680
+ });
681
+ });
682
+ describe("Pattern Properties", () => {
683
+ // https://json-schema.org/understanding-json-schema/reference/object.html#pattern-properties
684
+ it("should create a valid jsonchema with false value", () => {
685
+ const schema = JsonSchema.from({
686
+ type: "object",
687
+ patternProperties: {
688
+ "^S_": {type: "string"},
689
+ "^I_": {type: "integer"}
690
+ },
691
+ additionalProperties: false
692
+ }).toObject();
693
+
694
+ expect(schema).toEqual({
695
+ type: "object",
696
+ patternProperties: {
697
+ "^S_": {type: "string"},
698
+ "^I_": {
699
+ type: "integer",
700
+ multipleOf: 1
701
+ }
702
+ },
703
+ additionalProperties: false
704
+ });
705
+
706
+ const validate = new Ajv({strict: true}).compile(schema);
707
+ expect(validate({S_25: "This is a string"})).toBe(true);
708
+ expect(validate({I_0: 42})).toBe(true);
709
+ expect(validate({S_0: 42})).toBe(false);
710
+ expect(validate({I_42: "This is a string"})).toBe(false);
711
+ expect(validate({keyword: "value"})).toBe(false);
712
+ });
713
+ it("should create a valid jsonchema with schema value", () => {
714
+ const schema = JsonSchema.from({
715
+ type: "object",
716
+ properties: {
717
+ builtin: {type: "number"}
718
+ },
719
+ patternProperties: {
720
+ "^S_": {type: "string"},
721
+ "^I_": {type: "integer"}
722
+ },
723
+ additionalProperties: JsonSchema.from({type: "string"})
724
+ }).toObject();
725
+
726
+ expect(schema).toEqual({
727
+ type: "object",
728
+ properties: {
729
+ builtin: {type: "number"}
730
+ },
731
+ patternProperties: {
732
+ "^S_": {type: "string"},
733
+ "^I_": {type: "integer", multipleOf: 1.0}
734
+ },
735
+ additionalProperties: {type: "string"}
736
+ });
737
+
738
+ const validate = new Ajv({strict: true}).compile(schema);
739
+ expect(validate({S_25: "This is a string"})).toBe(true);
740
+ expect(validate({keyword: "value"})).toBe(true);
741
+ expect(validate({keyword: 42})).toBe(false);
742
+ });
743
+ });
744
+ });
745
+ describe("Array", () => {
746
+ describe("Basic", () => {
747
+ // https://json-schema.org/understanding-json-schema/reference/array.html#array
748
+ it("should create a new jsonSchema", () => {
749
+ const schema = JsonSchema.from({type: Array}).toObject();
750
+ const validate = new Ajv({strict: true}).compile(schema);
751
+
752
+ expect(schema).toEqual({
753
+ type: "array"
754
+ });
755
+
756
+ expect(validate([1, 2, 3, 4, 5])).toBe(true);
757
+ expect(validate([3, "different", {types: "of values"}])).toBe(true);
758
+ expect(validate({Not: "an array"})).toBe(false);
759
+ });
760
+ });
761
+ describe("Items", () => {
762
+ // https://json-schema.org/understanding-json-schema/reference/array.html#items
763
+ describe("List validation", () => {
764
+ // https://json-schema.org/understanding-json-schema/reference/array.html#list-validation
765
+ it("should create a new jsonSchema (items)", () => {
766
+ const schema = JsonSchema.from({
767
+ type: Array,
768
+ items: JsonSchema.from({
769
+ type: "number"
770
+ })
771
+ }).toObject();
772
+
773
+ const validate = new Ajv({strict: true}).compile(schema);
774
+
775
+ expect(schema).toEqual({
776
+ type: "array",
777
+ items: {
778
+ type: "number"
779
+ }
780
+ });
781
+
782
+ expect(validate([1, 2, 3, 4, 5])).toBe(true);
783
+ expect(validate([3, "different", {types: "of values"}])).toBe(false);
784
+ expect(validate([])).toBe(true);
785
+ });
786
+
787
+ it("should create a new jsonSchema (contains)", () => {
788
+ const schema = JsonSchema.from({
789
+ type: "array",
790
+ contains: {
791
+ type: "number"
792
+ }
793
+ }).toObject();
794
+
795
+ const validate = new Ajv({strict: true}).compile(schema);
796
+
797
+ expect(schema).toEqual({
798
+ type: "array",
799
+ contains: {
800
+ type: "number"
801
+ }
802
+ });
803
+
804
+ expect(validate(["life", "universe", "everything", 42])).toBe(true);
805
+ expect(validate(["life", "universe", "everything", "forty-two"])).toBe(false);
806
+ expect(validate([1, 2, 3, 4, 5])).toBe(true);
807
+ });
808
+ });
809
+ describe("Tuple validation", () => {
810
+ // https://json-schema.org/understanding-json-schema/reference/array.html#tuple-validation
811
+ it("should create a new jsonSchema", () => {
812
+ const schema = JsonSchema.from({
813
+ type: "array",
814
+ items: [
815
+ {
816
+ type: "number"
817
+ },
818
+ {
819
+ type: "string"
820
+ },
821
+ {
822
+ type: "string",
823
+ enum: ["Street", "Avenue", "Boulevard"]
824
+ },
825
+ {
826
+ type: "string",
827
+ enum: ["NW", "NE", "SW", "SE"]
828
+ }
829
+ ]
830
+ }).toObject();
831
+
832
+ const validate = new Ajv().compile(schema);
833
+
834
+ expect(schema).toEqual({
835
+ type: "array",
836
+ items: [
837
+ {
838
+ type: "number"
839
+ },
840
+ {
841
+ type: "string"
842
+ },
843
+ {
844
+ type: "string",
845
+ enum: ["Street", "Avenue", "Boulevard"]
846
+ },
847
+ {
848
+ type: "string",
849
+ enum: ["NW", "NE", "SW", "SE"]
850
+ }
851
+ ]
852
+ });
853
+
854
+ expect(validate([1600, "Pennsylvania", "Avenue", "NW"])).toBe(true);
855
+ expect(validate([24, "Sussex", "Drive"])).toBe(false);
856
+ expect(validate(["Palais de l'Élysée"])).toBe(false);
857
+ expect(validate([10, "Downing", "Street"])).toBe(true);
858
+ expect(validate([1600, "Pennsylvania", "Avenue", "NW", "Washington"])).toBe(true);
859
+ });
860
+ it("should create a new jsonSchema (additionalItems=false)", () => {
861
+ const schema = JsonSchema.from({
862
+ type: "array",
863
+ items: [
864
+ {
865
+ type: "number"
866
+ },
867
+ {
868
+ type: "string"
869
+ },
870
+ {
871
+ type: "string",
872
+ enum: ["Street", "Avenue", "Boulevard"]
873
+ },
874
+ {
875
+ type: "string",
876
+ enum: ["NW", "NE", "SW", "SE"]
877
+ }
878
+ ],
879
+ additionalItems: false
880
+ }).toObject();
881
+
882
+ const validate = new Ajv().compile(schema);
883
+
884
+ expect(schema).toEqual({
885
+ type: "array",
886
+ items: [
887
+ {
888
+ type: "number"
889
+ },
890
+ {
891
+ type: "string"
892
+ },
893
+ {
894
+ type: "string",
895
+ enum: ["Street", "Avenue", "Boulevard"]
896
+ },
897
+ {
898
+ type: "string",
899
+ enum: ["NW", "NE", "SW", "SE"]
900
+ }
901
+ ],
902
+ additionalItems: false
903
+ });
904
+
905
+ expect(validate([1600, "Pennsylvania", "Avenue", "NW"])).toBe(true);
906
+ expect(validate([1600, "Pennsylvania", "Avenue"])).toBe(true);
907
+ expect(validate([1600, "Pennsylvania", "Avenue", "NW", "Washington"])).toBe(false);
908
+ });
909
+ it("should create a new jsonSchema (additionalItems=Schema)", () => {
910
+ const schema = JsonSchema.from({
911
+ type: "array",
912
+ items: [
913
+ {
914
+ type: "number"
915
+ },
916
+ {
917
+ type: "string"
918
+ },
919
+ {
920
+ type: "string",
921
+ enum: ["Street", "Avenue", "Boulevard"]
922
+ },
923
+ {
924
+ type: "string",
925
+ enum: ["NW", "NE", "SW", "SE"]
926
+ }
927
+ ],
928
+ additionalItems: {type: "string"}
929
+ }).toObject();
930
+
931
+ const validate = new Ajv().compile(schema);
932
+
933
+ expect(schema).toEqual({
934
+ type: "array",
935
+ items: [
936
+ {
937
+ type: "number"
938
+ },
939
+ {
940
+ type: "string"
941
+ },
942
+ {
943
+ type: "string",
944
+ enum: ["Street", "Avenue", "Boulevard"]
945
+ },
946
+ {
947
+ type: "string",
948
+ enum: ["NW", "NE", "SW", "SE"]
949
+ }
950
+ ],
951
+ additionalItems: {type: "string"}
952
+ });
953
+
954
+ expect(validate([1600, "Pennsylvania", "Avenue", "NW"])).toBe(true);
955
+ expect(validate([1600, "Pennsylvania", "Avenue", "NW", 20500])).toBe(false);
956
+ });
957
+ });
958
+ describe("Length", () => {
959
+ // https://json-schema.org/understanding-json-schema/reference/array.html#length
960
+ it("should create a new jsonSchema", () => {
961
+ const schema = JsonSchema.from({
962
+ type: "array",
963
+ minItems: 2,
964
+ maxItems: 3
965
+ }).toObject();
966
+
967
+ const validate = new Ajv({strict: true}).compile(schema);
968
+
969
+ expect(validate([])).toBe(false);
970
+ expect(validate([1])).toBe(false);
971
+ expect(validate([1, 2])).toBe(true);
972
+ expect(validate([1, 2, 3])).toBe(true);
973
+ expect(validate([1, 2, 3, 4])).toBe(false);
974
+ });
975
+ });
976
+ describe("Uniqueness", () => {
977
+ // https://json-schema.org/understanding-json-schema/reference/array.html#uniqueness
978
+ it("should create a new jsonSchema", () => {
979
+ const schema = JsonSchema.from({
980
+ type: "array",
981
+ uniqueItems: true
982
+ }).toObject();
983
+
984
+ const validate = new Ajv({strict: true}).compile(schema);
985
+
986
+ expect(validate([1, 2, 3, 4, 5])).toBe(true);
987
+ expect(validate([1, 2, 3, 3, 4])).toBe(false);
988
+ expect(validate([])).toBe(true);
989
+ });
990
+ });
991
+ });
992
+ });
993
+ describe("Boolean", () => {
994
+ // https://json-schema.org/understanding-json-schema/reference/boolean.html
995
+ it("should create a new jsonSchema", () => {
996
+ const schema = JsonSchema.from({type: Boolean}).toObject();
997
+ const validate = new Ajv({strict: true}).compile(schema);
998
+
999
+ expect(schema).toEqual({
1000
+ type: "boolean"
1001
+ });
1002
+
1003
+ expect(validate(true)).toBe(true);
1004
+ expect(validate(false)).toBe(true);
1005
+ expect(validate("true")).toBe(false);
1006
+ expect(validate(0)).toBe(false);
1007
+ });
1008
+ });
1009
+ describe("Null", () => {
1010
+ it("should create a new jsonSchema", () => {
1011
+ const schema = JsonSchema.from({type: null}).toObject();
1012
+
1013
+ expect(schema).toEqual({
1014
+ type: "null"
1015
+ });
1016
+
1017
+ const validate = new Ajv({strict: true}).compile(schema);
1018
+
1019
+ expect(validate(null)).toBe(true);
1020
+ expect(validate(false)).toBe(false);
1021
+ expect(validate(0)).toBe(false);
1022
+ expect(validate("")).toBe(false);
1023
+ });
1024
+ });
1025
+ describe("Generic keywords", () => {
1026
+ // https://json-schema.org/understanding-json-schema/reference/generic.html#generic-keywords
1027
+
1028
+ describe("Annotations", () => {
1029
+ // https://json-schema.org/understanding-json-schema/reference/generic.html#annotations
1030
+ it("should create a new jsonSchema", () => {
1031
+ const schema = JsonSchema.from({
1032
+ title: "Match anything",
1033
+ description: "This is a schema that matches anything.",
1034
+ default: "Default value",
1035
+ examples: ["Anything", 4035]
1036
+ }).toObject();
1037
+
1038
+ expect(schema).toEqual({
1039
+ title: "Match anything",
1040
+ description: "This is a schema that matches anything.",
1041
+ default: "Default value",
1042
+ examples: ["Anything", 4035]
1043
+ });
1044
+ });
1045
+ });
1046
+
1047
+ describe("Enumerated values", () => {
1048
+ // https://json-schema.org/understanding-json-schema/reference/generic.html#enumerated-values
1049
+ it("should create a new jsonSchema", () => {
1050
+ const schema = JsonSchema.from({
1051
+ type: "string",
1052
+ enum: ["red", "amber", "green", "green"]
1053
+ }).toObject();
1054
+
1055
+ expect(schema).toEqual({
1056
+ type: "string",
1057
+ enum: ["red", "amber", "green"]
1058
+ });
1059
+
1060
+ const validate = new Ajv({strict: true}).compile(schema);
1061
+
1062
+ expect(validate("red")).toBe(true);
1063
+ expect(validate("blue")).toBe(false);
1064
+ });
1065
+ it("should create a new jsonSchema (without type)", () => {
1066
+ const schema = JsonSchema.from({
1067
+ enum: ["red", "amber", "green", null, 42]
1068
+ }).toObject();
1069
+
1070
+ expect(schema).toEqual({
1071
+ anyOf: [
1072
+ {
1073
+ type: "null"
1074
+ },
1075
+ {
1076
+ enum: ["red", "amber", "green"],
1077
+ type: "string"
1078
+ },
1079
+ {
1080
+ enum: [42],
1081
+ type: "number"
1082
+ }
1083
+ ]
1084
+ });
1085
+
1086
+ const validate = new Ajv({allowUnionTypes: true}).compile(schema);
1087
+
1088
+ expect(validate("red")).toBe(true);
1089
+ expect(validate(null)).toBe(true);
1090
+ expect(validate(42)).toBe(true);
1091
+ expect(validate(0)).toBe(false);
1092
+ });
1093
+ });
1094
+
1095
+ describe("Constant values", () => {
1096
+ // https://json-schema.org/understanding-json-schema/reference/generic.html#constant-values
1097
+ it("should create a new jsonSchema", () => {
1098
+ const schema = JsonSchema.from({
1099
+ type: "object",
1100
+ properties: {
1101
+ country: {
1102
+ const: "United States of America"
1103
+ }
1104
+ }
1105
+ }).toObject();
1106
+
1107
+ expect(schema).toEqual({
1108
+ type: "object",
1109
+ properties: {
1110
+ country: {
1111
+ const: "United States of America"
1112
+ }
1113
+ }
1114
+ });
1115
+
1116
+ const validate = new Ajv({strict: true}).compile(schema);
1117
+
1118
+ expect(validate({country: "United States of America"})).toBe(true);
1119
+ expect(validate({country: "Canada"})).toBe(false);
1120
+ });
1121
+ });
1122
+
1123
+ describe("props", () => {
1124
+ it("should create a new jsonSchema", () => {
1125
+ const result = JsonSchema.from({
1126
+ type: String,
1127
+ $id: "$id",
1128
+ $schema: "$schema",
1129
+ default: "default",
1130
+ const: "const",
1131
+ description: "description",
1132
+ maxItems: 10,
1133
+ minItems: 0,
1134
+ uniqueItems: false,
1135
+ maxProperties: 0,
1136
+ minProperties: 1,
1137
+ multipleOf: 3,
1138
+ maximum: 4,
1139
+ exclusiveMaximum: 5,
1140
+ minimum: 6,
1141
+ exclusiveMinimum: 7,
1142
+ maxLength: 8,
1143
+ minLength: 9,
1144
+ title: "title"
1145
+ }).toObject();
1146
+
1147
+ expect(result).toEqual({
1148
+ type: "string",
1149
+ $id: "$id",
1150
+ $schema: "$schema",
1151
+ default: "default",
1152
+ const: "const",
1153
+ description: "description",
1154
+ maxItems: 10,
1155
+ minItems: 0,
1156
+ uniqueItems: false,
1157
+ maxProperties: 0,
1158
+ minProperties: 1,
1159
+ multipleOf: 3,
1160
+ maximum: 4,
1161
+ exclusiveMaximum: 5,
1162
+ minimum: 6,
1163
+ exclusiveMinimum: 7,
1164
+ maxLength: 8,
1165
+ minLength: 9,
1166
+ title: "title"
1167
+ });
1168
+ });
1169
+ });
1170
+ });
1171
+ describe("Combining Schema", () => {
1172
+ // https://json-schema.org/understanding-json-schema/reference/combining.html#combining-schemas
1173
+ describe("anyOf", () => {
1174
+ it("should create a new jsonSchema", () => {
1175
+ const schema = JsonSchema.from({
1176
+ anyOf: [
1177
+ {type: "string", maxLength: 5},
1178
+ {type: "number", minimum: 0}
1179
+ ]
1180
+ }).toObject();
1181
+
1182
+ expect(schema).toEqual({
1183
+ anyOf: [
1184
+ {type: "string", maxLength: 5},
1185
+ {type: "number", minimum: 0}
1186
+ ]
1187
+ });
1188
+
1189
+ const validate = new Ajv({strict: true}).compile(schema);
1190
+
1191
+ expect(validate("short")).toBe(true);
1192
+ expect(validate("too long")).toBe(false);
1193
+ expect(validate(12)).toBe(true);
1194
+ expect(validate(-5)).toBe(false);
1195
+ });
1196
+ });
1197
+ describe("allOf", () => {
1198
+ // https://json-schema.org/understanding-json-schema/reference/combining.html#allof
1199
+ it("should create a new jsonSchema", () => {
1200
+ const schema = JsonSchema.from({
1201
+ allOf: [
1202
+ {type: "string", maxLength: 5},
1203
+ {type: "number", minimum: 0}
1204
+ ]
1205
+ }).toObject();
1206
+
1207
+ expect(schema).toEqual({
1208
+ allOf: [
1209
+ {type: "string", maxLength: 5},
1210
+ {type: "number", minimum: 0}
1211
+ ]
1212
+ });
1213
+
1214
+ const validate = new Ajv({strict: true}).compile(schema);
1215
+
1216
+ expect(validate("short")).toBe(false);
1217
+ expect(validate("too long")).toBe(false);
1218
+ });
1219
+ });
1220
+ describe("oneOf", () => {
1221
+ // https://json-schema.org/understanding-json-schema/reference/combining.html#oneof
1222
+ it("should create a new jsonSchema", () => {
1223
+ const schema = JsonSchema.from({
1224
+ oneOf: [
1225
+ {type: "number", multipleOf: 5},
1226
+ {type: "number", multipleOf: 3}
1227
+ ]
1228
+ }).toObject();
1229
+
1230
+ const validate = new Ajv({strict: true}).compile(schema);
1231
+
1232
+ expect(validate(10)).toBe(true);
1233
+ expect(validate(9)).toBe(true);
1234
+ expect(validate(2)).toBe(false);
1235
+ expect(validate(15)).toBe(false);
1236
+ });
1237
+ });
1238
+ describe("not", () => {
1239
+ // https://json-schema.org/understanding-json-schema/reference/combining.html#not
1240
+ it("should create a new jsonSchema", () => {
1241
+ const schema = JsonSchema.from({not: {type: "string"}}).toObject();
1242
+
1243
+ const validate = new Ajv({strict: true}).compile(schema);
1244
+
1245
+ expect(validate(42)).toBe(true);
1246
+ expect(validate({key: "value"})).toBe(true);
1247
+ expect(validate("I am a string")).toBe(false);
1248
+ });
1249
+ });
1250
+ });
1251
+ describe("Collection", () => {
1252
+ it("should create a new jsonSchema (Array)", () => {
1253
+ const result = JsonSchema.from({type: Array}).toObject();
1254
+ expect(JsonSchema.from({type: Array}).isCollection).toBe(true);
1255
+ expect(result).toEqual({
1256
+ type: "array"
1257
+ });
1258
+ });
1259
+
1260
+ it("should create a new jsonSchema (Map)", () => {
1261
+ const result = JsonSchema.from({type: Map}).toObject();
1262
+
1263
+ expect(result).toEqual({
1264
+ type: "object"
1265
+ });
1266
+ });
1267
+
1268
+ it("should create a new jsonSchema (Set)", () => {
1269
+ const result = JsonSchema.from({type: Set}).toObject();
1270
+
1271
+ expect(result).toEqual({
1272
+ type: "array",
1273
+ uniqueItems: true
1274
+ });
1275
+ });
1276
+ });
1277
+ describe("Class", () => {
1278
+ it("should create a new jsonSchema", () => {
1279
+ const result = JsonSchema.from({
1280
+ type: class Test {}
1281
+ }).toObject();
1282
+
1283
+ expect(result).toEqual({
1284
+ type: "object"
1285
+ });
1286
+ });
1287
+ });
1288
+ describe("Circular ref", () => {
1289
+ it("should create and validate schema", () => {
1290
+ const schema = JsonSchema.from({
1291
+ $ref: "#/definitions/Post",
1292
+ definitions: {
1293
+ Post: {
1294
+ type: "object",
1295
+ properties: {
1296
+ name: {
1297
+ type: "string"
1298
+ },
1299
+ owner: {
1300
+ $ref: "#/definitions/User"
1301
+ }
1302
+ }
1303
+ },
1304
+ User: {
1305
+ type: "object",
1306
+ properties: {
1307
+ id: {
1308
+ type: "string"
1309
+ },
1310
+ posts: {
1311
+ type: "array",
1312
+ items: {
1313
+ $ref: "#/definitions/Post"
1314
+ }
1315
+ }
1316
+ }
1317
+ }
1318
+ }
1319
+ }).toObject();
1320
+
1321
+ const validate = new Ajv({strict: true}).compile(schema);
1322
+
1323
+ expect(schema).toEqual({
1324
+ $ref: "#/definitions/Post",
1325
+ definitions: {
1326
+ Post: {
1327
+ type: "object",
1328
+ properties: {
1329
+ name: {
1330
+ type: "string"
1331
+ },
1332
+ owner: {
1333
+ $ref: "#/definitions/User"
1334
+ }
1335
+ }
1336
+ },
1337
+ User: {
1338
+ type: "object",
1339
+ properties: {
1340
+ id: {
1341
+ type: "string"
1342
+ },
1343
+ posts: {
1344
+ type: "array",
1345
+ items: {
1346
+ $ref: "#/definitions/Post"
1347
+ }
1348
+ }
1349
+ }
1350
+ }
1351
+ }
1352
+ });
1353
+
1354
+ expect(
1355
+ validate({
1356
+ id: "post-id",
1357
+ owner: {
1358
+ id: "user-id",
1359
+ posts: [
1360
+ {
1361
+ id: "post-id"
1362
+ }
1363
+ ]
1364
+ }
1365
+ })
1366
+ ).toBeDefined();
1367
+ });
1368
+ });
1369
+ describe("Alias", () => {
1370
+ it("should create new jsonSchema (useAlias = true)", () => {
1371
+ const schema = JsonSchema.from({
1372
+ type: "object",
1373
+ properties: {
1374
+ prop: {
1375
+ type: "string"
1376
+ }
1377
+ },
1378
+ required: ["prop"]
1379
+ }).addAlias("prop", "aliasProp");
1380
+
1381
+ const jsonSchema = schema.toObject();
1382
+
1383
+ expect(schema.getAliasOf("prop")).toBe("aliasProp");
1384
+ expect(schema.getTarget()).toBe("object");
1385
+ expect(jsonSchema).toEqual({
1386
+ type: "object",
1387
+ properties: {
1388
+ aliasProp: {
1389
+ type: "string"
1390
+ }
1391
+ },
1392
+ required: ["aliasProp"]
1393
+ });
1394
+ });
1395
+
1396
+ it("should create new jsonSchema (useAlias = false)", () => {
1397
+ const schema = JsonSchema.from({
1398
+ type: "object",
1399
+ properties: {
1400
+ prop: {
1401
+ type: "string"
1402
+ }
1403
+ },
1404
+ required: ["prop"]
1405
+ })
1406
+ .addAlias("prop", "aliasProp")
1407
+ .removeAlias("prop2")
1408
+ .toObject({useAlias: false});
1409
+
1410
+ expect(schema).toEqual({
1411
+ type: "object",
1412
+ properties: {
1413
+ prop: {
1414
+ type: "string"
1415
+ }
1416
+ },
1417
+ required: ["prop"]
1418
+ });
1419
+ });
1420
+ });
1421
+ describe("Mixed types", () => {
1422
+ it("should create a new jsonSchema", () => {
1423
+ const result = JsonSchema.from({type: [String, Number]}).toObject();
1424
+
1425
+ expect(result).toEqual({
1426
+ type: ["string", "number"]
1427
+ });
1428
+ });
1429
+ it("should create a new jsonSchema (2)", () => {
1430
+ const result = JsonSchema.from({type: ["string", "null"]}).toObject();
1431
+
1432
+ expect(result).toEqual({
1433
+ type: ["string", "null"]
1434
+ });
1435
+ });
1436
+ });
1437
+
1438
+ describe("Any types", () => {
1439
+ it("should create a new jsonSchema", () => {
1440
+ const result = JsonSchema.from({type: Object}).any().toObject();
1441
+
1442
+ expect(result).toEqual({
1443
+ anyOf: [
1444
+ {
1445
+ type: "null"
1446
+ },
1447
+ {
1448
+ multipleOf: 1,
1449
+ type: "integer"
1450
+ },
1451
+ {
1452
+ type: "number"
1453
+ },
1454
+ {
1455
+ type: "string"
1456
+ },
1457
+ {
1458
+ type: "boolean"
1459
+ },
1460
+ {
1461
+ type: "array"
1462
+ },
1463
+ {
1464
+ type: "object"
1465
+ }
1466
+ ]
1467
+ });
1468
+ });
1469
+ });
1470
+ });