@bedrockio/model 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/dist/cjs/access.js +4 -0
- package/dist/cjs/assign.js +2 -3
- package/dist/cjs/disallowed.js +40 -0
- package/dist/cjs/include.js +3 -2
- package/dist/cjs/load.js +15 -3
- package/dist/cjs/schema.js +29 -12
- package/dist/cjs/search.js +16 -16
- package/dist/cjs/serialization.js +2 -3
- package/dist/cjs/soft-delete.js +234 -55
- package/dist/cjs/testing.js +8 -0
- package/dist/cjs/validation.js +109 -43
- package/package.json +9 -7
- package/src/access.js +3 -0
- package/src/disallowed.js +63 -0
- package/src/include.js +1 -0
- package/src/load.js +12 -1
- package/src/schema.js +25 -5
- package/src/search.js +21 -10
- package/src/soft-delete.js +238 -85
- package/src/testing.js +7 -0
- package/src/validation.js +134 -43
- package/types/access.d.ts +7 -0
- package/types/access.d.ts.map +1 -0
- package/types/assign.d.ts +2 -0
- package/types/assign.d.ts.map +1 -0
- package/types/const.d.ts +9 -0
- package/types/const.d.ts.map +1 -0
- package/types/disallowed.d.ts +2 -0
- package/types/disallowed.d.ts.map +1 -0
- package/types/errors.d.ts +9 -0
- package/types/errors.d.ts.map +1 -0
- package/types/include.d.ts +4 -0
- package/types/include.d.ts.map +1 -0
- package/types/index.d.ts +6 -0
- package/types/index.d.ts.map +1 -0
- package/types/load.d.ts +15 -0
- package/types/load.d.ts.map +1 -0
- package/types/references.d.ts +2 -0
- package/types/references.d.ts.map +1 -0
- package/types/schema.d.ts +71 -0
- package/types/schema.d.ts.map +1 -0
- package/types/search.d.ts +303 -0
- package/types/search.d.ts.map +1 -0
- package/types/serialization.d.ts +6 -0
- package/types/serialization.d.ts.map +1 -0
- package/types/slug.d.ts +2 -0
- package/types/slug.d.ts.map +1 -0
- package/types/soft-delete.d.ts +4 -0
- package/types/soft-delete.d.ts.map +1 -0
- package/types/testing.d.ts +11 -0
- package/types/testing.d.ts.map +1 -0
- package/types/utils.d.ts +8 -0
- package/types/utils.d.ts.map +1 -0
- package/types/validation.d.ts +13 -0
- package/types/validation.d.ts.map +1 -0
- package/types/warn.d.ts +2 -0
- package/types/warn.d.ts.map +1 -0
- package/babel.config.cjs +0 -41
- package/jest.config.js +0 -8
- package/test/assign.test.js +0 -225
- package/test/definitions/custom-model.json +0 -9
- package/test/definitions/special-category.json +0 -18
- package/test/include.test.js +0 -896
- package/test/load.test.js +0 -47
- package/test/references.test.js +0 -71
- package/test/schema.test.js +0 -919
- package/test/search.test.js +0 -652
- package/test/serialization.test.js +0 -748
- package/test/setup.js +0 -27
- package/test/slug.test.js +0 -112
- package/test/soft-delete.test.js +0 -333
- package/test/validation.test.js +0 -1925
package/test/schema.test.js
DELETED
|
@@ -1,919 +0,0 @@
|
|
|
1
|
-
import mongoose from 'mongoose';
|
|
2
|
-
|
|
3
|
-
import { createTestModel } from '../src/testing';
|
|
4
|
-
import { normalizeAttributes } from '../src/schema';
|
|
5
|
-
|
|
6
|
-
describe('normalizeAttributes', () => {
|
|
7
|
-
it('should accept a correctly formatted definition', async () => {
|
|
8
|
-
const attributes = {
|
|
9
|
-
name: {
|
|
10
|
-
type: 'String',
|
|
11
|
-
},
|
|
12
|
-
age: {
|
|
13
|
-
type: 'Number',
|
|
14
|
-
},
|
|
15
|
-
dob: {
|
|
16
|
-
type: 'Date',
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
expect(normalizeAttributes(attributes)).toEqual({
|
|
20
|
-
name: {
|
|
21
|
-
type: 'String',
|
|
22
|
-
},
|
|
23
|
-
age: {
|
|
24
|
-
type: 'Number',
|
|
25
|
-
},
|
|
26
|
-
dob: {
|
|
27
|
-
type: 'Date',
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should convert strings to type definitions', async () => {
|
|
33
|
-
const attributes = {
|
|
34
|
-
name: 'String',
|
|
35
|
-
age: 'Number',
|
|
36
|
-
dob: 'Date',
|
|
37
|
-
};
|
|
38
|
-
expect(normalizeAttributes(attributes)).toEqual({
|
|
39
|
-
name: {
|
|
40
|
-
type: 'String',
|
|
41
|
-
},
|
|
42
|
-
age: {
|
|
43
|
-
type: 'Number',
|
|
44
|
-
},
|
|
45
|
-
dob: {
|
|
46
|
-
type: 'Date',
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should convert objects to type definitions', async () => {
|
|
52
|
-
const attributes = {
|
|
53
|
-
profile: {
|
|
54
|
-
firstName: 'String',
|
|
55
|
-
lastName: 'String',
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
expect(normalizeAttributes(attributes)).toEqual({
|
|
59
|
-
profile: {
|
|
60
|
-
firstName: {
|
|
61
|
-
type: 'String',
|
|
62
|
-
},
|
|
63
|
-
lastName: {
|
|
64
|
-
type: 'String',
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should convert array of strings to type definitions', async () => {
|
|
71
|
-
const attributes = {
|
|
72
|
-
names: ['String'],
|
|
73
|
-
};
|
|
74
|
-
expect(normalizeAttributes(attributes)).toEqual({
|
|
75
|
-
names: {
|
|
76
|
-
type: [
|
|
77
|
-
{
|
|
78
|
-
type: 'String',
|
|
79
|
-
},
|
|
80
|
-
],
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should convert array of objects to type definitions', async () => {
|
|
86
|
-
const attributes = {
|
|
87
|
-
profiles: [
|
|
88
|
-
{
|
|
89
|
-
firstName: 'String',
|
|
90
|
-
lastName: 'String',
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
};
|
|
94
|
-
expect(normalizeAttributes(attributes)).toEqual({
|
|
95
|
-
profiles: {
|
|
96
|
-
type: [
|
|
97
|
-
{
|
|
98
|
-
firstName: {
|
|
99
|
-
type: 'String',
|
|
100
|
-
},
|
|
101
|
-
lastName: {
|
|
102
|
-
type: 'String',
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
],
|
|
106
|
-
},
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should convert deeply nested attributes', async () => {
|
|
111
|
-
const attributes = {
|
|
112
|
-
a: [
|
|
113
|
-
{
|
|
114
|
-
b: {
|
|
115
|
-
c: 'String',
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
],
|
|
119
|
-
};
|
|
120
|
-
expect(normalizeAttributes(attributes)).toEqual({
|
|
121
|
-
a: {
|
|
122
|
-
type: [
|
|
123
|
-
{
|
|
124
|
-
b: {
|
|
125
|
-
c: {
|
|
126
|
-
type: 'String',
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
],
|
|
131
|
-
},
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('should allow arrays of unstructured objects', async () => {
|
|
136
|
-
const attributes = {
|
|
137
|
-
tokens: ['Object'],
|
|
138
|
-
};
|
|
139
|
-
expect(normalizeAttributes(attributes)).toEqual({
|
|
140
|
-
tokens: {
|
|
141
|
-
type: [
|
|
142
|
-
{
|
|
143
|
-
type: 'Object',
|
|
144
|
-
},
|
|
145
|
-
],
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('should convert tuples', async () => {
|
|
151
|
-
const attributes = {
|
|
152
|
-
location: ['Number', 'Number'],
|
|
153
|
-
};
|
|
154
|
-
expect(normalizeAttributes(attributes)).toEqual({
|
|
155
|
-
location: {
|
|
156
|
-
type: [
|
|
157
|
-
{
|
|
158
|
-
type: 'Number',
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
type: 'Number',
|
|
162
|
-
},
|
|
163
|
-
],
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('should allow empty arrays', async () => {
|
|
169
|
-
const attributes = {
|
|
170
|
-
tokens: [],
|
|
171
|
-
};
|
|
172
|
-
expect(normalizeAttributes(attributes)).toEqual({
|
|
173
|
-
tokens: {
|
|
174
|
-
type: [],
|
|
175
|
-
},
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it('should error on unknown types', async () => {
|
|
180
|
-
const attributes = {
|
|
181
|
-
profile: {
|
|
182
|
-
name: 'foo',
|
|
183
|
-
},
|
|
184
|
-
};
|
|
185
|
-
expect(() => {
|
|
186
|
-
normalizeAttributes(attributes);
|
|
187
|
-
}).toThrow('Invalid type "foo" for "profile.name".');
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should error on lowercase', async () => {
|
|
191
|
-
const attributes = {
|
|
192
|
-
profile: {
|
|
193
|
-
name: 'string',
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
expect(() => {
|
|
197
|
-
normalizeAttributes(attributes);
|
|
198
|
-
}).toThrow('Type "string" in "profile.name" should be "String".');
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it('should error on Mixed type', async () => {
|
|
202
|
-
const attributes = {
|
|
203
|
-
name: 'Mixed',
|
|
204
|
-
};
|
|
205
|
-
expect(() => {
|
|
206
|
-
normalizeAttributes(attributes);
|
|
207
|
-
}).toThrow('Type "Mixed" is not allowed. Use "Object" instead.');
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('should error on ObjectId with no ref', async () => {
|
|
211
|
-
const attributes = {
|
|
212
|
-
user: {
|
|
213
|
-
type: 'ObjectId',
|
|
214
|
-
},
|
|
215
|
-
};
|
|
216
|
-
expect(() => {
|
|
217
|
-
normalizeAttributes(attributes);
|
|
218
|
-
}).toThrow('Ref must be passed for "user".');
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it('should error on ref but not ObjectId', async () => {
|
|
222
|
-
const attributes = {
|
|
223
|
-
user: {
|
|
224
|
-
ref: 'User',
|
|
225
|
-
},
|
|
226
|
-
};
|
|
227
|
-
expect(() => {
|
|
228
|
-
normalizeAttributes(attributes);
|
|
229
|
-
}).toThrow('Ref field "user" must be type "ObjectId".');
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('should error on native functions for type', async () => {
|
|
233
|
-
const attributes = {
|
|
234
|
-
name: String,
|
|
235
|
-
};
|
|
236
|
-
expect(() => {
|
|
237
|
-
normalizeAttributes(attributes);
|
|
238
|
-
}).toThrow('Native functions are not allowed as types.');
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('should set array fields with validator', async () => {
|
|
242
|
-
const attributes = {
|
|
243
|
-
names: [
|
|
244
|
-
{
|
|
245
|
-
type: 'String',
|
|
246
|
-
validate: 'email',
|
|
247
|
-
},
|
|
248
|
-
],
|
|
249
|
-
};
|
|
250
|
-
expect(normalizeAttributes(attributes)).toEqual({
|
|
251
|
-
names: {
|
|
252
|
-
type: [
|
|
253
|
-
{
|
|
254
|
-
type: 'String',
|
|
255
|
-
validate: 'email',
|
|
256
|
-
},
|
|
257
|
-
],
|
|
258
|
-
},
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it('should normalize a location schema', async () => {
|
|
263
|
-
const attributes = {
|
|
264
|
-
location: {
|
|
265
|
-
type: {
|
|
266
|
-
type: 'String',
|
|
267
|
-
default: 'Point',
|
|
268
|
-
},
|
|
269
|
-
coordinates: {
|
|
270
|
-
type: ['Number'],
|
|
271
|
-
default: [],
|
|
272
|
-
},
|
|
273
|
-
},
|
|
274
|
-
};
|
|
275
|
-
expect(normalizeAttributes(attributes)).toEqual({
|
|
276
|
-
location: {
|
|
277
|
-
type: {
|
|
278
|
-
type: 'String',
|
|
279
|
-
default: 'Point',
|
|
280
|
-
},
|
|
281
|
-
coordinates: {
|
|
282
|
-
type: [
|
|
283
|
-
{
|
|
284
|
-
type: 'Number',
|
|
285
|
-
},
|
|
286
|
-
],
|
|
287
|
-
default: [],
|
|
288
|
-
},
|
|
289
|
-
},
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
describe('createSchema', () => {
|
|
295
|
-
describe('basic', () => {
|
|
296
|
-
it('should create a basic schema', async () => {
|
|
297
|
-
const User = createTestModel({
|
|
298
|
-
name: {
|
|
299
|
-
type: 'String',
|
|
300
|
-
validate: /[a-z]/,
|
|
301
|
-
},
|
|
302
|
-
});
|
|
303
|
-
const user = new User({ name: 'foo' });
|
|
304
|
-
expect(user.name).toBe('foo');
|
|
305
|
-
await expect(async () => {
|
|
306
|
-
user.name = 'FOO';
|
|
307
|
-
await user.save();
|
|
308
|
-
}).rejects.toThrow();
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
it('should convert a string match to a regexp', async () => {
|
|
312
|
-
const User = createTestModel({
|
|
313
|
-
color: {
|
|
314
|
-
type: 'String',
|
|
315
|
-
match: '/^#[0-9a-f]{6}$/i',
|
|
316
|
-
},
|
|
317
|
-
});
|
|
318
|
-
const user = await User.create({
|
|
319
|
-
color: '#ffffff',
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
await expect(async () => {
|
|
323
|
-
user.color = 'foo';
|
|
324
|
-
await user.save();
|
|
325
|
-
}).rejects.toThrow();
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
it('should error when type is unknown', async () => {
|
|
329
|
-
expect(() => {
|
|
330
|
-
createTestModel({
|
|
331
|
-
image: {
|
|
332
|
-
type: 'Object',
|
|
333
|
-
ref: 'Upload',
|
|
334
|
-
},
|
|
335
|
-
});
|
|
336
|
-
}).toThrow();
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
it('should error when type is Mixed', async () => {
|
|
340
|
-
expect(() => {
|
|
341
|
-
createTestModel({
|
|
342
|
-
object: 'Mixed',
|
|
343
|
-
});
|
|
344
|
-
}).toThrow('Type "Mixed" is not allowed. Use "Object" instead.');
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
it('should not error when ObjectId has a refPath', async () => {
|
|
348
|
-
const User = createTestModel({
|
|
349
|
-
image: {
|
|
350
|
-
type: 'ObjectId',
|
|
351
|
-
refPath: 'fakePath',
|
|
352
|
-
},
|
|
353
|
-
});
|
|
354
|
-
expect(User.schema.obj.image.type).toBe('ObjectId');
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
it('should not error when a literal ref field is defined', async () => {
|
|
358
|
-
const User = createTestModel({
|
|
359
|
-
name: 'String',
|
|
360
|
-
ref: 'String',
|
|
361
|
-
});
|
|
362
|
-
expect(User.schema.obj.ref).toEqual({
|
|
363
|
-
type: 'String',
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
describe('arrays', () => {
|
|
369
|
-
it('should create a schema with an array field', async () => {
|
|
370
|
-
const User = createTestModel({
|
|
371
|
-
names: [
|
|
372
|
-
{
|
|
373
|
-
type: 'String',
|
|
374
|
-
validate: /[a-z]/,
|
|
375
|
-
},
|
|
376
|
-
],
|
|
377
|
-
});
|
|
378
|
-
const user = new User({ names: ['foo'] });
|
|
379
|
-
|
|
380
|
-
expect(Array.from(user.names)).toEqual(['foo']);
|
|
381
|
-
|
|
382
|
-
await expect(async () => {
|
|
383
|
-
user.names = ['FOO'];
|
|
384
|
-
await user.save();
|
|
385
|
-
}).rejects.toThrow();
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
it('should allow string syntax', async () => {
|
|
389
|
-
const User = createTestModel({
|
|
390
|
-
names: ['String'],
|
|
391
|
-
});
|
|
392
|
-
const user = new User({ names: ['foo'] });
|
|
393
|
-
expect(Array.from(user.names)).toEqual(['foo']);
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
it('should allow a specified default', async () => {
|
|
397
|
-
const User = createTestModel({
|
|
398
|
-
names: {
|
|
399
|
-
type: ['String'],
|
|
400
|
-
default: undefined,
|
|
401
|
-
},
|
|
402
|
-
});
|
|
403
|
-
const user = new User();
|
|
404
|
-
expect(user.names).toBeUndefined();
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
describe('extensions', () => {
|
|
408
|
-
it('should allow extended array syntax', async () => {
|
|
409
|
-
const User = createTestModel({
|
|
410
|
-
names: {
|
|
411
|
-
type: 'Array',
|
|
412
|
-
attributes: {
|
|
413
|
-
firstName: 'String',
|
|
414
|
-
lastName: 'String',
|
|
415
|
-
},
|
|
416
|
-
default: [
|
|
417
|
-
{
|
|
418
|
-
firstName: 'John',
|
|
419
|
-
lastName: 'Doe',
|
|
420
|
-
},
|
|
421
|
-
],
|
|
422
|
-
},
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
const user = await User.create({});
|
|
426
|
-
expect(user.names).toMatchObject([
|
|
427
|
-
{
|
|
428
|
-
firstName: 'John',
|
|
429
|
-
lastName: 'Doe',
|
|
430
|
-
},
|
|
431
|
-
]);
|
|
432
|
-
|
|
433
|
-
await expect(
|
|
434
|
-
User.create({
|
|
435
|
-
names: ['John Doe'],
|
|
436
|
-
})
|
|
437
|
-
).rejects.toThrow();
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
it('should allow nested extended array syntax', async () => {
|
|
441
|
-
const User = createTestModel({
|
|
442
|
-
tokens: {
|
|
443
|
-
type: 'Array',
|
|
444
|
-
attributes: {
|
|
445
|
-
id: {
|
|
446
|
-
type: 'String',
|
|
447
|
-
required: true,
|
|
448
|
-
},
|
|
449
|
-
name: {
|
|
450
|
-
type: 'String',
|
|
451
|
-
required: true,
|
|
452
|
-
},
|
|
453
|
-
},
|
|
454
|
-
},
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
await expect(
|
|
458
|
-
User.create({
|
|
459
|
-
tokens: [
|
|
460
|
-
{
|
|
461
|
-
id: 'foo',
|
|
462
|
-
},
|
|
463
|
-
],
|
|
464
|
-
})
|
|
465
|
-
).rejects.toThrow();
|
|
466
|
-
|
|
467
|
-
await expect(
|
|
468
|
-
User.create({
|
|
469
|
-
tokens: [
|
|
470
|
-
{
|
|
471
|
-
id: 'foo',
|
|
472
|
-
name: 'bar',
|
|
473
|
-
},
|
|
474
|
-
],
|
|
475
|
-
})
|
|
476
|
-
).resolves.not.toThrow();
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
it('should validate array min/max length', async () => {
|
|
480
|
-
const User = createTestModel({
|
|
481
|
-
names: {
|
|
482
|
-
type: ['String'],
|
|
483
|
-
minLength: 1,
|
|
484
|
-
maxLength: 2,
|
|
485
|
-
},
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
await expect(
|
|
489
|
-
User.create({
|
|
490
|
-
names: [],
|
|
491
|
-
})
|
|
492
|
-
).rejects.toThrow('Must have at least 1 element.');
|
|
493
|
-
|
|
494
|
-
await expect(
|
|
495
|
-
User.create({
|
|
496
|
-
names: ['foo'],
|
|
497
|
-
})
|
|
498
|
-
).resolves.not.toThrow();
|
|
499
|
-
|
|
500
|
-
await expect(
|
|
501
|
-
User.create({
|
|
502
|
-
names: ['foo', 'bar'],
|
|
503
|
-
})
|
|
504
|
-
).resolves.not.toThrow();
|
|
505
|
-
|
|
506
|
-
await expect(
|
|
507
|
-
User.create({
|
|
508
|
-
names: ['foo', 'bar', 'baz'],
|
|
509
|
-
})
|
|
510
|
-
).rejects.toThrow('Cannot have more than 2 elements.');
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
it('should validate tuple types', async () => {
|
|
514
|
-
const User = createTestModel({
|
|
515
|
-
location: ['Number', 'Number'],
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
await expect(
|
|
519
|
-
User.create({
|
|
520
|
-
location: [],
|
|
521
|
-
})
|
|
522
|
-
).rejects.toThrow();
|
|
523
|
-
|
|
524
|
-
await expect(
|
|
525
|
-
User.create({
|
|
526
|
-
location: [35],
|
|
527
|
-
})
|
|
528
|
-
).rejects.toThrow();
|
|
529
|
-
|
|
530
|
-
await expect(
|
|
531
|
-
User.create({
|
|
532
|
-
location: [35, 139],
|
|
533
|
-
})
|
|
534
|
-
).resolves.not.toThrow();
|
|
535
|
-
|
|
536
|
-
await expect(
|
|
537
|
-
User.create({
|
|
538
|
-
location: [35, 139, 13],
|
|
539
|
-
})
|
|
540
|
-
).rejects.toThrow();
|
|
541
|
-
|
|
542
|
-
await expect(
|
|
543
|
-
User.create({
|
|
544
|
-
location: [35, '139'],
|
|
545
|
-
})
|
|
546
|
-
).rejects.toThrow();
|
|
547
|
-
});
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
it('should hoist read/write scopes in array field as a special case', async () => {
|
|
551
|
-
const User = createTestModel({
|
|
552
|
-
tokens: [
|
|
553
|
-
{
|
|
554
|
-
type: 'String',
|
|
555
|
-
readAccess: 'none',
|
|
556
|
-
writeAccess: 'none',
|
|
557
|
-
},
|
|
558
|
-
],
|
|
559
|
-
});
|
|
560
|
-
expect(User.schema.obj).toMatchObject({
|
|
561
|
-
tokens: {
|
|
562
|
-
type: [
|
|
563
|
-
{
|
|
564
|
-
type: 'String',
|
|
565
|
-
},
|
|
566
|
-
],
|
|
567
|
-
readAccess: 'none',
|
|
568
|
-
writeAccess: 'none',
|
|
569
|
-
},
|
|
570
|
-
});
|
|
571
|
-
});
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
describe('objects', () => {
|
|
575
|
-
it('should create a schema for a nested field', async () => {
|
|
576
|
-
const User = createTestModel({
|
|
577
|
-
profile: {
|
|
578
|
-
name: {
|
|
579
|
-
type: 'String',
|
|
580
|
-
validate: /[a-z]/,
|
|
581
|
-
},
|
|
582
|
-
},
|
|
583
|
-
});
|
|
584
|
-
const user = new User({
|
|
585
|
-
profile: {
|
|
586
|
-
name: 'foo',
|
|
587
|
-
},
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
expect(user.profile.name).toBe('foo');
|
|
591
|
-
|
|
592
|
-
await expect(async () => {
|
|
593
|
-
user.profile.name = 'FOO';
|
|
594
|
-
await user.save();
|
|
595
|
-
}).rejects.toThrow();
|
|
596
|
-
});
|
|
597
|
-
|
|
598
|
-
it('should accept a schema for a nested field', async () => {
|
|
599
|
-
const Profile = createTestModel({
|
|
600
|
-
name: {
|
|
601
|
-
type: 'String',
|
|
602
|
-
validate: /[a-z]/,
|
|
603
|
-
},
|
|
604
|
-
});
|
|
605
|
-
const User = createTestModel({
|
|
606
|
-
profile: Profile.schema,
|
|
607
|
-
});
|
|
608
|
-
const user = new User({
|
|
609
|
-
profile: {
|
|
610
|
-
name: 'foo',
|
|
611
|
-
},
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
expect(user.profile.name).toBe('foo');
|
|
615
|
-
|
|
616
|
-
await expect(async () => {
|
|
617
|
-
user.profile.name = 'FOO';
|
|
618
|
-
await user.save();
|
|
619
|
-
}).rejects.toThrow();
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
it('should not fail on a literal type field', async () => {
|
|
623
|
-
const User = createTestModel({
|
|
624
|
-
location: {
|
|
625
|
-
type: {
|
|
626
|
-
type: 'String',
|
|
627
|
-
default: 'Point',
|
|
628
|
-
},
|
|
629
|
-
coordinates: ['Number'],
|
|
630
|
-
},
|
|
631
|
-
});
|
|
632
|
-
const user = await User.create({});
|
|
633
|
-
expect(user.toObject()).toMatchObject({
|
|
634
|
-
location: {
|
|
635
|
-
type: 'Point',
|
|
636
|
-
},
|
|
637
|
-
});
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
describe('extensions', () => {
|
|
641
|
-
it('should allow required objects', async () => {
|
|
642
|
-
const User = createTestModel({
|
|
643
|
-
profile: {
|
|
644
|
-
type: 'Object',
|
|
645
|
-
required: true,
|
|
646
|
-
attributes: {
|
|
647
|
-
firstName: 'String',
|
|
648
|
-
lastName: 'String',
|
|
649
|
-
},
|
|
650
|
-
},
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
await expect(
|
|
654
|
-
User.create({
|
|
655
|
-
profile: {
|
|
656
|
-
firstName: 'John',
|
|
657
|
-
lastName: 'Doe',
|
|
658
|
-
},
|
|
659
|
-
})
|
|
660
|
-
).resolves.not.toThrow();
|
|
661
|
-
|
|
662
|
-
await expect(User.create({})).rejects.toThrow();
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
it('should set a default on an object type', async () => {
|
|
666
|
-
const User = createTestModel({
|
|
667
|
-
profile: {
|
|
668
|
-
type: 'Object',
|
|
669
|
-
required: true,
|
|
670
|
-
default: {
|
|
671
|
-
firstName: 'John',
|
|
672
|
-
lastName: 'Doe',
|
|
673
|
-
},
|
|
674
|
-
attributes: {
|
|
675
|
-
firstName: 'String',
|
|
676
|
-
lastName: 'String',
|
|
677
|
-
},
|
|
678
|
-
},
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
const user = await User.create({});
|
|
682
|
-
expect(user.profile.firstName).toBe('John');
|
|
683
|
-
expect(user.profile.lastName).toBe('Doe');
|
|
684
|
-
});
|
|
685
|
-
|
|
686
|
-
it('should work with nested extension', async () => {
|
|
687
|
-
const User = createTestModel({
|
|
688
|
-
account: {
|
|
689
|
-
profile: {
|
|
690
|
-
type: 'Object',
|
|
691
|
-
required: true,
|
|
692
|
-
attributes: {
|
|
693
|
-
firstName: 'String',
|
|
694
|
-
lastName: 'String',
|
|
695
|
-
},
|
|
696
|
-
},
|
|
697
|
-
},
|
|
698
|
-
});
|
|
699
|
-
await expect(
|
|
700
|
-
User.create({
|
|
701
|
-
account: {
|
|
702
|
-
profile: {
|
|
703
|
-
firstName: 'John',
|
|
704
|
-
lastName: 'Doe',
|
|
705
|
-
},
|
|
706
|
-
},
|
|
707
|
-
})
|
|
708
|
-
).resolves.not.toThrow();
|
|
709
|
-
|
|
710
|
-
await expect(
|
|
711
|
-
User.create({
|
|
712
|
-
account: {},
|
|
713
|
-
})
|
|
714
|
-
).rejects.toThrow();
|
|
715
|
-
});
|
|
716
|
-
|
|
717
|
-
it('should work with extension in array', async () => {
|
|
718
|
-
const User = createTestModel({
|
|
719
|
-
accounts: [
|
|
720
|
-
{
|
|
721
|
-
profile: {
|
|
722
|
-
type: 'Object',
|
|
723
|
-
required: true,
|
|
724
|
-
attributes: {
|
|
725
|
-
firstName: 'String',
|
|
726
|
-
lastName: 'String',
|
|
727
|
-
},
|
|
728
|
-
},
|
|
729
|
-
},
|
|
730
|
-
],
|
|
731
|
-
});
|
|
732
|
-
await expect(
|
|
733
|
-
User.create({
|
|
734
|
-
accounts: [
|
|
735
|
-
{
|
|
736
|
-
profile: {
|
|
737
|
-
firstName: 'John',
|
|
738
|
-
lastName: 'Doe',
|
|
739
|
-
},
|
|
740
|
-
},
|
|
741
|
-
],
|
|
742
|
-
})
|
|
743
|
-
).resolves.not.toThrow();
|
|
744
|
-
|
|
745
|
-
await expect(
|
|
746
|
-
User.create({
|
|
747
|
-
accounts: [{}],
|
|
748
|
-
})
|
|
749
|
-
).rejects.toThrow();
|
|
750
|
-
});
|
|
751
|
-
|
|
752
|
-
it('should allow a way to disambigate a typedef from fields', async () => {
|
|
753
|
-
const User = createTestModel({
|
|
754
|
-
location: {
|
|
755
|
-
type: 'Object',
|
|
756
|
-
attributes: {
|
|
757
|
-
type: {
|
|
758
|
-
type: 'String',
|
|
759
|
-
},
|
|
760
|
-
},
|
|
761
|
-
default: {
|
|
762
|
-
type: 'Point',
|
|
763
|
-
},
|
|
764
|
-
},
|
|
765
|
-
});
|
|
766
|
-
|
|
767
|
-
const user = await User.create({});
|
|
768
|
-
expect(user.toObject()).toMatchObject({
|
|
769
|
-
location: {
|
|
770
|
-
type: 'Point',
|
|
771
|
-
},
|
|
772
|
-
});
|
|
773
|
-
});
|
|
774
|
-
|
|
775
|
-
it('should apply a scope attributes to all properties', async () => {
|
|
776
|
-
const User = createTestModel({
|
|
777
|
-
private: {
|
|
778
|
-
type: 'Scope',
|
|
779
|
-
readAccess: 'none',
|
|
780
|
-
writeAccess: 'none',
|
|
781
|
-
attributes: {
|
|
782
|
-
firstName: 'String',
|
|
783
|
-
lastName: 'String',
|
|
784
|
-
},
|
|
785
|
-
},
|
|
786
|
-
});
|
|
787
|
-
|
|
788
|
-
expect(User.schema.obj).toMatchObject({
|
|
789
|
-
firstName: {
|
|
790
|
-
type: 'String',
|
|
791
|
-
readAccess: 'none',
|
|
792
|
-
writeAccess: 'none',
|
|
793
|
-
},
|
|
794
|
-
lastName: {
|
|
795
|
-
type: 'String',
|
|
796
|
-
readAccess: 'none',
|
|
797
|
-
writeAccess: 'none',
|
|
798
|
-
},
|
|
799
|
-
});
|
|
800
|
-
});
|
|
801
|
-
});
|
|
802
|
-
});
|
|
803
|
-
|
|
804
|
-
describe('defaults', () => {
|
|
805
|
-
it('should add timestamps by default', async () => {
|
|
806
|
-
const User = createTestModel();
|
|
807
|
-
const user = new User();
|
|
808
|
-
await user.save();
|
|
809
|
-
expect(user.createdAt).toBeInstanceOf(Date);
|
|
810
|
-
expect(user.updatedAt).toBeInstanceOf(Date);
|
|
811
|
-
});
|
|
812
|
-
|
|
813
|
-
it('should add deletedAt by default', async () => {
|
|
814
|
-
const User = createTestModel();
|
|
815
|
-
const user = new User();
|
|
816
|
-
await user.save();
|
|
817
|
-
await user.delete();
|
|
818
|
-
expect(user.deletedAt).toBeInstanceOf(Date);
|
|
819
|
-
});
|
|
820
|
-
});
|
|
821
|
-
|
|
822
|
-
describe('mongoose validation shortcuts', () => {
|
|
823
|
-
it('should validate an email field', async () => {
|
|
824
|
-
let user;
|
|
825
|
-
const User = createTestModel({
|
|
826
|
-
email: {
|
|
827
|
-
type: 'String',
|
|
828
|
-
validate: 'email',
|
|
829
|
-
},
|
|
830
|
-
});
|
|
831
|
-
|
|
832
|
-
await expect(async () => {
|
|
833
|
-
user = new User();
|
|
834
|
-
await user.validate();
|
|
835
|
-
}).not.toThrow();
|
|
836
|
-
|
|
837
|
-
await expect(async () => {
|
|
838
|
-
user = new User({
|
|
839
|
-
email: 'good@email.com',
|
|
840
|
-
});
|
|
841
|
-
await user.validate();
|
|
842
|
-
}).not.toThrow();
|
|
843
|
-
|
|
844
|
-
await expect(async () => {
|
|
845
|
-
// Note that null is expected to error here as it
|
|
846
|
-
// will fail the type validation. To allow "null"
|
|
847
|
-
// to be a JSON readable signal to unset a field
|
|
848
|
-
// in mongoose it must be converted to "undefined".
|
|
849
|
-
// This is part of why the "assign" method exists.
|
|
850
|
-
user = new User({
|
|
851
|
-
email: null,
|
|
852
|
-
});
|
|
853
|
-
await user.validate();
|
|
854
|
-
}).rejects.toThrow(mongoose.Error.ValidationError);
|
|
855
|
-
|
|
856
|
-
await expect(async () => {
|
|
857
|
-
user = new User({
|
|
858
|
-
email: 'bad@email',
|
|
859
|
-
});
|
|
860
|
-
await user.validate();
|
|
861
|
-
}).rejects.toThrow(mongoose.Error.ValidationError);
|
|
862
|
-
});
|
|
863
|
-
|
|
864
|
-
it('should validate a required email field', async () => {
|
|
865
|
-
let user;
|
|
866
|
-
const User = createTestModel({
|
|
867
|
-
email: {
|
|
868
|
-
type: 'String',
|
|
869
|
-
required: true,
|
|
870
|
-
validate: 'email',
|
|
871
|
-
},
|
|
872
|
-
});
|
|
873
|
-
|
|
874
|
-
await expect(async () => {
|
|
875
|
-
user = new User({
|
|
876
|
-
email: 'good@email.com',
|
|
877
|
-
});
|
|
878
|
-
await user.validate();
|
|
879
|
-
}).not.toThrow();
|
|
880
|
-
|
|
881
|
-
await expect(async () => {
|
|
882
|
-
user = new User({
|
|
883
|
-
email: 'bad@email',
|
|
884
|
-
});
|
|
885
|
-
await user.validate();
|
|
886
|
-
}).rejects.toThrow();
|
|
887
|
-
|
|
888
|
-
await expect(async () => {
|
|
889
|
-
user = new User({
|
|
890
|
-
email: '',
|
|
891
|
-
});
|
|
892
|
-
await user.validate();
|
|
893
|
-
}).rejects.toThrow(mongoose.Error.ValidationError);
|
|
894
|
-
});
|
|
895
|
-
|
|
896
|
-
it('should validate a nested email field', async () => {
|
|
897
|
-
const User = createTestModel({
|
|
898
|
-
emails: [
|
|
899
|
-
{
|
|
900
|
-
type: 'String',
|
|
901
|
-
validate: 'email',
|
|
902
|
-
},
|
|
903
|
-
],
|
|
904
|
-
});
|
|
905
|
-
|
|
906
|
-
await expect(
|
|
907
|
-
User.create({
|
|
908
|
-
emails: ['good@email.com'],
|
|
909
|
-
})
|
|
910
|
-
).resolves.not.toThrow();
|
|
911
|
-
|
|
912
|
-
await expect(
|
|
913
|
-
User.create({
|
|
914
|
-
emails: ['bad@email'],
|
|
915
|
-
})
|
|
916
|
-
).rejects.toThrow();
|
|
917
|
-
});
|
|
918
|
-
});
|
|
919
|
-
});
|