1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 | const jdb = require('../');
|
8 | const DataSource = jdb.DataSource;
|
9 | const path = require('path');
|
10 | const fs = require('fs');
|
11 | const assert = require('assert');
|
12 | const async = require('async');
|
13 | const should = require('./init.js');
|
14 | const Memory = require('../lib/connectors/memory').Memory;
|
15 |
|
16 | describe('Memory connector', function() {
|
17 | const file = path.join(__dirname, 'memory.json');
|
18 |
|
19 | function readModels(done) {
|
20 | fs.readFile(file, function(err, data) {
|
21 | const json = JSON.parse(data.toString());
|
22 | assert(json.models);
|
23 | assert(json.ids.User);
|
24 | done(err, json);
|
25 | });
|
26 | }
|
27 |
|
28 | before(function(done) {
|
29 | fs.unlink(file, function(err) {
|
30 | if (!err || err.code === 'ENOENT') {
|
31 | done();
|
32 | }
|
33 | });
|
34 | });
|
35 |
|
36 | describe('with file', function() {
|
37 | let ds;
|
38 |
|
39 | function createUserModel() {
|
40 | const ds = new DataSource({
|
41 | connector: 'memory',
|
42 | file: file,
|
43 | });
|
44 |
|
45 | const User = ds.createModel('User', {
|
46 | id: {
|
47 | type: Number,
|
48 | id: true,
|
49 | generated: true,
|
50 | },
|
51 | name: String,
|
52 | bio: String,
|
53 | approved: Boolean,
|
54 | joinedAt: Date,
|
55 | age: Number,
|
56 | });
|
57 | return User;
|
58 | }
|
59 |
|
60 | let User;
|
61 | const ids = [];
|
62 |
|
63 | before(function() {
|
64 | User = createUserModel();
|
65 | ds = User.dataSource;
|
66 | });
|
67 |
|
68 | it('should allow multiple connects', function(done) {
|
69 | ds.connected = false;
|
70 | async.times(10, function(n, next) {
|
71 | ds.connect(next);
|
72 | }, done);
|
73 | });
|
74 |
|
75 | it('should persist create', function(done) {
|
76 | let count = 0;
|
77 | async.eachSeries(['John1', 'John2', 'John3'], function(item, cb) {
|
78 | User.create({name: item}, function(err, result) {
|
79 | ids.push(result.id);
|
80 | count++;
|
81 | readModels(function(err, json) {
|
82 | assert.equal(Object.keys(json.models.User).length, count);
|
83 | cb(err);
|
84 | });
|
85 | });
|
86 | }, done);
|
87 | });
|
88 |
|
89 | |
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 | it('should not have out of sequence read/write', function(done) {
|
96 |
|
97 |
|
98 | const User = createUserModel();
|
99 | const ds = User.dataSource;
|
100 |
|
101 | async.times(10, function(n, next) {
|
102 | if (n === 10) {
|
103 |
|
104 | return ds.connect(next);
|
105 | }
|
106 | ds.connect();
|
107 | next();
|
108 | }, function(err) {
|
109 | async.eachSeries(['John4', 'John5'], function(item, cb) {
|
110 | const count = 0;
|
111 | User.create({name: item}, function(err, result) {
|
112 | ids.push(result.id);
|
113 | cb(err);
|
114 | });
|
115 | }, function(err) {
|
116 | if (err) return done(err);
|
117 | readModels(function(err, json) {
|
118 | assert.equal(Object.keys(json.models.User).length, 5);
|
119 | done();
|
120 | });
|
121 | });
|
122 | });
|
123 | });
|
124 |
|
125 | it('should persist delete', function(done) {
|
126 |
|
127 |
|
128 | ds.disconnect(function() {
|
129 |
|
130 | User.deleteById(ids[0], function(err) {
|
131 | if (err) {
|
132 | return done(err);
|
133 | }
|
134 | readModels(function(err, json) {
|
135 | if (err) {
|
136 | return done(err);
|
137 | }
|
138 | assert.equal(Object.keys(json.models.User).length, 4);
|
139 | done();
|
140 | });
|
141 | });
|
142 | });
|
143 | });
|
144 |
|
145 | it('should persist upsert', function(done) {
|
146 | User.upsert({id: ids[1], name: 'John'}, function(err, result) {
|
147 | if (err) {
|
148 | return done(err);
|
149 | }
|
150 | readModels(function(err, json) {
|
151 | if (err) {
|
152 | return done(err);
|
153 | }
|
154 | assert.equal(Object.keys(json.models.User).length, 4);
|
155 | const user = JSON.parse(json.models.User[ids[1]]);
|
156 | assert.equal(user.name, 'John');
|
157 | assert(user.id === ids[1]);
|
158 | done();
|
159 | });
|
160 | });
|
161 | });
|
162 |
|
163 | it('should persist update', function(done) {
|
164 | User.update({id: ids[1]}, {name: 'John1'},
|
165 | function(err, result) {
|
166 | if (err) {
|
167 | return done(err);
|
168 | }
|
169 | readModels(function(err, json) {
|
170 | if (err) {
|
171 | return done(err);
|
172 | }
|
173 | assert.equal(Object.keys(json.models.User).length, 4);
|
174 | const user = JSON.parse(json.models.User[ids[1]]);
|
175 | assert.equal(user.name, 'John1');
|
176 | assert(user.id === ids[1]);
|
177 | done();
|
178 | });
|
179 | });
|
180 | });
|
181 |
|
182 |
|
183 | it('should load from the json file', function(done) {
|
184 | User.find(function(err, users) {
|
185 |
|
186 | assert.equal(users.length, 4);
|
187 | done(err);
|
188 | });
|
189 | });
|
190 | });
|
191 |
|
192 | describe('Query for memory connector', function() {
|
193 | const ds = new DataSource({
|
194 | connector: 'memory',
|
195 | });
|
196 |
|
197 | const User = ds.define('User', {
|
198 | seq: {type: Number, index: true},
|
199 | name: {type: String, index: true, sort: true},
|
200 | email: {type: String, index: true},
|
201 | birthday: {type: Date, index: true},
|
202 | role: {type: String, index: true},
|
203 | order: {type: Number, index: true, sort: true},
|
204 | tag: {type: String, index: true},
|
205 | vip: {type: Boolean},
|
206 | address: {
|
207 | street: String,
|
208 | city: String,
|
209 | state: String,
|
210 | zipCode: String,
|
211 | tags: [
|
212 | {
|
213 | tag: String,
|
214 | },
|
215 | ],
|
216 | },
|
217 | friends: [
|
218 | {
|
219 | name: String,
|
220 | },
|
221 | ],
|
222 | });
|
223 |
|
224 | before(seed);
|
225 | it('should allow to find using like', function(done) {
|
226 | User.find({where: {name: {like: '%St%'}}}, function(err, posts) {
|
227 | should.not.exist(err);
|
228 | posts.should.have.property('length', 2);
|
229 | done();
|
230 | });
|
231 | });
|
232 |
|
233 | it('should properly sanitize like invalid query', async () => {
|
234 | const users = await User.find({where: {tag: {like: '['}}});
|
235 | users.should.have.length(1);
|
236 | users[0].should.have.property('name', 'John Lennon');
|
237 | });
|
238 |
|
239 | it('should allow to find using like with regexp', function(done) {
|
240 | User.find({where: {name: {like: /.*St.*/}}}, function(err, posts) {
|
241 | should.not.exist(err);
|
242 | posts.should.have.property('length', 2);
|
243 | done();
|
244 | });
|
245 | });
|
246 |
|
247 | it('should support like for no match', function(done) {
|
248 | User.find({where: {name: {like: 'M%XY'}}}, function(err, posts) {
|
249 | should.not.exist(err);
|
250 | posts.should.have.property('length', 0);
|
251 | done();
|
252 | });
|
253 | });
|
254 |
|
255 | it('should allow to find using nlike', function(done) {
|
256 | User.find({where: {name: {nlike: '%St%'}}}, function(err, posts) {
|
257 | should.not.exist(err);
|
258 | posts.should.have.property('length', 4);
|
259 | done();
|
260 | });
|
261 | });
|
262 |
|
263 | it('should sanitize nlike invalid query', async () => {
|
264 | const users = await User.find({where: {name: {nlike: '['}}});
|
265 | users.should.have.length(6);
|
266 | });
|
267 |
|
268 | it('should allow to find using nlike with regexp', function(done) {
|
269 | User.find({where: {name: {nlike: /.*St.*/}}}, function(err, posts) {
|
270 | should.not.exist(err);
|
271 | posts.should.have.property('length', 4);
|
272 | done();
|
273 | });
|
274 | });
|
275 |
|
276 | it('should support nlike for no match', function(done) {
|
277 | User.find({where: {name: {nlike: 'M%XY'}}}, function(err, posts) {
|
278 | should.not.exist(err);
|
279 | posts.should.have.property('length', 6);
|
280 | done();
|
281 | });
|
282 | });
|
283 |
|
284 | it('should throw if the like value is not string or regexp', function(done) {
|
285 | User.find({where: {name: {like: 123}}}, function(err, posts) {
|
286 | should.exist(err);
|
287 | done();
|
288 | });
|
289 | });
|
290 |
|
291 | it('should throw if the nlike value is not string or regexp', function(done) {
|
292 | User.find({where: {name: {nlike: 123}}}, function(err, posts) {
|
293 | should.exist(err);
|
294 | done();
|
295 | });
|
296 | });
|
297 |
|
298 | it('should throw if the inq value is not an array', function(done) {
|
299 | User.find({where: {name: {inq: '12'}}}, function(err, posts) {
|
300 | should.exist(err);
|
301 | done();
|
302 | });
|
303 | });
|
304 |
|
305 | it('should throw if the nin value is not an array', function(done) {
|
306 | User.find({where: {name: {nin: '12'}}}, function(err, posts) {
|
307 | should.exist(err);
|
308 | done();
|
309 | });
|
310 | });
|
311 |
|
312 | it('should throw if the between value is not an array', function(done) {
|
313 | User.find({where: {name: {between: '12'}}}, function(err, posts) {
|
314 | should.exist(err);
|
315 | done();
|
316 | });
|
317 | });
|
318 |
|
319 | it('should throw if the between value is not an array of length 2', function(done) {
|
320 | User.find({where: {name: {between: ['12']}}}, function(err, posts) {
|
321 | should.exist(err);
|
322 | done();
|
323 | });
|
324 | });
|
325 |
|
326 | it('should successfully extract 5 users from the db', function(done) {
|
327 | User.find({where: {seq: {between: [1, 5]}}}, function(err, users) {
|
328 | should(users.length).be.equal(5);
|
329 | done();
|
330 | });
|
331 | });
|
332 |
|
333 | it('should successfully extract 1 user (Lennon) from the db', function(done) {
|
334 | User.find({where: {birthday: {between: [new Date(1970, 0), new Date(1990, 0)]}}},
|
335 | function(err, users) {
|
336 | should(users.length).be.equal(1);
|
337 | should(users[0].name).be.equal('John Lennon');
|
338 | done();
|
339 | });
|
340 | });
|
341 |
|
342 | it('should successfully extract 2 users from the db', function(done) {
|
343 | User.find({where: {birthday: {between: [new Date(1940, 0), new Date(1990, 0)]}}},
|
344 | function(err, users) {
|
345 | should(users.length).be.equal(2);
|
346 | done();
|
347 | });
|
348 | });
|
349 |
|
350 | it('should successfully extract 2 users using implied and', function(done) {
|
351 | User.find({where: {role: 'lead', vip: true}}, function(err, users) {
|
352 | should(users.length).be.equal(2);
|
353 | should(users[0].name).be.equal('John Lennon');
|
354 | should(users[1].name).be.equal('Paul McCartney');
|
355 | done();
|
356 | });
|
357 | });
|
358 |
|
359 | it('should successfully extract 2 users using implied and & and', function(done) {
|
360 | User.find({where: {
|
361 | name: 'John Lennon',
|
362 | and: [{role: 'lead'}, {vip: true}],
|
363 | }}, function(err, users) {
|
364 | should(users.length).be.equal(1);
|
365 | should(users[0].name).be.equal('John Lennon');
|
366 | done();
|
367 | });
|
368 | });
|
369 |
|
370 | it('should successfully extract 2 users using date range', function(done) {
|
371 | User.find({where: {birthday: {between:
|
372 | [new Date(1940, 0).toISOString(), new Date(1990, 0).toISOString()]}}},
|
373 | function(err, users) {
|
374 | should(users.length).be.equal(2);
|
375 | done();
|
376 | });
|
377 | });
|
378 |
|
379 | it('should successfully extract 0 user from the db', function(done) {
|
380 | User.find({where: {birthday: {between: [new Date(1990, 0), Date.now()]}}},
|
381 | function(err, users) {
|
382 | should(users.length).be.equal(0);
|
383 | done();
|
384 | });
|
385 | });
|
386 |
|
387 | it('should successfully extract 2 users matching over array values', function(done) {
|
388 | User.find({
|
389 | where: {
|
390 | children: {
|
391 | regexp: /an/,
|
392 | },
|
393 | },
|
394 | }, function(err, users) {
|
395 | should.not.exist(err);
|
396 | users.length.should.be.equal(2);
|
397 | users[0].name.should.be.equal('John Lennon');
|
398 | users[1].name.should.be.equal('George Harrison');
|
399 | done();
|
400 | });
|
401 | });
|
402 |
|
403 | it('should successfully extract 1 users matching over array values', function(done) {
|
404 | User.find({
|
405 | where: {
|
406 | children: 'Dhani',
|
407 | },
|
408 | }, function(err, users) {
|
409 | should.not.exist(err);
|
410 | users.length.should.be.equal(1);
|
411 | users[0].name.should.be.equal('George Harrison');
|
412 | done();
|
413 | });
|
414 | });
|
415 |
|
416 | it('should successfully extract 5 users matching a neq filter over array values', function(done) {
|
417 | User.find({
|
418 | where: {
|
419 | children: {neq: 'Dhani'},
|
420 | },
|
421 | }, function(err, users) {
|
422 | should.not.exist(err);
|
423 | users.length.should.be.equal(5);
|
424 | done();
|
425 | });
|
426 | });
|
427 |
|
428 | it('should successfully extract 3 users with inq', function(done) {
|
429 | User.find({
|
430 | where: {seq: {inq: [0, 1, 5]}},
|
431 | }, function(err, users) {
|
432 | should.not.exist(err);
|
433 | users.length.should.be.equal(3);
|
434 | done();
|
435 | });
|
436 | });
|
437 |
|
438 | it('should successfully extract 4 users with nin', function(done) {
|
439 | User.find({
|
440 | where: {seq: {nin: [2, 3]}},
|
441 | }, function(err, users) {
|
442 | should.not.exist(err);
|
443 | users.length.should.be.equal(4);
|
444 | done();
|
445 | });
|
446 | });
|
447 |
|
448 | it('should count using date string', function(done) {
|
449 | User.count({birthday: {lt: new Date(1990, 0).toISOString()}},
|
450 | function(err, count) {
|
451 | should(count).be.equal(2);
|
452 | done();
|
453 | });
|
454 | });
|
455 |
|
456 | it('should support order with multiple fields', function(done) {
|
457 | User.find({order: 'vip ASC, seq DESC'}, function(err, posts) {
|
458 | should.not.exist(err);
|
459 | posts[0].seq.should.be.eql(4);
|
460 | posts[1].seq.should.be.eql(3);
|
461 | done();
|
462 | });
|
463 | });
|
464 |
|
465 | it('should sort undefined values to the end when ordered DESC', function(done) {
|
466 | User.find({order: 'vip ASC, order DESC'}, function(err, posts) {
|
467 | should.not.exist(err);
|
468 |
|
469 | posts[4].seq.should.be.eql(1);
|
470 | posts[5].seq.should.be.eql(0);
|
471 | done();
|
472 | });
|
473 | });
|
474 |
|
475 | it('should throw if order has wrong direction', function(done) {
|
476 | User.find({order: 'seq ABC'}, function(err, posts) {
|
477 | should.exist(err);
|
478 | done();
|
479 | });
|
480 | });
|
481 |
|
482 | it('should support neq operator for number', function(done) {
|
483 | User.find({where: {seq: {neq: 4}}}, function(err, users) {
|
484 | should.not.exist(err);
|
485 | users.length.should.be.equal(5);
|
486 | for (let i = 0; i < users.length; i++) {
|
487 | users[i].seq.should.not.be.equal(4);
|
488 | }
|
489 | done();
|
490 | });
|
491 | });
|
492 |
|
493 | it('should support neq operator for string', function(done) {
|
494 | User.find({where: {role: {neq: 'lead'}}}, function(err, users) {
|
495 | should.not.exist(err);
|
496 | users.length.should.be.equal(4);
|
497 | for (let i = 0; i < users.length; i++) {
|
498 | if (users[i].role) {
|
499 | users[i].role.not.be.equal('lead');
|
500 | }
|
501 | }
|
502 | done();
|
503 | });
|
504 | });
|
505 |
|
506 | it('should support neq operator for null', function(done) {
|
507 | User.find({where: {role: {neq: null}}}, function(err, users) {
|
508 | should.not.exist(err);
|
509 | users.length.should.be.equal(2);
|
510 | for (let i = 0; i < users.length; i++) {
|
511 | should.exist(users[i].role);
|
512 | }
|
513 | done();
|
514 | });
|
515 | });
|
516 |
|
517 | it('should work when a regex is provided without the regexp operator',
|
518 | function(done) {
|
519 | User.find({where: {name: /John.*/i}}, function(err, users) {
|
520 | should.not.exist(err);
|
521 | users.length.should.equal(1);
|
522 | users[0].name.should.equal('John Lennon');
|
523 | done();
|
524 | });
|
525 | });
|
526 |
|
527 | it('should support the regexp operator with regex strings', function(done) {
|
528 | User.find({where: {name: {regexp: 'non$'}}}, function(err, users) {
|
529 | should.not.exist(err);
|
530 | users.length.should.equal(1);
|
531 | users[0].name.should.equal('John Lennon');
|
532 | done();
|
533 | });
|
534 | });
|
535 |
|
536 | it('should support the regexp operator with regex literals', function(done) {
|
537 | User.find({where: {name: {regexp: /^J/}}}, function(err, users) {
|
538 | should.not.exist(err);
|
539 | users.length.should.equal(1);
|
540 | users[0].name.should.equal('John Lennon');
|
541 | done();
|
542 | });
|
543 | });
|
544 |
|
545 | it('should support the regexp operator with regex objects', function(done) {
|
546 | User.find({where: {name: {regexp: new RegExp(/^J/)}}}, function(err,
|
547 | users) {
|
548 | should.not.exist(err);
|
549 | users.length.should.equal(1);
|
550 | users[0].name.should.equal('John Lennon');
|
551 | done();
|
552 | });
|
553 | });
|
554 |
|
555 | it('should deserialize values after saving in upsert', function(done) {
|
556 | User.findOne({where: {seq: 1}}, function(err, paul) {
|
557 | User.updateOrCreate({id: paul.id, name: 'Sir Paul McCartney'},
|
558 | function(err, sirpaul) {
|
559 | should.not.exist(err);
|
560 | sirpaul.birthday.should.be.instanceOf(Date);
|
561 | sirpaul.order.should.be.instanceOf(Number);
|
562 | sirpaul.vip.should.be.instanceOf(Boolean);
|
563 | done();
|
564 | });
|
565 | });
|
566 | });
|
567 |
|
568 | function seed(done) {
|
569 | const beatles = [
|
570 | {
|
571 | seq: 0,
|
572 | name: 'John Lennon',
|
573 | email: 'john@b3atl3s.co.uk',
|
574 | role: 'lead',
|
575 | birthday: new Date('1980-12-08'),
|
576 | vip: true,
|
577 | tag: '[singer]',
|
578 | address: {
|
579 | street: '123 A St',
|
580 | city: 'San Jose',
|
581 | state: 'CA',
|
582 | zipCode: '95131',
|
583 | tags: [
|
584 | {tag: 'business'},
|
585 | {tag: 'rent'},
|
586 | ],
|
587 | },
|
588 | friends: [
|
589 | {name: 'Paul McCartney'},
|
590 | {name: 'George Harrison'},
|
591 | {name: 'Ringo Starr'},
|
592 | ],
|
593 | children: ['Sean', 'Julian'],
|
594 | },
|
595 | {
|
596 | seq: 1,
|
597 | name: 'Paul McCartney',
|
598 | email: 'paul@b3atl3s.co.uk',
|
599 | role: 'lead',
|
600 | birthday: new Date('1942-06-18'),
|
601 | order: 1,
|
602 | vip: true,
|
603 | address: {
|
604 | street: '456 B St',
|
605 | city: 'San Mateo',
|
606 | state: 'CA',
|
607 | zipCode: '94065',
|
608 | },
|
609 | friends: [
|
610 | {name: 'John Lennon'},
|
611 | {name: 'George Harrison'},
|
612 | {name: 'Ringo Starr'},
|
613 | ],
|
614 | children: ['Stella', 'Mary', 'Heather', 'Beatrice', 'James'],
|
615 | },
|
616 | {seq: 2, name: 'George Harrison', order: 5, vip: false, children: ['Dhani']},
|
617 | {seq: 3, name: 'Ringo Starr', order: 6, vip: false},
|
618 | {seq: 4, name: 'Pete Best', order: 4, children: []},
|
619 | {seq: 5, name: 'Stuart Sutcliffe', order: 3, vip: true},
|
620 | ];
|
621 |
|
622 | async.series([
|
623 | User.destroyAll.bind(User),
|
624 | function(cb) {
|
625 | async.each(beatles, User.create.bind(User), cb);
|
626 | },
|
627 | ], done);
|
628 | }
|
629 | });
|
630 |
|
631 | it('should use collection setting', function(done) {
|
632 | const ds = new DataSource({
|
633 | connector: 'memory',
|
634 | });
|
635 |
|
636 | const Product = ds.createModel('Product', {
|
637 | name: String,
|
638 | });
|
639 |
|
640 | const Tool = ds.createModel('Tool', {
|
641 | name: String,
|
642 | }, {memory: {collection: 'Product'}});
|
643 |
|
644 | const Widget = ds.createModel('Widget', {
|
645 | name: String,
|
646 | }, {memory: {collection: 'Product'}});
|
647 |
|
648 | ds.connector.getCollection('Tool').should.equal('Product');
|
649 | ds.connector.getCollection('Widget').should.equal('Product');
|
650 |
|
651 | async.series([
|
652 | function(next) {
|
653 | Tool.create({name: 'Tool A'}, next);
|
654 | },
|
655 | function(next) {
|
656 | Tool.create({name: 'Tool B'}, next);
|
657 | },
|
658 | function(next) {
|
659 | Widget.create({name: 'Widget A'}, next);
|
660 | },
|
661 | ], function(err) {
|
662 | Product.find(function(err, products) {
|
663 | should.not.exist(err);
|
664 | products.should.have.length(3);
|
665 | products[0].toObject().should.eql({name: 'Tool A', id: 1});
|
666 | products[1].toObject().should.eql({name: 'Tool B', id: 2});
|
667 | products[2].toObject().should.eql({name: 'Widget A', id: 3});
|
668 | done();
|
669 | });
|
670 | });
|
671 | });
|
672 |
|
673 | it('should refuse to create object with duplicate id', function(done) {
|
674 | const ds = new DataSource({connector: 'memory'});
|
675 | const Product = ds.define('ProductTest', {name: String}, {forceId: false});
|
676 | ds.automigrate('ProductTest', function(err) {
|
677 | if (err) return done(err);
|
678 |
|
679 | Product.create({name: 'a-name'}, function(err, p) {
|
680 | if (err) return done(err);
|
681 | Product.create({id: p.id, name: 'duplicate'}, function(err) {
|
682 | if (!err) {
|
683 | return done(new Error('Create should have rejected duplicate id.'));
|
684 | }
|
685 | err.message.should.match(/duplicate/i);
|
686 | err.statusCode.should.equal(409);
|
687 | done();
|
688 | });
|
689 | });
|
690 | });
|
691 | });
|
692 |
|
693 | describe('automigrate', function() {
|
694 | let ds;
|
695 | beforeEach(function() {
|
696 | ds = new DataSource({
|
697 | connector: 'memory',
|
698 | });
|
699 |
|
700 | ds.createModel('m1', {
|
701 | name: String,
|
702 | });
|
703 | });
|
704 |
|
705 | it('automigrate all models', function(done) {
|
706 | ds.automigrate(function(err) {
|
707 | done(err);
|
708 | });
|
709 | });
|
710 |
|
711 | it('automigrate all models - promise variant', function(done) {
|
712 | ds.automigrate()
|
713 | .then(function(result) {
|
714 | done();
|
715 | })
|
716 | .catch(function(err) {
|
717 | done(err);
|
718 | });
|
719 | });
|
720 |
|
721 | it('automigrate one model', function(done) {
|
722 | ds.automigrate('m1', function(err) {
|
723 | done(err);
|
724 | });
|
725 | });
|
726 |
|
727 | it('automigrate one model - promise variant', function(done) {
|
728 | ds.automigrate('m1')
|
729 | .then(function(result) {
|
730 | done();
|
731 | })
|
732 | .catch(function(err) {
|
733 | done(err);
|
734 | });
|
735 | });
|
736 |
|
737 | it('automigrate one or more models in an array', function(done) {
|
738 | ds.automigrate(['m1'], function(err) {
|
739 | done(err);
|
740 | });
|
741 | });
|
742 |
|
743 | it('automigrate one or more models in an array - promise variant', function(done) {
|
744 | ds.automigrate(['m1'])
|
745 | .then(function(result) {
|
746 | done();
|
747 | })
|
748 | .catch(function(err) {
|
749 | done(err);
|
750 | });
|
751 | });
|
752 |
|
753 | it('automigrate reports errors for models not attached', function(done) {
|
754 | ds.automigrate(['m1', 'm2'], function(err) {
|
755 | err.should.be.an.instanceOf(Error);
|
756 | done();
|
757 | });
|
758 | });
|
759 |
|
760 | it('automigrate reports errors for models not attached - promise variant', function(done) {
|
761 | ds.automigrate(['m1', 'm2'])
|
762 | .then(function() {
|
763 | done(new Error('automigrate() should have failed'));
|
764 | })
|
765 | .catch(function(err) {
|
766 | err.should.be.an.instanceOf(Error);
|
767 | done();
|
768 | });
|
769 | });
|
770 | });
|
771 |
|
772 | describe('findOrCreate', function() {
|
773 | let ds, Cars;
|
774 | before(function() {
|
775 | ds = new DataSource({connector: 'memory'});
|
776 | Cars = ds.define('Cars', {
|
777 | color: String,
|
778 | });
|
779 | });
|
780 |
|
781 | it('should create a specific object once and in the subsequent calls it should find it', function(done) {
|
782 | let creationNum = 0;
|
783 | async.times(100, function(n, next) {
|
784 | const initialData = {color: 'white'};
|
785 | const query = {'where': initialData};
|
786 | Cars.findOrCreate(query, initialData, function(err, car, created) {
|
787 | if (created) creationNum++;
|
788 | next(err, car);
|
789 | });
|
790 | }, function(err, cars) {
|
791 | if (err) done(err);
|
792 | Cars.find(function(err, data) {
|
793 | if (err) done(err);
|
794 | data.length.should.equal(1);
|
795 | data[0].color.should.equal('white');
|
796 | creationNum.should.equal(1);
|
797 | done();
|
798 | });
|
799 | });
|
800 | });
|
801 | });
|
802 |
|
803 | describe('automigrate when NO models are attached', function() {
|
804 | let ds;
|
805 | beforeEach(function() {
|
806 | ds = new DataSource({
|
807 | connector: 'memory',
|
808 | });
|
809 | });
|
810 |
|
811 | it('automigrate does NOT report error when NO models are attached', function(done) {
|
812 | ds.automigrate(function(err) {
|
813 | done();
|
814 | });
|
815 | });
|
816 |
|
817 | it('automigrate does NOT report error when NO models are attached - promise variant', function(done) {
|
818 | ds.automigrate()
|
819 | .then(done)
|
820 | .catch(function(err) {
|
821 | done(err);
|
822 | });
|
823 | });
|
824 | });
|
825 |
|
826 | describe('With mocked autoupdate', function() {
|
827 | let ds, model;
|
828 | beforeEach(function() {
|
829 | ds = new DataSource({
|
830 | connector: 'memory',
|
831 | });
|
832 |
|
833 | ds.connector.autoupdate = function(models, cb) {
|
834 | process.nextTick(cb);
|
835 | };
|
836 |
|
837 | model = ds.createModel('m1', {
|
838 | name: String,
|
839 | });
|
840 |
|
841 | ds.automigrate();
|
842 |
|
843 | ds.createModel('m1', {
|
844 | name: String,
|
845 | address: String,
|
846 | });
|
847 | });
|
848 |
|
849 | it('autoupdates all models', function(done) {
|
850 | ds.autoupdate(function(err, result) {
|
851 | done(err);
|
852 | });
|
853 | });
|
854 |
|
855 | it('autoupdates all models - promise variant', function(done) {
|
856 | ds.autoupdate()
|
857 | .then(function(result) {
|
858 | done();
|
859 | })
|
860 | .catch(function(err) {
|
861 | done(err);
|
862 | });
|
863 | });
|
864 |
|
865 | it('autoupdates one model', function(done) {
|
866 | ds.autoupdate('m1', function(err) {
|
867 | done(err);
|
868 | });
|
869 | });
|
870 |
|
871 | it('autoupdates one model - promise variant', function(done) {
|
872 | ds.autoupdate('m1')
|
873 | .then(function(result) {
|
874 | done();
|
875 | })
|
876 | .catch(function(err) {
|
877 | done(err);
|
878 | });
|
879 | });
|
880 |
|
881 | it('autoupdates one or more models in an array', function(done) {
|
882 | ds.autoupdate(['m1'], function(err) {
|
883 | done(err);
|
884 | });
|
885 | });
|
886 |
|
887 | it('autoupdates one or more models in an array - promise variant', function(done) {
|
888 | ds.autoupdate(['m1'])
|
889 | .then(function(result) {
|
890 | done();
|
891 | })
|
892 | .catch(function(err) {
|
893 | done(err);
|
894 | });
|
895 | });
|
896 |
|
897 | it('autoupdate reports errors for models not attached', function(done) {
|
898 | ds.autoupdate(['m1', 'm2'], function(err) {
|
899 | err.should.be.an.instanceOf(Error);
|
900 | done();
|
901 | });
|
902 | });
|
903 |
|
904 | it('autoupdate reports errors for models not attached - promise variant', function(done) {
|
905 | ds.autoupdate(['m1', 'm2'])
|
906 | .then(function() {
|
907 | done(new Error('automigrate() should have failed'));
|
908 | })
|
909 | .catch(function(err) {
|
910 | err.should.be.an.instanceOf(Error);
|
911 | done();
|
912 | });
|
913 | });
|
914 | });
|
915 | });
|
916 |
|
917 | describe('Optimized connector', function() {
|
918 | const ds = new DataSource({connector: Memory});
|
919 |
|
920 | require('./persistence-hooks.suite')(ds, should, {
|
921 | replaceOrCreateReportsNewInstance: true,
|
922 | });
|
923 | });
|
924 |
|
925 | describe('Unoptimized connector', function() {
|
926 | const ds = new DataSource({connector: Memory});
|
927 |
|
928 |
|
929 | ds.connector.updateOrCreate = false;
|
930 | ds.connector.findOrCreate = false;
|
931 | ds.connector.upsertWithWhere = false;
|
932 |
|
933 |
|
934 | ds.connector.buildNearFilter = false;
|
935 |
|
936 | require('./persistence-hooks.suite')(ds, should, {
|
937 | replaceOrCreateReportsNewInstance: true,
|
938 | });
|
939 | });
|
940 |
|
941 | describe('Memory connector with options', function() {
|
942 | const savedOptions = {};
|
943 | let ds, Post;
|
944 |
|
945 | before(function() {
|
946 | ds = new DataSource({connector: 'memory'});
|
947 | ds.connector.create = function(model, data, options, cb) {
|
948 | savedOptions.create = options;
|
949 | process.nextTick(function() {
|
950 | cb(null, 1);
|
951 | });
|
952 | };
|
953 |
|
954 | ds.connector.update = function(model, where, data, options, cb) {
|
955 | savedOptions.update = options;
|
956 | process.nextTick(function() {
|
957 | cb(null, {count: 1});
|
958 | });
|
959 | };
|
960 |
|
961 | ds.connector.all = function(model, filter, options, cb) {
|
962 | savedOptions.find = options;
|
963 | process.nextTick(function() {
|
964 | cb(null, [{title: 't1', content: 'c1'}]);
|
965 | });
|
966 | };
|
967 |
|
968 | Post = ds.define('Post', {
|
969 | title: String,
|
970 | content: String,
|
971 | });
|
972 | });
|
973 |
|
974 | it('should receive options from the find method', function(done) {
|
975 | const opts = {transaction: 'tx1'};
|
976 | Post.find({where: {title: 't1'}}, opts, function(err, p) {
|
977 | savedOptions.find.should.be.eql(opts);
|
978 | done(err);
|
979 | });
|
980 | });
|
981 |
|
982 | it('should treat first object arg as filter for find', function(done) {
|
983 | const filter = {title: 't1'};
|
984 | Post.find(filter, function(err, p) {
|
985 | savedOptions.find.should.be.eql({});
|
986 | done(err);
|
987 | });
|
988 | });
|
989 |
|
990 | it('should receive options from the create method', function(done) {
|
991 | const opts = {transaction: 'tx3'};
|
992 | Post.create({title: 't1', content: 'c1'}, opts, function(err, p) {
|
993 | savedOptions.create.should.be.eql(opts);
|
994 | done(err);
|
995 | });
|
996 | });
|
997 |
|
998 | it('should receive options from the update method', function(done) {
|
999 | const opts = {transaction: 'tx4'};
|
1000 | Post.update({title: 't1'}, {content: 'c1 --> c2'},
|
1001 | opts, function(err, p) {
|
1002 | savedOptions.update.should.be.eql(opts);
|
1003 | done(err);
|
1004 | });
|
1005 | });
|
1006 | });
|
1007 |
|
1008 | describe('Memory connector with observers', function() {
|
1009 | const ds = new DataSource({
|
1010 | connector: 'memory',
|
1011 | });
|
1012 |
|
1013 | it('should have observer mixed into the connector', function() {
|
1014 | ds.connector.observe.should.be.a.function;
|
1015 | ds.connector.notifyObserversOf.should.be.a.function;
|
1016 | });
|
1017 |
|
1018 | it('should notify observers', function(done) {
|
1019 | const events = [];
|
1020 | ds.connector.execute = function(command, params, options, cb) {
|
1021 | const self = this;
|
1022 | const context = {command: command, params: params, options: options};
|
1023 | self.notifyObserversOf('before execute', context, function(err) {
|
1024 | process.nextTick(function() {
|
1025 | if (err) return cb(err);
|
1026 | events.push('execute');
|
1027 | self.notifyObserversOf('after execute', context, function(err) {
|
1028 | cb(err);
|
1029 | });
|
1030 | });
|
1031 | });
|
1032 | };
|
1033 |
|
1034 | ds.connector.observe('before execute', function(context, next) {
|
1035 | events.push('before execute');
|
1036 | next();
|
1037 | });
|
1038 |
|
1039 | ds.connector.observe('after execute', function(context, next) {
|
1040 | events.push('after execute');
|
1041 | next();
|
1042 | });
|
1043 |
|
1044 | ds.connector.execute('test', [1, 2], {x: 2}, function(err) {
|
1045 | if (err) return done(err);
|
1046 | events.should.eql(['before execute', 'execute', 'after execute']);
|
1047 | done();
|
1048 | });
|
1049 | });
|
1050 | });
|