UNPKG

22 kBJavaScriptView Raw
1'use strict';
2var ObjectManage = require('../lib/ObjectManage')
3var expect = require('chai').expect
4var EventEmitter = require('events').EventEmitter
5
6
7describe('ObjectManage',function(){
8 var data1 = {test1: 'val1', test2: 'val2'}
9 , data2 = {test3: 'val3', test4: 'val4'}
10 , data3 = {test5: {test6: 'val6'}}
11
12 describe('Prototype',function(){
13 it('should have a merge function',function(){
14 expect(ObjectManage.prototype.hasOwnProperty('$merge')).to.equal(true)
15 expect(ObjectManage.prototype.$merge).to.be.a('function')
16 })
17 })
18
19 describe('Construction',function(){
20 it('should accept data to the constructor',function(){
21 var obj = new ObjectManage(data1)
22 expect(obj.test1).to.equal('val1')
23 expect(obj.test2).to.equal('val2')
24 })
25 it('should be able to merge in data after constructing',function(){
26 var obj = new ObjectManage(data1,data2)
27 expect(obj.test3).to.equal('val3')
28 expect(obj.test4).to.equal('val4')
29 })
30 })
31
32 describe('Static methods',function(){
33 it('should be able to clone an existing object',function(){
34 var obj = ObjectManage.$clone(data1)
35 expect(obj.test1).to.equal('val1')
36 })
37 it('should be able to extract internals from ObjectManage',function(){
38 var obj1 = new ObjectManage(data1)
39 var int = ObjectManage.$extract(obj1)
40 expect(int.$e).to.be.instanceof(EventEmitter)
41 })
42 it('should be able to clone ObjectManage objects',function(){
43 var obj1 = new ObjectManage(data1)
44 var obj2 = ObjectManage.$clone(obj1)
45 expect(obj2.$get('test1')).to.equal('val1')
46 })
47 it('should be able to extend an existing object',function(){
48 var obj = ObjectManage.$clone(data1)
49 obj = ObjectManage.$extend(obj)
50 expect(obj.$get('test1')).to.equal('val1')
51 })
52
53 })
54
55 describe('Setters and Getters',function(){
56 it('should be able to get a nested key',function(){
57 var obj = new ObjectManage(data1,data3)
58 expect(obj.$get('test5.test6')).to.equal('val6')
59 })
60 it('should be able to set a nested key',function(){
61 var obj = new ObjectManage(data1,data3)
62 obj.$set('test5.test7','val7')
63 expect(obj.test5.test7).to.equal('val7')
64 })
65 it('should overwrite a key to object if nested below',function(){
66 var obj = new ObjectManage(data1,data2,data3)
67 obj.$set('test5.test6.new','val8')
68 expect(obj.test5.test6.new).to.equal('val8')
69 })
70 it('should returned undefined on a missing key',function(){
71 var obj = new ObjectManage(data1)
72 expect(obj.$get('test5.test6')).to.equal(undefined)
73 })
74 it('should return a path tree as an array',function(){
75 var obj = new ObjectManage(
76 data1,
77 data2,
78 data3,
79 {
80 test7: {
81 test8: {
82 test9: 'foo',
83 test10: 'bar'
84 }
85 }
86 }
87 )
88 expect(obj.$getPaths()).to.include.members([
89 'test1',
90 'test2',
91 'test3',
92 'test4',
93 'test5',
94 'test5.test6',
95 'test7',
96 'test7.test8',
97 'test7.test8.test9',
98 'test7.test8.test10'
99 ])
100 })
101 })
102
103 describe('Path Normalization',function(){
104 var obj
105 beforeEach(function(){
106 obj = new ObjectManage(data3)
107 })
108 it('should allow a period separated string',function(){
109 expect(obj.$get('test5.test6')).to.equal('val6')
110 })
111 it('should allow an array of path parts',function(){
112 expect(obj.$get(['test5','test6'])).to.equal('val6')
113 })
114 it('should allow a function that returns a path',function(){
115 expect(obj.$get(function(){return 'test5.test6'})).to.equal('val6')
116 })
117 it('should allow a function to return an array of path parts',function(){
118 expect(obj.$get(function(){return ['test5','test6']})).to.equal('val6')
119 })
120 it('should allow a toString of an object that returns a path',function(){
121 expect(obj.$get({toString: function(){return 'test5.test6'}})).to.equal('val6')
122 })
123 })
124
125 describe('Removal and Existence',function(){
126 it('should return true if the property exists',function(){
127 var obj = new ObjectManage(data1)
128 expect(obj.$exists('test1')).to.equal(true)
129 })
130 it('should return false if the property does not exist',function(){
131 var obj = new ObjectManage()
132 expect(obj.$exists('test1')).to.equal(false)
133 })
134 it('should remove a property and all its children',function(){
135 var obj = new ObjectManage(data1,data2,data3)
136 obj.$remove('test5')
137 expect(obj.hasOwnProperty('test5')).to.equal(false)
138 })
139 it('should reset the data object',function(){
140 var obj = new ObjectManage(data1,data2,data3)
141 obj.$reset()
142 expect(obj.$get('test1')).to.equal(undefined)
143 })
144 it('should hand back only the data using strip',function(){
145 var obj = new ObjectManage(data1)
146 obj = obj.$strip()
147 expect(obj.test1).to.equal('val1')
148 })
149 })
150
151 describe('Merging',function(){
152 it('should always have an object at .data',function(){
153 var obj = new ObjectManage()
154 obj.$load(undefined)
155 expect(obj).to.be.an('object')
156 })
157 it('should return the whole object if no argument if passed to get',function(){
158 var obj = new ObjectManage(data1,data2,data3)
159 expect(Object.keys(obj.$get()).length).to.equal(5)
160 })
161 it('should merge recursively',function(){
162 var obj = new ObjectManage(data3)
163 obj.$load({test5: {test7: 'val7'}})
164 expect(obj.$get('test5.test6')).to.equal('val6')
165 expect(obj.$get('test5.test7')).to.equal('val7')
166 })
167 })
168
169 describe('Events',function(){
170 it('should be able to watch the data object',function(done){
171 var obj = new ObjectManage()
172 obj.$e.once('load',function(data){
173 expect(data.test5.test6).to.equal('val6')
174 done()
175 })
176 obj.$load(data3)
177 })
178 it('should emit a set event',function(done){
179 var obj = new ObjectManage()
180 obj.$e.once('set',function(path,value){
181 expect(path).to.equal('foo')
182 expect(value).to.equal('baz')
183 done()
184 })
185 obj.$set('foo','baz')
186 })
187 it('should emit a get event',function(done){
188 var obj = new ObjectManage(data1)
189 obj.$e.once('get',function(path,value){
190 expect(path).to.equal('test1')
191 expect(value).to.equal('val1')
192 done()
193 })
194 obj.$get('test1')
195 })
196 it('should emit an exists event',function(done){
197 var obj = new ObjectManage(data1)
198 obj.$e.once('exists',function(path,exists){
199 expect(path).to.equal('foo')
200 expect(exists).to.equal(false)
201 done()
202 })
203 obj.$exists('foo')
204 })
205 it('should emit a remove event',function(done){
206 var obj = new ObjectManage(data1)
207 obj.$e.once('remove',function(path,removed){
208 expect(path).to.equal('test1')
209 expect(removed).to.equal(true)
210 done()
211 })
212 obj.$remove('test1')
213 })
214 it('should emit a load event',function(done){
215 var obj = new ObjectManage()
216 obj.$e.once('load',function(data){
217 expect(data.test1).to.equal('val1')
218 done()
219 })
220 obj.$load(data1)
221 })
222 })
223
224 describe('Set with Validation',function(){
225 it('should validate using drop',function(done){
226 var obj = new ObjectManage()
227 obj.$validateSet = function(path){
228 expect(path).to.equal('test1')
229 this.drop(path + ' must be set to val2')
230 }
231 obj.$e.once('drop',function(verb,message,path,value){
232 expect(verb).to.equal('set')
233 expect(message).to.equal('test1 must be set to val2')
234 expect(path).to.equal('test1')
235 expect(value).to.equal('val3')
236 done()
237 })
238 obj.$set('test1','val3')
239 })
240 it('should validate using reject',function(done){
241 var obj = new ObjectManage()
242 obj.$validateSet = function(path){
243 expect(path).to.equal('test1')
244 this.reject(path + ' must be set to val2')
245 }
246 obj.$e.once('reject',function(verb,message,path,value){
247 expect(verb).to.equal('set')
248 expect(message).to.equal('test1 must be set to val2')
249 expect(path).to.equal('test1')
250 expect(value).to.equal('val3')
251 done()
252 })
253 obj.$set('test1','val3')
254 })
255 it('should validate using warn',function(done){
256 var obj = new ObjectManage()
257 obj.$validateSet = function(path,value){
258 expect(path).to.equal('test1')
259 this.warn(path + ' must be set to val2',value)
260 }
261 obj.$e.once('warn',function(verb,message,path,value){
262 expect(verb).to.equal('set')
263 expect(message).to.equal('test1 must be set to val2')
264 expect(path).to.equal('test1')
265 expect(value).to.equal('val3')
266 expect(obj.$get('test1')).to.equal('val3')
267 done()
268 })
269 obj.$set('test1','val3')
270 })
271 it('should validate using error',function(done){
272 var obj = new ObjectManage()
273 obj.$validateSet = function(path){
274 expect(path).to.equal('test1')
275 this.error(path + ' must be set to val2')
276 }
277 obj.$e.once('error',function(verb,message,path,value){
278 expect(verb).to.equal('set')
279 expect(message).to.equal('test1 must be set to val2')
280 expect(path).to.equal('test1')
281 expect(value).to.equal('val3')
282 done()
283 })
284 expect(function(){obj.$set('test1','val3')}).to.throw('test1 must be set to val2')
285 })
286 it('should validate using ok',function(done){
287 var obj = new ObjectManage()
288 obj.$validateSet = function(path,value){
289 expect(path).to.equal('test1')
290 this.ok(value)
291 }
292 obj.$e.once('set',function(path,value){
293 expect(path).to.equal('test1')
294 expect(value).to.equal('val3')
295 expect(obj.$get('test1')).to.equal('val3')
296 done()
297 })
298 obj.$set('test1','val3')
299 })
300 it('should validate using return',function(done){
301 var obj = new ObjectManage()
302 obj.$validateSet = function(path,value){
303 expect(path).to.equal('test1')
304 return value
305 }
306 obj.$e.once('set',function(path,value){
307 expect(path).to.equal('test1')
308 expect(value).to.equal('val3')
309 expect(obj.$get('test1')).to.equal('val3')
310 done()
311 })
312 obj.$set('test1','val3')
313 })
314 })
315
316 describe('Get with Validation',function(){
317 it('should validate using drop',function(done){
318 var obj = new ObjectManage()
319 obj.$validateGet = function(path){
320 expect(path).to.equal('test1')
321 this.drop(path + ' must be set to val2')
322 }
323 obj.$e.once('drop',function(verb,message,path,value){
324 expect(verb).to.equal('get')
325 expect(message).to.equal('test1 must be set to val2')
326 expect(path).to.equal('test1')
327 expect(value).to.equal('val3')
328 done()
329 })
330 obj.$set('test1','val3')
331 obj.$get('test1')
332 })
333 it('should validate using reject',function(done){
334 var obj = new ObjectManage()
335 obj.$validateGet = function(path){
336 expect(path).to.equal('test1')
337 this.reject(path + ' must be set to val2')
338 }
339 obj.$e.once('reject',function(verb,message,path,value){
340 expect(verb).to.equal('get')
341 expect(message).to.equal('test1 must be set to val2')
342 expect(path).to.equal('test1')
343 expect(value).to.equal('val3')
344 done()
345 })
346 obj.$set('test1','val3')
347 obj.$get('test1')
348 })
349 it('should validate using warn',function(done){
350 var obj = new ObjectManage()
351 obj.$validateGet = function(path,value){
352 expect(path).to.equal('test1')
353 this.warn(path + ' must be set to val2',value)
354 }
355 obj.$e.once('warn',function(verb,message,path,value){
356 expect(verb).to.equal('get')
357 expect(message).to.equal('test1 must be set to val2')
358 expect(path).to.equal('test1')
359 expect(value).to.equal('val3')
360 done()
361 })
362 obj.$set('test1','val3')
363 obj.$get('test1')
364 })
365 it('should validate using error',function(done){
366 var obj = new ObjectManage()
367 obj.$validateGet = function(path){
368 expect(path).to.equal('test1')
369 this.error(path + ' must be set to val2')
370 }
371 obj.$e.once('error',function(verb,message,path,value){
372 expect(verb).to.equal('get')
373 expect(message).to.equal('test1 must be set to val2')
374 expect(path).to.equal('test1')
375 expect(value).to.equal('val3')
376 done()
377 })
378 obj.$set('test1','val3')
379 expect(function(){obj.$get('test1')}).to.throw('test1 must be set to val2')
380 })
381 it('should validate using ok',function(done){
382 var obj = new ObjectManage()
383 obj.$validateGet = function(path){
384 expect(path).to.equal('test1')
385 this.ok('val4')
386 }
387 obj.$e.once('get',function(path,value){
388 expect(path).to.equal('test1')
389 expect(value).to.equal('val4')
390 done()
391 })
392 obj.$set('test1','val3')
393 obj.$get('test1')
394 })
395 it('should validate using return',function(done){
396 var obj = new ObjectManage()
397 obj.$validateGet = function(path){
398 expect(path).to.equal('test1')
399 return 'val4'
400 }
401 obj.$e.once('get',function(path,value){
402 expect(path).to.equal('test1')
403 expect(value).to.equal('val4')
404 done()
405 })
406 obj.$set('test1','val3')
407 obj.$get('test1')
408 })
409 })
410
411 describe('Circular Reference Handling',function(){
412 it('should fail on circular referenced objects',function(){
413 var x = {a: function(){return null}}
414 x.b = x.a
415 var obj = new ObjectManage()
416 function load(){
417 obj.$load(x)
418 }
419 expect(load).to.throw(Error)
420 })
421 it('should work using merge-recursive',function(){
422 var obj = new ObjectManage()
423 obj.$merge = ObjectManage.prototype.$mergeRecursive
424 obj.$load(data1)
425 obj.$load(data3)
426 expect(obj.$get('test5.test6')).to.equal('val6')
427 })
428 it('should count object depth accurately',function(){
429 var testObject = {foo: {foo: {foo: 'baz'}}}
430 , obj = new ObjectManage()
431 expect(obj.$countDepth(testObject)).to.equal(3)
432 })
433 it('should not mistake object length for depth',function(){
434 var testObject = {}
435 for(var i = 0; i < 100; i++){
436 testObject[i] = {name: 'foo'}
437 }
438 var obj = new ObjectManage()
439 expect(obj.$countDepth(testObject)).to.equal(2)
440 })
441 it('should warn on objects more than 50 levels deep',function(){
442 //setup console mocking
443 var oldLog = console.log
444 var oldTrace = console.trace
445 var log = []
446 var traced = false
447 console.log = function(msg){
448 log.push(msg)
449 }
450 console.trace = function(){
451 traced = true
452 }
453 var buildObject = function(object,depth,count){
454 if(undefined === object) object = {}
455 if(undefined === count) count = 1
456 if(undefined === depth) depth = 100
457 if(count > depth) return object
458 object[count] = buildObject(object[count],depth,(count + 1))
459 return object
460 }
461 //var obj = new ObjectManage()
462 var badObject = buildObject()
463 var obj = new ObjectManage()
464 //verify the limiter on depth is working
465 expect(obj.$countDepth(badObject)).to.equal(obj.$maxDepth)
466 //verify that we can adjust the maxDepth
467 var oldMaxDepth = obj.$maxDepth
468 obj.$maxDepth = 20
469 expect(obj.$countDepth(badObject)).to.equal(20)
470 //restore original
471 obj.$maxDepth = oldMaxDepth
472 //load the bad object which should puke
473 obj.$load(badObject)
474 //verify the warning was thrown
475 expect(log[0]).to.equal('WARN [object-manage]: Object being merged is too deep (50)')
476 expect(traced).to.equal(true)
477 //cleanup
478 log = []
479 traced = false
480 //set the max depth to a lower value and try again
481 obj.$maxDepth = 10
482 //load the bad object which should puke
483 obj.$load(badObject)
484 expect(log[0]).to.equal('WARN [object-manage]: Object being merged is too deep (10)')
485 expect(traced).to.equal(true)
486 //restore native console functions
487 console.log = oldLog
488 console.trace = oldTrace
489 })
490 })
491
492 describe('Storage Drivers',function(){
493 it('should return the instance so the storage method can be chained',function(){
494 var obj = new ObjectManage().$storage('memory')
495 expect(obj).to.be.an.instanceof(ObjectManage)
496 })
497 it('should allow setting a handle',function(){
498 var obj = new ObjectManage().$storage('memory')
499 obj.$setHandle('foo')
500 expect(obj.$getHandle()).to.equal('foo')
501 })
502 describe('Memory Driver',function(){
503 it('should save with the memory driver',function(done){
504 var obj = new ObjectManage()
505 obj.$set('foo','yes')
506 obj.$save(function(err,handle,data){
507 if(err) throw err
508 expect(handle).to.not.equal(null)
509 expect(data.foo).to.equal('yes')
510 done()
511 })
512 })
513 it('should restore with the memory driver',function(done){
514 var obj = new ObjectManage()
515 obj.$set('foo','yes')
516 obj.$save(function(err,handle){
517 if(err) throw err
518 expect(handle).to.not.equal(null)
519 //try to restore the object
520 var obj2 = new ObjectManage()
521 obj2.$restore(handle,function(err,data){
522 if(err) throw err
523 expect(data.foo).to.equal('yes')
524 expect(obj2.$get('foo')).to.equal('yes')
525 done()
526 })
527 })
528 })
529 it('should flush with the memory driver',function(done){
530 var obj = new ObjectManage()
531 obj.$set('foo','yes')
532 obj.$save(function(err,handle){
533 if(err) throw err
534 expect(handle).to.not.equal(null)
535 //try to restore the object
536 var obj2 = new ObjectManage()
537 obj2.$restore(handle,function(err,data){
538 if(err) throw err
539 expect(data.foo).to.equal('yes')
540 expect(obj2.$get('foo')).to.equal('yes')
541 obj2.$flush(function(err){
542 if(err) throw err
543 done()
544 })
545 })
546 })
547 })
548 })
549 describe('Redis Driver',function(done){
550 it('should connect with full options',function(){
551 var options = {
552 driver: 'redis',
553 options: {
554 host: '127.0.0.1',
555 port: 6379
556 },
557 ready: function(err){
558 if(err) throw err
559 console.log('redis is ready')
560 done()
561 }
562 }
563 new ObjectManage().$storage(options)
564 })
565 it('should save with the redis driver',function(done){
566 var obj = new ObjectManage().$storage('redis')
567 obj.$set('foo','yes')
568 obj.$save(function(err,handle,data){
569 if(err) throw err
570 expect(handle).to.not.equal(null)
571 expect(data.foo).to.equal('yes')
572 done()
573 })
574 })
575 it('should restore with the redis driver',function(done){
576 var obj = new ObjectManage().$storage('redis')
577 obj.$set('foo','yes')
578 obj.$save(function(err,handle){
579 if(err) throw err
580 expect(handle).to.not.equal(null)
581 //try to restore the object
582 var obj2 = new ObjectManage().$storage('redis')
583 obj2.$restore(handle,function(err,data){
584 if(err) throw err
585 expect(data.foo).to.equal('yes')
586 expect(obj2.$get('foo')).to.equal('yes')
587 done()
588 })
589 })
590 })
591 it('should flush with the redis driver',function(done){
592 var obj = new ObjectManage().$storage('redis')
593 obj.$set('foo','yes')
594 obj.$save(function(err,handle){
595 if(err) throw err
596 expect(handle).to.not.equal(null)
597 //try to restore the object
598 var obj2 = new ObjectManage().$storage('redis')
599 obj2.$restore(handle,function(err,data){
600 if(err) throw err
601 expect(data.foo).to.equal('yes')
602 expect(obj2.$get('foo')).to.equal('yes')
603 obj2.$flush(function(err){
604 if(err) throw err
605 done()
606 })
607 })
608 })
609 })
610 })
611 describe('Storage Events',function(){
612 it('should emit a generateHandle event',function(done){
613 var obj = new ObjectManage()
614 obj.$e.once('generateHandle',function(handle){
615 expect(handle).to.not.equal(null)
616 done()
617 })
618 obj.$generateHandle()
619 })
620 it('should emit a save event',function(done){
621 var obj = new ObjectManage()
622 obj.$e.once('save',function(handle,data){
623 expect(handle).to.not.equal(null)
624 expect(data.foo).to.equal('yes')
625 done()
626 })
627 obj.$set('foo','yes')
628 obj.$save()
629 })
630 it('should emit a restore event',function(done){
631 var obj1 = new ObjectManage()
632 obj1.$set('foo','yes')
633 obj1.$save(function(err,handle){
634 if(err) throw err
635 var obj = new ObjectManage()
636 obj.$e.once('restore',function(err,data){
637 if(err) throw err
638 expect(data.foo).to.equal('yes')
639 done()
640 })
641 obj.$restore(handle)
642 })
643 })
644 it('should emit a flush event',function(done){
645 var obj1 = new ObjectManage()
646 obj1.$set('foo','yes')
647 obj1.$save(function(err,handle){
648 if(err) throw err
649 var obj = new ObjectManage()
650 obj.$e.once('restore',function(err,data){
651 if(err) throw err
652 expect(data.foo).to.equal('yes')
653 obj.$e.once('flush',function(err){
654 if(err) throw err
655 done()
656 })
657 obj.$flush()
658 })
659 obj.$restore(handle)
660 })
661 })
662 })
663 })
664})