@atproto/lexicon 0.7.3 → 0.7.4
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/CHANGELOG.md +16 -0
- package/dist/lexicons.d.ts +1 -1
- package/dist/lexicons.d.ts.map +1 -1
- package/dist/lexicons.js.map +1 -1
- package/dist/serialize.d.ts +1 -1
- package/dist/serialize.d.ts.map +1 -1
- package/dist/serialize.js +8 -7
- package/dist/serialize.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +1 -0
- package/dist/validation.js.map +1 -1
- package/dist/validators/blob.d.ts +1 -1
- package/dist/validators/blob.d.ts.map +1 -1
- package/dist/validators/blob.js +1 -0
- package/dist/validators/blob.js.map +1 -1
- package/dist/validators/complex.d.ts +1 -1
- package/dist/validators/complex.d.ts.map +1 -1
- package/dist/validators/complex.js +2 -1
- package/dist/validators/complex.js.map +1 -1
- package/dist/validators/formats.d.ts +1 -1
- package/dist/validators/formats.d.ts.map +1 -1
- package/dist/validators/formats.js.map +1 -1
- package/dist/validators/primitives.d.ts +1 -1
- package/dist/validators/primitives.d.ts.map +1 -1
- package/dist/validators/primitives.js +2 -1
- package/dist/validators/primitives.js.map +1 -1
- package/dist/validators/xrpc.d.ts +1 -1
- package/dist/validators/xrpc.d.ts.map +1 -1
- package/dist/validators/xrpc.js +3 -2
- package/dist/validators/xrpc.js.map +1 -1
- package/package.json +18 -13
- package/jest.config.cjs +0 -21
- package/src/blob-refs.ts +0 -65
- package/src/index.ts +0 -4
- package/src/lexicons.ts +0 -253
- package/src/serialize.ts +0 -102
- package/src/types.ts +0 -483
- package/src/util.ts +0 -58
- package/src/validation.ts +0 -84
- package/src/validators/blob.ts +0 -19
- package/src/validators/complex.ts +0 -212
- package/src/validators/formats.ts +0 -72
- package/src/validators/primitives.ts +0 -416
- package/src/validators/xrpc.ts +0 -53
- package/tests/_scaffolds/lexicons.ts +0 -545
- package/tests/general.test.ts +0 -1243
- package/tsconfig.build.json +0 -8
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -7
package/tests/general.test.ts
DELETED
|
@@ -1,1243 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import { CID } from 'multiformats/cid'
|
|
3
|
-
import { LexiconDoc, Lexicons, parseLexiconDoc } from '../src/index.js'
|
|
4
|
-
import LexiconDocs from './_scaffolds/lexicons.js'
|
|
5
|
-
|
|
6
|
-
describe('Lexicons collection', () => {
|
|
7
|
-
const lex = new Lexicons(LexiconDocs)
|
|
8
|
-
|
|
9
|
-
it('Adds schemas', () => {
|
|
10
|
-
expect(() => lex.add(LexiconDocs[0])).toThrow()
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
it('Correctly references all definitions', () => {
|
|
14
|
-
expect(lex.getDef('com.example.kitchenSink')).toEqual(
|
|
15
|
-
LexiconDocs[0].defs.main,
|
|
16
|
-
)
|
|
17
|
-
expect(lex.getDef('lex:com.example.kitchenSink')).toEqual(
|
|
18
|
-
LexiconDocs[0].defs.main,
|
|
19
|
-
)
|
|
20
|
-
expect(lex.getDef('com.example.kitchenSink#main')).toEqual(
|
|
21
|
-
LexiconDocs[0].defs.main,
|
|
22
|
-
)
|
|
23
|
-
expect(lex.getDef('lex:com.example.kitchenSink#main')).toEqual(
|
|
24
|
-
LexiconDocs[0].defs.main,
|
|
25
|
-
)
|
|
26
|
-
expect(lex.getDef('com.example.kitchenSink#object')).toEqual(
|
|
27
|
-
LexiconDocs[0].defs.object,
|
|
28
|
-
)
|
|
29
|
-
expect(lex.getDef('lex:com.example.kitchenSink#object')).toEqual(
|
|
30
|
-
LexiconDocs[0].defs.object,
|
|
31
|
-
)
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
describe('General validation', () => {
|
|
36
|
-
const lex = new Lexicons(LexiconDocs)
|
|
37
|
-
it('Validates records correctly', () => {
|
|
38
|
-
{
|
|
39
|
-
const res = lex.validate('com.example.kitchenSink', {
|
|
40
|
-
$type: 'com.example.kitchenSink',
|
|
41
|
-
object: {
|
|
42
|
-
object: { boolean: true },
|
|
43
|
-
array: ['one', 'two'],
|
|
44
|
-
boolean: true,
|
|
45
|
-
integer: 123,
|
|
46
|
-
string: 'string',
|
|
47
|
-
},
|
|
48
|
-
array: ['one', 'two'],
|
|
49
|
-
boolean: true,
|
|
50
|
-
integer: 123,
|
|
51
|
-
string: 'string',
|
|
52
|
-
datetime: new Date().toISOString(),
|
|
53
|
-
atUri: 'at://did:web:example.com/com.example.test/self',
|
|
54
|
-
did: 'did:web:example.com',
|
|
55
|
-
cid: 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
|
|
56
|
-
bytes: new Uint8Array([0, 1, 2, 3]),
|
|
57
|
-
cidLink: CID.parse(
|
|
58
|
-
'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
|
|
59
|
-
),
|
|
60
|
-
})
|
|
61
|
-
expect(res.success).toBe(true)
|
|
62
|
-
}
|
|
63
|
-
{
|
|
64
|
-
const res = lex.validate('com.example.kitchenSink', {})
|
|
65
|
-
expect(res.success).toBe(false)
|
|
66
|
-
if (res.success) throw new Error('Asserted')
|
|
67
|
-
expect(res.error?.message).toBe('Record must have the property "object"')
|
|
68
|
-
}
|
|
69
|
-
})
|
|
70
|
-
it('Validates objects correctly', () => {
|
|
71
|
-
{
|
|
72
|
-
const res = lex.validate('com.example.kitchenSink#object', {
|
|
73
|
-
object: { boolean: true },
|
|
74
|
-
array: ['one', 'two'],
|
|
75
|
-
boolean: true,
|
|
76
|
-
integer: 123,
|
|
77
|
-
string: 'string',
|
|
78
|
-
})
|
|
79
|
-
expect(res.success).toBe(true)
|
|
80
|
-
}
|
|
81
|
-
{
|
|
82
|
-
const res = lex.validate('com.example.kitchenSink#object', {})
|
|
83
|
-
assert(!res.success)
|
|
84
|
-
expect(res.error.message).toBe('Object must have the property "object"')
|
|
85
|
-
}
|
|
86
|
-
})
|
|
87
|
-
it('fails when a required property is missing', () => {
|
|
88
|
-
const schema = {
|
|
89
|
-
lexicon: 1,
|
|
90
|
-
id: 'com.example.kitchenSink',
|
|
91
|
-
defs: {
|
|
92
|
-
test: {
|
|
93
|
-
type: 'object',
|
|
94
|
-
required: ['foo'],
|
|
95
|
-
properties: {},
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
}
|
|
99
|
-
expect(() => {
|
|
100
|
-
parseLexiconDoc(schema)
|
|
101
|
-
}).toThrow('Required field \\"foo\\" not defined')
|
|
102
|
-
})
|
|
103
|
-
it('allows unknown fields to be present', () => {
|
|
104
|
-
const schema = {
|
|
105
|
-
lexicon: 1,
|
|
106
|
-
id: 'com.example.unknownFields',
|
|
107
|
-
defs: {
|
|
108
|
-
test: {
|
|
109
|
-
type: 'object',
|
|
110
|
-
properties: {},
|
|
111
|
-
foo: 3,
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
expect(() => {
|
|
117
|
-
parseLexiconDoc(schema)
|
|
118
|
-
}).not.toThrow()
|
|
119
|
-
})
|
|
120
|
-
it('fails lexicon parsing when uri is invalid', () => {
|
|
121
|
-
const schema: LexiconDoc = {
|
|
122
|
-
lexicon: 1,
|
|
123
|
-
id: 'com.example.invalidUri',
|
|
124
|
-
defs: {
|
|
125
|
-
main: {
|
|
126
|
-
type: 'object',
|
|
127
|
-
properties: {
|
|
128
|
-
test: { type: 'ref', ref: 'com.example.invalid#test#test' },
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
expect(() => {
|
|
135
|
-
new Lexicons([schema])
|
|
136
|
-
}).toThrow('Uri can only have one hash segment')
|
|
137
|
-
})
|
|
138
|
-
it('fails validation when ref uri has multiple hash segments', () => {
|
|
139
|
-
const schema: LexiconDoc = {
|
|
140
|
-
lexicon: 1,
|
|
141
|
-
id: 'com.example.invalidUri',
|
|
142
|
-
defs: {
|
|
143
|
-
main: {
|
|
144
|
-
type: 'object',
|
|
145
|
-
properties: {
|
|
146
|
-
test: { type: 'integer' },
|
|
147
|
-
},
|
|
148
|
-
},
|
|
149
|
-
object: {
|
|
150
|
-
type: 'object',
|
|
151
|
-
required: ['test'],
|
|
152
|
-
properties: {
|
|
153
|
-
test: {
|
|
154
|
-
type: 'union',
|
|
155
|
-
refs: ['com.example.invalidUri'],
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
}
|
|
161
|
-
const lexicons = new Lexicons([schema])
|
|
162
|
-
expect(() => {
|
|
163
|
-
lexicons.validate('com.example.invalidUri#object', {
|
|
164
|
-
test: {
|
|
165
|
-
$type: 'com.example.invalidUri#main#main',
|
|
166
|
-
test: 123,
|
|
167
|
-
},
|
|
168
|
-
})
|
|
169
|
-
}).toThrow('Uri can only have one hash segment')
|
|
170
|
-
})
|
|
171
|
-
it('union handles both implicit and explicit #main', () => {
|
|
172
|
-
const schemas: LexiconDoc[] = [
|
|
173
|
-
{
|
|
174
|
-
lexicon: 1,
|
|
175
|
-
id: 'com.example.implicitMain',
|
|
176
|
-
defs: {
|
|
177
|
-
main: {
|
|
178
|
-
type: 'object',
|
|
179
|
-
required: ['test'],
|
|
180
|
-
properties: {
|
|
181
|
-
test: { type: 'string' },
|
|
182
|
-
},
|
|
183
|
-
},
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
lexicon: 1,
|
|
188
|
-
id: 'com.example.testImplicitMain',
|
|
189
|
-
defs: {
|
|
190
|
-
main: {
|
|
191
|
-
type: 'object',
|
|
192
|
-
required: ['union'],
|
|
193
|
-
properties: {
|
|
194
|
-
union: {
|
|
195
|
-
type: 'union',
|
|
196
|
-
refs: ['com.example.implicitMain'],
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
},
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
lexicon: 1,
|
|
204
|
-
id: 'com.example.testExplicitMain',
|
|
205
|
-
defs: {
|
|
206
|
-
main: {
|
|
207
|
-
type: 'object',
|
|
208
|
-
required: ['union'],
|
|
209
|
-
properties: {
|
|
210
|
-
union: {
|
|
211
|
-
type: 'union',
|
|
212
|
-
refs: ['com.example.implicitMain#main'],
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
]
|
|
219
|
-
|
|
220
|
-
const lexicon = new Lexicons(schemas)
|
|
221
|
-
|
|
222
|
-
let result = lexicon.validate('com.example.testImplicitMain', {
|
|
223
|
-
union: {
|
|
224
|
-
$type: 'com.example.implicitMain',
|
|
225
|
-
test: 123,
|
|
226
|
-
},
|
|
227
|
-
})
|
|
228
|
-
expect(result.success).toBeFalsy()
|
|
229
|
-
expect(result['error']?.message).toBe('Object/union/test must be a string')
|
|
230
|
-
|
|
231
|
-
result = lexicon.validate('com.example.testImplicitMain', {
|
|
232
|
-
union: {
|
|
233
|
-
$type: 'com.example.implicitMain#main',
|
|
234
|
-
test: 123,
|
|
235
|
-
},
|
|
236
|
-
})
|
|
237
|
-
expect(result.success).toBeFalsy()
|
|
238
|
-
expect(result['error']?.message).toBe('Object/union/test must be a string')
|
|
239
|
-
|
|
240
|
-
result = lexicon.validate('com.example.testExplicitMain', {
|
|
241
|
-
union: {
|
|
242
|
-
$type: 'com.example.implicitMain',
|
|
243
|
-
test: 123,
|
|
244
|
-
},
|
|
245
|
-
})
|
|
246
|
-
expect(result.success).toBeFalsy()
|
|
247
|
-
expect(result['error']?.message).toBe('Object/union/test must be a string')
|
|
248
|
-
|
|
249
|
-
result = lexicon.validate('com.example.testExplicitMain', {
|
|
250
|
-
union: {
|
|
251
|
-
$type: 'com.example.implicitMain#main',
|
|
252
|
-
test: 123,
|
|
253
|
-
},
|
|
254
|
-
})
|
|
255
|
-
expect(result.success).toBeFalsy()
|
|
256
|
-
expect(result['error']?.message).toBe('Object/union/test must be a string')
|
|
257
|
-
})
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
describe('Record validation', () => {
|
|
261
|
-
const lex = new Lexicons(LexiconDocs)
|
|
262
|
-
|
|
263
|
-
const passingSink = {
|
|
264
|
-
$type: 'com.example.kitchenSink',
|
|
265
|
-
object: {
|
|
266
|
-
object: { boolean: true },
|
|
267
|
-
array: ['one', 'two'],
|
|
268
|
-
boolean: true,
|
|
269
|
-
integer: 123,
|
|
270
|
-
string: 'string',
|
|
271
|
-
},
|
|
272
|
-
array: ['one', 'two'],
|
|
273
|
-
boolean: true,
|
|
274
|
-
integer: 123,
|
|
275
|
-
string: 'string',
|
|
276
|
-
bytes: new Uint8Array([0, 1, 2, 3]),
|
|
277
|
-
cidLink: CID.parse(
|
|
278
|
-
'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
|
|
279
|
-
),
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
it('Passes valid schemas', () => {
|
|
283
|
-
lex.assertValidRecord('com.example.kitchenSink', passingSink)
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
it('Fails invalid input types', () => {
|
|
287
|
-
expect(() =>
|
|
288
|
-
lex.assertValidRecord('com.example.kitchenSink', undefined),
|
|
289
|
-
).toThrow('Record must be an object')
|
|
290
|
-
expect(() =>
|
|
291
|
-
lex.assertValidRecord('com.example.kitchenSink', 1234),
|
|
292
|
-
).toThrow('Record must be an object')
|
|
293
|
-
expect(() =>
|
|
294
|
-
lex.assertValidRecord('com.example.kitchenSink', 'string'),
|
|
295
|
-
).toThrow('Record must be an object')
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
it('Fails incorrect $type', () => {
|
|
299
|
-
expect(() => lex.assertValidRecord('com.example.kitchenSink', {})).toThrow(
|
|
300
|
-
'Record/$type must be a string',
|
|
301
|
-
)
|
|
302
|
-
expect(() =>
|
|
303
|
-
lex.assertValidRecord('com.example.kitchenSink', { $type: 'foo' }),
|
|
304
|
-
).toThrow('Invalid $type: must be lex:com.example.kitchenSink, got foo')
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
it('Fails missing required', () => {
|
|
308
|
-
expect(() =>
|
|
309
|
-
lex.assertValidRecord('com.example.kitchenSink', {
|
|
310
|
-
$type: 'com.example.kitchenSink',
|
|
311
|
-
array: ['one', 'two'],
|
|
312
|
-
boolean: true,
|
|
313
|
-
integer: 123,
|
|
314
|
-
string: 'string',
|
|
315
|
-
datetime: new Date().toISOString(),
|
|
316
|
-
atUri: 'at://did:web:example.com/com.example.test/self',
|
|
317
|
-
did: 'did:web:example.com',
|
|
318
|
-
cid: 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
|
|
319
|
-
bytes: new Uint8Array([0, 1, 2, 3]),
|
|
320
|
-
cidLink: CID.parse(
|
|
321
|
-
'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
|
|
322
|
-
),
|
|
323
|
-
}),
|
|
324
|
-
).toThrow('Record must have the property "object"')
|
|
325
|
-
expect(() =>
|
|
326
|
-
lex.assertValidRecord('com.example.kitchenSink', {
|
|
327
|
-
...passingSink,
|
|
328
|
-
object: undefined,
|
|
329
|
-
}),
|
|
330
|
-
).toThrow('Record must have the property "object"')
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
it('Fails incorrect types', () => {
|
|
334
|
-
expect(() =>
|
|
335
|
-
lex.assertValidRecord('com.example.kitchenSink', {
|
|
336
|
-
...passingSink,
|
|
337
|
-
object: {
|
|
338
|
-
...passingSink.object,
|
|
339
|
-
object: { boolean: '1234' },
|
|
340
|
-
},
|
|
341
|
-
}),
|
|
342
|
-
).toThrow('Record/object/object/boolean must be a boolean')
|
|
343
|
-
expect(() =>
|
|
344
|
-
lex.assertValidRecord('com.example.kitchenSink', {
|
|
345
|
-
...passingSink,
|
|
346
|
-
object: true,
|
|
347
|
-
}),
|
|
348
|
-
).toThrow('Record/object must be an object')
|
|
349
|
-
expect(() =>
|
|
350
|
-
lex.assertValidRecord('com.example.kitchenSink', {
|
|
351
|
-
...passingSink,
|
|
352
|
-
array: 1234,
|
|
353
|
-
}),
|
|
354
|
-
).toThrow('Record/array must be an array')
|
|
355
|
-
expect(() =>
|
|
356
|
-
lex.assertValidRecord('com.example.kitchenSink', {
|
|
357
|
-
...passingSink,
|
|
358
|
-
integer: true,
|
|
359
|
-
}),
|
|
360
|
-
).toThrow('Record/integer must be an integer')
|
|
361
|
-
expect(() =>
|
|
362
|
-
lex.assertValidRecord('com.example.kitchenSink', {
|
|
363
|
-
...passingSink,
|
|
364
|
-
string: {},
|
|
365
|
-
}),
|
|
366
|
-
).toThrow('Record/string must be a string')
|
|
367
|
-
expect(() =>
|
|
368
|
-
lex.assertValidRecord('com.example.kitchenSink', {
|
|
369
|
-
...passingSink,
|
|
370
|
-
bytes: 1234,
|
|
371
|
-
}),
|
|
372
|
-
).toThrow('Record/bytes must be a byte array')
|
|
373
|
-
expect(() =>
|
|
374
|
-
lex.assertValidRecord('com.example.kitchenSink', {
|
|
375
|
-
...passingSink,
|
|
376
|
-
cidLink: 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
|
|
377
|
-
}),
|
|
378
|
-
).toThrow('Record/cidLink must be a CID')
|
|
379
|
-
})
|
|
380
|
-
|
|
381
|
-
it('Handles optional properties correctly', () => {
|
|
382
|
-
lex.assertValidRecord('com.example.optional', {
|
|
383
|
-
$type: 'com.example.optional',
|
|
384
|
-
})
|
|
385
|
-
})
|
|
386
|
-
|
|
387
|
-
it('Handles default properties correctly', () => {
|
|
388
|
-
const result = lex.assertValidRecord('com.example.default', {
|
|
389
|
-
$type: 'com.example.default',
|
|
390
|
-
object: {},
|
|
391
|
-
})
|
|
392
|
-
expect(result).toEqual({
|
|
393
|
-
$type: 'com.example.default',
|
|
394
|
-
boolean: false,
|
|
395
|
-
integer: 0,
|
|
396
|
-
string: '',
|
|
397
|
-
object: {
|
|
398
|
-
boolean: true,
|
|
399
|
-
integer: 1,
|
|
400
|
-
string: 'x',
|
|
401
|
-
},
|
|
402
|
-
})
|
|
403
|
-
expect(result).not.toHaveProperty('datetime')
|
|
404
|
-
})
|
|
405
|
-
|
|
406
|
-
it('Handles unions correctly', () => {
|
|
407
|
-
lex.assertValidRecord('com.example.union', {
|
|
408
|
-
$type: 'com.example.union',
|
|
409
|
-
unionOpen: {
|
|
410
|
-
$type: 'com.example.kitchenSink#object',
|
|
411
|
-
object: { boolean: true },
|
|
412
|
-
array: ['one', 'two'],
|
|
413
|
-
boolean: true,
|
|
414
|
-
integer: 123,
|
|
415
|
-
string: 'string',
|
|
416
|
-
},
|
|
417
|
-
unionClosed: {
|
|
418
|
-
$type: 'com.example.kitchenSink#subobject',
|
|
419
|
-
boolean: true,
|
|
420
|
-
},
|
|
421
|
-
})
|
|
422
|
-
lex.assertValidRecord('com.example.union', {
|
|
423
|
-
$type: 'com.example.union',
|
|
424
|
-
unionOpen: {
|
|
425
|
-
$type: 'com.example.other',
|
|
426
|
-
},
|
|
427
|
-
unionClosed: {
|
|
428
|
-
$type: 'com.example.kitchenSink#subobject',
|
|
429
|
-
boolean: true,
|
|
430
|
-
},
|
|
431
|
-
})
|
|
432
|
-
expect(() =>
|
|
433
|
-
lex.assertValidRecord('com.example.union', {
|
|
434
|
-
$type: 'com.example.union',
|
|
435
|
-
unionOpen: {},
|
|
436
|
-
unionClosed: {},
|
|
437
|
-
}),
|
|
438
|
-
).toThrow(
|
|
439
|
-
'Record/unionOpen must be an object which includes the "$type" property',
|
|
440
|
-
)
|
|
441
|
-
expect(() =>
|
|
442
|
-
lex.assertValidRecord('com.example.union', {
|
|
443
|
-
$type: 'com.example.union',
|
|
444
|
-
unionOpen: {
|
|
445
|
-
$type: 'com.example.other',
|
|
446
|
-
},
|
|
447
|
-
unionClosed: {
|
|
448
|
-
$type: 'com.example.other',
|
|
449
|
-
boolean: true,
|
|
450
|
-
},
|
|
451
|
-
}),
|
|
452
|
-
).toThrow(
|
|
453
|
-
'Record/unionClosed $type must be one of lex:com.example.kitchenSink#object, lex:com.example.kitchenSink#subobject',
|
|
454
|
-
)
|
|
455
|
-
})
|
|
456
|
-
|
|
457
|
-
it('Handles unknowns correctly', () => {
|
|
458
|
-
lex.assertValidRecord('com.example.unknown', {
|
|
459
|
-
$type: 'com.example.unknown',
|
|
460
|
-
unknown: { foo: 'bar' },
|
|
461
|
-
})
|
|
462
|
-
expect(() =>
|
|
463
|
-
lex.assertValidRecord('com.example.unknown', {
|
|
464
|
-
$type: 'com.example.unknown',
|
|
465
|
-
}),
|
|
466
|
-
).toThrow('Record must have the property "unknown"')
|
|
467
|
-
})
|
|
468
|
-
|
|
469
|
-
it('Applies array length constraints', () => {
|
|
470
|
-
lex.assertValidRecord('com.example.arrayLength', {
|
|
471
|
-
$type: 'com.example.arrayLength',
|
|
472
|
-
array: [1, 2, 3],
|
|
473
|
-
})
|
|
474
|
-
expect(() =>
|
|
475
|
-
lex.assertValidRecord('com.example.arrayLength', {
|
|
476
|
-
$type: 'com.example.arrayLength',
|
|
477
|
-
array: [1],
|
|
478
|
-
}),
|
|
479
|
-
).toThrow('Record/array must not have fewer than 2 elements')
|
|
480
|
-
expect(() =>
|
|
481
|
-
lex.assertValidRecord('com.example.arrayLength', {
|
|
482
|
-
$type: 'com.example.arrayLength',
|
|
483
|
-
array: [1, 2, 3, 4, 5],
|
|
484
|
-
}),
|
|
485
|
-
).toThrow('Record/array must not have more than 4 elements')
|
|
486
|
-
})
|
|
487
|
-
|
|
488
|
-
it('Applies array item constraints', () => {
|
|
489
|
-
expect(() =>
|
|
490
|
-
lex.assertValidRecord('com.example.arrayLength', {
|
|
491
|
-
$type: 'com.example.arrayLength',
|
|
492
|
-
array: [1, '2', 3],
|
|
493
|
-
}),
|
|
494
|
-
).toThrow('Record/array/1 must be an integer')
|
|
495
|
-
expect(() =>
|
|
496
|
-
lex.assertValidRecord('com.example.arrayLength', {
|
|
497
|
-
$type: 'com.example.arrayLength',
|
|
498
|
-
array: [1, undefined, 3],
|
|
499
|
-
}),
|
|
500
|
-
).toThrow('Record/array/1 must be an integer')
|
|
501
|
-
})
|
|
502
|
-
|
|
503
|
-
it('Applies boolean const constraint', () => {
|
|
504
|
-
lex.assertValidRecord('com.example.boolConst', {
|
|
505
|
-
$type: 'com.example.boolConst',
|
|
506
|
-
boolean: false,
|
|
507
|
-
})
|
|
508
|
-
expect(() =>
|
|
509
|
-
lex.assertValidRecord('com.example.boolConst', {
|
|
510
|
-
$type: 'com.example.boolConst',
|
|
511
|
-
boolean: true,
|
|
512
|
-
}),
|
|
513
|
-
).toThrow('Record/boolean must be false')
|
|
514
|
-
})
|
|
515
|
-
|
|
516
|
-
it('Applies integer range constraint', () => {
|
|
517
|
-
lex.assertValidRecord('com.example.integerRange', {
|
|
518
|
-
$type: 'com.example.integerRange',
|
|
519
|
-
integer: 2,
|
|
520
|
-
})
|
|
521
|
-
expect(() =>
|
|
522
|
-
lex.assertValidRecord('com.example.integerRange', {
|
|
523
|
-
$type: 'com.example.integerRange',
|
|
524
|
-
integer: 1,
|
|
525
|
-
}),
|
|
526
|
-
).toThrow('Record/integer can not be less than 2')
|
|
527
|
-
expect(() =>
|
|
528
|
-
lex.assertValidRecord('com.example.integerRange', {
|
|
529
|
-
$type: 'com.example.integerRange',
|
|
530
|
-
integer: 5,
|
|
531
|
-
}),
|
|
532
|
-
).toThrow('Record/integer can not be greater than 4')
|
|
533
|
-
})
|
|
534
|
-
|
|
535
|
-
it('Applies integer enum constraint', () => {
|
|
536
|
-
lex.assertValidRecord('com.example.integerEnum', {
|
|
537
|
-
$type: 'com.example.integerEnum',
|
|
538
|
-
integer: 2,
|
|
539
|
-
})
|
|
540
|
-
expect(() =>
|
|
541
|
-
lex.assertValidRecord('com.example.integerEnum', {
|
|
542
|
-
$type: 'com.example.integerEnum',
|
|
543
|
-
integer: 0,
|
|
544
|
-
}),
|
|
545
|
-
).toThrow('Record/integer must be one of (1|2)')
|
|
546
|
-
})
|
|
547
|
-
|
|
548
|
-
it('Applies integer const constraint', () => {
|
|
549
|
-
lex.assertValidRecord('com.example.integerConst', {
|
|
550
|
-
$type: 'com.example.integerConst',
|
|
551
|
-
integer: 0,
|
|
552
|
-
})
|
|
553
|
-
expect(() =>
|
|
554
|
-
lex.assertValidRecord('com.example.integerConst', {
|
|
555
|
-
$type: 'com.example.integerConst',
|
|
556
|
-
integer: 1,
|
|
557
|
-
}),
|
|
558
|
-
).toThrow('Record/integer must be 0')
|
|
559
|
-
})
|
|
560
|
-
|
|
561
|
-
it('Applies integer whole-number constraint', () => {
|
|
562
|
-
expect(() =>
|
|
563
|
-
lex.assertValidRecord('com.example.integerRange', {
|
|
564
|
-
$type: 'com.example.integerRange',
|
|
565
|
-
integer: 2.5,
|
|
566
|
-
}),
|
|
567
|
-
).toThrow('Record/integer must be an integer')
|
|
568
|
-
})
|
|
569
|
-
|
|
570
|
-
it('Applies string length constraint', () => {
|
|
571
|
-
// Shorter than two UTF8 characters
|
|
572
|
-
expect(() =>
|
|
573
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
574
|
-
$type: 'com.example.stringLength',
|
|
575
|
-
string: '',
|
|
576
|
-
}),
|
|
577
|
-
).toThrow('Record/string must not be shorter than 2 characters')
|
|
578
|
-
expect(() =>
|
|
579
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
580
|
-
$type: 'com.example.stringLength',
|
|
581
|
-
string: 'a',
|
|
582
|
-
}),
|
|
583
|
-
).toThrow('Record/string must not be shorter than 2 characters')
|
|
584
|
-
|
|
585
|
-
// Two to four UTF8 characters
|
|
586
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
587
|
-
$type: 'com.example.stringLength',
|
|
588
|
-
string: 'ab',
|
|
589
|
-
})
|
|
590
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
591
|
-
$type: 'com.example.stringLength',
|
|
592
|
-
string: '\u0301', // Combining acute accent (2 bytes)
|
|
593
|
-
})
|
|
594
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
595
|
-
$type: 'com.example.stringLength',
|
|
596
|
-
string: 'a\u0301', // 'a' + combining acute accent (1 + 2 bytes = 3 bytes)
|
|
597
|
-
})
|
|
598
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
599
|
-
$type: 'com.example.stringLength',
|
|
600
|
-
string: 'aé', // 'a' (1 byte) + 'é' (2 bytes) = 3 bytes
|
|
601
|
-
})
|
|
602
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
603
|
-
$type: 'com.example.stringLength',
|
|
604
|
-
string: 'abc',
|
|
605
|
-
})
|
|
606
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
607
|
-
$type: 'com.example.stringLength',
|
|
608
|
-
string: '一', // CJK character (3 bytes)
|
|
609
|
-
})
|
|
610
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
611
|
-
$type: 'com.example.stringLength',
|
|
612
|
-
string: '\uD83D', // Unpaired high surrogate (3 bytes)
|
|
613
|
-
})
|
|
614
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
615
|
-
$type: 'com.example.stringLength',
|
|
616
|
-
string: 'abcd',
|
|
617
|
-
})
|
|
618
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
619
|
-
$type: 'com.example.stringLength',
|
|
620
|
-
string: 'éé', // 'é' + 'é' (2 + 2 bytes = 4 bytes)
|
|
621
|
-
})
|
|
622
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
623
|
-
$type: 'com.example.stringLength',
|
|
624
|
-
string: 'aaé', // 1 + 1 + 2 = 4 bytes
|
|
625
|
-
})
|
|
626
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
627
|
-
$type: 'com.example.stringLength',
|
|
628
|
-
string: '👋', // 4 bytes
|
|
629
|
-
})
|
|
630
|
-
|
|
631
|
-
expect(() =>
|
|
632
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
633
|
-
$type: 'com.example.stringLength',
|
|
634
|
-
string: 'abcde',
|
|
635
|
-
}),
|
|
636
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
637
|
-
expect(() =>
|
|
638
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
639
|
-
$type: 'com.example.stringLength',
|
|
640
|
-
string: 'a\u0301\u0301', // 1 + (2 * 2) = 5 bytes
|
|
641
|
-
}),
|
|
642
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
643
|
-
expect(() =>
|
|
644
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
645
|
-
$type: 'com.example.stringLength',
|
|
646
|
-
string: '\uD83D\uD83D', // Two unpaired high surrogates (3 * 2 = 6 bytes)
|
|
647
|
-
}),
|
|
648
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
649
|
-
expect(() =>
|
|
650
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
651
|
-
$type: 'com.example.stringLength',
|
|
652
|
-
string: 'ééé', // 2 + 2 + 2 bytes = 6 bytes
|
|
653
|
-
}),
|
|
654
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
655
|
-
expect(() =>
|
|
656
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
657
|
-
$type: 'com.example.stringLength',
|
|
658
|
-
string: '👋a', // 4 + 1 bytes = 5 bytes
|
|
659
|
-
}),
|
|
660
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
661
|
-
expect(() =>
|
|
662
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
663
|
-
$type: 'com.example.stringLength',
|
|
664
|
-
string: '👨👨', // 4 + 4 = 8 bytes
|
|
665
|
-
}),
|
|
666
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
667
|
-
expect(() =>
|
|
668
|
-
lex.assertValidRecord('com.example.stringLength', {
|
|
669
|
-
$type: 'com.example.stringLength',
|
|
670
|
-
string: '👨👩👧👧', // 4 emojis × 4 bytes + 3 ZWJs × 3 bytes = 25 bytes
|
|
671
|
-
}),
|
|
672
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
673
|
-
})
|
|
674
|
-
|
|
675
|
-
it('Applies string length constraint (no minLength)', () => {
|
|
676
|
-
// Shorter than two UTF8 characters
|
|
677
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
678
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
679
|
-
string: '',
|
|
680
|
-
})
|
|
681
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
682
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
683
|
-
string: 'a',
|
|
684
|
-
})
|
|
685
|
-
|
|
686
|
-
// Two to four UTF8 characters
|
|
687
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
688
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
689
|
-
string: 'ab',
|
|
690
|
-
})
|
|
691
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
692
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
693
|
-
string: '\u0301', // Combining acute accent (2 bytes)
|
|
694
|
-
})
|
|
695
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
696
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
697
|
-
string: 'a\u0301', // 'a' + combining acute accent (1 + 2 bytes = 3 bytes)
|
|
698
|
-
})
|
|
699
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
700
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
701
|
-
string: 'aé', // 'a' (1 byte) + 'é' (2 bytes) = 3 bytes
|
|
702
|
-
})
|
|
703
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
704
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
705
|
-
string: 'abc',
|
|
706
|
-
})
|
|
707
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
708
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
709
|
-
string: '一', // CJK character (3 bytes)
|
|
710
|
-
})
|
|
711
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
712
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
713
|
-
string: '\uD83D', // Unpaired high surrogate (3 bytes)
|
|
714
|
-
})
|
|
715
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
716
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
717
|
-
string: 'abcd',
|
|
718
|
-
})
|
|
719
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
720
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
721
|
-
string: 'éé', // 'é' + 'é' (2 + 2 bytes = 4 bytes)
|
|
722
|
-
})
|
|
723
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
724
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
725
|
-
string: 'aaé', // 1 + 1 + 2 = 4 bytes
|
|
726
|
-
})
|
|
727
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
728
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
729
|
-
string: '👋', // 4 bytes
|
|
730
|
-
})
|
|
731
|
-
|
|
732
|
-
expect(() =>
|
|
733
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
734
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
735
|
-
string: 'abcde',
|
|
736
|
-
}),
|
|
737
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
738
|
-
expect(() =>
|
|
739
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
740
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
741
|
-
string: 'a\u0301\u0301', // 1 + (2 * 2) = 5 bytes
|
|
742
|
-
}),
|
|
743
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
744
|
-
expect(() =>
|
|
745
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
746
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
747
|
-
string: '\uD83D\uD83D', // Two unpaired high surrogates (3 * 2 = 6 bytes)
|
|
748
|
-
}),
|
|
749
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
750
|
-
expect(() =>
|
|
751
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
752
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
753
|
-
string: 'ééé', // 2 + 2 + 2 bytes = 6 bytes
|
|
754
|
-
}),
|
|
755
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
756
|
-
expect(() =>
|
|
757
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
758
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
759
|
-
string: '👋a', // 4 + 1 bytes = 5 bytes
|
|
760
|
-
}),
|
|
761
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
762
|
-
expect(() =>
|
|
763
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
764
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
765
|
-
string: '👨👨', // 4 + 4 = 8 bytes
|
|
766
|
-
}),
|
|
767
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
768
|
-
expect(() =>
|
|
769
|
-
lex.assertValidRecord('com.example.stringLengthNoMinLength', {
|
|
770
|
-
$type: 'com.example.stringLengthNoMinLength',
|
|
771
|
-
string: '👨👩👧👧', // 4 emojis × 4 bytes + 3 ZWJs × 3 bytes = 25 bytes
|
|
772
|
-
}),
|
|
773
|
-
).toThrow('Record/string must not be longer than 4 characters')
|
|
774
|
-
})
|
|
775
|
-
|
|
776
|
-
it('Applies grapheme string length constraint', () => {
|
|
777
|
-
// Shorter than two graphemes
|
|
778
|
-
expect(() =>
|
|
779
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
780
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
781
|
-
string: '',
|
|
782
|
-
}),
|
|
783
|
-
).toThrow('Record/string must not be shorter than 2 graphemes')
|
|
784
|
-
expect(() =>
|
|
785
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
786
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
787
|
-
string: '\u0301\u0301\u0301', // Three combining acute accents
|
|
788
|
-
}),
|
|
789
|
-
).toThrow('Record/string must not be shorter than 2 graphemes')
|
|
790
|
-
expect(() =>
|
|
791
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
792
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
793
|
-
string: 'a',
|
|
794
|
-
}),
|
|
795
|
-
).toThrow('Record/string must not be shorter than 2 graphemes')
|
|
796
|
-
expect(() =>
|
|
797
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
798
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
799
|
-
string: 'a\u0301\u0301\u0301\u0301', // 'á́́́' ('a' with four combining acute accents)
|
|
800
|
-
}),
|
|
801
|
-
).toThrow('Record/string must not be shorter than 2 graphemes')
|
|
802
|
-
expect(() =>
|
|
803
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
804
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
805
|
-
string: '5\uFE0F', // '5️' with emoji presentation
|
|
806
|
-
}),
|
|
807
|
-
).toThrow('Record/string must not be shorter than 2 graphemes')
|
|
808
|
-
expect(() =>
|
|
809
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
810
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
811
|
-
string: '👨👩👧👧',
|
|
812
|
-
}),
|
|
813
|
-
).toThrow('Record/string must not be shorter than 2 graphemes')
|
|
814
|
-
|
|
815
|
-
// Two to four graphemes
|
|
816
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
817
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
818
|
-
string: 'ab',
|
|
819
|
-
})
|
|
820
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
821
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
822
|
-
string: 'a\u0301b', // 'áb' with combining accent
|
|
823
|
-
})
|
|
824
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
825
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
826
|
-
string: 'a\u0301b\u0301', // 'áb́'
|
|
827
|
-
})
|
|
828
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
829
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
830
|
-
string: '😀😀',
|
|
831
|
-
})
|
|
832
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
833
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
834
|
-
string: '12👨👩👧👧',
|
|
835
|
-
})
|
|
836
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
837
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
838
|
-
string: 'abcd',
|
|
839
|
-
})
|
|
840
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
841
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
842
|
-
string: 'a\u0301b\u0301c\u0301d\u0301', // 'áb́ćd́'
|
|
843
|
-
})
|
|
844
|
-
|
|
845
|
-
// Longer than four graphemes
|
|
846
|
-
expect(() =>
|
|
847
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
848
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
849
|
-
string: 'abcde',
|
|
850
|
-
}),
|
|
851
|
-
).toThrow('Record/string must not be longer than 4 graphemes')
|
|
852
|
-
expect(() =>
|
|
853
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
854
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
855
|
-
string: 'a\u0301b\u0301c\u0301d\u0301e\u0301', // 'áb́ćd́é'
|
|
856
|
-
}),
|
|
857
|
-
).toThrow('Record/string must not be longer than 4 graphemes')
|
|
858
|
-
expect(() =>
|
|
859
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
860
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
861
|
-
string: '😀😀😀😀😀',
|
|
862
|
-
}),
|
|
863
|
-
).toThrow('Record/string must not be longer than 4 graphemes')
|
|
864
|
-
expect(() =>
|
|
865
|
-
lex.assertValidRecord('com.example.stringLengthGrapheme', {
|
|
866
|
-
$type: 'com.example.stringLengthGrapheme',
|
|
867
|
-
string: 'ab😀de',
|
|
868
|
-
}),
|
|
869
|
-
).toThrow('Record/string must not be longer than 4 graphemes')
|
|
870
|
-
})
|
|
871
|
-
|
|
872
|
-
it('Applies string enum constraint', () => {
|
|
873
|
-
lex.assertValidRecord('com.example.stringEnum', {
|
|
874
|
-
$type: 'com.example.stringEnum',
|
|
875
|
-
string: 'a',
|
|
876
|
-
})
|
|
877
|
-
expect(() =>
|
|
878
|
-
lex.assertValidRecord('com.example.stringEnum', {
|
|
879
|
-
$type: 'com.example.stringEnum',
|
|
880
|
-
string: 'c',
|
|
881
|
-
}),
|
|
882
|
-
).toThrow('Record/string must be one of (a|b)')
|
|
883
|
-
})
|
|
884
|
-
|
|
885
|
-
it('Applies string const constraint', () => {
|
|
886
|
-
lex.assertValidRecord('com.example.stringConst', {
|
|
887
|
-
$type: 'com.example.stringConst',
|
|
888
|
-
string: 'a',
|
|
889
|
-
})
|
|
890
|
-
expect(() =>
|
|
891
|
-
lex.assertValidRecord('com.example.stringConst', {
|
|
892
|
-
$type: 'com.example.stringConst',
|
|
893
|
-
string: 'b',
|
|
894
|
-
}),
|
|
895
|
-
).toThrow('Record/string must be a')
|
|
896
|
-
})
|
|
897
|
-
|
|
898
|
-
it('Applies datetime formatting constraint', () => {
|
|
899
|
-
for (const datetime of [
|
|
900
|
-
'2022-12-12T00:50:36.809Z',
|
|
901
|
-
'2022-12-12T00:50:36Z',
|
|
902
|
-
'2022-12-12T00:50:36.8Z',
|
|
903
|
-
'2022-12-12T00:50:36.80Z',
|
|
904
|
-
'2022-12-12T00:50:36+00:00',
|
|
905
|
-
'2022-12-12T00:50:36.8+00:00',
|
|
906
|
-
'2022-12-11T19:50:36-05:00',
|
|
907
|
-
'2022-12-11T19:50:36.8-05:00',
|
|
908
|
-
'2022-12-11T19:50:36.80-05:00',
|
|
909
|
-
'2022-12-11T19:50:36.809-05:00',
|
|
910
|
-
]) {
|
|
911
|
-
lex.assertValidRecord('com.example.datetime', {
|
|
912
|
-
$type: 'com.example.datetime',
|
|
913
|
-
datetime,
|
|
914
|
-
})
|
|
915
|
-
}
|
|
916
|
-
expect(() =>
|
|
917
|
-
lex.assertValidRecord('com.example.datetime', {
|
|
918
|
-
$type: 'com.example.datetime',
|
|
919
|
-
datetime: 'bad date',
|
|
920
|
-
}),
|
|
921
|
-
).toThrow(
|
|
922
|
-
'Record/datetime must be an valid atproto datetime (both RFC-3339 and ISO-8601)',
|
|
923
|
-
)
|
|
924
|
-
})
|
|
925
|
-
|
|
926
|
-
it('Applies uri formatting constraint', () => {
|
|
927
|
-
for (const uri of [
|
|
928
|
-
'https://example.com',
|
|
929
|
-
'https://example.com/with/path',
|
|
930
|
-
'https://example.com/with/path?and=query',
|
|
931
|
-
'at://bsky.social',
|
|
932
|
-
'did:example:test',
|
|
933
|
-
]) {
|
|
934
|
-
lex.assertValidRecord('com.example.uri', {
|
|
935
|
-
$type: 'com.example.uri',
|
|
936
|
-
uri,
|
|
937
|
-
})
|
|
938
|
-
}
|
|
939
|
-
expect(() =>
|
|
940
|
-
lex.assertValidRecord('com.example.uri', {
|
|
941
|
-
$type: 'com.example.uri',
|
|
942
|
-
uri: 'not a uri',
|
|
943
|
-
}),
|
|
944
|
-
).toThrow('Record/uri must be a uri')
|
|
945
|
-
})
|
|
946
|
-
|
|
947
|
-
it('Applies at-uri formatting constraint', () => {
|
|
948
|
-
lex.assertValidRecord('com.example.atUri', {
|
|
949
|
-
$type: 'com.example.atUri',
|
|
950
|
-
atUri: 'at://did:web:example.com/com.example.test/self',
|
|
951
|
-
})
|
|
952
|
-
expect(() =>
|
|
953
|
-
lex.assertValidRecord('com.example.atUri', {
|
|
954
|
-
$type: 'com.example.atUri',
|
|
955
|
-
atUri: 'http://not-atproto.com',
|
|
956
|
-
}),
|
|
957
|
-
).toThrow('Record/atUri must be a valid at-uri')
|
|
958
|
-
})
|
|
959
|
-
|
|
960
|
-
it('Applies did formatting constraint', () => {
|
|
961
|
-
lex.assertValidRecord('com.example.did', {
|
|
962
|
-
$type: 'com.example.did',
|
|
963
|
-
did: 'did:web:example.com',
|
|
964
|
-
})
|
|
965
|
-
lex.assertValidRecord('com.example.did', {
|
|
966
|
-
$type: 'com.example.did',
|
|
967
|
-
did: 'did:plc:12345678abcdefghijklmnop',
|
|
968
|
-
})
|
|
969
|
-
|
|
970
|
-
expect(() =>
|
|
971
|
-
lex.assertValidRecord('com.example.did', {
|
|
972
|
-
$type: 'com.example.did',
|
|
973
|
-
did: 'bad did',
|
|
974
|
-
}),
|
|
975
|
-
).toThrow('Record/did must be a valid did')
|
|
976
|
-
expect(() =>
|
|
977
|
-
lex.assertValidRecord('com.example.did', {
|
|
978
|
-
$type: 'com.example.did',
|
|
979
|
-
did: 'did:short',
|
|
980
|
-
}),
|
|
981
|
-
).toThrow('Record/did must be a valid did')
|
|
982
|
-
})
|
|
983
|
-
|
|
984
|
-
it('Applies handle formatting constraint', () => {
|
|
985
|
-
lex.assertValidRecord('com.example.handle', {
|
|
986
|
-
$type: 'com.example.handle',
|
|
987
|
-
handle: 'test.bsky.social',
|
|
988
|
-
})
|
|
989
|
-
lex.assertValidRecord('com.example.handle', {
|
|
990
|
-
$type: 'com.example.handle',
|
|
991
|
-
handle: 'bsky.test',
|
|
992
|
-
})
|
|
993
|
-
|
|
994
|
-
expect(() =>
|
|
995
|
-
lex.assertValidRecord('com.example.handle', {
|
|
996
|
-
$type: 'com.example.handle',
|
|
997
|
-
handle: 'bad handle',
|
|
998
|
-
}),
|
|
999
|
-
).toThrow('Record/handle must be a valid handle')
|
|
1000
|
-
expect(() =>
|
|
1001
|
-
lex.assertValidRecord('com.example.handle', {
|
|
1002
|
-
$type: 'com.example.handle',
|
|
1003
|
-
handle: '-bad-.test',
|
|
1004
|
-
}),
|
|
1005
|
-
).toThrow('Record/handle must be a valid handle')
|
|
1006
|
-
})
|
|
1007
|
-
|
|
1008
|
-
it('Applies at-identifier formatting constraint', () => {
|
|
1009
|
-
lex.assertValidRecord('com.example.atIdentifier', {
|
|
1010
|
-
$type: 'com.example.atIdentifier',
|
|
1011
|
-
atIdentifier: 'bsky.test',
|
|
1012
|
-
})
|
|
1013
|
-
lex.assertValidRecord('com.example.atIdentifier', {
|
|
1014
|
-
$type: 'com.example.atIdentifier',
|
|
1015
|
-
atIdentifier: 'did:plc:12345678abcdefghijklmnop',
|
|
1016
|
-
})
|
|
1017
|
-
|
|
1018
|
-
expect(() =>
|
|
1019
|
-
lex.assertValidRecord('com.example.atIdentifier', {
|
|
1020
|
-
$type: 'com.example.atIdentifier',
|
|
1021
|
-
atIdentifier: 'bad id',
|
|
1022
|
-
}),
|
|
1023
|
-
).toThrow('Record/atIdentifier must be a valid did or a handle')
|
|
1024
|
-
expect(() =>
|
|
1025
|
-
lex.assertValidRecord('com.example.atIdentifier', {
|
|
1026
|
-
$type: 'com.example.atIdentifier',
|
|
1027
|
-
atIdentifier: '-bad-.test',
|
|
1028
|
-
}),
|
|
1029
|
-
).toThrow('Record/atIdentifier must be a valid did or a handle')
|
|
1030
|
-
})
|
|
1031
|
-
|
|
1032
|
-
it('Applies nsid formatting constraint', () => {
|
|
1033
|
-
lex.assertValidRecord('com.example.nsid', {
|
|
1034
|
-
$type: 'com.example.nsid',
|
|
1035
|
-
nsid: 'com.atproto.test',
|
|
1036
|
-
})
|
|
1037
|
-
lex.assertValidRecord('com.example.nsid', {
|
|
1038
|
-
$type: 'com.example.nsid',
|
|
1039
|
-
nsid: 'app.bsky.nested.test',
|
|
1040
|
-
})
|
|
1041
|
-
|
|
1042
|
-
expect(() =>
|
|
1043
|
-
lex.assertValidRecord('com.example.nsid', {
|
|
1044
|
-
$type: 'com.example.nsid',
|
|
1045
|
-
nsid: 'bad nsid',
|
|
1046
|
-
}),
|
|
1047
|
-
).toThrow('Record/nsid must be a valid nsid')
|
|
1048
|
-
expect(() =>
|
|
1049
|
-
lex.assertValidRecord('com.example.nsid', {
|
|
1050
|
-
$type: 'com.example.nsid',
|
|
1051
|
-
nsid: 'com.bad-.foo',
|
|
1052
|
-
}),
|
|
1053
|
-
).toThrow('Record/nsid must be a valid nsid')
|
|
1054
|
-
})
|
|
1055
|
-
|
|
1056
|
-
it('Applies cid formatting constraint', () => {
|
|
1057
|
-
lex.assertValidRecord('com.example.cid', {
|
|
1058
|
-
$type: 'com.example.cid',
|
|
1059
|
-
cid: 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
|
|
1060
|
-
})
|
|
1061
|
-
expect(() =>
|
|
1062
|
-
lex.assertValidRecord('com.example.cid', {
|
|
1063
|
-
$type: 'com.example.cid',
|
|
1064
|
-
cid: 'abapsdofiuwrpoiasdfuaspdfoiu',
|
|
1065
|
-
}),
|
|
1066
|
-
).toThrow('Record/cid must be a cid string')
|
|
1067
|
-
})
|
|
1068
|
-
|
|
1069
|
-
it('Applies language formatting constraint', () => {
|
|
1070
|
-
lex.assertValidRecord('com.example.language', {
|
|
1071
|
-
$type: 'com.example.language',
|
|
1072
|
-
language: 'en-US-boont',
|
|
1073
|
-
})
|
|
1074
|
-
expect(() =>
|
|
1075
|
-
lex.assertValidRecord('com.example.language', {
|
|
1076
|
-
$type: 'com.example.language',
|
|
1077
|
-
language: 'not-a-language-',
|
|
1078
|
-
}),
|
|
1079
|
-
).toThrow('Record/language must be a well-formed BCP 47 language tag')
|
|
1080
|
-
})
|
|
1081
|
-
|
|
1082
|
-
it('Applies bytes length constraints', () => {
|
|
1083
|
-
lex.assertValidRecord('com.example.byteLength', {
|
|
1084
|
-
$type: 'com.example.byteLength',
|
|
1085
|
-
bytes: new Uint8Array([1, 2, 3]),
|
|
1086
|
-
})
|
|
1087
|
-
expect(() =>
|
|
1088
|
-
lex.assertValidRecord('com.example.byteLength', {
|
|
1089
|
-
$type: 'com.example.byteLength',
|
|
1090
|
-
bytes: new Uint8Array([1]),
|
|
1091
|
-
}),
|
|
1092
|
-
).toThrow('Record/bytes must not be smaller than 2 bytes')
|
|
1093
|
-
expect(() =>
|
|
1094
|
-
lex.assertValidRecord('com.example.byteLength', {
|
|
1095
|
-
$type: 'com.example.byteLength',
|
|
1096
|
-
bytes: new Uint8Array([1, 2, 3, 4, 5]),
|
|
1097
|
-
}),
|
|
1098
|
-
).toThrow('Record/bytes must not be larger than 4 bytes')
|
|
1099
|
-
})
|
|
1100
|
-
})
|
|
1101
|
-
|
|
1102
|
-
describe('XRPC parameter validation', () => {
|
|
1103
|
-
const lex = new Lexicons(LexiconDocs)
|
|
1104
|
-
|
|
1105
|
-
it('Passes valid parameters', () => {
|
|
1106
|
-
const queryResult = lex.assertValidXrpcParams('com.example.query', {
|
|
1107
|
-
boolean: true,
|
|
1108
|
-
integer: 123,
|
|
1109
|
-
string: 'string',
|
|
1110
|
-
array: ['x', 'y'],
|
|
1111
|
-
})
|
|
1112
|
-
expect(queryResult).toEqual({
|
|
1113
|
-
boolean: true,
|
|
1114
|
-
integer: 123,
|
|
1115
|
-
string: 'string',
|
|
1116
|
-
array: ['x', 'y'],
|
|
1117
|
-
def: 0,
|
|
1118
|
-
})
|
|
1119
|
-
const paramResult = lex.assertValidXrpcParams('com.example.procedure', {
|
|
1120
|
-
boolean: true,
|
|
1121
|
-
integer: 123,
|
|
1122
|
-
string: 'string',
|
|
1123
|
-
array: ['x', 'y'],
|
|
1124
|
-
def: 1,
|
|
1125
|
-
})
|
|
1126
|
-
expect(paramResult).toEqual({
|
|
1127
|
-
boolean: true,
|
|
1128
|
-
integer: 123,
|
|
1129
|
-
string: 'string',
|
|
1130
|
-
array: ['x', 'y'],
|
|
1131
|
-
def: 1,
|
|
1132
|
-
})
|
|
1133
|
-
})
|
|
1134
|
-
|
|
1135
|
-
it('Handles required correctly', () => {
|
|
1136
|
-
lex.assertValidXrpcParams('com.example.query', {
|
|
1137
|
-
boolean: true,
|
|
1138
|
-
integer: 123,
|
|
1139
|
-
})
|
|
1140
|
-
expect(() =>
|
|
1141
|
-
lex.assertValidXrpcParams('com.example.query', {
|
|
1142
|
-
boolean: true,
|
|
1143
|
-
}),
|
|
1144
|
-
).toThrow('Params must have the property "integer"')
|
|
1145
|
-
expect(() =>
|
|
1146
|
-
lex.assertValidXrpcParams('com.example.query', {
|
|
1147
|
-
boolean: true,
|
|
1148
|
-
integer: undefined,
|
|
1149
|
-
}),
|
|
1150
|
-
).toThrow('Params must have the property "integer"')
|
|
1151
|
-
})
|
|
1152
|
-
|
|
1153
|
-
it('Validates parameter types', () => {
|
|
1154
|
-
expect(() =>
|
|
1155
|
-
lex.assertValidXrpcParams('com.example.query', {
|
|
1156
|
-
boolean: 'string',
|
|
1157
|
-
integer: 123,
|
|
1158
|
-
string: 'string',
|
|
1159
|
-
}),
|
|
1160
|
-
).toThrow('boolean must be a boolean')
|
|
1161
|
-
expect(() =>
|
|
1162
|
-
lex.assertValidXrpcParams('com.example.query', {
|
|
1163
|
-
boolean: true,
|
|
1164
|
-
float: 123.45,
|
|
1165
|
-
integer: 123,
|
|
1166
|
-
string: 'string',
|
|
1167
|
-
array: 'x',
|
|
1168
|
-
}),
|
|
1169
|
-
).toThrow('array must be an array')
|
|
1170
|
-
})
|
|
1171
|
-
})
|
|
1172
|
-
|
|
1173
|
-
describe('XRPC input validation', () => {
|
|
1174
|
-
const lex = new Lexicons(LexiconDocs)
|
|
1175
|
-
|
|
1176
|
-
it('Passes valid inputs', () => {
|
|
1177
|
-
lex.assertValidXrpcInput('com.example.procedure', {
|
|
1178
|
-
object: { boolean: true },
|
|
1179
|
-
array: ['one', 'two'],
|
|
1180
|
-
boolean: true,
|
|
1181
|
-
float: 123.45,
|
|
1182
|
-
integer: 123,
|
|
1183
|
-
string: 'string',
|
|
1184
|
-
})
|
|
1185
|
-
})
|
|
1186
|
-
|
|
1187
|
-
it('Validates the input', () => {
|
|
1188
|
-
// dont need to check this extensively since it's the same logic as tested in record validation
|
|
1189
|
-
expect(() =>
|
|
1190
|
-
lex.assertValidXrpcInput('com.example.procedure', {
|
|
1191
|
-
object: { boolean: 'string' },
|
|
1192
|
-
array: ['one', 'two'],
|
|
1193
|
-
boolean: true,
|
|
1194
|
-
float: 123.45,
|
|
1195
|
-
integer: 123,
|
|
1196
|
-
string: 'string',
|
|
1197
|
-
}),
|
|
1198
|
-
).toThrow('Input/object/boolean must be a boolean')
|
|
1199
|
-
expect(() => lex.assertValidXrpcInput('com.example.procedure', {})).toThrow(
|
|
1200
|
-
'Input must have the property "object"',
|
|
1201
|
-
)
|
|
1202
|
-
})
|
|
1203
|
-
})
|
|
1204
|
-
|
|
1205
|
-
describe('XRPC output validation', () => {
|
|
1206
|
-
const lex = new Lexicons(LexiconDocs)
|
|
1207
|
-
|
|
1208
|
-
it('Passes valid outputs', () => {
|
|
1209
|
-
lex.assertValidXrpcOutput('com.example.query', {
|
|
1210
|
-
object: { boolean: true },
|
|
1211
|
-
array: ['one', 'two'],
|
|
1212
|
-
boolean: true,
|
|
1213
|
-
float: 123.45,
|
|
1214
|
-
integer: 123,
|
|
1215
|
-
string: 'string',
|
|
1216
|
-
})
|
|
1217
|
-
lex.assertValidXrpcOutput('com.example.procedure', {
|
|
1218
|
-
object: { boolean: true },
|
|
1219
|
-
array: ['one', 'two'],
|
|
1220
|
-
boolean: true,
|
|
1221
|
-
float: 123.45,
|
|
1222
|
-
integer: 123,
|
|
1223
|
-
string: 'string',
|
|
1224
|
-
})
|
|
1225
|
-
})
|
|
1226
|
-
|
|
1227
|
-
it('Validates the output', () => {
|
|
1228
|
-
// dont need to check this extensively since it's the same logic as tested in record validation
|
|
1229
|
-
expect(() =>
|
|
1230
|
-
lex.assertValidXrpcOutput('com.example.query', {
|
|
1231
|
-
object: { boolean: 'string' },
|
|
1232
|
-
array: ['one', 'two'],
|
|
1233
|
-
boolean: true,
|
|
1234
|
-
float: 123.45,
|
|
1235
|
-
integer: 123,
|
|
1236
|
-
string: 'string',
|
|
1237
|
-
}),
|
|
1238
|
-
).toThrow('Output/object/boolean must be a boolean')
|
|
1239
|
-
expect(() =>
|
|
1240
|
-
lex.assertValidXrpcOutput('com.example.procedure', {}),
|
|
1241
|
-
).toThrow('Output must have the property "object"')
|
|
1242
|
-
})
|
|
1243
|
-
})
|