UNPKG

gun

Version:

A realtime, decentralized, offline-first, graph data synchronization engine.

1,743 lines (1,657 loc) 237 kB
describe('Gun', function(){ var root; (function(){ var env; if(typeof global !== 'undefined'){ env = global } if(typeof window !== 'undefined'){ env = window } root = env.window? env.window : global; try{ env.window && root.localStorage && root.localStorage.clear() }catch(e){} try{ localStorage.clear() }catch(e){} try{ require('fs').unlinkSync('data.json') }catch(e){} try{ require('../lib/fsrm')('radatatest') }catch(e){} //root.Gun = root.Gun || require('../gun'); if(root.Gun){ root.Gun = root.Gun; root.Gun.TESTING = true; } else { require('../lib/yson'); root.Gun = require('../gun'); root.Gun.TESTING = true; require('../lib/store'); require('../lib/rfs'); //console.log("UNDO THIS SO RAD & SEA RUN!"); require('./rad/rad.js'); require('./sea/sea.js'); } }(this)); //Gun.log.squelch = true; var gleak = {globals: {}, check: function(){ // via tobyho var leaked = [] for (var key in gleak.globe){ if (!(key in gleak.globals)){ leaked.push(key)} } if (leaked.length > 0){ console.log("GLOBAL LEAK!", leaked); return leaked } }}; (function(env){ for (var key in (gleak.globe = env)){ gleak.globals[key] = true } }(this)); var t = {}; describe('Utility', function(){ it('deleting old GUN tests (may take long time)', function(done){ done(); // Mocha doesn't print test until after its done, so show this first. }); it('deleted', function(done){ this.timeout(60 * 1000); if(!Gun.window){ return done() } indexedDB.deleteDatabase('radatatest').onsuccess = function(e){ done() } }); var u; /* // causes logger to no longer log. it('verbose console.log debugging', function(done) { var gun = Gun(); var log = root.console.log, counter = 1; root.console.log = function(a,b,c){ --counter; //log(a,b,c); } Gun.log.verbose = true; gun.put('bar', function(err, yay){ // intentionally trigger an error that will get logged. expect(counter).to.be(0); Gun.log.verbose = false; gun.put('bar', function(err, yay){ // intentionally trigger an error that will get logged. expect(counter).to.be(0); root.console.log = log; done(); }); }); } ); */ describe('YSON', function(){ it('parse', function(){ //var json = require('fs').readFileSync('./radix.json').toString(); //var json = require('fs').readFileSync('./data.json').toString(); //var json = require('fs').readFileSync('./big.json').toString(); //var json = require('fs').readFileSync('./stats.json').toString(); //var json = require('fs').readFileSync('./video.json').toString(); }); it('backslash', function(done){ var o = {z:"test\"wow\\"}; JSON.stringifyAsync(o, function(err,t){ JSON.parseAsync(t, function(err,data){ expect(data).to.be.eql(o); next(); }) }); function next(){ JSON.parseAsync('{"webRTCsdp":"v=0\r\no=-"}', function(err,data){ var o = {webRTCsdp: 'v=0\r\no=-'}; expect(data).to.be.eql(o); JSON.stringifyAsync(o, function(err,t){ expect(JSON.parse(t)).to.be.eql(o); expect(t).to.be(JSON.stringify(o)); expect(t).to.be('{"webRTCsdp":"v=0\\r\\no=-"}'); JSON.parseAsync(t, function(err,d){ expect(d).to.be.eql(o); done(); }) }); }) } }); it('stringify', function(done){ function Foo(){}; Foo.prototype.toJSON = function(){}; //var obj = {"what\"lol": {"a": 1, "b": true, "c": false, "d": null, "wow": [{"z": 9}, true, "hi", 3.3]}}; var obj = {"what": {"a": 1, "b": true, "c": false, "d": null, "wow": [{"z": 9}, true, "hi", 3.3]}}; var obj = [{x:"test 😎\\😄🔥",z:"test\\","what\"lol": {"0": 1.01},a:true,b: new Foo,c:3,y:"yes","get":{"#":"chat"},wow:undefined,foo:[1,function(){}, function(){}, 'go'],blah:{a:5,toJSON:function(){ return 9 }}}, {webRTCsdp: "v=0\r\no=-"}, [[]], 10e9, NaN]; JSON.stringifyAsync(obj, function(err, text){ JSON.parseAsync(text, function(err, data){ expect(data).to.be.eql([{x:"test 😎\\😄🔥",z:"test\\","what\"lol": {"0": 1.01},a:true,c:3,y:"yes","get":{"#":"chat"},foo:[1,null,null,'go'],blah:9}, {webRTCsdp: "v=0\r\no=-"}, [[]], 10e9, null]); var obj = {a: [], b: [""], c: ["", 1], d: [1, ""], e: {"":[]}, "a\"b": {0: 1}, wow: {'': {cool: 1}}};obj.lol = {0: {sweet: 9}};obj.wat = {"": 'cool'};obj.oh = {phew: {}, "": {}}; JSON.stringifyAsync(obj, function(err, text2){ JSON.parseAsync(text2, function(err, data){ expect(data).to.be.eql(obj); done(); }) }) }) }); }); }); describe('Type Check', function(){ it('random text',function(){ expect(String.random().length).to.be(24); expect(String.random(11).length).to.be(11); expect(String.random(4).length).to.be(4); t.tr = String.random(2,'as'); expect((t.tr=='as'||t.tr=='aa'||t.tr=='sa'||t.tr=='ss')).to.be.ok(); }); it('match text',function(){ expect(String.match("user/mark", 'user/mark')).to.be.ok(); expect(String.match("user/mark/nadal", {'=': 'user/mark'})).to.not.be.ok(); expect(String.match("user/mark/nadal", {'*': 'user/'})).to.be.ok(); expect(String.match("email/mark@gunDB.io", {'*': 'user/'})).to.not.be.ok(); expect(String.match("user/mark/nadal", {'>': 'user/j', '<': 'user/o'})).to.be.ok(); expect(String.match("user/timber/nadal", {'>': 'user/c', '<': 'user/j'})).to.not.be.ok(); expect(String.match("user/timber/nadal", {'>': 'user/m', '<': 'user/u'})).to.be.ok(); expect(String.match("user/mark/nadal", {'>': 'user/a', '<': 'user/c'})).to.not.be.ok(); expect(String.match("mary", {'<': 'm'})).to.not.be.ok(); expect(String.match("mary", {'>': 'm'})).to.be.ok(); expect(String.match("m", {'>': 'm'})).to.be.ok(); // lex is inclusive because it evaluates stricter (=) to looser (>) comparisons, see docs. expect(String.match("m", {'<': 'm'})).to.be.ok(); // lex is inclusive because it evaluates stricter (=) to looser (<) comparisons, see docs. return; // below is OLD bloat, still available in lib/match.js }); it('plain object',function(){ expect(Object.plain({})).to.be(true); expect(Object.plain({a:1})).to.be(true); expect(Object.plain(u)).to.be(false); expect(Object.plain()).to.be(false); expect(Object.plain(undefined)).to.be(false); expect(Object.plain(null)).to.be(false); expect(Object.plain(NaN)).to.be(false); expect(Object.plain(0)).to.be(false); expect(Object.plain(1)).to.be(false); expect(Object.plain('')).to.be(false); expect(Object.plain('a')).to.be(false); expect(Object.plain([])).to.be(false); expect(Object.plain([1])).to.be(false); expect(Object.plain(false)).to.be(false); expect(Object.plain(true)).to.be(false); expect(Object.plain(function(){})).to.be(false); expect(Object.plain(new Date())).to.be(false); expect(Object.plain(/regex/)).to.be(false); this.document && expect(Object.plain(document.createElement('div'))).to.be(false); expect(Object.plain(new (function Class(){ this.x = 1; this.y = 2 })())).to.be(true); }); it('empty',function(){ expect(Object.empty()).to.be(true); expect(Object.empty({a:false})).to.be(false); expect(Object.empty({a:false},['a'])).to.be(true); expect(Object.empty({a:false},['a'])).to.be(true); expect(Object.empty({a:false,b:1},['a'])).to.be(false); expect(Object.empty({a:false,b:1},['a'])).to.be(false); expect(Object.empty({a:false,b:1},['a','b'])).to.be(true); expect(Object.empty({a:false,b:1,c:3},['a','b'])).to.be(false); expect(Object.empty({1:1},'danger')).to.be(false); }); }); describe('Functions', function(){ /* it.skip('sum',function(done){ // deprecate? var obj = {a:2, b:2, c:3, d: 9}; Gun.obj.map(obj, function(num, key){ setTimeout(this.add(function(){ this.done(null, num * num); }, key), parseInt((""+Math.random()).substring(2,5))); }, Gun.fn.sum(function(err, val){ expect(val.a).to.eql(4); expect(val.b).to.eql(4); expect(val.c).to.eql(9); expect(val.d).to.eql(81); done(); })); }); */ }); describe('On', function(){ it('subscribe', function(done){ var e = {on: Gun.on}; e.on('foo', function(a){ done.first = true; expect(a).to.be(1); this.to.next(a); }); e.on('foo', function(a){ expect(a).to.be(1); expect(done.first).to.be.ok(); done(); }); e.on('foo', 1); }); it('unsubscribe', function(done){ var e = {on: Gun.on}; e.on('foo', function(a){ this.off(); done.first = a; expect(a).to.be(1); this.to.next(a); }); e.on('foo', function(a){ var to = this; expect(a).to.be(done.second? 2 : 1); expect(done.first).to.be(1); done.second = true; if(a === 2){ setTimeout(function(){ expect(e.tag.foo.to === to).to.be.ok(); done(); }, 10); } }); e.on('foo', 1); e.on('foo', 2); }); it('stun', function(done){ var e = {on: Gun.on}; e.on('foo', function(a, ev){ if(2 === a){ done.first2 = true; this.to.next(a); return; } setTimeout(function(){ expect(done.second).to.not.be.ok(); expect(done.second2).to.be.ok(); expect(done.first2).to.be.ok(); done(); },10); }); e.on('foo', function(a, ev){ if(2 === a){ done.second2 = true; } else { done.second = true; } }); e.on('foo', 1); e.on('foo', 2); }); it('resume', function(done){ var e = {on: Gun.on}; e.on('foo', function(a, ev){ var to = this.to; setTimeout(function(){ expect(done.second).to.not.be.ok(); to.next(a); },10); }); e.on('foo', function(a){ done.second = true; expect(a).to.be(1); done(); }); e.on('foo', 1); }); it('double resume', function(done){ var e = {on: Gun.on}; e.on('foo', function(a, ev){ var to = this.to; setTimeout(function(){ if(1 === a){ done.first1 = true; expect(done.second).to.not.be.ok(); } if(2 === a){ done.first2 = true; } to.next(a); },10); }); e.on('foo', function(a, ev){ done.second = true; if(1 === a){ expect(done.first2).to.not.be.ok(); done.second1 = true; } if(2 === a){ expect(done.first2).to.be.ok(); if(done.second1){ done(); } } }); e.on('foo', 1); e.on('foo', 2); }); it('double resume different event', function(done){ var e = {on: Gun.on}; e.on('foo', function(a, ev){ var to = this.to; setTimeout(function(){ done.first1 = true; to.next(a); },10); }); e.on('foo', function(a){ if(1 === a){ expect(done.first1).to.be.ok(); done(); } }); e.on('foo', 1); e.on('bar', 2); }); it('resume params', function(done){ var e = {on: Gun.on}; e.on('foo', function(a, ev){ var to = this.to; setTimeout(function(){ expect(done.second).to.not.be.ok(); to.next(0); },10); }); e.on('foo', function(a){ done.second = true; expect(a).to.be(0); done(); }); e.on('foo', 1); }); it('map', function(done){ var e = {on: Gun.on}; e.on('foo', function(a, ev){ var to = this.to; Object.keys(a.it).forEach(function(f){var v = a.it[f]; setTimeout(function(){ var emit = {field: 'where', soul: f}; to.next(emit); },10); }) }); e.on('foo', function(a, ev){ var to = this.to; setTimeout(function(){ to.next({node: a.soul}); },100); }); e.on('foo', function(a){ if('a' == a.node){ done.a = true; } else { expect(done.a).to.be.ok(); done(); } }); e.on('foo', {field: 'where', it: {a: 1, b: 2}}); }); it('map synchronous', function(done){ var e = {on: Gun.on}; e.on('foo', function(a, ev){ var to = this.to; Object.keys(a.node).forEach(function(f){var v = a.node[f]; //setTimeout(function(){ var emit = {field: 'where', soul: f}; to.next(emit); //},10); }) }); e.on('foo', function(a, ev){ var to = this.to; setTimeout(function(){ to.next({node: a.soul}); },100); }); e.on('foo', function(a){ expect(this.as.hi).to.be(1); if('a' == a.node){ done.a = true; } else { expect(done.a).to.be.ok(); done(); } }, {hi: 1}).on.on('foo', {field: 'where', node: {a: 1, b: 2}}); }); it('synchronous async', function(done){ var e = {on: Gun.on}; e.on('foo', function(a){ expect(a.b).to.be(5); done.first = true; this.to.next(a); }); e.on('foo', function(a, ev){ expect(a.b).to.be(5); done.second = true; var to = this.to; setTimeout(function(){ to.next({c: 9, again: a.again}); },100); }); e.on('foo', function(a){ this.off(); expect(a.again).to.not.be.ok(); expect(a.c).to.be(9); expect(done.first).to.be.ok(); expect(done.second).to.be.ok(); done(); }).on.on('foo', {b: 5}).on.on('foo', {b:5, again: true}); }); }); describe('flow', function(){ var i = 0; function flow(){ var f = function(arg){ var cb = f.cb? f.cb.fn : f.fn; if(cb){ f.cb = cb; var ff = flow(); ff.f = f; cb(ff); return; } if(f.f){ f.f(arg); f.cb = 0; return; } }, cb; f.flow = function(fn){ cb = (cb || f).fn = fn; return f; }; return f; } it('intermittent interruption', function(done){ var f = flow(); //var f = {flow: flow} f.flow(function(f){ //console.log(1); f.flow(function(f){ //console.log(2); f({yes: 'please'}); }); setTimeout(function(){ f.flow(function(f){ //console.log(2.1); f({forever: 'there'}); }); f({strange: 'places'}); //console.log("-----"); f({earlier: 'location'}); },100); }); f.flow(function(f){ //console.log(3); f({ok: 'now'}); }); f.flow(function(f){ //console.log(4); done(); }); setTimeout(function(){ f({hello: 'world'}); }, 100); }); var i = 0; ;(function(exports){ function next(arg){ var n = this; if(arg instanceof Function){ if(!n.fn){ return n.fn = arg, n } var f = {next: next, fn: arg, first: n.first || n}; n.last = (n.last || n).to = f; return n; } if(n.fn){ var sub = {next: next, from: n.to || (n.first || {}).from}; n.fn(sub); return; } if(n.from){ n.from.next(arg); return; } } exports.next = next; }(Gun)); it('intermittent interruptions', function(done){ //var f = flow(); var f = {next: Gun.next}; // for now f.next(function(f){ //console.log(1, f); f.next(function(f){ //console.log(2, f); f.next({yes: 'please'}); }); setTimeout(function(){ f.next(function(f){ //console.log(2.1, f); f.next({forever: 'there'}); }); f.next({strange: 'places'}); //console.log("-----"); f.next({earlier: 'location'}); },100); }); f.next(function(f){ //console.log(3); f.next({ok: 'now'}); }); f.next(function(f){ //console.log(4); if(!done.a){ return done.a = true } done(); }); setTimeout(function(){ f.next({hello: 'world'}); }, 100); }); }); describe('Gun Safety', function(){ /* WARNING NOTE: Internal API has significant breaking changes! */ var gun = Gun(); it('is',function(){ expect(Gun.is(gun)).to.be(true); expect(Gun.is(true)).to.be(false); expect(Gun.is(false)).to.be(false); expect(Gun.is(0)).to.be(false); expect(Gun.is(1)).to.be(false); expect(Gun.is('')).to.be(false); expect(Gun.is('a')).to.be(false); expect(Gun.is(Infinity)).to.be(false); expect(Gun.is(-Infinity)).to.be(false); expect(Gun.is(NaN)).to.be(false); expect(Gun.is([])).to.be(false); expect(Gun.is([1])).to.be(false); expect(Gun.is({})).to.be(false); expect(Gun.is({a:1})).to.be(false); expect(Gun.is(function(){})).to.be(false); }); it('valid',function(){ expect(Gun.valid(false)).to.be(true); expect(Gun.valid(true)).to.be(true); expect(Gun.valid(0)).to.be(true); expect(Gun.valid(1)).to.be(true); expect(Gun.valid('')).to.be(true); expect(Gun.valid('a')).to.be(true); expect(Gun.valid({'#':'somesoulidhere'})).to.be('somesoulidhere'); expect(Gun.valid({'#':'somesoulidhere', and: 'nope'})).to.be(false); expect(Gun.valid(Infinity)).to.be(false); // boohoo :( expect(Gun.valid(-Infinity)).to.be(false); // boohoo :( expect(Gun.valid(NaN)).to.be(false); expect(Gun.valid([])).to.be(false); expect(Gun.valid([1])).to.be(false); expect(Gun.valid({})).to.be(false); expect(Gun.valid({a:1})).to.be(false); expect(Gun.valid(function(){})).to.be(false); }); it('is link',function(){ expect(Gun.valid({'#':'somesoulidhere'})).to.be('somesoulidhere'); expect(Gun.valid({'#':'somethingelsehere'})).to.be('somethingelsehere'); expect('string' == typeof Gun.valid({'#':'somesoulidhere', and: 'nope'})).to.be(false); expect('string' == typeof Gun.valid({or: 'nope', '#':'somesoulidhere'})).to.be(false); expect('string' == typeof Gun.valid(false)).to.be(false); expect('string' == typeof Gun.valid(true)).to.be(false); expect('string' == typeof Gun.valid('')).to.be(false); expect('string' == typeof Gun.valid('a')).to.be(false); expect('string' == typeof Gun.valid(0)).to.be(false); expect('string' == typeof Gun.valid(1)).to.be(false); expect('string' == typeof Gun.valid(Infinity)).to.be(false); // boohoo :( expect('string' == typeof Gun.valid(-Infinity)).to.be(false); // boohoo :( expect('string' == typeof Gun.valid(NaN)).to.be(false); expect('string' == typeof Gun.valid([])).to.be(false); expect('string' == typeof Gun.valid([1])).to.be(false); expect('string' == typeof Gun.valid({})).to.be(false); expect('string' == typeof Gun.valid({a:1})).to.be(false); expect('string' == typeof Gun.valid(function(){})).to.be(false); }); it.skip('is lex',function(){ expect(Gun.is.lex({'#': 'soul'})).to.eql({soul: 'soul'}); expect(Gun.is.lex({'.': 'field'})).to.eql({field: 'field'}); expect(Gun.is.lex({'=': 'value'})).to.eql({value: 'value'}); expect(Gun.is.lex({'>': 'state'})).to.eql({state: 'state'}); expect(Gun.is.lex({'#': {'=': 'soul'}})).to.eql({soul: {'=': 'soul'}}); expect(Gun.is.lex({'#': {'=': 'soul'}, '.': []})).to.be(false); expect(Gun.is.lex({'#': {'=': 'soul'}, 'asdf': 'oye'})).to.be(false); expect(Gun.is.lex()).to.be(false); expect(Gun.is.lex('')).to.be(false); }); it.skip('is lex ify',function(){ expect(Gun.is.lex.ify({'#': 'soul', '.': 'field', soul: 'foo', field: 'field', state: 0})).to.eql({'#': 'soul', '.': 'field', '>': 0}); }); }); }); describe('ify', function(){ console.log("TODO: BUG! Upgrade IFY tests to new internal API!"); return; var test, gun = Gun(); it('null', function(done){ Gun.ify(null, function(err, ctx){ expect(err).to.be.ok(); done(); }); }); it('basic', function(done){ var data = {a: false, b: true, c: 0, d: 1, e: '', f: 'g', h: null}; Gun.ify(data, function(err, ctx){ expect(err).to.not.be.ok(); expect(ctx.err).to.not.be.ok(); expect(ctx.root).to.eql(data); expect(ctx.root === data).to.not.ok(); done(); }, {pure: true}); }); it('basic soul', function(done){ var data = {_: {'#': 'SOUL'}, a: false, b: true, c: 0, d: 1, e: '', f: 'g', h: null}; Gun.ify(data, function(err, ctx){ expect(err).to.not.be.ok(); expect(ctx.err).to.not.be.ok(); expect(ctx.root).to.eql(data); expect(ctx.root === data).to.not.be.ok(); expect(Gun.node.soul(ctx.root) === Gun.node.soul(data)); done(); }, {pure: true}); }); it('arrays', function(done){ var data = {before: {path: 'kill'}, one: {two: {lol: 'troll', three: [9, 8, 7, 6, 5]}}}; Gun.ify(data, function(err, ctx){ expect(err).to.be.ok(); expect((err.err || err).indexOf("one.two.three")).to.not.be(-1); done(); }); }); it('undefined', function(done){ var data = {z: undefined, x: 'bye'}; Gun.ify(data, function(err, ctx){ expect(err).to.be.ok(); done(); }); }); it('NaN', function(done){ var data = {a: NaN, b: 2}; Gun.ify(data, function(err, ctx){ expect(err).to.be.ok(); done(); }); }); it('Infinity', function(done){ // SAD DAY PANDA BEAR :( :( :(... Mark wants Infinity. JSON won't allow. var data = {a: 1, b: Infinity}; Gun.ify(data, function(err, ctx){ expect(err).to.be.ok(); done(); }); }); it('function', function(done){ var data = {c: function(){}, d: 'hi'}; Gun.ify(data, function(err, ctx){ expect(err).to.be.ok(); done(); }); }); it('extraneous', function(done){ var data = {_: {'#': 'shhh', meta: {yay: 1}}, sneak: true}; Gun.ify(data, function(err, ctx){ expect(err).to.not.be.ok(); // extraneous metadata needs to be stored, but it can't be used for data. done(); }); }); it('document', function(done){ var data = {users: {1: {where: {lat: Math.random(), lng: Math.random(), i: 1}}}}; Gun.ify(data, function(err, ctx){ var soul, node; expect(soul = Gun.val.link.is(ctx.root.users)).to.be.ok(); node = ctx.graph[soul]; expect(soul = Gun.val.link.is(node[1])).to.be.ok(); node = ctx.graph[soul]; expect(soul = Gun.val.link.is(node.where)).to.be.ok(); node = ctx.graph[soul]; expect(node.lat).to.be.ok(); expect(node.lng).to.be.ok(); expect(node.i).to.be(1); done(); }); }); return; // TODO! Fix GUN to handle this! data = {}; data.sneak = false; data.both = {inside: 'meta data'}; data._ = {'#': 'shhh', data: {yay: 1}, spin: data.both}; test = Gun.ify(data); expect(test.err.meta).to.be.ok(); // TODO: Fail: this passes, somehow? Fix ify code! }); describe('Schedule', function(){ console.log("TODO: BUG! Upgrade SCHEDULE tests to new internal API!"); return; it('one', function(done){ Gun.schedule(Gun.time.is(), function(){ expect(true).to.be(true); done(); //setTimeout(function(){ done() },1); }); }); it('many', function(done){ Gun.schedule(Gun.time.is() + 50, function(){ done.first = true; }); Gun.schedule(Gun.time.is() + 100, function(){ done.second = true; }); Gun.schedule(Gun.time.is() + 200, function(){ done.third = true; expect(done.first).to.be(true); expect(done.second).to.be(true); expect(done.third).to.be(true); done(); //setTimeout(function(){ done() },1); }); }); }); describe('Union', function(){ console.log("TODO: BUG! Upgrade UNION tests to new internal API!"); return; var gun = Gun(); it('fail', function(){ var prime = { 'asdf': { _: {'#': 'asdf', '>':{ a: 'cheating' }}, a: 0 } } expect(gun.__.graph['asdf']).to.not.be.ok(); var ctx = Gun.HAM.graph(gun, prime); expect(ctx).to.not.be.ok(); });return; it('basic', function(done){ var prime = { 'asdf': { _: {'#': 'asdf', '>':{ a: Gun.time.is() }}, a: 0 } } expect(gun.__.graph['asdf']).to.not.be.ok(); var ctx = Gun.union(gun, prime, function(){ expect(gun.__.graph['asdf'].a).to.be(0); done(); }); }); it('disjoint', function(done){ var prime = { 'asdf': { _: {'#': 'asdf', '>':{ b: Gun.time.is() }}, b: 'c' } } expect(gun.__.graph['asdf'].a).to.be(0); expect(gun.__.graph['asdf'].b).to.not.be.ok(); var ctx = Gun.union(gun, prime, function(){ expect(gun.__.graph['asdf'].a).to.be(0); expect(gun.__.graph['asdf'].b).to.be('c'); done(); }); }); it('mutate', function(done){ var prime = { 'asdf': { _: {'#': 'asdf', '>':{ b: Gun.time.is() }}, b: 'd' } } expect(gun.__.graph['asdf'].b).to.be('c'); var ctx = Gun.union(gun, prime, function(){ expect(gun.__.graph['asdf'].b).to.be('d'); done(); }); }); it('disjoint past', function(done){ var prime = { 'asdf': { _: {'#': 'asdf', '>':{ x: 0 // beginning of time! }}, x: 'hi' } } expect(gun.__.graph['asdf'].x).to.not.be.ok(); var ctx = Gun.union(gun, prime, function(){ expect(gun.__.graph['asdf'].x).to.be('hi'); done(); }); }); it('past', function(done){ var prime = { 'asdf': { _: {'#': 'asdf', '>':{ x: Gun.time.is() - (60 * 1000) // above lower boundary, below now or upper boundary. }}, x: 'hello' } } expect(gun.__.graph['asdf'].x).to.be('hi'); var ctx = Gun.union(gun, prime, function(){ expect(gun.__.graph['asdf'].x).to.be('hello'); done(); }); }); it('future', function(done){ var prime = { 'asdf': { _: {'#': 'asdf', '>':{ x: Gun.time.is() + (200) // above now or upper boundary, aka future. }}, x: 'how are you?' } } expect(gun.__.graph['asdf'].x).to.be('hello'); var now = Gun.time.is(); var ctx = Gun.union(gun, prime, function(){ expect(Gun.time.is() - now).to.be.above(100); expect(gun.__.graph['asdf'].x).to.be('how are you?'); done(); }); }); var to = 5000; it('disjoint future', function(done){ var prime = { 'asdf': { _: {'#': 'asdf', '>':{ y: Gun.time.is() + (200) // above now or upper boundary, aka future. }}, y: 'goodbye' } } expect(gun.__.graph['asdf'].y).to.not.be.ok(); var now = Gun.time.is(); var ctx = Gun.union(gun, prime, function(){ expect(Gun.time.is() - now).to.be.above(100); expect(gun.__.graph['asdf'].y).to.be('goodbye'); done(); }); }); it('disjoint future max', function(done){ var prime = { 'asdf': { _: {'#': 'asdf', '>':{ y: Gun.time.is() + (2), // above now or upper boundary, aka future. z: Gun.time.is() + (200) // above now or upper boundary, aka future. }}, y: 'bye', z: 'who' } } expect(gun.__.graph['asdf'].y).to.be('goodbye'); expect(gun.__.graph['asdf'].z).to.not.be.ok(); var now = Gun.time.is(); var ctx = Gun.union(gun, prime, function(){ expect(Gun.time.is() - now).to.be.above(100); expect(gun.__.graph['asdf'].y).to.be('bye'); expect(gun.__.graph['asdf'].z).to.be('who'); done(); //setTimeout(function(){ done() },1); }); }); it('future max', function(done){ var prime = { 'asdf': { _: {'#': 'asdf', '>':{ w: Gun.time.is() + (2), // above now or upper boundary, aka future. x: Gun.time.is() - (60 * 1000), // above now or upper boundary, aka future. y: Gun.time.is() + (200), // above now or upper boundary, aka future. z: Gun.time.is() + (50) // above now or upper boundary, aka future. }}, w: true, x: 'nothing', y: 'farewell', z: 'doctor who' } } expect(gun.__.graph['asdf'].w).to.not.be.ok(); expect(gun.__.graph['asdf'].x).to.be('how are you?'); expect(gun.__.graph['asdf'].y).to.be('bye'); expect(gun.__.graph['asdf'].z).to.be('who'); var now = Gun.time.is(); var ctx = Gun.union(gun, prime, function(){ expect(Gun.time.is() - now).to.be.above(100); expect(gun.__.graph['asdf'].w).to.be(true); expect(gun.__.graph['asdf'].x).to.be('how are you?'); expect(gun.__.graph['asdf'].y).to.be('farewell'); expect(gun.__.graph['asdf'].z).to.be('doctor who'); done(); //setTimeout(function(){ done() },1); }); }); it('two nodes', function(done){ // chat app problem where disk dropped the last data, turns out it was a union problem! var state = Gun.time.is(); var prime = { 'sadf': { _: {'#': 'sadf', '>':{ 1: state }}, 1: {'#': 'fdsa'} }, 'fdsa': { _: {'#': 'fdsa', '>':{ msg: state }}, msg: "Let's chat!" } } expect(gun.__.graph['sadf']).to.not.be.ok(); expect(gun.__.graph['fdsa']).to.not.be.ok(); var ctx = Gun.union(gun, prime, function(){ expect(gun.__.graph['sadf'][1]).to.be.ok(); expect(gun.__.graph['fdsa'].msg).to.be("Let's chat!"); done(); }); }); it('append third node', function(done){ // chat app problem where disk dropped the last data, turns out it was a union problem! var state = Gun.time.is(); var prime = { 'sadf': { _: {'#': 'sadf', '>':{ 2: state }}, 2: {'#': 'fads'} }, 'fads': { _: {'#': 'fads', '>':{ msg: state }}, msg: "hi" } } expect(gun.__.graph['sadf']).to.be.ok(); expect(gun.__.graph['fdsa']).to.be.ok(); var ctx = Gun.union(gun, prime, function(){ expect(gun.__.graph['sadf'][1]).to.be.ok(); expect(gun.__.graph['sadf'][2]).to.be.ok(); expect(gun.__.graph['fads'].msg).to.be("hi"); done(); }); }); it('ify null', function(){ var node = Gun.union.ify(null, 'pseudo'); expect(Gun.node.soul(node)).to.be('pseudo'); }); it('ify node', function(){ var graph = { 'asdf': { _: {'#': 'asdf', '>': { x: Gun.time.is(), y: Gun.time.is() }}, x: 1, y: 2 }, 'soul': { _: {'#': 'soul', '~': 1, '>': { 'asdf': Gun.time.is() }}, 'asdf': {'#': 'asdf'} } } var node = Gun.union.ify(graph, 'soul'); expect(Gun.node.soul(node)).to.be('soul'); expect(node.x).to.be(1); expect(node.y).to.be(2); }); it('ify graph', function(){ var graph = { 'asdf': { _: {'#': 'asdf', '>': { a: Gun.time.is() - 2, z: Gun.time.is() - 2 }}, a: 1, z: 1 }, 'fdsa': { _: {'#': 'fdsa', '>': { b: Gun.time.is() - 1, z: Gun.time.is() - 1 }}, b: 2, z: 2 }, 'sadf': { _: {'#': 'sadf', '>': { c: Gun.time.is(), z: Gun.time.is() - 100 }}, c: 3, z: 3 }, 'soul': { _: {'#': 'soul', '~': 1, '>': { 'asdf': Gun.time.is(), 'fdsa': Gun.time.is(), 'sadf': Gun.time.is() }}, 'asdf': {'#': 'asdf'}, 'fdsa': {'#': 'fdsa'}, 'sadf': {'#': 'sadf'} } } var node = Gun.union.ify(graph, 'soul'); expect(Gun.node.soul(node)).to.be('soul'); expect(node.a).to.be(1); expect(node.b).to.be(2); expect(node.c).to.be(3); expect(node.z).to.be(2); }); }); describe('API', function(){ var gopt = {wire:{put:function(n,cb){cb()},get:function(k,cb){cb()}}}; if(Gun.window && location.search){ /*console.log("LOCALHOST PEER MUST BE ON!"); var peer = {url: 'http://localhost:8765/gun'}; Gun.on('opt', function(root){ if(root.opt.test_no_peer){ return this.to.next(root) } root.opt.peers = root.opt.peers || {}; root.opt.peers['http://localhost:8765/gun'] = peer; this.to.next(root); });*/ } var goff = Gun(); Gun.statedisk = function(o,s,cb){ goff.get(s).put(o, cb, {turn: function(fn){fn()}}); }; var gun = Gun(); var nopasstun = function(done, g){ g = (g || gun)._.root; setTimeout(function(){ expect(g.pass).to.not.be.ok(); expect(g.stun).to.not.be.ok(); done && done(); },9); } it.skip('gun chain separation', function(done){ // TODO: UNDO! var gun = Gun(); var c1 = gun.put({hello: 'world'}); var c2 = gun.put({hi: 'earth'}); c1.on(function(val){ expect(val.hi).to.not.be.ok(); }); c2.on(function(val){ expect(val.hello).to.not.be.ok(); if(done.c){ return } done(); done.c = 1; }); }); describe.skip('timeywimey', function(){ // TODO: UNDO! it('kitty', function(done){ var g1 = gun.put({hey: 'kitty'}).key('timeywimey/kitty'); var g2 = gun.get('timeywimey/kitty').on(function(val){ delete val._; //console.log("kitty?", val); expect(val.hey).to.be('kitty'); expect(val.hi).to.not.be.ok(); expect(val.hello).to.not.be.ok(); expect(val.foo).to.not.be.ok(); if(done.c){ return } done(); done.c = 1; }); }); it('kitty puppy', function(done){ var g3 = gun.put({hey: 'kitty'}).key('timeywimey/kitty/puppy'); var g4 = gun.put({hi: 'puppy'}).key('timeywimey/kitty/puppy'); var g5 = gun.get('timeywimey/kitty/puppy').on(function(val){ //delete val._; //console.log("puppy?", val); expect(val.hey).to.be('kitty'); expect(val.hi).to.be('puppy'); if(done.c){ return } done(); done.c = 1; }); }); it('hello', function(done){ gun.get('timeywimey/hello').on(function(val){ //delete val._; //console.log("hello?", val); expect(val.hello).to.be('world'); if(done.c){ return } done(); done.c = 1; }); gun.put({hello: 'world'}).key('timeywimey/hello'); }); it('hello foo', function(done){ gun.get('timeywimey/hello/foo').on(function(val){ //delete val._; expect(val.hello).to.be('world'); if(val.foo){ expect(val.foo).to.be('bar'); if(done.c){ return } done(); done.c = 1; } }); gun.put({hello: 'world'}).key('timeywimey/hello/foo'); gun.put({foo: 'bar'}).key('timeywimey/hello/foo'); }); it('all', function(done){ gun.put({hey: 'kitty'}).key('timeywimey/all'); gun.put({hi: 'puppy'}).key('timeywimey/all'); gun.get('timeywimey/all').on(function(val){ // console.log('all', done.c, val); expect(val.hey).to.be('kitty'); expect(val.hi).to.be('puppy'); if(val.hello){ expect(val.hello).to.be('world'); done.hello = true; } if(val.foo){ expect(val.foo).to.be('bar'); if(done.c || !done.hello){ return } done(); done.c = 1; } }); gun.put({hello: 'world'}).key('timeywimey/all'); gun.put({foo: 'bar'}).key('timeywimey/all'); }); }); describe('predictable souls', function(){ it('public', function(done){ gun.get('z').get('y').get('x').put({c: {b: {a: 1}}}, function(){ if(done.c){ return } done.c = 1; var g = gun._.graph; expect(g['z']).to.be.ok(); expect(g['z/y']).to.be.ok(); expect(g['z/y/x']).to.be.ok(); expect(g['z/y/x/c']).to.be.ok(); expect(g['z/y/x/c/b']).to.be.ok(); nopasstun(done, gun); }); }); it('no not found on incremental write', function(done){ gun.get('nnfoiw').get('y').put({a:1}, function(ack){ if(ack.err){ return } nopasstun(done, gun); }) }); it('public mix', function(done){ var ref = gun.get('zasdf').put({a: 9}); var at = gun.get('zfdsa').get('y').get('x').get('c').put(ref); at.get('foo').get('bar').put('yay', function(ack){ done.a = 1; end() }); ref.get('foo').get('ah').put(1, function(ack){ done.b = 1; end() }); function end(ack){ if(!done.a || !done.b){ return } if(done.c){ return } done.c = 1; var g = gun._.graph; expect(Object.keys(g['zasdf']||'').sort()).to.be.eql(['_', 'a', 'foo'].sort()); expect(Object.keys(g['zasdf/foo']||'').sort()).to.be.eql(['_', 'bar', 'ah'].sort()); nopasstun(done, gun); }; //setTimeout(function(){ console.log('???', gun._.stun); }, 1700); }); }); describe('plural chains', function(){ this.timeout(9000); it('uncached synchronous map on', function(done){ /* Biggest challenges so far: - Unsubscribe individual mapped next. ! - Performance deduplication on asking relation's next. ! - Replying immediately to parent cached contexts. - Performant read lock on write contexts. - Proxying event across maps. */ Gun.statedisk({ alice: { age: 26, name: "Alice", pet: {a:1, name: "Fluffy"} }, bob: { age: 29, name: "Bob!", pet: {b:2, name: "Frisky"} } }, 'u/m', function(){ var check = {}, count = {}; gun.get('u/m').map().on(function(v,f){ check[f] = v; count[f] = (count[f] || 0) + 1; //console.log("***********", f, v); if(check.alice && check.bob){ clearTimeout(done.to); done.to = setTimeout(function(){ expect(check.alice.age).to.be(26); expect(check.alice.name).to.be('Alice'); expect('string' == typeof Gun.valid(check.alice.pet)).to.be.ok(); //expect(count.alice).to.be(1); expect(check.bob.age).to.be(29); expect(check.bob.name).to.be('Bob!'); expect('string' == typeof Gun.valid(check.bob.pet)).to.be.ok(); //expect(count.bob).to.be(1); nopasstun(done, gun); },10); } }); }, 1000); }); it('uncached synchronous map get on', function(done){ Gun.statedisk({ alice: { age: 26, name: "alice", pet: {a:1, name: "Fluffy"} }, bob: { age: 29, name: "bob", pet: {b:2, name: "Frisky"} } }, 'u/m/p', function(){ var check = {}, count = {}; gun.get('u/m/p').map().get('name').on(function(v,f){ //console.log("*****************", f, v); check[v] = f; count[v] = (count[v] || 0) + 1; if(check.alice && check.bob){ clearTimeout(done.to); done.to = setTimeout(function(){ expect(check.alice).to.be('name'); expect(check.bob).to.be('name'); //expect(count.alice).to.be(1); //expect(count.bob).to.be(1); nopasstun(done, gun); },10); } }); }, 1000); }); it('uncached synchronous map get on node', function(done){ Gun.statedisk({ alice: { age: 26, name: "alice", pet: {a:1, name: "Fluffy"} }, bob: { age: 29, name: "bob", pet: {b:2, name: "Frisky"} } }, 'u/m/p/n', function() { var check = {}, count = {}; gun.get('u/m/p/n').map().get('pet').on(function(v,f){ //console.log("********************", f,v); check[v.name] = v; count[v.name] = (count[v.name] || 0) + 1; if(check.Fluffy && check.Frisky){ clearTimeout(done.to); done.to = setTimeout(function(){ expect(check.Fluffy.a).to.be(1); expect(check.Frisky.b).to.be(2); //expect(count.Fluffy).to.be(1); //expect(count.Frisky).to.be(1); //expect(count['undefined']).to.not.be.ok(); if(done.c){return}done.c=1; nopasstun(done, gun); },10); } }); }, 1000); }); it('uncached synchronous map get on node get', function(done){ var gun = Gun(); Gun.statedisk({ alice: { age: 26, name: "alice", pet: {a:1, name: "Fluffy"} }, bob: { age: 29, name: "bob", pet: {b:2, name: "Frisky"} } }, 'u/m/p/n/p', function() { var check = {}, count = {}; //console.debug.i=1;console.log('-------------------'); gun.get('u/m/p/n/p').map().get('pet').get('name').on(function(v,f){ check[v] = f; count[v] = (count[v] || 0) + 1; //console.log("*****************", f, v); if(check.Fluffy && check.Frisky){ clearTimeout(done.to); done.to = setTimeout(function(){ expect(check.Fluffy).to.be('name'); expect(check.Frisky).to.be('name'); //console.log("????", gun._.graph); //Gun.obj.map(gun._.graph, function(n,s){ Object.keys(gun._.graph).forEach(function(s,n){ n = gun._.graph[s]; if('u/m/p/n/p' === s){ return } var a = Object.keys(n);//Gun.obj.map(n, function(v,f,t){t(v)}); expect(a.length).to.be(2); // make sure that ONLY the selected properties were loaded, not the whole node. }); //expect(count.Fluffy).to.be(1); //expect(count.Frisky).to.be(1); nopasstun(done, gun); },10); } }); }, 1000); }); it('uncached synchronous map on mutate', function(done){ Gun.statedisk({ alice: { age: 26, name: "Alice", pet: {a:1, name: "Fluffy"} }, bob: { age: 29, name: "Bob", pet: {b:2, name: "Frisky"} } }, 'u/m/mutate', function() { var check = {}, count = {}; gun.get('u/m/mutate').map().get('name').get(function(at,ev){ var e = at.err, v = at.put, f = at.get; //console.log("****************", f,v); check[v] = f; count[v] = (count[v] || 0) + 1; if(check.Alice && check.Bob && check['undefined']){ clearTimeout(done.to); done.to = setTimeout(function(){ //expect(count.Alice).to.be(1); //expect(count.Bob).to.be(1); //expect(count['undefined']).to.be(1); if(done.c){ return } done.c = 1; nopasstun(done, gun); },10); } }); setTimeout(function(){ gun.get('u/m/mutate').get('alice').put(7); }, 300); }, 1000); }); it('uncached synchronous map on mutate node', function(done){ Gun.statedisk({ alice: {_:{'#':'umaliceo'}, age: 26, name: "Alice", pet: {a:1, name: "Fluffy"} }, bob: { age: 29, name: "Bob", pet: {b:2, name: "Frisky"} } }, 'u/m/mutate/n', function() { var check = {}, count = {}; gun.get('u/m/mutate/n').map().get('name').get(function(at,ev){ var e = at.err, v = at.put, f = at.get; check[v] = f; count[v] = (count[v] || 0) + 1; //console.log("************", f,v); if(check.Alice && check.Bob && check['undefined'] && check['Alice Zzxyz']){ clearTimeout(done.to); done.to = setTimeout(function(){ expect(done.last).to.be.ok(); expect(check['Alice Aabca']).to.not.be.ok(); expect(count.Alice).to.be(1); expect(count.Bob).to.be(1); expect(count['undefined']).to.be(1); expect(count['Alice Zzxyz']).to.be(1); nopasstun(done, gun); },200); } }); setTimeout(function(){ gun.get('u/m/mutate/n').get('alice').put({ _:{'#':'u/m/m/n/soul'}, name: 'Alice Zzxyz' }); setTimeout(function(){ gun.get('umaliceo').put({ name: 'Alice Aabca' }); done.last = true; }, 10); }, 300); }, 1000); }); it('uncached synchronous map on mutate node uncached', function(done){ Gun.statedisk({ alice: {_:{'#':'umaliceo1'}, age: 26, name: "Alice", pet: {a:1, name: "Fluffy"} }, bob: { age: 29, name: "Bob", pet: {b:2, name: "Frisky"} } }, 'u/m/mutate/n/u', function() { var check = {}, count = {}; gun.get('u/m/mutate/n/u').map().on(function(v,f){ check[v.name] = f; count[v.name] = (count[v.name] || 0) + 1; if(check.Alice && check.Bob && check['Alice Zzxyz']){ clearTimeout(done.to); //console.log("****", f, v) done.to = setTimeout(function(){ expect(done.last).to.be.ok(); //expect(check['Alice Aabca']).to.not.be.ok(); //expect(count['Alice']).to.be(1); //expect(count['Bob']).to.be(1); //expect(count['Alice Zzxyz']).to.be(1); if(done.c){ return } done.c = 1; nopasstun(done, gun); },200); } }); setTimeout(function(){ Gun.statedisk({ name: 'Alice Zzxyz' }, 'u/m/m/n/u/soul', function() { //console.debug.i=1;console.log("---------------"); gun.get('u/m/mutate/n/u').put({ alice: {'#':'u/m/m/n/u/soul'}, }); /* { users: {_:#users alice: {#newalice} } } */ setTimeout(function(){ gun.get('umaliceo1').put({ name: 'Alice Aabca' }); done.last = true; }, 10); }, 1000); }, 300); }, 1000); }); it('uncached synchronous map on get mutate node uncached', function(done){ Gun.statedisk({ alice: {_:{'#':'umaliceo2'}, age: 26, name: "Alice", pet: {a:1, name: "Fluffy"} }, bob: { age: 29, name: "Bob", pet: {b:2, name: "Frisky"} } }, 'u/m/p/mutate/n/u', function() { var check = {}, count = {}; gun.get('u/m/p/mutate/n/u').map().get('name').on(function(v,f){ check[v] = f; count[v] = (count[v] || 0) + 1; //console.log("*************", f,v); if(check.Alice && check.Bob && check['Alice Zzxyz']){ clearTimeout(done.to); done.to = setTimeout(function(){ var a = Object.keys(gun._.graph['u/m/p/m/n/u/soul']); //Gun.obj.map(gun._.graph['u/m/p/m/n/u/soul'], function(v,f,t){t(v)}); expect(a.length).to.be(2); expect(done.last).to.be.ok(); expect(check['Alice Aabca']).to.not.be.ok(); //expect(count.Alice).to.be(1); //expect(count.Bob).to.be(1); //expect(count['Alice Zzxyz']).to.be(1); nopasstun(done, gun); },200); } }); setTimeout(function(){ Gun.statedisk({ name: 'Alice Zzxyz', age: 34 }, 'u/m/p/m/n/u/soul', function() { gun.get('u/m/p/mutate/n/u').put({ alice: {'#':'u/m/p/m/n/u/soul'}, }); setTimeout(function(){ gun.get('umaliceo2').put({ name: 'Alice Aabca' }); done.last = true; }, 10); }, 1000); }, 300); }, 1000); }); it('uncached synchronous map on get node mutate node uncached', function(done){ Gun.statedisk({ alice: {_:{'#':'umaliceo3'}, age: 26, name: "Alice", pet: {_:{'#':'sflufso'},a:1, name: "Fluffy"} }, bob: { age: 29, name: "Bob", pet: {b:2, name: "Frisky"} } }, 'u/m/p/n/mutate/n/u', function() { var check = {}, count = {}; gun.get('u/m/p/n/mutate/n/u').map().get('pet').on(function(v,f){ check[v.name] = f; count[v.name] = (count[v.name] || 0) + 1; //console.log("*****************", f,v, check); if(check.Fluffy && check.Frisky && check.Fuzzball){ clearTimeout(done.to); done.to = setTimeout(function(){ expect(done.last).to.be.ok(); expect(check['Fluffs']).to.not.be.ok(); expect(count.Fluffy).to.be(1); expect(count.Frisky).to.be(1); expect(count.Fuzzball).to.be(1); nopasstun(done, gun); },200); } }); setTimeout(function(){ Gun.statedisk({ name: 'Alice Zzxyz', age: 34, pet: {c:3, name: "Fuzzball"} }, 'alice/fuzz/soul', function() { gun.get('u/m/p/n/mutate/n/u').put({ alice: {'#':'alice/fuzz/soul'}, }); setTimeout(function(){ gun.get('sflufso').put({ name: 'Fluffs' }); done.last = true; }, 10); }, 1000); }, 300); }, 1000); }); it("unlink deeply nested", function(done){ Gun.statedisk({ a: {_:{'#':'audn'}, age: 26, name: "Alice", b: {_:{'#':'budn'}, c: {_:{'#':'cudn'}, id: 'first', level: 3}, level: 2} } }, 'udn', function() { var check = {}, count = {}; gun.get('udn').get('a').get('b').get('c').on(function(data){ //console.log("udn.a.b.c:", data); check[data.id] = 1; count[data.id] = (count[data.id] || 0) + 1; expect(data.foo).to.not.be.ok(); //console.log("*****************", f,v, check); if(check.first && check.other){ clearTimeout(done.to); done.to = setTimeout(function(){ expect(done.last).to.be.ok(); expect(check.firsta).to.not.be.ok(); expect(count.first).to.be(1); expect(count.other).to.be(1); nopasstun(done, gun); },200); } }); setTimeout(function(){ Gun.statedisk({ name: 'Alice2', age: 34, b: {_:{'#':'2budn'}, c: {_:{'#':'2cudn'}, id: 'other', level: 3}, level: 2} }, '2audn', function() { //console.only.i=1;console.log('============================='); gun.get('udn').put({ a: {'#':'2audn'} }); setTimeout(function(){ //console.log("- - - - - - - - - - - -"); gun.get('cudn').put({id: 'firsta', foo: 'bar'}); done.last = 1; }, 50); }); },50); }); }); it("get before put in memory", function(done){ var gun = Gun(); var check = {}; var count = {}; gun.get('g/n/m/f/l/n/r').map().on(function(v,f){ //console.log("***********", f,v); check[f] = v; count[f] = (count[f] || 0) + 1; if(check.alice && check.bob && check.alice.PhD){ clearTimeout(done.to); done.to = setTimeout(function(){ expect(check.alice.age).to.be(24); expect(check.bob.age).to.be(26); expect(check.alice.PhD).to.be(true); //expect(count.alice).to.be(2); //expect(count.bob).to.be(1); if(done.c){return} done.c=1; nopasstun(done, gun); },50); } }); gun.put({_:{'#':'g/n/m/f/l/n/r'}, alice: {_:{'#':'GALICE1'}, name: "alice", age: 24, spouse: { name: "carl", age: 25, work: { name: "GUN INC" } }, bout: {huh:1} }, bob: { name: "bob", age: 26, spouse: { name: "diana", age: 27, work: { name: "ACME INC" } } } }); setTimeout(function(){ gun.get('GALICE1').put({PhD: true}); },300); }); it("in memory get after", function(done){ var gun = Gun(); gun.put({_:{'#':'g/n/m/f/l/n'}, alice: {_:{'#':'GALICE2'}, name: "alice", age: 24, spouse: { name: "carl", age: 25, work: { name: "GUN INC" } }, bout: {huh:1} }, bob: { name: "bob", age: 26, spouse: { name: "diana", age: 27, work: { name: "ACME INC" } } } }); var check = {}; //gun.get('g/n/m/f/l/n').get('bob.spouse.work').on(function(v,f){ console.log("!!!!!!!!!", f, v);});return; gun.get('g/n/m/f/l/n').map().on(function(v,f){ check[f] = v; //console.log("*******************", f, v); if(check.alice && check.bob && c