@forgehive/schema 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +207 -0
- package/dist/index.d.ts +79 -71
- package/dist/index.js +75 -182
- package/dist/test/custom-validations.test.js +45 -131
- package/dist/test/dates.test.js +41 -21
- package/dist/test/describe.test.d.ts +1 -0
- package/dist/test/describe.test.js +172 -0
- package/dist/test/from.test.d.ts +1 -0
- package/dist/test/from.test.js +162 -0
- package/dist/test/index.test.js +130 -30
- package/dist/test/optionals.test.js +22 -18
- package/dist/test/parse.test.d.ts +1 -0
- package/dist/test/parse.test.js +134 -0
- package/dist/test/record.test.js +47 -62
- package/package.json +2 -2
- package/src/index.ts +112 -250
- package/src/test/custom-validations.test.ts +45 -132
- package/src/test/dates.test.ts +45 -22
- package/src/test/describe.test.ts +190 -0
- package/src/test/from.test.ts +191 -0
- package/src/test/index.test.ts +148 -31
- package/src/test/optionals.test.ts +23 -18
- package/src/test/parse.test.ts +160 -0
- package/src/test/record.test.ts +48 -72
- package/tsconfig.json +2 -3
|
@@ -2,21 +2,9 @@ import { Schema } from '../index'
|
|
|
2
2
|
|
|
3
3
|
describe('Schema Custom Number Validations', () => {
|
|
4
4
|
it('should validate number min/max constraints', () => {
|
|
5
|
-
const schema = Schema
|
|
6
|
-
age:
|
|
7
|
-
|
|
8
|
-
validations: {
|
|
9
|
-
min: 18,
|
|
10
|
-
max: 100
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
score: {
|
|
14
|
-
type: 'number',
|
|
15
|
-
validations: {
|
|
16
|
-
min: 0,
|
|
17
|
-
max: 100
|
|
18
|
-
}
|
|
19
|
-
}
|
|
5
|
+
const schema = new Schema({
|
|
6
|
+
age: Schema.number().min(18).max(100),
|
|
7
|
+
score: Schema.number().min(0).max(100)
|
|
20
8
|
})
|
|
21
9
|
|
|
22
10
|
// Valid cases
|
|
@@ -31,37 +19,24 @@ describe('Schema Custom Number Validations', () => {
|
|
|
31
19
|
expect(schema.validate({ age: 25, score: 101 })).toBe(false)
|
|
32
20
|
})
|
|
33
21
|
|
|
34
|
-
it('should
|
|
35
|
-
const schema = Schema
|
|
36
|
-
age:
|
|
37
|
-
type: 'number',
|
|
38
|
-
validations: {
|
|
39
|
-
min: 18,
|
|
40
|
-
max: 100
|
|
41
|
-
}
|
|
42
|
-
}
|
|
22
|
+
it('should describe number validations as JSON Schema', () => {
|
|
23
|
+
const schema = new Schema({
|
|
24
|
+
age: Schema.number().min(18).max(100)
|
|
43
25
|
})
|
|
44
26
|
|
|
45
27
|
const description = schema.describe()
|
|
46
|
-
expect(description.age).toEqual({
|
|
28
|
+
expect(description.properties?.age).toEqual({
|
|
47
29
|
type: 'number',
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
max: 100
|
|
51
|
-
}
|
|
30
|
+
minimum: 18,
|
|
31
|
+
maximum: 100
|
|
52
32
|
})
|
|
53
33
|
})
|
|
54
34
|
})
|
|
55
35
|
|
|
56
36
|
describe('Schema Custom String Validations', () => {
|
|
57
37
|
it('should validate string email format', () => {
|
|
58
|
-
const schema = Schema
|
|
59
|
-
email:
|
|
60
|
-
type: 'string',
|
|
61
|
-
validations: {
|
|
62
|
-
email: true
|
|
63
|
-
}
|
|
64
|
-
}
|
|
38
|
+
const schema = new Schema({
|
|
39
|
+
email: Schema.email()
|
|
65
40
|
})
|
|
66
41
|
|
|
67
42
|
// Valid cases
|
|
@@ -76,14 +51,8 @@ describe('Schema Custom String Validations', () => {
|
|
|
76
51
|
})
|
|
77
52
|
|
|
78
53
|
it('should validate string length constraints', () => {
|
|
79
|
-
const schema = Schema
|
|
80
|
-
username:
|
|
81
|
-
type: 'string',
|
|
82
|
-
validations: {
|
|
83
|
-
minLength: 3,
|
|
84
|
-
maxLength: 20
|
|
85
|
-
}
|
|
86
|
-
}
|
|
54
|
+
const schema = new Schema({
|
|
55
|
+
username: Schema.string().min(3).max(20)
|
|
87
56
|
})
|
|
88
57
|
|
|
89
58
|
// Valid cases
|
|
@@ -97,13 +66,8 @@ describe('Schema Custom String Validations', () => {
|
|
|
97
66
|
})
|
|
98
67
|
|
|
99
68
|
it('should validate string regex pattern', () => {
|
|
100
|
-
const schema = Schema
|
|
101
|
-
username:
|
|
102
|
-
type: 'string',
|
|
103
|
-
validations: {
|
|
104
|
-
regex: '^[a-zA-Z0-9_]+$'
|
|
105
|
-
}
|
|
106
|
-
}
|
|
69
|
+
const schema = new Schema({
|
|
70
|
+
username: Schema.string().regex(/^[a-zA-Z0-9_]+$/)
|
|
107
71
|
})
|
|
108
72
|
|
|
109
73
|
// Valid cases
|
|
@@ -117,58 +81,35 @@ describe('Schema Custom String Validations', () => {
|
|
|
117
81
|
expect(schema.validate({ username: 'user name' })).toBe(false)
|
|
118
82
|
})
|
|
119
83
|
|
|
120
|
-
it('should
|
|
121
|
-
const schema = Schema
|
|
122
|
-
email:
|
|
123
|
-
type: 'string',
|
|
124
|
-
validations: {
|
|
125
|
-
email: true,
|
|
126
|
-
minLength: 5,
|
|
127
|
-
maxLength: 100
|
|
128
|
-
}
|
|
129
|
-
}
|
|
84
|
+
it('should describe string length validations as JSON Schema', () => {
|
|
85
|
+
const schema = new Schema({
|
|
86
|
+
email: Schema.email().min(5).max(100)
|
|
130
87
|
})
|
|
131
88
|
|
|
132
89
|
const description = schema.describe()
|
|
133
|
-
expect(description.email).
|
|
90
|
+
expect(description.properties?.email).toMatchObject({
|
|
134
91
|
type: 'string',
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
maxLength: 100
|
|
139
|
-
}
|
|
92
|
+
format: 'email',
|
|
93
|
+
minLength: 5,
|
|
94
|
+
maxLength: 100
|
|
140
95
|
})
|
|
141
96
|
})
|
|
142
97
|
|
|
143
|
-
it('should
|
|
144
|
-
const schema = Schema
|
|
145
|
-
username:
|
|
146
|
-
type: 'string',
|
|
147
|
-
validations: {
|
|
148
|
-
regex: '^[a-zA-Z0-9_]+$'
|
|
149
|
-
}
|
|
150
|
-
}
|
|
98
|
+
it('should describe string regex validation as a pattern', () => {
|
|
99
|
+
const schema = new Schema({
|
|
100
|
+
username: Schema.string().regex(/^[a-zA-Z0-9_]+$/)
|
|
151
101
|
})
|
|
152
102
|
|
|
153
103
|
const description = schema.describe()
|
|
154
|
-
expect(description.username).
|
|
104
|
+
expect(description.properties?.username).toMatchObject({
|
|
155
105
|
type: 'string',
|
|
156
|
-
|
|
157
|
-
regex: '^[a-zA-Z0-9_]+$'
|
|
158
|
-
}
|
|
106
|
+
pattern: '^[a-zA-Z0-9_]+$'
|
|
159
107
|
})
|
|
160
108
|
})
|
|
161
109
|
|
|
162
110
|
it('should handle regex with other string validations', () => {
|
|
163
|
-
const schema = Schema
|
|
164
|
-
username:
|
|
165
|
-
type: 'string',
|
|
166
|
-
validations: {
|
|
167
|
-
regex: '^[a-zA-Z0-9_]+$',
|
|
168
|
-
minLength: 3,
|
|
169
|
-
maxLength: 20
|
|
170
|
-
}
|
|
171
|
-
}
|
|
111
|
+
const schema = new Schema({
|
|
112
|
+
username: Schema.string().regex(/^[a-zA-Z0-9_]+$/).min(3).max(20)
|
|
172
113
|
})
|
|
173
114
|
|
|
174
115
|
// Valid cases
|
|
@@ -185,13 +126,8 @@ describe('Schema Custom String Validations', () => {
|
|
|
185
126
|
})
|
|
186
127
|
|
|
187
128
|
it('should handle regex patterns containing forward slashes', () => {
|
|
188
|
-
const schema = Schema
|
|
189
|
-
path:
|
|
190
|
-
type: 'string',
|
|
191
|
-
validations: {
|
|
192
|
-
regex: '^/api/v[0-9]+/users/[0-9]+$'
|
|
193
|
-
}
|
|
194
|
-
}
|
|
129
|
+
const schema = new Schema({
|
|
130
|
+
path: Schema.string().regex(/^\/api\/v[0-9]+\/users\/[0-9]+$/)
|
|
195
131
|
})
|
|
196
132
|
|
|
197
133
|
// Valid cases
|
|
@@ -204,28 +140,20 @@ describe('Schema Custom String Validations', () => {
|
|
|
204
140
|
expect(schema.validate({ path: '/api/v1/users' })).toBe(false)
|
|
205
141
|
expect(schema.validate({ path: '/api/v1/users/abc' })).toBe(false)
|
|
206
142
|
|
|
207
|
-
// Verify the description preserves the
|
|
143
|
+
// Verify the description preserves the pattern (regex source keeps escaped slashes)
|
|
208
144
|
const description = schema.describe()
|
|
209
|
-
expect(description.path).
|
|
145
|
+
expect(description.properties?.path).toMatchObject({
|
|
210
146
|
type: 'string',
|
|
211
|
-
|
|
212
|
-
regex: '^/api/v[0-9]+/users/[0-9]+$'
|
|
213
|
-
}
|
|
147
|
+
pattern: '^\\/api\\/v[0-9]+\\/users\\/[0-9]+$'
|
|
214
148
|
})
|
|
215
149
|
})
|
|
216
150
|
|
|
217
151
|
it('should handle round trip of regex patterns containing forward slashes', () => {
|
|
218
|
-
const schema = Schema
|
|
219
|
-
path:
|
|
220
|
-
type: 'string',
|
|
221
|
-
validations: {
|
|
222
|
-
regex: '^/api/v[0-9]+/users/[0-9]+$'
|
|
223
|
-
}
|
|
224
|
-
}
|
|
152
|
+
const schema = new Schema({
|
|
153
|
+
path: Schema.string().regex(/^\/api\/v[0-9]+\/users\/[0-9]+$/)
|
|
225
154
|
})
|
|
226
155
|
|
|
227
156
|
const description = schema.describe()
|
|
228
|
-
|
|
229
157
|
const cloneSchema = Schema.from(description)
|
|
230
158
|
|
|
231
159
|
// Valid cases
|
|
@@ -238,36 +166,21 @@ describe('Schema Custom String Validations', () => {
|
|
|
238
166
|
expect(cloneSchema.validate({ path: '/api/v1/users' })).toBe(false)
|
|
239
167
|
expect(cloneSchema.validate({ path: '/api/v1/users/abc' })).toBe(false)
|
|
240
168
|
|
|
241
|
-
// Verify the description preserves the
|
|
169
|
+
// Verify the description preserves the pattern (regex source keeps escaped slashes)
|
|
242
170
|
const cloneDescription = cloneSchema.describe()
|
|
243
|
-
expect(cloneDescription).toMatchObject(
|
|
171
|
+
expect(cloneDescription.properties?.path).toMatchObject({
|
|
172
|
+
type: 'string',
|
|
173
|
+
pattern: '^\\/api\\/v[0-9]+\\/users\\/[0-9]+$'
|
|
174
|
+
})
|
|
244
175
|
})
|
|
245
176
|
})
|
|
246
177
|
|
|
247
178
|
describe('Schema Roundtrip', () => {
|
|
248
179
|
it('should maintain custom validations through describe/from cycle', () => {
|
|
249
|
-
const originalSchema = Schema
|
|
250
|
-
age:
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
min: 18,
|
|
254
|
-
max: 100
|
|
255
|
-
}
|
|
256
|
-
},
|
|
257
|
-
email: {
|
|
258
|
-
type: 'string',
|
|
259
|
-
validations: {
|
|
260
|
-
email: true
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
|
-
username: {
|
|
264
|
-
type: 'string',
|
|
265
|
-
validations: {
|
|
266
|
-
regex: '^[a-zA-Z0-9_]+$',
|
|
267
|
-
minLength: 3,
|
|
268
|
-
maxLength: 20
|
|
269
|
-
}
|
|
270
|
-
}
|
|
180
|
+
const originalSchema = new Schema({
|
|
181
|
+
age: Schema.number().min(18).max(100),
|
|
182
|
+
email: Schema.email(),
|
|
183
|
+
username: Schema.string().regex(/^[a-zA-Z0-9_]+$/).min(3).max(20)
|
|
271
184
|
})
|
|
272
185
|
|
|
273
186
|
const description = originalSchema.describe()
|
package/src/test/dates.test.ts
CHANGED
|
@@ -1,32 +1,42 @@
|
|
|
1
1
|
import { Schema } from '../index'
|
|
2
2
|
|
|
3
|
+
// Dates are represented as ISO 8601 date-time strings (Schema.date() ->
|
|
4
|
+
// z.iso.datetime()), which are natively representable in JSON Schema.
|
|
5
|
+
|
|
3
6
|
describe('Schema single date field', () => {
|
|
4
|
-
const schema = Schema
|
|
5
|
-
createdAt:
|
|
7
|
+
const schema = new Schema({
|
|
8
|
+
createdAt: Schema.date()
|
|
6
9
|
})
|
|
7
10
|
|
|
8
|
-
it('should validate a valid date', () => {
|
|
11
|
+
it('should validate a valid ISO date-time string', () => {
|
|
9
12
|
const data = {
|
|
10
|
-
createdAt: new Date()
|
|
13
|
+
createdAt: new Date().toISOString()
|
|
11
14
|
}
|
|
12
15
|
expect(schema.validate(data)).toBe(true)
|
|
13
16
|
})
|
|
14
17
|
|
|
15
|
-
it('should validate a
|
|
18
|
+
it('should validate a fixed ISO date-time string', () => {
|
|
16
19
|
const data = {
|
|
17
|
-
createdAt:
|
|
20
|
+
createdAt: '2024-03-20T12:00:00Z'
|
|
18
21
|
}
|
|
19
22
|
expect(schema.validate(data)).toBe(true)
|
|
20
23
|
})
|
|
21
24
|
|
|
22
|
-
it('should reject invalid date', () => {
|
|
25
|
+
it('should reject an invalid date string', () => {
|
|
23
26
|
const data = {
|
|
24
27
|
createdAt: 'invalid-date'
|
|
25
28
|
}
|
|
26
29
|
expect(schema.validate(data)).toBe(false)
|
|
27
30
|
})
|
|
28
31
|
|
|
29
|
-
it('should reject
|
|
32
|
+
it('should reject a Date instance (runtime type is a string now)', () => {
|
|
33
|
+
const data = {
|
|
34
|
+
createdAt: new Date()
|
|
35
|
+
}
|
|
36
|
+
expect(schema.validate(data)).toBe(false)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should reject a non-date value', () => {
|
|
30
40
|
const data = {
|
|
31
41
|
createdAt: 123
|
|
32
42
|
}
|
|
@@ -35,32 +45,32 @@ describe('Schema single date field', () => {
|
|
|
35
45
|
})
|
|
36
46
|
|
|
37
47
|
describe('Schema array of dates', () => {
|
|
38
|
-
const schema = Schema
|
|
39
|
-
timestamps:
|
|
48
|
+
const schema = new Schema({
|
|
49
|
+
timestamps: Schema.array(Schema.date())
|
|
40
50
|
})
|
|
41
51
|
|
|
42
|
-
it('should validate an array of valid
|
|
52
|
+
it('should validate an array of valid ISO date-time strings', () => {
|
|
43
53
|
const data = {
|
|
44
|
-
timestamps: [new Date(), new Date()]
|
|
54
|
+
timestamps: [new Date().toISOString(), new Date().toISOString()]
|
|
45
55
|
}
|
|
46
56
|
expect(schema.validate(data)).toBe(true)
|
|
47
57
|
})
|
|
48
58
|
|
|
49
|
-
it('should validate an array of
|
|
59
|
+
it('should validate an array of fixed ISO date-time strings', () => {
|
|
50
60
|
const data = {
|
|
51
|
-
timestamps: [
|
|
61
|
+
timestamps: ['2024-03-20T12:00:00Z', '2024-03-21T12:00:00Z']
|
|
52
62
|
}
|
|
53
63
|
expect(schema.validate(data)).toBe(true)
|
|
54
64
|
})
|
|
55
65
|
|
|
56
|
-
it('should reject array with invalid date', () => {
|
|
66
|
+
it('should reject an array with an invalid date string', () => {
|
|
57
67
|
const data = {
|
|
58
|
-
timestamps: [new Date(), 'invalid-date']
|
|
68
|
+
timestamps: [new Date().toISOString(), 'invalid-date']
|
|
59
69
|
}
|
|
60
70
|
expect(schema.validate(data)).toBe(false)
|
|
61
71
|
})
|
|
62
72
|
|
|
63
|
-
it('should reject non-array value', () => {
|
|
73
|
+
it('should reject a non-array value', () => {
|
|
64
74
|
const data = {
|
|
65
75
|
timestamps: 'not-an-array'
|
|
66
76
|
}
|
|
@@ -69,17 +79,30 @@ describe('Schema array of dates', () => {
|
|
|
69
79
|
})
|
|
70
80
|
|
|
71
81
|
describe('Dates Schema description', () => {
|
|
72
|
-
it('should
|
|
82
|
+
it('should describe date fields as date-time formatted strings', () => {
|
|
73
83
|
const schema = new Schema({
|
|
74
84
|
createdAt: Schema.date(),
|
|
75
85
|
timestamps: Schema.array(Schema.date())
|
|
76
86
|
})
|
|
77
87
|
|
|
78
88
|
const description = schema.describe()
|
|
79
|
-
expect(description).
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
expect(description.properties?.createdAt).toMatchObject({
|
|
90
|
+
type: 'string',
|
|
91
|
+
format: 'date-time'
|
|
92
|
+
})
|
|
93
|
+
expect(description.properties?.timestamps).toMatchObject({
|
|
94
|
+
type: 'array',
|
|
95
|
+
items: { type: 'string', format: 'date-time' }
|
|
82
96
|
})
|
|
83
97
|
})
|
|
84
|
-
})
|
|
85
98
|
|
|
99
|
+
it('should round-trip date validation through describe/from', () => {
|
|
100
|
+
const schema = new Schema({
|
|
101
|
+
createdAt: Schema.date()
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const clone = Schema.from(schema.describe())
|
|
105
|
+
expect(clone.validate({ createdAt: '2024-03-20T12:00:00Z' })).toBe(true)
|
|
106
|
+
expect(clone.validate({ createdAt: 'invalid-date' })).toBe(false)
|
|
107
|
+
})
|
|
108
|
+
})
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { Schema } from '../index'
|
|
2
|
+
|
|
3
|
+
// `describe()` serializes a Schema to standard JSON Schema (draft 2020-12).
|
|
4
|
+
// Each test builds a Schema with the Schema.* helpers and asserts the EXACT
|
|
5
|
+
// JSON Schema it produces, so the wire format is obvious at a glance.
|
|
6
|
+
|
|
7
|
+
const $schema = 'https://json-schema.org/draft/2020-12/schema'
|
|
8
|
+
|
|
9
|
+
describe('Schema.describe() -> JSON Schema', () => {
|
|
10
|
+
it('basic types', () => {
|
|
11
|
+
const schema = new Schema({
|
|
12
|
+
name: Schema.string(),
|
|
13
|
+
age: Schema.number(),
|
|
14
|
+
active: Schema.boolean(),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
expect(schema.describe()).toEqual({
|
|
18
|
+
$schema,
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {
|
|
21
|
+
name: { type: 'string' },
|
|
22
|
+
age: { type: 'number' },
|
|
23
|
+
active: { type: 'boolean' },
|
|
24
|
+
},
|
|
25
|
+
required: ['name', 'age', 'active'],
|
|
26
|
+
additionalProperties: false,
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('string validations', () => {
|
|
31
|
+
const schema = new Schema({
|
|
32
|
+
username: Schema.string().min(3).max(20).regex(/^[a-zA-Z0-9_]+$/),
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
expect(schema.describe()).toEqual({
|
|
36
|
+
$schema,
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
username: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
minLength: 3,
|
|
42
|
+
maxLength: 20,
|
|
43
|
+
pattern: '^[a-zA-Z0-9_]+$',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
required: ['username'],
|
|
47
|
+
additionalProperties: false,
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('number validations', () => {
|
|
52
|
+
const schema = new Schema({
|
|
53
|
+
age: Schema.number().min(18).max(100),
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
expect(schema.describe()).toEqual({
|
|
57
|
+
$schema,
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
age: { type: 'number', minimum: 18, maximum: 100 },
|
|
61
|
+
},
|
|
62
|
+
required: ['age'],
|
|
63
|
+
additionalProperties: false,
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('optional fields are omitted from `required`', () => {
|
|
68
|
+
const schema = new Schema({
|
|
69
|
+
name: Schema.string(),
|
|
70
|
+
age: Schema.number().optional(),
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
expect(schema.describe()).toEqual({
|
|
74
|
+
$schema,
|
|
75
|
+
type: 'object',
|
|
76
|
+
properties: {
|
|
77
|
+
name: { type: 'string' },
|
|
78
|
+
age: { type: 'number' },
|
|
79
|
+
},
|
|
80
|
+
required: ['name'], // age is optional, so it is not required
|
|
81
|
+
additionalProperties: false,
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('arrays', () => {
|
|
86
|
+
const schema = new Schema({
|
|
87
|
+
tags: Schema.array(Schema.string()),
|
|
88
|
+
scores: Schema.array(Schema.number()),
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
expect(schema.describe()).toEqual({
|
|
92
|
+
$schema,
|
|
93
|
+
type: 'object',
|
|
94
|
+
properties: {
|
|
95
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
96
|
+
scores: { type: 'array', items: { type: 'number' } },
|
|
97
|
+
},
|
|
98
|
+
required: ['tags', 'scores'],
|
|
99
|
+
additionalProperties: false,
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('records', () => {
|
|
104
|
+
const schema = new Schema({
|
|
105
|
+
strings: Schema.stringRecord(),
|
|
106
|
+
mixed: Schema.mixedRecord(),
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
expect(schema.describe()).toEqual({
|
|
110
|
+
$schema,
|
|
111
|
+
type: 'object',
|
|
112
|
+
properties: {
|
|
113
|
+
strings: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
propertyNames: { type: 'string' },
|
|
116
|
+
additionalProperties: { type: 'string' },
|
|
117
|
+
},
|
|
118
|
+
mixed: {
|
|
119
|
+
type: 'object',
|
|
120
|
+
propertyNames: { type: 'string' },
|
|
121
|
+
additionalProperties: {
|
|
122
|
+
anyOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
required: ['strings', 'mixed'],
|
|
127
|
+
additionalProperties: false,
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('nested objects', () => {
|
|
132
|
+
const schema = new Schema({
|
|
133
|
+
user: Schema.object({
|
|
134
|
+
name: Schema.string(),
|
|
135
|
+
age: Schema.number().optional(),
|
|
136
|
+
}),
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
expect(schema.describe()).toEqual({
|
|
140
|
+
$schema,
|
|
141
|
+
type: 'object',
|
|
142
|
+
properties: {
|
|
143
|
+
user: {
|
|
144
|
+
type: 'object',
|
|
145
|
+
properties: {
|
|
146
|
+
name: { type: 'string' },
|
|
147
|
+
age: { type: 'number' },
|
|
148
|
+
},
|
|
149
|
+
required: ['name'],
|
|
150
|
+
additionalProperties: false,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
required: ['user'],
|
|
154
|
+
additionalProperties: false,
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('element descriptions', () => {
|
|
159
|
+
const schema = new Schema({
|
|
160
|
+
name: Schema.string().describe('The name of the user'),
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
expect(schema.describe()).toEqual({
|
|
164
|
+
$schema,
|
|
165
|
+
type: 'object',
|
|
166
|
+
properties: {
|
|
167
|
+
name: { type: 'string', description: 'The name of the user' },
|
|
168
|
+
},
|
|
169
|
+
required: ['name'],
|
|
170
|
+
additionalProperties: false,
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('string formats: email, uuid, url, date', () => {
|
|
175
|
+
const schema = new Schema({
|
|
176
|
+
email: Schema.email(),
|
|
177
|
+
id: Schema.uuid(),
|
|
178
|
+
site: Schema.url(),
|
|
179
|
+
when: Schema.date(),
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// Format keywords are the stable part; the generated `pattern` strings are
|
|
183
|
+
// long and version-specific, so we assert the meaningful keys.
|
|
184
|
+
const properties = schema.describe().properties
|
|
185
|
+
expect(properties?.email).toMatchObject({ type: 'string', format: 'email' })
|
|
186
|
+
expect(properties?.id).toMatchObject({ type: 'string', format: 'uuid' })
|
|
187
|
+
expect(properties?.site).toMatchObject({ type: 'string', format: 'uri' })
|
|
188
|
+
expect(properties?.when).toMatchObject({ type: 'string', format: 'date-time' })
|
|
189
|
+
})
|
|
190
|
+
})
|