1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 | const Schema = require('../index').Schema;
|
9 | const Text = Schema.Text;
|
10 |
|
11 | let nbSchemaRequests = 0;
|
12 |
|
13 | let batch;
|
14 | let schemaName;
|
15 |
|
16 | function it(name, cases) {
|
17 | batch[schemaName][name] = cases;
|
18 | }
|
19 |
|
20 | function skip(name) {
|
21 | delete batch[schemaName][name];
|
22 | }
|
23 |
|
24 | module.exports = function testSchema(exportCasesHere, dataSource) {
|
25 | batch = exportCasesHere;
|
26 | schemaName = dataSource.name;
|
27 | if (dataSource.name.match(/^\/.*\/test\/\.\.$/)) {
|
28 | schemaName = schemaName.split('/').slice(-3).shift();
|
29 | }
|
30 | let start;
|
31 |
|
32 | batch['should connect to database'] = function(test) {
|
33 | start = Date.now();
|
34 | if (dataSource.connected) return test.done();
|
35 | dataSource.on('connected', test.done);
|
36 | };
|
37 |
|
38 | dataSource.log = function(a) {
|
39 | console.log(a);
|
40 | nbSchemaRequests++;
|
41 | };
|
42 |
|
43 | batch[schemaName] = {};
|
44 |
|
45 | testOrm(dataSource);
|
46 |
|
47 | batch['all tests done'] = function(test) {
|
48 | test.done();
|
49 | process.nextTick(allTestsDone);
|
50 | };
|
51 |
|
52 | function allTestsDone() {
|
53 |
|
54 | console.log('Test done in %dms\n', Date.now() - start);
|
55 | }
|
56 | };
|
57 |
|
58 | Object.defineProperty(module.exports, 'it', {
|
59 | writable: true,
|
60 | enumerable: false,
|
61 | configurable: true,
|
62 | value: it,
|
63 | });
|
64 |
|
65 | Object.defineProperty(module.exports, 'skip', {
|
66 | writable: true,
|
67 | enumerable: false,
|
68 | configurable: true,
|
69 | value: skip,
|
70 | });
|
71 |
|
72 | function clearAndCreate(model, data, callback) {
|
73 | const createdItems = [];
|
74 | model.destroyAll(function() {
|
75 | nextItem(null, null);
|
76 | });
|
77 |
|
78 | let itemIndex = 0;
|
79 |
|
80 | function nextItem(err, lastItem) {
|
81 | if (lastItem !== null) {
|
82 | createdItems.push(lastItem);
|
83 | }
|
84 | if (itemIndex >= data.length) {
|
85 | callback(createdItems);
|
86 | return;
|
87 | }
|
88 | model.create(data[itemIndex], nextItem);
|
89 | itemIndex++;
|
90 | }
|
91 | }
|
92 |
|
93 |
|
94 | function testOrm(dataSource) {
|
95 | const requestsAreCounted = dataSource.name !== 'mongodb';
|
96 |
|
97 | let Post, User, Passport, Log, Dog;
|
98 |
|
99 | it('should define class', function(test) {
|
100 | User = dataSource.define('User', {
|
101 | name: {type: String, index: true},
|
102 | email: {type: String, index: true},
|
103 | bio: Text,
|
104 | approved: Boolean,
|
105 | joinedAt: Date,
|
106 | age: Number,
|
107 | passwd: {type: String, index: true},
|
108 | });
|
109 |
|
110 | Dog = dataSource.define('Dog', {
|
111 | name: {type: String, limit: 64, allowNull: false},
|
112 | });
|
113 |
|
114 | Log = dataSource.define('Log', {
|
115 | ownerId: {type: Number, allowNull: true},
|
116 | name: {type: String, limit: 64, allowNull: false},
|
117 | });
|
118 |
|
119 | Log.belongsTo(Dog, {as: 'owner', foreignKey: 'ownerId'});
|
120 |
|
121 | dataSource.extendModel('User', {
|
122 | settings: {type: Schema.JSON},
|
123 | extra: Object,
|
124 | });
|
125 |
|
126 | const newuser = new User({settings: {hey: 'you'}});
|
127 | test.ok(newuser.settings);
|
128 |
|
129 | Post = dataSource.define('Post', {
|
130 | title: {type: String, length: 255, index: true},
|
131 | subject: {type: String},
|
132 | content: {type: Text},
|
133 | date: {type: Date, default: function() {
|
134 | return new Date;
|
135 | }, index: true},
|
136 | published: {type: Boolean, default: false, index: true},
|
137 | likes: [],
|
138 | related: [RelatedPost],
|
139 | }, {table: 'posts'});
|
140 |
|
141 | function RelatedPost() {
|
142 | }
|
143 |
|
144 | RelatedPost.prototype.someMethod = function() {
|
145 | return this.parent;
|
146 | };
|
147 |
|
148 | Post.validateAsync('title', function(err, done) {
|
149 | process.nextTick(done);
|
150 | });
|
151 |
|
152 | User.hasMany(Post, {as: 'posts', foreignKey: 'userId'});
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 | Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 | Passport = dataSource.define('Passport', {
|
174 | number: String,
|
175 | });
|
176 |
|
177 | Passport.belongsTo(User, {as: 'owner', foreignKey: 'ownerId'});
|
178 | User.hasMany(Passport, {as: 'passports', foreignKey: 'ownerId'});
|
179 |
|
180 | const user = new User;
|
181 |
|
182 | test.ok(User instanceof Function);
|
183 |
|
184 |
|
185 | test.ok(User.find instanceof Function);
|
186 | test.ok(User.create instanceof Function);
|
187 |
|
188 |
|
189 | test.ok(user.save instanceof Function);
|
190 |
|
191 | dataSource.automigrate(function(err) {
|
192 | if (err) {
|
193 | console.log('Error while migrating');
|
194 | console.log(err);
|
195 | } else {
|
196 | test.done();
|
197 | }
|
198 | });
|
199 | });
|
200 |
|
201 | it('should initialize object properly', function(test) {
|
202 | const hw = 'Hello word',
|
203 | now = Date.now(),
|
204 | post = new Post({title: hw}),
|
205 | anotherPost = Post({title: 'Resig style constructor'});
|
206 |
|
207 | test.equal(post.title, hw);
|
208 | test.ok(!post.propertyChanged('title'), 'property changed: title');
|
209 | post.title = 'Goodbye, Lenin';
|
210 | test.equal(post.title_was, hw);
|
211 | test.ok(post.propertyChanged('title'));
|
212 | test.strictEqual(post.published, false);
|
213 | test.ok(post.date >= now);
|
214 | test.ok(post.isNewRecord());
|
215 | test.ok(anotherPost instanceof Post);
|
216 | test.ok(anotherPost.title, 'Resig style constructor');
|
217 | test.done();
|
218 | });
|
219 |
|
220 | it('should save object', function(test) {
|
221 | const title = 'Initial title', title2 = 'Hello world',
|
222 | date = new Date;
|
223 |
|
224 | Post.create({
|
225 | title: title,
|
226 | date: date,
|
227 | }, function(err, obj) {
|
228 | test.ok(obj.id, 'Object id should present');
|
229 | test.equals(obj.title, title);
|
230 |
|
231 | obj.title = title2;
|
232 | test.ok(obj.propertyChanged('title'), 'Title changed');
|
233 | obj.save(function(err, obj) {
|
234 | test.equal(obj.title, title2);
|
235 | test.ok(!obj.propertyChanged('title'));
|
236 |
|
237 | const p = new Post({title: 1});
|
238 | p.title = 2;
|
239 | p.save(function(err, obj) {
|
240 | test.ok(!p.propertyChanged('title'));
|
241 | p.title = 3;
|
242 | test.ok(p.propertyChanged('title'));
|
243 | test.equal(p.title_was, 2);
|
244 | p.save(function() {
|
245 | test.equal(p.title_was, 3);
|
246 | test.ok(!p.propertyChanged('title'));
|
247 | test.done();
|
248 | });
|
249 | });
|
250 | });
|
251 | });
|
252 | });
|
253 |
|
254 | it('should create object with initial data', function(test) {
|
255 | const title = 'Initial title',
|
256 | date = new Date;
|
257 |
|
258 | Post.create({
|
259 | title: title,
|
260 | date: date,
|
261 | }, function(err, obj) {
|
262 | test.ok(obj.id);
|
263 | test.equals(obj.title, title);
|
264 | test.equals(obj.date, date);
|
265 | Post.findById(obj.id, function() {
|
266 | test.equal(obj.title, title);
|
267 | test.equal(obj.date.toString(), date.toString());
|
268 | test.done();
|
269 | });
|
270 | });
|
271 | });
|
272 |
|
273 | it('should save only dataSource-defined field in database', function(test) {
|
274 | Post.create({title: '1602', nonSchemaField: 'some value'}, function(err, post) {
|
275 | test.ok(!post.nonSchemaField);
|
276 | post.a = 1;
|
277 | post.save(function() {
|
278 | test.ok(post.a);
|
279 | post.reload(function(err, psto) {
|
280 | test.ok(!psto.a);
|
281 | test.done();
|
282 | });
|
283 | });
|
284 | });
|
285 | });
|
286 |
|
287 | |
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 | it('should not re-instantiate object on saving', function(test) {
|
304 | const title = 'Initial title';
|
305 | const post = new Post({title: title});
|
306 | post.save(function(err, savedPost) {
|
307 | test.strictEqual(post, savedPost);
|
308 | test.done();
|
309 | });
|
310 | });
|
311 |
|
312 | it('should destroy object', function(test) {
|
313 | Post.create(function(err, post) {
|
314 | Post.exists(post.id, function(err, exists) {
|
315 | test.ok(exists, 'Object exists');
|
316 | post.destroy(function() {
|
317 | Post.exists(post.id, function(err, exists) {
|
318 | if (err) console.log(err);
|
319 | test.ok(!exists, 'Hey! ORM told me that object exists, ' +
|
320 | ' but it looks like it doesn\'t. Something went wrong...');
|
321 | Post.findById(post.id, function(err, obj) {
|
322 | test.equal(obj, null, 'Param obj should be null');
|
323 | test.done();
|
324 | });
|
325 | });
|
326 | });
|
327 | });
|
328 | });
|
329 | });
|
330 |
|
331 | it('should handle virtual attributes', function(test) {
|
332 | const salt = 's0m3s3cr3t5a1t';
|
333 |
|
334 | User.setter.passwd = function(password) {
|
335 | this._passwd = calcHash(password, salt);
|
336 | };
|
337 |
|
338 | function calcHash(pass, salt) {
|
339 | const crypto = require('crypto');
|
340 | const hash = crypto.createHash('sha256');
|
341 | hash.update(pass);
|
342 | hash.update(salt);
|
343 | return hash.digest('base64');
|
344 | }
|
345 |
|
346 | const u = new User;
|
347 | u.passwd = 's3cr3t';
|
348 | test.equal(u.passwd, calcHash('s3cr3t', salt));
|
349 | test.done();
|
350 | });
|
351 |
|
352 |
|
353 |
|
354 |
|
355 |
|
356 |
|
357 |
|
358 |
|
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 | it('should update single attribute', function(test) {
|
365 | Post.create({title: 'title', content: 'content', published: true}, function(err, post) {
|
366 | post.content = 'New content';
|
367 | post.updateAttribute('title', 'New title', function() {
|
368 | test.equal(post.title, 'New title');
|
369 | test.ok(!post.propertyChanged('title'));
|
370 | test.equal(post.content, 'New content', 'dirty state saved');
|
371 | test.ok(post.propertyChanged('content'));
|
372 | post.reload(function(err, post) {
|
373 | test.equal(post.title, 'New title');
|
374 | test.ok(!post.propertyChanged('title'), 'title not changed');
|
375 | test.equal(post.content, 'content', 'real value turned back');
|
376 | test.ok(!post.propertyChanged('content'), 'content unchanged');
|
377 | test.done();
|
378 | });
|
379 | });
|
380 | });
|
381 | });
|
382 |
|
383 | let countOfposts, countOfpostsFiltered;
|
384 | it('should fetch collection', function(test) {
|
385 | Post.all(function(err, posts) {
|
386 | countOfposts = posts.length;
|
387 | test.ok(countOfposts > 0);
|
388 | test.ok(posts[0] instanceof Post);
|
389 | countOfpostsFiltered = posts.filter(function(p) {
|
390 | return p.title === 'title';
|
391 | }).length;
|
392 | test.done();
|
393 | });
|
394 | });
|
395 |
|
396 | it('should find records filtered with multiple attributes', function(test) {
|
397 | const d = new Date;
|
398 | Post.create({title: 'title', content: 'content', published: true, date: d}, function(err, post) {
|
399 | Post.all({where: {title: 'title', date: d, published: true}}, function(err, res) {
|
400 | test.equals(res.length, 1, 'Filtering Posts returns one post');
|
401 | test.done();
|
402 | });
|
403 | });
|
404 | });
|
405 |
|
406 | if (
|
407 | !dataSource.name.match(/redis/) &&
|
408 | dataSource.name !== 'memory' &&
|
409 | dataSource.name !== 'neo4j' &&
|
410 | dataSource.name !== 'cradle'
|
411 | )
|
412 | it('relations key is working', function(test) {
|
413 | test.ok(User.relations, 'Relations key should be defined');
|
414 | test.ok(User.relations.posts, 'posts relation should exist on User');
|
415 | test.equal(User.relations.posts.type, 'hasMany', 'Type of hasMany relation is hasMany');
|
416 | test.equal(User.relations.posts.multiple, true, 'hasMany relations are multiple');
|
417 | test.equal(User.relations.posts.keyFrom, 'id', 'keyFrom is primary key of model table');
|
418 | test.equal(User.relations.posts.keyTo, 'userId', 'keyTo is foreign key of related model table');
|
419 |
|
420 | test.ok(Post.relations, 'Relations key should be defined');
|
421 | test.ok(Post.relations.author, 'author relation should exist on Post');
|
422 | test.equal(Post.relations.author.type, 'belongsTo', 'Type of belongsTo relation is belongsTo');
|
423 | test.equal(Post.relations.author.multiple, false, 'belongsTo relations are not multiple');
|
424 | test.equal(Post.relations.author.keyFrom, 'userId', 'keyFrom is foreign key of model table');
|
425 | test.equal(Post.relations.author.keyTo, 'id', 'keyTo is primary key of related model table');
|
426 | test.done();
|
427 | });
|
428 |
|
429 | it('should handle hasMany relationship', function(test) {
|
430 | User.create(function(err, u) {
|
431 | if (err) return console.log(err);
|
432 | test.ok(u.posts, 'Method defined: posts');
|
433 | test.ok(u.posts.build, 'Method defined: posts.build');
|
434 | test.ok(u.posts.create, 'Method defined: posts.create');
|
435 | u.posts.create(function(err, post) {
|
436 | if (err) return console.log(err);
|
437 | u.posts(function(err, posts) {
|
438 | test.equal(posts.pop().id.toString(), post.id.toString());
|
439 | test.done();
|
440 | });
|
441 | });
|
442 | });
|
443 | });
|
444 |
|
445 | it('should navigate variations of belongsTo regardless of column name', function(test) {
|
446 | Dog.create({name: 'theDog'}, function(err, obj) {
|
447 | test.ok(obj instanceof Dog);
|
448 | Log.create({name: 'theLog', ownerId: obj.id}, function(err, obj) {
|
449 | test.ok(obj instanceof Log);
|
450 | obj.owner(function(err, obj) {
|
451 | test.ok(!err, 'Should not have an error.');
|
452 | if (err) {
|
453 | console.log('Found: ' + err);
|
454 | }
|
455 | test.ok(obj, 'Should not find null or undefined.');
|
456 | test.ok(obj instanceof Dog, 'Should find a Dog.');
|
457 | if (obj) {
|
458 | test.ok(obj.name, 'Should have a name.');
|
459 | }
|
460 | if (obj && obj.name) {
|
461 | test.equal(obj.name, 'theDog', 'The owner of theLog is theDog.');
|
462 | }
|
463 | test.done();
|
464 | });
|
465 | });
|
466 | });
|
467 | });
|
468 |
|
469 | it('hasMany should support additional conditions', function(test) {
|
470 | User.create(function(e, u) {
|
471 | u.posts.create({}, function(e, p) {
|
472 | u.posts({where: {id: p.id}}, function(e, posts) {
|
473 | test.equal(posts.length, 1, 'There should be only 1 post.');
|
474 | test.done();
|
475 | });
|
476 | });
|
477 | });
|
478 | });
|
479 |
|
480 |
|
481 | it('hasMany should be cached', function(test) {
|
482 |
|
483 |
|
484 |
|
485 |
|
486 | Post.all(function(err, posts) {
|
487 |
|
488 | for (let i = 0; i < posts.length; i++) {
|
489 | const post = posts[i];
|
490 | if (post.userId) {
|
491 |
|
492 | User.findById(post.userId, function(err, user) {
|
493 | User.create(function(err, voidUser) {
|
494 | Post.create({userId: user.id}, function() {
|
495 |
|
496 |
|
497 | user.posts(function(err, data) {
|
498 | const nbInitialRequests = nbSchemaRequests;
|
499 | user.posts(function(err, data2) {
|
500 | test.equal(data.length, 2, 'There should be 2 posts.');
|
501 | test.equal(data.length, data2.length, 'Posts should be the same, since we are loading on the same object.');
|
502 | requestsAreCounted && test.equal(nbInitialRequests, nbSchemaRequests, 'There should not be any request because value is cached.');
|
503 |
|
504 | if (dataSource.name === 'mongodb') {
|
505 | test.done();
|
506 | } else {
|
507 | user.posts({where: {id: data[0].id}}, function(err, data) {
|
508 | test.equal(data.length, 1, 'There should be only one post.');
|
509 | requestsAreCounted && test.equal(nbInitialRequests + 1, nbSchemaRequests, 'There should be one additional request since we added conditions.');
|
510 |
|
511 | user.posts(function(err, data) {
|
512 | test.equal(data.length, 2, 'Previous get shouldn\'t have changed cached value though, since there was additional conditions.');
|
513 | requestsAreCounted && test.equal(nbInitialRequests + 1, nbSchemaRequests, 'There should not be any request because value is cached.');
|
514 |
|
515 |
|
516 | voidUser.posts(function(err, data) {
|
517 | const nbInitialRequests = nbSchemaRequests;
|
518 | voidUser.posts(function(err, data2) {
|
519 | test.equal(data.length, 0, 'There shouldn\'t be any posts (1/2).');
|
520 | test.equal(data2.length, 0, 'There shouldn\'t be any posts (2/2).');
|
521 | requestsAreCounted && test.equal(nbInitialRequests, nbSchemaRequests, 'There should not be any request because value is cached.');
|
522 |
|
523 | voidUser.posts(true, function(err, data3) {
|
524 | test.equal(data3.length, 0, 'There shouldn\'t be any posts.');
|
525 | requestsAreCounted && test.equal(nbInitialRequests + 1, nbSchemaRequests, 'There should be one additional request since we forced refresh.');
|
526 |
|
527 | test.done();
|
528 | });
|
529 | });
|
530 | });
|
531 | });
|
532 | });
|
533 | }
|
534 | });
|
535 | });
|
536 | });
|
537 | });
|
538 | });
|
539 | break;
|
540 | }
|
541 | }
|
542 | });
|
543 | });
|
544 |
|
545 |
|
546 |
|
547 |
|
548 |
|
549 |
|
550 |
|
551 |
|
552 | it('should support scopes', function(test) {
|
553 | let wait = 2;
|
554 |
|
555 | test.ok(Post.scope, 'Scope supported');
|
556 | Post.scope('published', {where: {published: true}});
|
557 | test.ok(typeof Post.published === 'function');
|
558 | test.ok(Post.published._scope.where.published === true);
|
559 | const post = Post.published.build();
|
560 | test.ok(post.published, 'Can build');
|
561 | test.ok(post.isNewRecord());
|
562 | Post.published.create(function(err, psto) {
|
563 | if (err) return console.log(err);
|
564 | test.ok(psto.published);
|
565 | test.ok(!psto.isNewRecord());
|
566 | done();
|
567 | });
|
568 |
|
569 | User.create(function(err, u) {
|
570 | if (err) return console.log(err);
|
571 | test.ok(typeof u.posts.published == 'function');
|
572 | test.ok(u.posts.published._scope.where.published);
|
573 | console.log(u.posts.published._scope);
|
574 | test.equal(u.posts.published._scope.where.userId, u.id);
|
575 | done();
|
576 | });
|
577 |
|
578 | function done() {
|
579 | if (--wait === 0) test.done();
|
580 | }
|
581 | });
|
582 |
|
583 | it('should return type of property', function(test) {
|
584 | test.equal(Post.getPropertyType('title'), 'String');
|
585 | test.equal(Post.getPropertyType('content'), 'Text');
|
586 | const p = new Post;
|
587 | test.equal(p.getPropertyType('title'), 'String');
|
588 | test.equal(p.getPropertyType('content'), 'Text');
|
589 | test.done();
|
590 | });
|
591 |
|
592 | it('should handle ORDER clause', function(test) {
|
593 | const titles = [
|
594 | {title: 'Title A', subject: 'B'},
|
595 | {title: 'Title Z', subject: 'A'},
|
596 | {title: 'Title M', subject: 'C'},
|
597 | {title: 'Title A', subject: 'A'},
|
598 | {title: 'Title B', subject: 'A'},
|
599 | {title: 'Title C', subject: 'D'},
|
600 | ];
|
601 | const isRedis = Post.dataSource.name === 'redis';
|
602 | const dates = isRedis ? [5, 9, 0, 17, 10, 9] : [
|
603 | new Date(1000 * 5),
|
604 | new Date(1000 * 9),
|
605 | new Date(1000 * 0),
|
606 | new Date(1000 * 17),
|
607 | new Date(1000 * 10),
|
608 | new Date(1000 * 9),
|
609 | ];
|
610 | titles.forEach(function(t, i) {
|
611 | Post.create({title: t.title, subject: t.subject, date: dates[i]}, done);
|
612 | });
|
613 |
|
614 | let i = 0, tests = 0;
|
615 |
|
616 | function done(err, obj) {
|
617 | if (++i === titles.length) {
|
618 | doFilterAndSortTest();
|
619 | doFilterAndSortReverseTest();
|
620 | doStringTest();
|
621 | doNumberTest();
|
622 |
|
623 | if (dataSource.name == 'mongoose') {
|
624 | doMultipleSortTest();
|
625 | doMultipleReverseSortTest();
|
626 | }
|
627 | }
|
628 | }
|
629 |
|
630 | function compare(a, b) {
|
631 | if (a.title < b.title) return -1;
|
632 | if (a.title > b.title) return 1;
|
633 | return 0;
|
634 | }
|
635 |
|
636 |
|
637 |
|
638 | function doStringTest() {
|
639 | tests += 1;
|
640 | Post.all({order: 'title'}, function(err, posts) {
|
641 | if (err) console.log(err);
|
642 | test.equal(posts.length, 6);
|
643 | titles.sort(compare).forEach(function(t, i) {
|
644 | if (posts[i]) test.equal(posts[i].title, t.title);
|
645 | });
|
646 | finished();
|
647 | });
|
648 | }
|
649 |
|
650 | function doNumberTest() {
|
651 | tests += 1;
|
652 | Post.all({order: 'date'}, function(err, posts) {
|
653 | if (err) console.log(err);
|
654 | test.equal(posts.length, 6);
|
655 | dates.sort(numerically).forEach(function(d, i) {
|
656 | if (posts[i])
|
657 | test.equal(posts[i].date.toString(), d.toString(), 'doNumberTest');
|
658 | });
|
659 | finished();
|
660 | });
|
661 | }
|
662 |
|
663 | function doFilterAndSortTest() {
|
664 | tests += 1;
|
665 | Post.all({where: {date: new Date(1000 * 9)}, order: 'title', limit: 3}, function(err, posts) {
|
666 | if (err) console.log(err);
|
667 | console.log(posts.length);
|
668 | test.equal(posts.length, 2, 'Exactly 2 posts returned by query');
|
669 | ['Title C', 'Title Z'].forEach(function(t, i) {
|
670 | if (posts[i]) {
|
671 | test.equal(posts[i].title, t, 'doFilterAndSortTest');
|
672 | }
|
673 | });
|
674 | finished();
|
675 | });
|
676 | }
|
677 |
|
678 | function doFilterAndSortReverseTest() {
|
679 | tests += 1;
|
680 | Post.all({where: {date: new Date(1000 * 9)}, order: 'title DESC', limit: 3}, function(err, posts) {
|
681 | if (err) console.log(err);
|
682 | test.equal(posts.length, 2, 'Exactly 2 posts returned by query');
|
683 | ['Title Z', 'Title C'].forEach(function(t, i) {
|
684 | if (posts[i]) {
|
685 | test.equal(posts[i].title, t, 'doFilterAndSortReverseTest');
|
686 | }
|
687 | });
|
688 | finished();
|
689 | });
|
690 | }
|
691 |
|
692 | function doMultipleSortTest() {
|
693 | tests += 1;
|
694 | Post.all({order: 'title ASC, subject ASC'}, function(err, posts) {
|
695 | if (err) console.log(err);
|
696 | test.equal(posts.length, 6);
|
697 | test.equal(posts[0].title, 'Title A');
|
698 | test.equal(posts[0].subject, 'A');
|
699 | test.equal(posts[1].title, 'Title A');
|
700 | test.equal(posts[1].subject, 'B');
|
701 | test.equal(posts[5].title, 'Title Z');
|
702 | finished();
|
703 | });
|
704 | }
|
705 |
|
706 | function doMultipleReverseSortTest() {
|
707 | tests += 1;
|
708 | Post.all({order: 'title ASC, subject DESC'}, function(err, posts) {
|
709 | if (err) console.log(err);
|
710 | test.equal(posts.length, 6);
|
711 | test.equal(posts[0].title, 'Title A');
|
712 | test.equal(posts[0].subject, 'B');
|
713 | test.equal(posts[1].title, 'Title A');
|
714 | test.equal(posts[1].subject, 'A');
|
715 | test.equal(posts[5].title, 'Title Z');
|
716 | finished();
|
717 | });
|
718 | }
|
719 |
|
720 | let fin = 0;
|
721 |
|
722 | function finished() {
|
723 | if (++fin === tests) {
|
724 | test.done();
|
725 | }
|
726 | }
|
727 |
|
728 |
|
729 |
|
730 | function numerically(a, b) {
|
731 | return a - b;
|
732 | }
|
733 | });
|
734 |
|
735 |
|
736 |
|
737 |
|
738 |
|
739 |
|
740 |
|
741 |
|
742 |
|
743 |
|
744 |
|
745 |
|
746 |
|
747 |
|
748 |
|
749 |
|
750 |
|
751 |
|
752 |
|
753 |
|
754 |
|
755 |
|
756 |
|
757 |
|
758 |
|
759 |
|
760 |
|
761 |
|
762 |
|
763 |
|
764 |
|
765 |
|
766 |
|
767 |
|
768 |
|
769 |
|
770 |
|
771 |
|
772 |
|
773 |
|
774 |
|
775 |
|
776 |
|
777 |
|
778 |
|
779 |
|
780 |
|
781 |
|
782 |
|
783 |
|
784 |
|
785 |
|
786 |
|
787 |
|
788 |
|
789 |
|
790 |
|
791 |
|
792 |
|
793 |
|
794 |
|
795 |
|
796 |
|
797 |
|
798 |
|
799 |
|
800 |
|
801 |
|
802 |
|
803 |
|
804 |
|
805 |
|
806 |
|
807 |
|
808 |
|
809 |
|
810 |
|
811 |
|
812 |
|
813 |
|
814 |
|
815 |
|
816 |
|
817 |
|
818 |
|
819 |
|
820 |
|
821 |
|
822 |
|
823 |
|
824 |
|
825 |
|
826 |
|
827 |
|
828 |
|
829 |
|
830 |
|
831 |
|
832 |
|
833 |
|
834 |
|
835 |
|
836 |
|
837 |
|
838 |
|
839 |
|
840 |
|
841 |
|
842 |
|
843 |
|
844 |
|
845 |
|
846 |
|
847 |
|
848 |
|
849 |
|
850 |
|
851 |
|
852 |
|
853 |
|
854 |
|
855 |
|
856 |
|
857 |
|
858 |
|
859 |
|
860 |
|
861 |
|
862 |
|
863 |
|
864 |
|
865 |
|
866 |
|
867 |
|
868 |
|
869 |
|
870 |
|
871 |
|
872 |
|
873 | it('should handle order clause with direction', function(test) {
|
874 | let wait = 0;
|
875 | const emails = [
|
876 | 'john@hcompany.com',
|
877 | 'tom@hcompany.com',
|
878 | 'admin@hcompany.com',
|
879 | 'tin@hcompany.com',
|
880 | 'mike@hcompany.com',
|
881 | 'susan@hcompany.com',
|
882 | 'test@hcompany.com',
|
883 | ];
|
884 | User.destroyAll(function() {
|
885 | emails.forEach(function(email) {
|
886 | wait += 1;
|
887 | User.create({email: email, name: 'Nick'}, done);
|
888 | });
|
889 | });
|
890 | let tests = 2;
|
891 |
|
892 | function done() {
|
893 | process.nextTick(function() {
|
894 | if (--wait === 0) {
|
895 | doSortTest();
|
896 | doReverseSortTest();
|
897 | }
|
898 | });
|
899 | }
|
900 |
|
901 | function doSortTest() {
|
902 | User.all({order: 'email ASC', where: {name: 'Nick'}}, function(err, users) {
|
903 | const _emails = emails.sort();
|
904 | users.forEach(function(user, i) {
|
905 | test.equal(_emails[i], user.email, 'ASC sorting');
|
906 | });
|
907 | testDone();
|
908 | });
|
909 | }
|
910 |
|
911 | function doReverseSortTest() {
|
912 | User.all({order: 'email DESC', where: {name: 'Nick'}}, function(err, users) {
|
913 | const _emails = emails.sort().reverse();
|
914 | users.forEach(function(user, i) {
|
915 | test.equal(_emails[i], user.email, 'DESC sorting');
|
916 | });
|
917 | testDone();
|
918 | });
|
919 | }
|
920 |
|
921 | function testDone() {
|
922 | if (--tests === 0) test.done();
|
923 | }
|
924 | });
|
925 |
|
926 | it('should return id in find result even after updateAttributes', function(test) {
|
927 | Post.create(function(err, post) {
|
928 | const id = post.id;
|
929 | test.ok(post.published === false);
|
930 | post.updateAttributes({title: 'hey', published: true}, function() {
|
931 | Post.find(id, function(err, post) {
|
932 | test.ok(!!post.published, 'Update boolean field');
|
933 | test.ok(post.id);
|
934 | test.done();
|
935 | });
|
936 | });
|
937 | });
|
938 | });
|
939 |
|
940 | it('should handle belongsTo correctly', function(test) {
|
941 | const passport = new Passport({ownerId: 16});
|
942 |
|
943 | test.equal(passport.owner(), 16);
|
944 |
|
945 | passport.owner(18);
|
946 | test.equal(passport.owner(), 18);
|
947 | test.done();
|
948 | });
|
949 |
|
950 | it('should query one record', function(test) {
|
951 | test.expect(4);
|
952 | Post.findOne(function(err, post) {
|
953 | test.ok(post && post.id);
|
954 | Post.findOne({where: {title: 'hey'}}, function(err, post) {
|
955 | if (err) {
|
956 | console.log(err);
|
957 | return test.done();
|
958 | }
|
959 | test.equal(post && post.constructor.modelName, 'Post');
|
960 | test.equal(post && post.title, 'hey');
|
961 | Post.findOne({where: {title: 'not exists'}}, function(err, post) {
|
962 | test.ok(post === null);
|
963 | test.done();
|
964 | });
|
965 | });
|
966 | });
|
967 | });
|
968 |
|
969 |
|
970 |
|
971 |
|
972 |
|
973 |
|
974 |
|
975 |
|
976 |
|
977 |
|
978 |
|
979 |
|
980 |
|
981 |
|
982 |
|
983 |
|
984 |
|
985 |
|
986 |
|
987 |
|
988 |
|
989 |
|
990 |
|
991 |
|
992 |
|
993 |
|
994 |
|
995 |
|
996 |
|
997 |
|
998 |
|
999 |
|
1000 |
|
1001 |
|
1002 |
|
1003 |
|
1004 |
|
1005 |
|
1006 |
|
1007 |
|
1008 |
|
1009 |
|
1010 |
|
1011 |
|
1012 |
|
1013 |
|
1014 |
|
1015 |
|
1016 |
|
1017 |
|
1018 | if (dataSource.name !== 'mongoose' && dataSource.name !== 'neo4j')
|
1019 | it('should update or create record', function(test) {
|
1020 | const newData = {
|
1021 | id: 1,
|
1022 | title: 'New title (really new)',
|
1023 | content: 'Some example content (updated)',
|
1024 | };
|
1025 | Post.updateOrCreate(newData, function(err, updatedPost) {
|
1026 | if (err) throw err;
|
1027 | test.ok(updatedPost);
|
1028 | if (!updatedPost) throw Error('No post!');
|
1029 |
|
1030 | if (dataSource.name !== 'mongodb') {
|
1031 | test.equal(newData.id, updatedPost.toObject().id);
|
1032 | }
|
1033 | test.equal(newData.title, updatedPost.toObject().title);
|
1034 | test.equal(newData.content, updatedPost.toObject().content);
|
1035 |
|
1036 | Post.findById(updatedPost.id, function(err, post) {
|
1037 | if (err) throw err;
|
1038 | if (!post) throw Error('No post!');
|
1039 | if (dataSource.name !== 'mongodb') {
|
1040 | test.equal(newData.id, post.toObject().id);
|
1041 | }
|
1042 | test.equal(newData.title, post.toObject().title);
|
1043 | test.equal(newData.content, post.toObject().content);
|
1044 | Post.updateOrCreate({id: 100001, title: 'hey'}, function(err, post) {
|
1045 | if (dataSource.name !== 'mongodb') test.equal(post.id, 100001);
|
1046 | test.equal(post.title, 'hey');
|
1047 | Post.findById(post.id, function(err, post) {
|
1048 | if (!post) throw Error('No post!');
|
1049 | test.done();
|
1050 | });
|
1051 | });
|
1052 | });
|
1053 | });
|
1054 | });
|
1055 |
|
1056 | it('should work with custom setters and getters', function(test) {
|
1057 | User.dataSource.defineForeignKey('User', 'passwd');
|
1058 | User.setter.passwd = function(pass) {
|
1059 | this._passwd = pass + 'salt';
|
1060 | };
|
1061 | const u = new User({passwd: 'qwerty'});
|
1062 | test.equal(u.passwd, 'qwertysalt');
|
1063 | u.save(function(err, user) {
|
1064 | User.findById(user.id, function(err, user) {
|
1065 | test.ok(user !== u);
|
1066 | test.equal(user.passwd, 'qwertysalt');
|
1067 | User.all({where: {passwd: 'qwertysalt'}}, function(err, users) {
|
1068 | test.ok(users[0] !== user);
|
1069 | test.equal(users[0].passwd, 'qwertysalt');
|
1070 | User.create({passwd: 'asalat'}, function(err, usr) {
|
1071 | test.equal(usr.passwd, 'asalatsalt');
|
1072 | User.upsert({passwd: 'heyman'}, function(err, us) {
|
1073 | test.equal(us.passwd, 'heymansalt');
|
1074 | User.findById(us.id, function(err, user) {
|
1075 | test.equal(user.passwd, 'heymansalt');
|
1076 | test.done();
|
1077 | });
|
1078 | });
|
1079 | });
|
1080 | });
|
1081 | });
|
1082 | });
|
1083 | });
|
1084 |
|
1085 | it('should work with typed and untyped nested collections', function(test) {
|
1086 | const post = new Post;
|
1087 | const like = post.likes.push({foo: 'bar'});
|
1088 | test.equal(like.constructor.name, 'ListItem');
|
1089 | const related = post.related.push({hello: 'world'});
|
1090 | test.ok(related.someMethod);
|
1091 | post.save(function(err, p) {
|
1092 | test.equal(p.likes.nextid, 2);
|
1093 | p.likes.push({second: 2});
|
1094 | p.likes.push({third: 3});
|
1095 | p.save(function(err) {
|
1096 | Post.findById(p.id, function(err, pp) {
|
1097 | test.equal(pp.likes.length, 3);
|
1098 | test.ok(pp.likes[3].third);
|
1099 | test.ok(pp.likes[2].second);
|
1100 | test.ok(pp.likes[1].foo);
|
1101 | pp.likes.remove(2);
|
1102 | test.equal(pp.likes.length, 2);
|
1103 | test.ok(!pp.likes[2]);
|
1104 | pp.likes.remove(pp.likes[1]);
|
1105 | test.equal(pp.likes.length, 1);
|
1106 | test.ok(!pp.likes[1]);
|
1107 | test.ok(pp.likes[3]);
|
1108 | pp.save(function() {
|
1109 | Post.findById(p.id, function(err, pp) {
|
1110 | test.equal(pp.likes.length, 1);
|
1111 | test.ok(!pp.likes[1]);
|
1112 | test.ok(pp.likes[3]);
|
1113 | test.done();
|
1114 | });
|
1115 | });
|
1116 | });
|
1117 | });
|
1118 | });
|
1119 | });
|
1120 |
|
1121 | it('should find or create', function(test) {
|
1122 | const email = 'some email ' + Math.random();
|
1123 | User.findOrCreate({where: {email: email}}, function(err, u, created) {
|
1124 | test.ok(u);
|
1125 | test.ok(!u.age);
|
1126 | test.ok(created);
|
1127 | User.findOrCreate({where: {email: email}}, {age: 21}, function(err, u2, created) {
|
1128 | test.equals(u.id.toString(), u2.id.toString(), 'Same user ids');
|
1129 | test.ok(!u2.age);
|
1130 | test.ok(!created);
|
1131 | test.done();
|
1132 | });
|
1133 | });
|
1134 | });
|
1135 | }
|