UNPKG

33.4 kBJavaScriptView Raw
1var oreo = require('..')
2var ok = require('assert').ok
3var fs = require('fs')
4var bluebird = require('bluebird')
5var async = require('../lib/async')
6
7var models = {
8 books: require('./Book')
9}
10
11var db
12var platforms = [
13 {
14 driver: 'pg',
15 user: 'postgres',
16 pass: '',
17 hosts: ['localhost:5432', 'localhost:5433', 'localhost:5430'],
18 name: 'oreo_test',
19 debug: false,
20 silent: true,
21 Promise: global.Promise || bluebird,
22 models: models
23 },
24 {
25 driver: 'pg',
26 user: 'postgres',
27 pass: '',
28 hosts: ['localhost:5432', 'localhost:5433', 'localhost:5430'],
29 name: 'oreo_test',
30 debug: false,
31 silent: true,
32 memoize: 150,
33 Promise: global.Promise || bluebird,
34 models: models
35 },
36 {
37 driver: 'mysql',
38 user: 'root',
39 pass: '',
40 hosts: ['localhost'],
41 name: 'oreo_test',
42 debug: false,
43 silent: true,
44 Promise: global.Promise || bluebird,
45 models: models
46 }
47]
48
49var showError = function (err) {
50 console.log(err.stack)
51}
52
53var no = function (err) {
54 ok(!err, err + '')
55}
56
57var mockRedis = function() {
58 var cache = {}
59 return {
60 get: function(key, cb) {
61 var val = cache[key]
62 if (val) {
63 val = JSON.parse(val)
64 val.fromCache = true
65 val = JSON.stringify(val)
66 }
67 cb(null, val)
68 },
69 set: function(key, val, cb) {
70 cache[key] = val
71 cb(null)
72 }
73 }
74}
75
76describe('oreo', function() {
77
78 it('should fail with unknown driver', function(done) {
79 db = oreo({
80 driver: 'mssql'
81 }, function(err) {
82 ok(!!err, 'did not fail')
83 done()
84 })
85 })
86
87 platforms.forEach(function(config) {
88
89 it('should connect and discover - cb', function(done) {
90 console.log('\n', config.driver)
91 db = oreo(config, function(err) {
92 no(err)
93 done()
94 })
95 })
96
97 it('should create tables', function(done) {
98 var sql = fs.readFileSync(__dirname + '/schema/' + config.driver + '.sql', 'utf8')
99 db.executeWrite(sql, function(err, rs) {
100 no(err)
101 done()
102 })
103 })
104
105 it('should rediscover and end - promise', function(done) {
106 db.discover().then(function(db) {
107 ok(!!db.authors, 'authors not discovered')
108 config.schema = JSON.parse(JSON.stringify(db))
109 db.authors.find().then(function () {
110 var isDone = false
111 db.end(function () {
112 if (isDone) return
113 isDone = true
114 done()
115 })
116 })
117 }).catch(showError)
118 })
119
120 it('should connect and discover - schema and onReady', function(done) {
121 var count = 0
122 var isDone = function () {
123 count++
124 if (count === 2) done()
125 }
126 db = oreo(config, function (err) {
127 no(err)
128 }).onReady(function() {
129 isDone()
130 })
131 db.onReady(function () {
132 ok(!!db.authors, 'authors not discovered')
133 isDone()
134 })
135 })
136
137 it('should rediscover - cb', function(done) {
138 db.discover(function(err) {
139 no(err)
140 ok(!!db.authors, 'authors not discovered')
141 done()
142 })
143 })
144
145 it('should insert - cb', function(done) {
146 db.authors.insert({
147 name: 'Jack Kerouac',
148 birthDate: '1922-03-12'
149 }, function(err, author) {
150 no(err)
151 ok(author.id === 1, 'did not insert author - should insert')
152 db.books.insert({
153 title: 'On the Road',
154 author_id: author.id
155 }, function(err, book) {
156 no(err)
157 ok(book.id === 1, 'did not insert book')
158 db.ratings.insert({
159 author_id: author.id,
160 book_id: book.id,
161 stars: 10
162 }, function(err, rating) {
163 no(err)
164 ok(rating.stars === 10, 'did not insert rating')
165 done()
166 })
167 })
168 })
169 })
170
171 it('should insert - promise', function(done) {
172 db.authors.insert({
173 name: 'Tom Wolfe',
174 birthDate: '1931-03-02'
175 }).then(function(author) {
176 ok(author.id === 2, 'did not insert author - should insert')
177 db.books.insert({
178 title: 'The Electric Kool-Aid Acid Test',
179 author_id: author.id
180 }).then(function(book) {
181 ok(book.id === 2, 'did not insert book')
182 db.ratings.insert({
183 author_id: author.id,
184 book_id: book.id,
185 stars: 9
186 }).then(function(rating) {
187 ok(rating.stars === 9, 'did not insert rating')
188 done()
189 })
190 })
191 }).catch(showError)
192 })
193
194 it('should save field with same name as 1-to-m fk - cb', function(done) {
195 db.authors.save({
196 id: 2,
197 books: [2]
198 }, function(err, author) {
199 no(err)
200 db.authors.get(2, function(err, author) {
201 ok(author.books.toString() === '2', 'did not save books value')
202 author.hydrate('books', function (err) {
203 ok(!!err, 'books should not be hydratable')
204 done()
205 })
206 })
207 })
208 })
209
210 it('should not save 1-to-m row with same name as field - cb', function(done) {
211 db.authors.save({
212 id: 2,
213 books: [
214 {id: 2, author_id: 2, title: 'Working Title'}
215 ]
216 }, function(err, author) {
217 ok(!!err, 'books should not save')
218 done()
219 })
220 })
221
222 it('should save field with same name as 1-to-1 fk - cb', function(done) {
223 db.authors.save({
224 id: 2,
225 Country: {
226 Code: 'US',
227 name: 'United States'
228 }
229 }, function(err, author) {
230 no(err)
231 ok(author.Country === 'US', 'author.Country')
232 db.authors.get(2, function(err, author) {
233 no(err)
234 ok(author.Country === 'US', 'did not save author.Country')
235 author.hydrate('Country', function (err) {
236 no(err)
237 ok(author.Country.name === 'United States', 'author.Country.name')
238 author.Country.update({
239 name: 'USA'
240 }, function (err, Country) {
241 no(err)
242 ok(Country.name === 'USA', 'Country.name not USA')
243 author.update({
244 Country: {
245 Code: 'CA',
246 name: 'Canada'
247 }
248 }, function (err, author) {
249 no(err)
250 ok(author.Country === 'CA', 'author.Country not CA')
251 author.update({
252 Country: 'MX'
253 }, function (err, author) {
254 ok(!!err, 'should violate fk constraint')
255 db.authors.get(2, function (err, author) {
256 no(err)
257 ok(author.Country === 'CA', 'author.Country should still be CA')
258 done()
259 })
260 })
261 })
262 })
263 })
264 })
265 })
266 })
267
268 it('should static save - cb', function(done) {
269 var data = {
270 id: 1408,
271 name: 'Stephen King'
272 }
273 db.authors.save(data, function(err, author) {
274 no(err)
275 ok(author.id === 1408, 'did not insert author')
276 db.authors.save(data, function(err, author) {
277 no(err)
278 ok(author.id === 1408, 'did not update author')
279 done()
280 })
281 })
282 })
283
284 it('should static save - promise', function(done) {
285 var data = {
286 id: 1984,
287 name: 'George Orwell'
288 }
289 db.authors.save(data).then(function(author) {
290 ok(author.id === 1984, 'did not insert author')
291 db.authors.save(data).then(function(author) {
292 ok(author.id === 1984, 'did not update author')
293 done()
294 })
295 }).catch(showError)
296 })
297
298 it('should get - cb', function(done) {
299 db.authors.get(1, function(err, author) {
300 no(err)
301 ok(author.birthDate === '1922-03-12', 'did not return correct date string')
302 ok(author.id === 1, 'did not get author')
303 done()
304 })
305 })
306
307 it('should get - promise', function(done) {
308 db.authors.get(1).then(function(author) {
309 ok(author.id === 1, 'did not get author')
310 done()
311 }).catch(showError)
312 })
313
314 it('should parameterize array of numbers', function () {
315 return db.execute(
316 'select * from authors where id in (:ids)',
317 { ids: [1, 2] }
318 ).then(function (authors) {
319 ok(authors.length === 2)
320 })
321 })
322
323 it('should parameterize array of strings', function () {
324 return db.execute(
325 'select * from authors where name in (:names)',
326 { names: ['Tom Wolfe', 'Jack Kerouac'] }
327 ).then(function (authors) {
328 ok(authors.length === 2)
329 })
330 })
331
332 it('should get id=0 - promise', function (done) {
333 if (config.driver === 'mysql') {
334 return done()
335 }
336 db.authors.insert({
337 id: 0,
338 name: 'Nobody'
339 })
340 .then(function (author) {
341 db.authors.get(0)
342 .then(function (author) {
343 ok(author.id === 0, 'did not get author')
344 author.delete()
345 .then(function () {
346 done()
347 })
348 })
349 })
350 })
351
352 it('should mget - cb', function(done) {
353 db.authors.mget([1, 1984], function(err, authors) {
354 no(err)
355 ok(authors[0].id === 1, 'did not get authors')
356 ok(authors[1].id === 1984, 'did not get second author')
357 done()
358 })
359 })
360
361 it('should mget with null value - promise', function(done) {
362 db.authors.mget([1984, 999999, 1]).then(function(authors) {
363 ok(authors[0].id === 1984, 'did not get first author')
364 ok(authors[1] === null, 'second author is not null')
365 ok(authors[2].id === 1, 'did not get third author')
366 done()
367 }).catch(showError)
368 })
369
370 it('should get (composite primary key object)', function(done) {
371 db.ratings.get({
372 author_id: 1,
373 book_id: 1
374 }, function(err, rating) {
375 no(err)
376 ok(rating.stars === 10, 'did not get rating')
377 done()
378 })
379 })
380
381 it('should get (composite primary key array)', function(done) {
382 db.ratings.get([1, 1], function(err, rating) {
383 no(err)
384 ok(rating.stars === 10, 'did not get rating')
385 done()
386 })
387 })
388
389 it('should find all - cb', function(done) {
390 db.authors.find(function(err, authors) {
391 no(err)
392 ok(authors.length === 4, 'authors.length')
393 done()
394 })
395 })
396
397 it('should find all - promise', function(done) {
398 db.authors.find().then(function(authors) {
399 ok(authors.length === 4, 'authors.length')
400 done()
401 }).catch(showError)
402 })
403
404 it('should count - promise', function(done) {
405 db.authors.count().then(function(count) {
406 ok(count === 4, 'count')
407 done()
408 }).catch(showError)
409 })
410
411 it('should find (where string)', function(done) {
412 db.authors.find({
413 where: "name = 'Jack Kerouac'"
414 }, function(err, authors) {
415 no(err)
416 ok(authors[0].id === 1, 'did not find author')
417 done()
418 })
419 })
420
421 it('should find (case-sensitive)', function(done) {
422 db.authors.find({
423 where: {
424 Country: 'CA'
425 }
426 }, function(err, authors) {
427 no(err)
428 ok(authors[0].id === 2, 'did not find author')
429 done()
430 })
431 })
432
433 it('should find (where array)', function(done) {
434 db.authors.find({
435 where: ["name = 'Jack Kerouac'"]
436 }, function(err, authors) {
437 no(err)
438 ok(authors[0].id === 1, 'did not find author')
439 done()
440 })
441 })
442
443 it('should find (where parameterized array)', function(done) {
444 var opts = {
445 where: ['name = :name'],
446 params: {
447 name: 'Jack Kerouac'
448 }
449 }
450 db.authors.find(opts, function(err, authors) {
451 no(err)
452 ok(authors[0].id === 1, 'did not find author')
453 ok(authors[0].name === opts.params.name, 'did not find author name')
454 done()
455 })
456 })
457
458 it('should find (where object)', function(done) {
459 db.authors.find({
460 where: {
461 name: 'Jack Kerouac'
462 }
463 }, function(err, authors) {
464 no(err)
465 ok(authors[0].id === 1, 'did not find author')
466 done()
467 })
468 })
469
470 it('should find (composite primary key)', function(done) {
471 db.ratings.find({
472 where: {
473 stars: 10
474 }
475 }, function(err, ratings) {
476 no(err)
477 ok(ratings[0].author_id === 1, 'did not find rating')
478 done()
479 })
480 })
481
482 it('should order by', function(done) {
483 db.authors.find({
484 order: 'id desc'
485 }).then(function(authors) {
486 ok(authors[0].id === 1984, 'order first')
487 ok(authors[1].id === 1408, 'order second')
488 done()
489 }).catch(showError)
490 })
491
492 it('should limit', function(done) {
493 db.authors.find({
494 limit: 2
495 }).then(function(authors) {
496 ok(authors.length === 2, 'limit')
497 done()
498 }).catch(showError)
499 })
500
501 it('should offset', function(done) {
502 db.authors.find({
503 order: 'id desc',
504 limit: 1,
505 offset: 1
506 }).then(function(authors) {
507 ok(authors[0].id === 1408, 'offset')
508 done()
509 }).catch(showError)
510 })
511
512 it('should findOne - cb', function(done) {
513 db.authors.findOne({
514 where: "name = 'Jack Kerouac'"
515 }, function(err, author) {
516 no(err)
517 ok(author.id === 1, 'did not findOne author')
518 done()
519 })
520 })
521
522 it('should findOne - promise', function(done) {
523 db.authors.findOne({
524 where: "name = 'Jack Kerouac'"
525 }).then(function(author) {
526 ok(author.id === 1, 'did not findOne author')
527 done()
528 }).catch(showError)
529 })
530
531 it('should update - cb', function(done) {
532 db.authors.get(1, function(err, author) {
533 no(err)
534 var new_name = 'Jim Kerouac'
535 author.update({
536 name: new_name
537 }, function(err, author) {
538 no(err)
539 ok(author.id === 1, 'did not get correct author')
540 ok(author.name === new_name, 'did not update author')
541 done()
542 })
543 })
544 })
545
546 it('should update - promise', function(done) {
547 db.authors.get(1).then(function(author) {
548 var new_name = 'Jeff Kerouac'
549 author.update({
550 name: new_name
551 }).then(function(author) {
552 ok(author.id === 1, 'did not get correct author')
553 ok(author.name === new_name, 'did not update author')
554 done()
555 })
556 }).catch(showError)
557 })
558
559 it('should hydrate - cb', function(done) {
560 db.books.get(1, function(err, book) {
561 no(err)
562 book.hydrate('author', function(err) {
563 ok(book.author.id === book.author_id, 'did not hydrate author')
564 ok(book.id === 1, 'weird')
565 done()
566 })
567 })
568 })
569
570 it('should hydrate - promise', function(done) {
571 db.books.get(1).then(function(book) {
572 book.hydrate('author').then(function() {
573 ok(book.author.id === book.author_id, 'did not hydrate author')
574 ok(book.id === 1, 'weird')
575 done()
576 })
577 }).catch(showError)
578 })
579
580 it('should hydrate composite foreign key', function(done) {
581 db.samples.insert({
582 author_id: 1,
583 book_id: 1,
584 description: 'this is an example'
585 }, function(err, data) {
586 no(err)
587 db.samples.get(data.id, function(err, sample) {
588 sample.hydrate('rating', function(err) {
589 ok(sample.rating.stars === 10, 'did not hydrate rating')
590 done()
591 })
592 })
593 })
594 })
595
596 it('should hydrate 1-to-m - promise', function(done) {
597 db.authors.get(1).then(function(author) {
598 author.hydrate('author:books').then(function() {
599 ok(author['author:books'].length === 1, 'did not hydrate author:books')
600 ok(!!author['author:books'][0].title, 'author:books[0].title')
601 done()
602 })
603 }).catch(showError)
604 })
605
606 it('should hydrate 1-to-m shorthand - promise', function(done) {
607 db.books.get(1).then(function(book) {
608 return book.hydrate('samples').then(function() {
609 ok(!!book.samples[0].description, 'book.samples[0].description')
610 done()
611 })
612 }).catch(showError)
613 })
614
615 it('should not hydrate ambiguous 1-to-m - promise', function(done) {
616 db.battles.insert({
617 author1_id: 1,
618 author2_id: 2
619 }).then(function (battle) {
620 return battle.hydrate('a1').then(function () {
621 var author = battle.a1
622 return author.hydrate('battles')
623 })
624 }).catch(function (err) {
625 ok(!!err, 'should have ambiguous hydration error')
626 done()
627 }).catch(showError)
628 })
629
630 it('should hydrate non-ambiguous 1-to-m - promise', function(done) {
631 db.battles.insert({
632 author1_id: 2,
633 author2_id: 1
634 }).then(function (battle) {
635 db.authors.get(1).then(function (author) {
636 author.hydrate(['a1:battles', 'a2:battles']).then(function () {
637 ok(!!author['a1:battles'][0].id, 'a1.battles.id')
638 ok(!!author['a2:battles'][0].id, 'a2.battles.id')
639 done()
640 })
641 })
642 }).catch(showError)
643 })
644
645 it('should not hydrate wrong 1-to-m - promise', function(done) {
646 db.books.get(1).then(function(book) {
647 return book.hydrate('author:books')
648 }).catch(function (err) {
649 ok(!!err, 'should have error')
650 done()
651 }).catch(showError)
652 })
653
654 it('should not hydrate shorthand 1-to-m conflicting column name - promise', function(done) {
655 db.authors.get(1).then(function(author) {
656 return author.hydrate('books')
657 }).catch(function (err) {
658 ok(!!err, 'should have error')
659 done()
660 }).catch(showError)
661 })
662
663 it('should hydrate in parallel - cb', function(done) {
664 db.samples.get(1, function(err, sample) {
665 ok(!err, err)
666 async.each([
667 function(next) {
668 sample.hydrate('book', next)
669 },
670 function(next) {
671 sample.hydrate('rating', next)
672 }
673 ], function (fn, end) {
674 fn(end)
675 }, function (err) {
676 no(err)
677 ok(sample.book.id === sample.book_id, 'did not hydrate book')
678 ok(sample.rating.author_id === sample.author_id, 'did not hydrate rating')
679 done()
680 })
681 })
682 })
683
684 it('should hydrate multiple in parallel - promise', function(done) {
685 db.samples.get(1).then(function(sample) {
686 sample.hydrate(['book', 'rating']).then(function () {
687 ok(sample.book.id === sample.book_id, 'did not hydrate book')
688 ok(sample.rating.author_id === sample.author_id, 'did not hydrate rating')
689 done()
690 })
691 }).catch(showError)
692 })
693
694 it('should get and hydrate - promise', function(done) {
695 db.samples.get([1, 1], {
696 hydrate: ['book', 'rating']
697 }).then(function(sample) {
698 ok(!!sample.id, 'sample.id')
699 ok(!!sample.book.id, 'sample.book.id')
700 ok(!!sample.rating.stars, 'sample.rating')
701 done()
702 }).catch(showError)
703 })
704
705 it('should find and hydrate - promise', function(done) {
706 db.books.find({
707 hydrate: 'author'
708 }).then(function(books) {
709 ok(!!books[0].id, 'books[0].id')
710 ok(!!books[0].author.id, 'books[0].author.id')
711 ok(!!books[1].id, 'books[1].id')
712 ok(!!books[1].author.id, 'books[1].author.id')
713 done()
714 }).catch(showError)
715 })
716
717 it('should findOne and hydrate - cb', function(done) {
718 db.books.findOne({
719 hydrate: ['author']
720 }, function(err, book) {
721 no(err)
722 ok(!!book.id, 'book.id')
723 ok(!!book.author.id, 'book.author.id')
724 done()
725 })
726 })
727
728 it('should set', function(done) {
729 db.authors.get(1, function(err, author) {
730 no(err)
731 var old_name = author.name
732 var new_name = 'Jack Kerouac'
733 author.set({
734 name: new_name
735 })
736 ok(author._data.name === old_name, 'did not set old name')
737 ok(author.name === new_name, 'did not set new name')
738 done()
739 })
740 })
741
742 it('should save a row instance - cb', function(done) {
743 db.authors.get(1, function(err, author) {
744 no(err)
745 var new_name = 'Jack2'
746 author.set({
747 name: new_name
748 })
749 author.save(function(err, author) {
750 ok(!err, err)
751 ok(author.id === 1, 'did not get correct author')
752 ok(author.name === new_name, 'did not save author')
753 db.authors.get(1, function(err, author) {
754 ok(!err, err)
755 ok(author.name === new_name, 'did not save new name')
756 done()
757 })
758 })
759 })
760 })
761
762 it('should save a row instance - promise', function(done) {
763 db.authors.get(1).then(function(author) {
764 var new_name = 'Jack3'
765 author.set({
766 name: new_name
767 })
768 author.save().then(function(author) {
769 ok(author.id === 1, 'did not get correct author')
770 ok(author.name === new_name, 'did not save author')
771 db.authors.get(1).then(function(author) {
772 ok(author.name === new_name, 'did not save new name')
773 done()
774 })
775 })
776 }).catch(showError)
777 })
778
779 xit('should error when calling save(data)', function(done) {
780 db.authors.get(1, function(err, author) {
781 no(err)
782 author.save({ name: 'Jack2' }, function(err, author) {
783 ok(!!err, err)
784 done()
785 })
786 })
787 })
788
789 it('should instantiate model and use constructor - cb', function(done) {
790 db.books.get(1, function(err, book) {
791 no(err)
792 ok(book instanceof db.books.Row, 'incorrect type')
793 ok(book.getTitle() === book.title, 'did not get title')
794 ok(book.getTitle2() === book.title, 'did not run model constructor')
795 var desc = Object.getOwnPropertyDescriptor(book, 'something')
796 ok(!desc.enumerable, 'did not modify data in constructor')
797 done()
798 })
799 })
800
801 it('should execute parameterized query - cb', function(done) {
802 db.execute([
803 'select id',
804 'from authors',
805 'where name = :name'
806 ], {
807 name: 'Jack3',
808 }, function(err, rs) {
809 no(err)
810 ok(rs[0].id === 1, 'wrong record')
811 done()
812 })
813 })
814
815 it('should execute parameterized query - promise', function(done) {
816 db.execute([
817 'select id',
818 'from authors',
819 'where name = :name'
820 ], {
821 name: 'Jack3',
822 }).then(function(rs) {
823 ok(rs[0].id === 1, 'wrong record')
824 done()
825 }).catch(showError)
826 })
827
828 it('should prevent semicolon sqli', function(done) {
829 db.books.find({
830 where: {
831 id: "1; update books set title = 'sqli' where id = '1"
832 }
833 }, function(err, books) {
834 if (err) return done() // postgres errors and that is cool
835 // mysql doesn't error, so let's make sure the injected sql didn't run
836 db.books.get(1, function(err, book) {
837 no(err)
838 ok(book.title !== 'sqli', 'injected update ran')
839 done()
840 })
841 })
842 })
843
844 it('should cache - cb', function(done) {
845 db.books.db._opts.cache = mockRedis()
846 db.books.get(1, function(err, book) {
847 no(err)
848 var new_title = 'New Title'
849 book.update({
850 title: new_title
851 }, function(err) {
852 no(err)
853 db.books.get(1, function(err, book) {
854 no(err)
855 ok(book.title === new_title, 'did not save new title')
856 ok(book.fromCache, 'did not get value from cache')
857 done()
858 })
859 })
860 })
861 })
862
863 it('should cache - promise', function(done) {
864 db.books.get(1).then(function(book) {
865 var new_title = 'New Title2'
866 book.update({
867 title: new_title
868 }).then(function() {
869 db.books.get(1).then(function(book) {
870 ok(book.title === new_title, 'did not save new title')
871 ok(book.fromCache, 'did not get value from cache')
872 db.books.db._opts.cache = null
873 done()
874 })
875 })
876 }).catch(showError)
877 })
878
879 it('should cache mget using composite keys - promise', function(done) {
880 db.authors.db._opts.cache = mockRedis()
881 db.authors.get(1).then(function (author) {
882 var list = [
883 { id: 1984 },
884 { id: 1 },
885 { id: 1408 }
886 ]
887 db.authors.mget(list).then(function(authors) {
888 ok(authors[0].id === list[0].id, 'did not get author 1')
889 ok(authors[1].id === list[1].id, 'did not get author 2')
890 ok(authors[2].id === list[2].id, 'did not get author 3')
891 db.authors.get([1408]).then(function (author) {
892 ok(author.id === 1408, 'did not get author')
893 done()
894 })
895 })
896 }).catch(showError)
897 })
898
899 it('should delete', function(done) {
900 var newBook = {
901 title: 'XYZ Book',
902 author: {
903 name: 'XYZ Author'
904 }
905 }
906 db.books.insert(newBook, function(err, book) {
907 no(err)
908 var bookId = book.id
909 var authorId = book.author_id
910 ok(!!book.id, 'did not insert book')
911 book.delete(function(err) {
912 no(err)
913 ok(!book.id, 'book should be deleted')
914 db.books.get(bookId, function (err, book) {
915 ok(!!err && !!err.notFound, 'should not find deleted book')
916 db.authors.get(authorId, function (err, author) {
917 no(err)
918 ok(!!author.id, 'should not delete author')
919 done()
920 })
921 })
922 })
923 })
924 })
925
926 it('should not delete without cascade', function(done) {
927 var newBook = {
928 title: 'XYZ Book',
929 author: {
930 name: 'XYZ Author'
931 }
932 }
933 db.books.insert(newBook, function(err, book) {
934 no(err)
935 var bookId = book.id
936 var authorId = book.author_id
937 ok(!!book.id, 'did not insert book')
938 book.hydrate('author', function (err) {
939 no(err)
940 book.author.delete(function(err) {
941 ok(!!err, 'should not delete without cascade')
942 done()
943 })
944 })
945 })
946 })
947
948 it('should save 1-to-1 nested object (insert + insert)', function(done) {
949 var newBook = {
950 id: 11,
951 title: 'Book #1',
952 author: {
953 name: 'Author #1'
954 }
955 }
956 db.books.save(newBook, function(err, book) {
957 no(err)
958 ok(book.id === 11, 'did not insert book')
959 book.hydrate('author', function(err) {
960 no(err)
961 ok(book.author_id === book.author.id, 'did not insert author')
962 done()
963 })
964 })
965 })
966
967 it('should save 1-to-1 nested object (update + insert) - promise', function(done) {
968 db.books.get(2).then(function(book) {
969 // replace the book's author with a newly inserted author
970 book.author = {
971 name: 'Author #2'
972 }
973 book.save().then(function(book) {
974 ok(book.id === 2, 'did not get book')
975 book.hydrate('author').then(function() {
976 ok(book.author_id === book.author.id, 'did not insert author')
977 done()
978 })
979 })
980 }).catch(showError)
981 })
982
983 it('should save 1-to-1-to-1 nested objects (insert + insert + insert)', function(done) {
984 var newBookData = {
985 title: 'my title',
986 author: {
987 name: 'Author #3',
988 Country: {
989 Code: 'US',
990 name: 'United States'
991 }
992 }
993 }
994 db.books.save(newBookData, function(err, book) {
995 no(err)
996 book.hydrate('author', function(err) {
997 no(err)
998 book.author.hydrate('Country', function(err) {
999 no(err)
1000 ok(book.author.Country.name === 'United States', 'did not save')
1001 done()
1002 })
1003 })
1004 })
1005 })
1006
1007 it('should not save shorthand 1-to-m w/ column name conflict', function(done) {
1008 var newAuthor = {
1009 name: 'Jimbo Jimson',
1010 books: [
1011 { title: 'My First Book' }
1012 ]
1013 }
1014 db.authors.save(newAuthor).catch(function (err) {
1015 ok(!!err, 'should have error')
1016 done()
1017 })
1018 })
1019
1020 it('should not save a 1-to-m field that is not an array', function(done) {
1021 var newAuthor = {
1022 name: 'Jimbo Jimson',
1023 'author:books': { title: 'My First Book' }
1024 }
1025 db.authors.save(newAuthor).catch(function (err) {
1026 ok(!!err, 'should have error')
1027 done()
1028 })
1029 })
1030
1031 it('should save 1-to-m (insert + insert)', function(done) {
1032 var newAuthor = {
1033 name: 'Jimbo Jimson',
1034 'author:books': [
1035 { title: 'My First Book' },
1036 { title: 'My Second Book' }
1037 ]
1038 }
1039 db.authors.save(newAuthor, function(err, author) {
1040 no(err)
1041 ok(!!author.id, 'did not insert author')
1042 ok(author.name === newAuthor.name, 'wrong author.name')
1043 var property = 'author:books'
1044 author.hydrate([property], function(err) {
1045 no(err)
1046 ok(!!author[property], 'did not hydrate books')
1047 ok(author[property].length === newAuthor[property].length, 'wrong number of books')
1048 var book = author[property][0]
1049 ok(book.author_id === author.id, 'did not insert book')
1050 ok(book.title === newAuthor[property][0].title, 'wrong title')
1051 done()
1052 })
1053 })
1054 })
1055
1056 it('should save shorthand 1-to-m (insert + insert)', function(done) {
1057 var newBook = {
1058 title: 'A Great Book',
1059 samples: [
1060 { description: 'Something!' },
1061 { description: 'Something else!' }
1062 ]
1063 }
1064 db.books.save(newBook, function(err, book) {
1065 no(err)
1066 ok(!!book.id, 'did not insert book')
1067 ok(book.title === newBook.title, 'wrong book.title')
1068 var property = 'samples'
1069 book.hydrate(property, function(err) {
1070 no(err)
1071 ok(!!book[property], 'did not hydrate samples')
1072 ok(book[property].length === newBook[property].length, 'wrong number of samples')
1073 var sample = book[property][0]
1074 ok(sample.book_id === book.id, 'did not insert sample')
1075 ok(sample.description === newBook[property][0].description, 'wrong description')
1076 done()
1077 })
1078 })
1079 })
1080
1081 it('should propagate errors', function() {
1082 return db.books.find().then(function (books) {
1083 console.log('ReferenceError', undefinedVariable)
1084 }).catch(function (err) {
1085 ok(!!err, 'should propogate error')
1086 })
1087 })
1088
1089 xit('TODO should populate linking table keys', function(done) {
1090 var newAuthor = {
1091 name: 'Chuck Palahniuk',
1092 ratings: [
1093 {
1094 stars: 5,
1095 book: { title: 'Fight Club' }
1096 },
1097 {
1098 stars: 4,
1099 book: { title: 'Choke' }
1100 }
1101 ]
1102 }
1103 db.authors.save(newAuthor, function(err, author) {
1104 no(err)
1105 ok(!!author.id, 'did not insert author')
1106 ok(author.name === newAuthor.name, 'wrong author.name')
1107 var property = 'ratings'
1108 author.hydrate([property], function(err) {
1109 no(err)
1110 ok(!!author[property], 'did not hydrate')
1111 ok(author[property].length === newAuthor[property].length, 'wrong qty')
1112 var rating = author[property][0]
1113 console.log('rating:', rating)
1114 rating.hydrate('book', function(err) {
1115 var book = rating.book
1116 console.log('book:', book)
1117 ok(book.author_id === author.id, 'did not save book.author_id')
1118 ok(book.title === newAuthor.ratings[0].book.title, 'wrong title')
1119 done()
1120 })
1121 })
1122 })
1123 })
1124
1125 // TODO:
1126 // save rows of the same table in parallel
1127 // should fail saving a 1-to-m row that attempts to modify a foreign key column
1128 // should not allow updating foreign key value(s) in a 1-to-1 nested save
1129 // should not allow updating a 1-to-m row if primary key is specified and fk doesn't match this.pk
1130 // should not allow updating a 1-to-1 row if primary key is specified and pk not match this.ftbl_id
1131 // composite primary key insert / update
1132 // primary key id is specified for insert
1133 // table has no primary key
1134 // update table with javascript Date value
1135 // test sql-injection during save - not just select
1136 // onReady
1137 // failed transactions rollback as expected saving 1-to-1 and 1-to-m
1138 // 1-to-1 and 1-to-m unmodified values should not be updated
1139 // uncaught error when trying to save to a 1-to-m that exists but linked to a different table
1140
1141 it('should kill the connection pool', function (done) {
1142 var isDone = false
1143 db.end(function () {
1144 if (isDone) return
1145 isDone = true
1146 done()
1147 })
1148 })
1149
1150 })
1151
1152})