From 2c96ffcdfa69a2c33409b861f78e5dc5af64e79e Mon Sep 17 00:00:00 2001 From: Mark Nadal Date: Wed, 1 Jul 2015 17:11:06 -0700 Subject: [PATCH] we won't have to rollback! --- gun.js | 116 ++++++++++++++++----------------- test/common.js | 173 +++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 192 insertions(+), 97 deletions(-) diff --git a/gun.js b/gun.js index 0d8ef16c..995a26ee 100644 --- a/gun.js +++ b/gun.js @@ -120,6 +120,19 @@ if(!ctx.count){ ctx.cb() } return ctx; } + Gun.union.pseudo = function(soul, graph, vertex){ + var c = 0, s; + ((vertex = vertex || {})._ = {})[Gun._.soul] = soul; + Gun.is.graph(graph, function(node, ss){ + c += 1; s = ss; + Gun.HAM(vertex, node, function(){}, function(vertex, field, value){ + (vertex._[Gun._.HAM] = vertex._[Gun._.HAM] || {})[field] = node._[Gun._.HAM][field]; + vertex[field] = value; + }, function(){}); + }); + if(1 == c){ return } + return vertex; + } Gun.HAM = function(vertex, delta, lower, now, upper){ upper.max = -Infinity; now.end = true; @@ -151,7 +164,7 @@ upper.call(state, vertex, field, incoming); // signals that there are still future modifications. Gun.schedule(ctx.incoming.state, function(){ update(incoming, field); - if(ctx.incoming.state === upper.max){ upper.last() } + if(ctx.incoming.state === upper.max){ (upper.last || function(){})() } }); } }); @@ -249,10 +262,11 @@ } proxy.event = function(cb, i){ return proxy(cb, i, 'event') }; proxy.once = function(cb, i){ return proxy(cb, i, 'once') }; - proxy.emit = function(at, on){setTimeout(function(){ + proxy.emit = function(at, on){//setTimeout(function(){ ((on = gun._.on(e)).e = on.e || {})[e] = at; gun._.on(e).emit(at); - },0)}; + //},0) + }; return proxy; } return gun; @@ -266,37 +280,19 @@ opt = opt || {}; if(opt.force){ load(key) } else if(ctx.soul){ - gun._.at('soul').emit({soul: ctx.soul, GET: 'SOUL'}); if(ctx.node = gun.__.graph[ctx.soul]){ // in memory - cb.call(gun, null, Gun.obj.copy(ctx.node)); - gun._.at('node').emit({soul: ctx.soul, GET: 'NODE'}); + cb.call(gun, null, Gun.obj.copy(ctx.node)) } else { load(key) } // not in memory + ((ctx.node = gun.__.graph[ctx.soul] = gun.__.graph[ctx.soul] || {})._ = {})[Gun._.soul] = ctx.soul; + gun._.at('soul').emit({soul: ctx.soul, GET: 'SOUL'}); } else if(ctx.key){ function get(soul){ - if(ctx.flag){ console.log('???????????') } - var graph = gun.__.key.s[ctx.key], pseudo; - Gun.is.graph(graph, function(node){ // TODO: REFACTOR!!!! - if(pseudo){ - var hack; - if(soul != ctx.key){ - soul = ctx.key; - ((pseudo = gun.__.graph[soul] = {})._ = {})[Gun._.soul] = soul; - (hack = Gun.obj.copy(ctx.node))._[Gun._.soul] = soul; - Gun.union(gun, hack); - } - (hack = Gun.obj.copy(node))._[Gun._.soul] = soul; - Gun.union(gun, hack); - } - pseudo = ctx.node = pseudo || node; - }); + var graph = gun.__.key.s[ctx.key]; + Gun.is.graph(graph, function(node){ ctx.node = node }); if(!(soul = Gun.is.soul.on(ctx.node))){ return } - cb.call(gun, null, Gun.obj.copy(ctx.node)); - gun._.at('soul').emit({soul: soul, GET: 'SOUL'}); - gun._.at('node').emit({soul: soul, GET: 'NODE'}); - /*if(!ctx.flag){ - Gun.union(gun, {_: {'#': soul}}); - }*/ + cb.call(gun, null, Gun.obj.copy(graph)); + gun._.at('soul').emit({soul: soul, key: ctx.key, GET: 'SOUL'}); } if(gun.__.key.s[ctx.key]){ get() } // in memory else if(ctx.flag = gun.__.flag.start[ctx.key]){ // will be in memory @@ -308,25 +304,25 @@ function load(key){ if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.get)){ ctx.hook(key, function(err, data){ // multiple times potentially - console.log("chain.get from load", err, data); + console.log("chain.get ", key, "from hook", err, data); if(err){ return cb.call(gun, err, data) } if(!data){ if(ctx.data || ctx.soul){ return } cb.call(gun, null, null); + if(!ctx.key){ return } // TODO: Maybe want to do a .not on a soul directly? gun.__.flag.end[ctx.key] = gun.__.flag.end[ctx.key] || function($){ // TODO: cover all edge cases, uniqueness? delete gun.__.flag.end[ctx.key]; gun._.at('soul').emit($); - gun._.at('node').emit($); }; console.log('chain.get null'); return gun._.at('null').emit({key: ctx.key, GET: 'NULL'}); } ctx.data = ctx.data || true; if(!Gun.is.graph(data, function(node, soul){ - if(true || ctx.soul === soul){ gun._.at('soul').emit({soul: soul, GET: 'SOUL'}) } if(err = Gun.union(gun, node).err){ return cb.call(gun, err, node) } - if(true || ctx.soul){ gun._.at('node').emit({soul: soul, GET: 'NODE'}) } + if(ctx.key){ (gun.__.key.s[ctx.key] = gun.__.key.s[ctx.key] || {})[soul] = gun.__.graph[soul] } + if(true || ctx.soul === soul){ gun._.at('soul').emit({soul: soul, key: ctx.key, GET: 'SOUL'}) } })){ return cb.call(gun, {err: Gun.log('Not a valid graph!') }, data) } cb.call(gun, null, data); }, opt); @@ -356,7 +352,7 @@ } index({soul: opt.soul}); } else { // will be injected via a put - (gun.__.flag.start[key] = gun._.at('node')).once(function($){ + (gun.__.flag.start[key] = gun._.at('soul')).once(function($){ console.log("chain.key"); (gun.__.key.s[key] = gun.__.key.s[key] || {})[$.soul] = gun.__.graph[$.soul]; delete gun.__.flag.start[key]; @@ -421,7 +417,7 @@ cb = cb || function(){}; if(!gun.back._.at){ return cb.call(gun, {err: Gun.log("No context!")}), gun } // TODO: Hmmm once also? figure it out later. - gun.back._.at('node').event(function($){ + gun.back._.at('soul').event(function($){ var ctx = {path: (Gun.text.ify(path) || '').split('.')}; (function trace($){ // TODO: Check for field as well and merge? var node = gun.__.graph[$.soul], field = Gun.text.ify(ctx.path.shift()), val; @@ -438,22 +434,20 @@ } else if(!Gun.obj.has(node, field)){ // TODO: THIS MAY NOT BE CORRECT BEHAVIOR!!!! cb.call(gun, null, null, field); - gun._.at('soul').emit({soul: $.soul, field: field, PATH: 'SOUL', WAS: 'ON'}); // WAS ON // if .put is after, makes sense. If anything else, makes sense to wait. - gun._.at('node').emit({soul: $.soul, field: field, PATH: 'NODE', WAS: 'ON'}); // WAS ON + gun._.at('soul').emit({soul: $.soul, field: field, PATH: 'SOUL', WAS: 'ON'}); // if .put is after, makes sense. If anything else, makes sense to wait. } else if(Gun.is.soul(val = node[field])){ gun.get(val, function(err, data){ cb.call(gun, err, data, field); // TODO: Should we attach field here, does map? if(err || !data){ return } - gun._.at('node').emit({soul: Gun.is.soul(val), field: null, from: $.soul, at: field, PATH: 'NODE', WAS: 'ON'}); // WAS ON console.log("PATH -> rel/val"); }); + ((ctx.node = gun.__.graph[ctx.soul] = gun.__.graph[ctx.soul] || {})._ = {})[Gun._.soul] = ctx.soul; gun._.at('soul').emit({soul: Gun.is.soul(val), field: null, from: $.soul, at: field, PATH: 'SOUL', WAS: 'ON'}); // WAS ON } else { console.log("PATH -> field/val", field, val); cb.call(gun, null, val, field); - gun._.at('soul').emit({soul: $.soul, field: field, PATH: 'SOUL', WAS: 'ON'}); // WAS ON - gun._.at('node').emit({soul: $.soul, field: field, PATH: 'NODE', WAS: 'ON'}); // WAS ON + gun._.at('soul').emit({soul: $.soul, field: field, PATH: 'SOUL', WAS: 'ON'}); } }($)); }); @@ -465,10 +459,11 @@ cb = cb || root.console.log.bind(root.console); opt = opt || {}; - console.log("REGISTER VAL", ctx.id = Gun.text.random(4)); gun.on(function($, delta, on){ - console.log("valvalvalvalval", ctx.id, $); var node = gun.__.graph[$.soul]; + if($.key){ + node = Gun.union.pseudo($.key, gun.__.key.s[$.key]) || node; + } if($.field){ if(ctx[$.soul + $.field]){ return } ctx[$.soul + $.field] = true; // TODO: unregister instead? @@ -489,16 +484,22 @@ cb = cb || function(){}; gun._.at('soul').event(function($){ // TODO: once per soul on graph. (?) - if(ctx[$.soul]){ return } - ctx[$.soul] = gun.__.on($.soul).event(on() || on); - function on(delta){ - - if(opt.raw){ return cb.call(gun, $, delta, this) } - var node = gun.__.graph[$.soul]; - if(!opt.end && delta && Gun.obj.empty(delta, Gun._.meta)){ return } - cb.call(gun, Gun.obj.copy(opt.change? delta || node : node), $.field || $.at); + if(ctx[$.soul]){ + ctx[$.soul](null, $); + } else { + (ctx[$.soul] = function(delta, $$){ + var $$ = $$ || $, node = gun.__.graph[$$.soul]; + if(opt.raw){ return cb.call(gun, $$, delta, this) } + if($$.key){ + node = Gun.union.pseudo($.key, gun.__.key.s[$.key]) || node; + } + if(!opt.end && delta && Gun.obj.empty(delta, Gun._.meta)){ return } + cb.call(gun, Gun.obj.copy(opt.change? delta || node : node), $$.field || $$.at); + })(); + gun.__.on($.soul).event(ctx[$.soul]); } }); + return gun; } /* @@ -524,10 +525,9 @@ } if(gun.back.not){ gun.back.not(call) } - //console.log("REGISTER PUT", val, call.id = Gun.text.random(4)); gun.back._.at('soul').event(function($){ // TODO: maybe once per soul? var ctx = {}, obj = val, $ = Gun.obj.copy($); - console.log("chain.put", call.id, val); + console.log("chain.put", val); if(Gun.is.value(obj)){ if($.from && $.at){ $.soul = $.from; @@ -584,8 +584,7 @@ Gun.union(gun, node); }); if($.from = Gun.is.soul(ify.root[$.field])){ $.soul = $.from; $.field = null } - gun._.at('soul').emit({soul: $.soul, field: $.field, PUT: 'SOUL', WAS: 'ON'}); // WAS ON - gun._.at('node').emit({soul: $.soul, field: $.field, PUT: 'NODE', WAS: 'ON'}); // WAS ON + gun._.at('soul').emit({soul: $.soul, field: $.field, key: $.key, PUT: 'SOUL', WAS: 'ON'}); // WAS ON if(Gun.fns.is(ctx.hook = gun.__.opt.hooks.put)){ ctx.hook(ify.graph, function(err, data){ // now iterate through those nodes to a persistence layer and get a callback once all are saved if(err){ return cb.call(gun, err) } @@ -613,16 +612,13 @@ gun.get(val, function(err, data){ // TODO: should map have support for `.not`? error? if(err || !data){ return } // TODO: Handle this! cb.call(this, data, field); - gun._.at('node').emit({soul: Gun.is.soul(val), field: null, from: soul, at: field, MAP: 'NODE'}); - // TODO: BUG is this correct? PROBABLY NOT FOO!!! }); + ((ctx.node = gun.__.graph[ctx.soul] = gun.__.graph[ctx.soul] || {})._ = {})[Gun._.soul] = ctx.soul; gun._.at('soul').emit({soul: Gun.is.soul(val), field: null, from: soul, at: field, MAP: 'SOUL'}); } else { if(opt.node){ return } // {node: true} maps over only sub nodes. cb.call(gun, val, field); gun._.at('soul').emit({soul: soul, field: field, MAP: 'SOUL'}); - console.log('chain.map on', field, val); - gun._.at('node').emit({soul: soul, field: field, MAP: 'NODE'}); } }); }, true); @@ -633,10 +629,9 @@ var gun = this, ctx = {}, drift = Gun.time.now(); cb = cb || function(){}; opt = opt || {}; - console.log("REGISTER SET", ctx.id = Gun.text.random(4)); if(!gun.back){ gun = gun.put({}) } - gun = gun.not(function(next, key){ console.log('NOT TIMES?'); return key? this.put({}).key(key) : this.put({}) }); + gun = gun.not(function(next, key){ return key? this.put({}).key(key) : this.put({}) }); if(!val && !Gun.is.value(val)){ return gun } var obj = {}; obj['I' + drift + 'R' + Gun.text.random(5)] = val; @@ -647,12 +642,11 @@ cb = cb || function(){}; gun._.at('null').once(function(key){ - if(key.soul || gun.__.key.s[key = (key || {}).key] || gun.__.flag.start[key]){ return } + if(key.soul || gun.__.key.s[key = (key || {}).key]){ return } // TODO! BUG? Removed a start flag check and tests passed, but is that an edge case? var kick = function(next){ if(++c){ return Gun.log("Warning! Multiple `not` resumes!"); } next._.at('soul').once(function($){ $.N0T = 'KICK SOUL'; gun._.at('soul').emit($) }); - next._.at('node').once(function($){ $.N0T = 'KICK NODE'; gun._.at('node').emit($) }); }, chain = gun.chain(), next = cb.call(chain, kick, key), c = -1; if(Gun.is(next)){ kick(next) } chain._.at('soul').emit({soul: Gun.roulette.call(chain), empty: true, key: key, N0T: 'SOUL', WAS: 'ON'}); // WAS ON diff --git a/test/common.js b/test/common.js index 7d2c8b5b..a86ee4a6 100644 --- a/test/common.js +++ b/test/common.js @@ -767,11 +767,67 @@ describe('Gun', function(){ }); }); + it('pseudo null', function(){ + var node = Gun.union.pseudo('pseudo'); + expect(Gun.is.soul.on(node)).to.be('pseudo'); + }); + + it('pseudo node', function(){ + + var graph = { + 'asdf': { + _: {'#': 'asdf', '>': { + x: Gun.time.is(), + y: Gun.time.is() + }}, + x: 1, + y: 2 + } + } + var node = Gun.union.pseudo('soul', graph); + expect(node).to.not.be.ok(); + }); + + it('pseudo 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 + } + } + var node = Gun.union.pseudo('soul', graph); + expect(Gun.is.soul.on(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 gun = Gun(); - (function(){ + it('put', function(done){ gun.put("hello", function(err){ expect(err).to.be.ok(); @@ -807,7 +863,11 @@ describe('Gun', function(){ expect(err).to.not.be.ok(); }).get('yes/key', function(err, data){ expect(err).to.not.be.ok(); - expect(data.hello).to.be('key'); + var c = 0; + Gun.is.graph(data, function(node){ + expect(c++).to.be(0); + expect(node.hello).to.be('key'); + }); done(); }); }); @@ -819,7 +879,11 @@ describe('Gun', function(){ gun.get('yes/a/key', function(err, data){ expect(err).to.not.be.ok(); - expect(data.hello).to.be('a key'); + var c = 0; + Gun.is.graph(data, function(node){ + expect(c++).to.be(0); + expect(node.hello).to.be('a key'); + }); done(); }); }); @@ -834,7 +898,11 @@ describe('Gun', function(){ it('get key', function(done){ gun.get('yes/key', function(err, data){ expect(err).to.not.be.ok(); - expect(data.hello).to.be('key'); + var c = 0; + Gun.is.graph(data, function(node){ + expect(c++).to.be(0); + expect(node.hello).to.be('key'); + }); }).key('hello/key', function(err, ok){ expect(err).to.not.be.ok(); done.key = true; @@ -856,7 +924,11 @@ describe('Gun', function(){ it('get node put node merge', function(done){ gun.get('hello/key', function(err, data){ expect(err).to.not.be.ok(); - done.soul = Gun.is.soul.on(data); + var c = 0; + Gun.is.graph(data, function(node){ + expect(c++).to.be(0); + done.soul = Gun.is.soul.on(node); + }); }).put({hi: 'you'}, function(err, ok){ expect(err).to.not.be.ok(); var node = gun.__.graph[done.soul]; @@ -898,8 +970,12 @@ describe('Gun', function(){ it('get node put node merge conflict', function(done){ gun.get('hello/key', function(err, data){ expect(err).to.not.be.ok(); - expect(data.hi).to.be('you'); - done.soul = Gun.is.soul.on(data); + var c = 0; + Gun.is.graph(data, function(node){ + expect(c++).to.be(0); + expect(node.hi).to.be('you'); + done.soul = Gun.is.soul.on(node); + }); }).put({hi: 'overwritten'}, function(err, ok){ expect(err).to.not.be.ok(); var node = gun.__.graph[done.soul]; @@ -920,8 +996,12 @@ describe('Gun', function(){ it('get node path put value', function(done){ gun.get('hello/key', function(err, data){ expect(err).to.not.be.ok(); - expect(data.hi).to.be('overwritten'); - done.soul = Gun.is.soul.on(data); + var c = 0; + Gun.is.graph(data, function(node){ + expect(c++).to.be(0); + expect(node.hi).to.be('overwritten'); + done.soul = Gun.is.soul.on(node); + }); }).path('hi').put('again', function(err, ok){ expect(err).to.not.be.ok(); var node = gun.__.graph[done.soul]; @@ -934,8 +1014,12 @@ describe('Gun', function(){ it('get node path put object', function(done){ gun.get('hello/key', function(err, data){ expect(err).to.not.be.ok(); - expect(data.hi).to.be('again'); - done.soul = Gun.is.soul.on(data); + var c = 0; + Gun.is.graph(data, function(node){ + expect(c++).to.be(0); + expect(node.hi).to.be('again'); + done.soul = Gun.is.soul.on(node); + }); }).path('hi').put({yay: "value"}, function(err, ok){ expect(err).to.not.be.ok(); var root = gun.__.graph[done.soul]; @@ -950,8 +1034,12 @@ describe('Gun', function(){ it('get node path put object merge', function(done){ gun.get('hello/key', function(err, data){ expect(err).to.not.be.ok(); - expect(done.ref = Gun.is.soul(data.hi)).to.be.ok(); - done.soul = Gun.is.soul.on(data); + var c = 0; + Gun.is.graph(data, function(node){ + expect(c++).to.be(0); + expect(done.ref = Gun.is.soul(node.hi)).to.be.ok(); + done.soul = Gun.is.soul.on(node); + }); }).path('hi').put({happy: "faces"}, function(err, ok){ expect(err).to.not.be.ok(); var root = gun.__.graph[done.soul]; @@ -968,8 +1056,12 @@ describe('Gun', function(){ it('get node path put value conflict relation', function(done){ gun.get('hello/key', function(err, data){ expect(err).to.not.be.ok(); - expect(done.ref = Gun.is.soul(data.hi)).to.be.ok(); - done.soul = Gun.is.soul.on(data); + var c = 0; + Gun.is.graph(data, function(node){ + expect(c++).to.be(0); + expect(done.ref = Gun.is.soul(node.hi)).to.be.ok(); + done.soul = Gun.is.soul.on(node); + }); }).path('hi').put('crushed', function(err, ok){ expect(err).to.not.be.ok(); var root = gun.__.graph[done.soul]; @@ -1317,10 +1409,10 @@ describe('Gun', function(){ gun.put({gender:'f', age:22, name:'beth' }).key('user/beth'); gun.get('user/alfred').val(function(a){ gun.get('user/beth').path('friend').put(a); // b - friend_of -> a - - gun.get('user/beth').val(function(b){ + gun.get('user/beth').val(function(b){ // TODO: We should have b.friend by now! gun.get('user/alfred').path('friend').put(b, function(){ // a - friend_of -> b - gun.get('user/beth').path('cat').put({name: "fluffy", age: 3, coat: "tabby"}, function(err, ok){ + gun.get('user/beth').path('cat').put({name: "fluffy", age: 3, coat: "tabby"}, function(err, ok){ + gun.get('user/alfred').path('friend.cat').key('the/cat'); gun.get('the/cat').val(function(c){ @@ -1480,24 +1572,23 @@ describe('Gun', function(){ }); it('set multiple', function(done){ - Gun.log.verbose = true; var gun = Gun().get('sets').set(), i = 0; gun.val(function(val){ - console.log("@@@@@@@@@ BEFORE", val); expect(done.soul = Gun.is.soul.on(val)).to.be.ok(); expect(Gun.obj.empty(val, '_')).to.be.ok(); }); - gun.set(1).set(2).set(3).set(4) // if you set an object you'd have to do a `.back` - .map().val(function(val){ // TODO! BUG! If we instead do gun.map().val() we will get stale data, fix this. + gun.set(1).set(2).set(3).set(4); // if you set an object you'd have to do a `.back` + gun.map().val(function(val){ i += 1; - console.log('@@@@@@@@@', i, val); expect(val).to.be(i); if(i % 4 === 0){ - done.i = 0; - Gun.obj.map(gun.__.graph, function(){ done.i++ }); - expect(done.i).to.be(1); // make sure there isn't double. - done() + setTimeout(function(){ + done.i = 0; + Gun.obj.map(gun.__.graph, function(){ done.i++ }); + expect(done.i).to.be(1); // make sure there isn't double. + done() + },10); } }); }); @@ -1506,11 +1597,11 @@ describe('Gun', function(){ var hooks = {get: function(key, cb, opt){ cb(); }, put: function(nodes, cb, opt){ - //root.console.log("put hook", nodes); + //console.log("put hook", nodes); Gun.union(gun1, nodes); cb(); }, key: function(key, soul, cb, opt){ - //root.console.log("key hook", key, soul); + //console.log("key hook", key, soul); gun1.key(key, null, soul); cb(); }}, @@ -1527,7 +1618,7 @@ describe('Gun', function(){ },10); },10); }); - }()); return; + it('get pseudo merge', function(done){ var gun = Gun(); @@ -1535,20 +1626,33 @@ describe('Gun', function(){ gun.put({b: 2, z: 0}).key('pseudo'); gun.get('pseudo').val(function(val){ - console.log('pseudo?!!!!!', val); expect(val.a).to.be(1); expect(val.b).to.be(2); expect(val.z).to.be(0); done(); }); }); - return; + + it('get pseudo merge on', function(done){ + var gun = Gun(); + + gun.put({a: 1, z: -1}).key('pseudon'); + gun.put({b: 2, z: 0}).key('pseudon'); + + gun.get('pseudon').on(function(val){ + expect(val.a).to.be(1); + expect(val.b).to.be(2); + expect(val.z).to.be(0); + done(); + }); + }); + it('get pseudo merge across peers', function(done){ Gun.on('opt').event(function(gun, o){ gun.__.opt.hooks = {get: function(key, cb, opt){ var other = (o.alice? gun2 : gun1); if(connect){ - console.log('connect to peer and get', key); + //console.log('connect to peer and get', key); other.get(key, cb); } else { cb(); @@ -1584,18 +1688,15 @@ describe('Gun', function(){ setTimeout(function(){ // CONNECT THE TWO PEERS connect = true; - Gun.log.verbose = true; gun1.get('pseudo/merge', null, {force: true}); // fake a browser refersh, in real world we should auto-reconnect gun2.get('pseudo/merge', null, {force: true}); // fake a browser refersh, in real world we should auto-reconnect setTimeout(function(){ gun1.val(function(val){ - console.log('@@@@@@@@@@@@1', val); expect(val.hello).to.be('world!'); expect(val.hi).to.be('mars!'); done.gun1 = true; }); gun2.val(function(val){ - console.log('@@@@@@@@@@@@2', val); expect(val.hello).to.be('world!'); expect(val.hi).to.be('mars!'); expect(done.gun1).to.be.ok();