UNPKG

18 kBJavaScriptView Raw
1var assert = require('assert');
2var util = require('util');
3var fs = require('fs');
4var https = require('https');
5var zetta = require('../zetta');
6var WebSocket = require('ws');
7var MemRegistry = require('./fixture/mem_registry');
8var MemPeerRegistry = require('./fixture/mem_peer_registry');
9
10var Device = require('zetta-device');
11var HttpDevice = require('zetta-http-device');
12var Scout = require('zetta-scout');
13var ExampleDevice = require('./fixture/example_driver');
14var Query = require('calypso').Query;
15
16describe('Zetta', function() {
17 var reg = null;
18 var peerRegistry = null;
19
20 beforeEach(function() {
21 reg = new MemRegistry();
22 peerRegistry = new MemPeerRegistry();
23 });
24
25 it('should be attached to the zetta as a function', function() {
26 assert.equal(typeof zetta, 'function');
27 });
28
29 it('has the name set using the name() function.', function() {
30 var z = zetta({ registry: reg, peerRegistry: peerRegistry }).name('local').silent();
31 assert.equal(z._name, 'local');
32 });
33
34 it('should throw error if setting name to *', function() {
35 assert.throws(function() {
36 var z = zetta({ registry: reg, peerRegistry: peerRegistry }).name('*').silent();
37 }, Error);
38 });
39
40 it('has the silent() function to suppress logging.', function() {
41 var z = zetta({ registry: reg, peerRegistry: peerRegistry }).name('local').silent();
42 });
43
44 it('errors thrown in zetta apps should propagate.', function(done) {
45 var d = require('domain').create();
46 d.on('error', function(err) {
47 assert.equal(err.message, '123');
48 d.dispose()
49 done();
50 });
51 d.run(function() {
52 zetta()
53 .silent()
54 .use(ExampleDevice)
55 .use(function(server) {
56 var ledQuery = server.where({ type: 'testdriver' });
57 server.observe(ledQuery, function(led) {
58 throw new Error('123');
59 })
60 })
61 .listen(0);
62 });
63 });
64
65 it('support tls options for https server', function(done) {
66 var options = {
67 key: fs.readFileSync(__dirname + '/fixture/keys/key.pem'),
68 cert: fs.readFileSync(__dirname + '/fixture/keys/cert.pem')
69 };
70
71 var z = zetta({ registry: reg, peerRegistry: peerRegistry, tls: options })
72 .silent()
73 .listen(0, function(err) {
74 if (err) return done(err);
75
76 var port = z.httpServer.server.address().port;
77 var req = https.get({
78 host: 'localhost',
79 port: port,
80 path: '/',
81 rejectUnauthorized: false
82 }, function(res) {
83 assert.equal(res.statusCode, 200);
84 done();
85 });
86 req.on('error', done);
87 });
88 });
89
90 it('has the logger() function to pass in custom logging.', function(done) {
91 var z = zetta({ registry: reg, peerRegistry: peerRegistry });
92 z.logger(function(log) {
93 log.on('message', function(level, event, msg, data) {
94 assert.equal(level, 'info');
95 assert.equal(event, 'custom');
96 assert.equal(msg, 'some message');
97 assert.equal(data.data, 1);
98 done();
99 });
100
101 z.log.info('custom', 'some message', {data: 1});
102 });
103 });
104
105
106 it('will load an app with the load() function', function(done) {
107 zetta({ registry: reg, peerRegistry: peerRegistry })
108 .silent()
109 .load(function(server) {
110 assert.ok(server);
111 done();
112 })
113 ._initApps(function(){});
114 });
115
116 it('will load an app with the use() function', function(done) {
117 zetta({ registry: reg, peerRegistry: peerRegistry })
118 .silent()
119 .use(function(server) {
120 assert.ok(server);
121 done();
122 })
123 ._initApps(function(){});
124 });
125
126 it('will load an app with the use() function and additional arguments', function(done) {
127 var app = function(server, opts) {
128 assert.ok(server);
129 assert.ok(opts);
130 assert.equal(opts.foo, 1);
131 done();
132 }
133
134 zetta({ registry: reg, peerRegistry: peerRegistry })
135 .silent()
136 .use(app, { foo: 1})
137 ._initApps(function() {
138
139 });
140
141 });
142
143 it('will load an app with the use() function and additional arguments', function(done) {
144 var app = function(server, foo, bar) {
145 assert.ok(server);
146 assert.equal(foo, 1);
147 assert.equal(bar, 2);
148 done();
149 }
150
151 zetta({ registry: reg, peerRegistry: peerRegistry })
152 .silent()
153 .use(app, 1, 2)
154 ._initApps(function() {
155
156 });
157
158 });
159 it('will load a driver with the use() function', function() {
160 var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent();
161 function TestDriver() {
162 Device.call(this);
163 }
164 util.inherits(TestDriver, Device);
165
166 TestDriver.prototype.init = function() {};
167
168 z.use(TestDriver);
169 var s = z._scouts[0];
170 assert.equal(s.server, z.runtime);
171 });
172
173 it('will load an HTTP driver with the use() function', function() {
174 var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent();
175 function TestDriver() {
176 HttpDevice.call(this);
177 }
178 util.inherits(TestDriver, HttpDevice);
179
180 TestDriver.prototype.init = function() {};
181
182 z.use(TestDriver);
183 var s = z._scouts[0];
184 assert.equal(s.server, z.runtime);
185 });
186
187 it('will load a scout with the use() function', function() {
188 var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent();
189 function TestScout() {
190 Scout.call(this);
191 }
192 util.inherits(TestScout, Scout);
193 z.use(TestScout);
194 assert.equal(z._scouts.length, 2);
195 var s = z._scouts[0];
196 assert.equal(s.server, z.runtime);
197 });
198
199 it('will set the what query is used for expose()', function() {
200 var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent();
201 z.expose('*');
202
203 assert.ok(z._exposeQuery);
204 });
205
206 it('will call init on the server prototype to ensure everything is wired up correctly.', function(done) {
207 function MockHttp(){}
208 MockHttp.prototype.init = function() {
209 done();
210 };
211 MockHttp.prototype.listen = function(port) {};
212
213 var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent();
214 z.httpServer = new MockHttp();
215 z.listen(0);
216
217 });
218
219 it('will apply arguments to httpServer when listen() is called', function(done) {
220 function MockHttp(){}
221 MockHttp.prototype.init = function(){};
222 MockHttp.prototype.listen = function(port) {
223 assert.equal(port, 0);
224 done();
225 };
226
227 var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent();
228 z.httpServer = new MockHttp();
229 z.listen(0);
230
231 });
232
233 it('will correctly apply the callback to httpServer when listen() is called', function(done) {
234 function MockHttp(){}
235 MockHttp.prototype.init = function(){};
236 MockHttp.prototype.listen = function(port, cb) {
237 assert.equal(port, 0);
238 cb(null);
239 };
240
241 var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent();
242 z.httpServer = new MockHttp();
243 z.listen(0, function(err) {
244 assert.ok(!err);
245 done();
246 });
247 });
248
249 it('should initialize device with proper properties set.', function(done) {
250 var z = zetta({ registry: reg, peerRegistry: peerRegistry })
251 .silent()
252 .use(ExampleDevice, 1, 'a')
253 ._run(function(err) {
254 if (err) {
255 return done(err);
256 }
257
258 var device = z.runtime._jsDevices[Object.keys(z.runtime._jsDevices)[0]];
259 device.call('change', done);
260 });
261 });
262
263 it('should initialize 3 devices with correct params when using multiple use', function(done) {
264 var z = zetta({ registry: reg, peerRegistry: peerRegistry })
265 .silent()
266 .use(ExampleDevice, 1, 'a')
267 .use(ExampleDevice, 2, 'b')
268 .use(ExampleDevice, 3, 'c')
269 ._run(function(err) {
270 if (err) {
271 return done(err);
272 }
273
274 var find = function(x, y) {
275 return Object.keys(z.runtime._jsDevices).some(function(key){
276 var device = z.runtime._jsDevices[key];
277 return device._x === x && device._y === y;
278 });
279 };
280
281 assert(find(1, 'a'));
282 assert(find(2, 'b'));
283 assert(find(3, 'c'));
284
285 done();
286 });
287 });
288
289
290
291 it('should provision 3 devices already in registry with correct params when using multiple use', function(done) {
292 var z = zetta({ registry: reg, peerRegistry: peerRegistry })
293 .silent()
294 .use(ExampleDevice, 1, 'a')
295 .use(ExampleDevice, 2, 'b')
296 .use(ExampleDevice, 3, 'c')
297 ._run(function(err) {
298 if (err) {
299 return done(err);
300 }
301
302 var find = function(x, y) {
303 var id = null;
304 Object.keys(z.runtime._jsDevices).some(function(key){
305 var device = z.runtime._jsDevices[key];
306 if (device._x === x && device._y === y) {
307 id = device.id;
308 return true;
309 }
310 });
311
312 return id;
313 };
314
315 assert(find(1, 'a'));
316 assert(find(2, 'b'));
317 assert(find(3, 'c'));
318
319 var z2 = zetta({ registry: reg, peerRegistry: peerRegistry })
320 .silent()
321 .use(ExampleDevice, 1, 'a')
322 .use(ExampleDevice, 2, 'b')
323 .use(ExampleDevice, 3, 'c')
324 ._run(function(err) {
325 if (err) {
326 return done(err);
327 }
328
329 var find2 = function(id, x, y) {
330 return Object.keys(z2.runtime._jsDevices).some(function(key){
331 var device = z2.runtime._jsDevices[key];
332 return device.id === id && device._x === x && device._y === y;
333 });
334 };
335
336 assert(find2(find(1, 'a'), 1, 'a'));
337 assert(find2(find(2, 'b'), 2, 'b'));
338 assert(find2(find(3, 'c'), 3, 'c'));
339 done();
340 });
341 });
342 });
343
344 it('should only call .init once on a device driver with .use(Device)', function(done) {
345 var called = 0;
346 var oldInit = ExampleDevice.prototype.init;
347 ExampleDevice.prototype.init = function(config) {
348 called++;
349 return oldInit.call(this, config);
350 };
351
352 var app = zetta({ peerRegistry: peerRegistry, registry: reg });
353 app.silent();
354 app.use(ExampleDevice);
355 app.listen(0);
356 setTimeout(function() {
357 ExampleDevice.prototype.init = oldInit;
358 assert.equal(called, 1);
359 done();
360 }, 10);
361 });
362
363 describe('peering', function() {
364 it('.link should add to peers', function(done){
365 var app = zetta({ peerRegistry: peerRegistry, registry: reg });
366 app.silent();
367 app.link('http://example.com/');
368 app._initPeers(app._peers, function(err) {
369 setTimeout(function() {
370 assert.equal(app._peerClients.length, 1);
371 done();
372 }, 100);
373 });
374 });
375
376 it('.link should not add to peers', function(done){
377
378 peerRegistry.db.put('1234567', JSON.stringify({id: '1234567', direction: 'initiator', url: 'http://example.com/', fromLink: true}), function(err){
379 var app = zetta({ peerRegistry: peerRegistry, registry: reg });
380 app.silent();
381 app._initPeers(app._peers, function(err) {
382 setTimeout(function() {
383 assert.equal(app._peerClients.length, 0);
384 done();
385 }, 100);
386 });
387 });
388 });
389
390 it('will delete fromLink peers in the registry', function(done) {
391 peerRegistry.db.put('1234567', JSON.stringify({ id:'1234567', direction: 'initiator', url: 'http://example.com/', fromLink: true}), function(err) {
392 var app = zetta({ peerRegistry: peerRegistry, registry: reg });
393 app._initPeers(app._peers, function(err) {
394 setTimeout(function(){
395 assert.equal(app._peerClients.length, 0);
396 peerRegistry.find(Query.of('peers'), function(err, results) {
397 assert.equal(results.length, 0);
398 done();
399 });
400 }, 100);
401 });
402 });
403
404 });
405
406 it('will init API peers.', function(done){
407
408 peerRegistry.db.put('1234567', JSON.stringify({id: '1234567', direction: 'initiator', url: 'http://example.com/'}), function(err){
409 var app = zetta({ peerRegistry: peerRegistry, registry: reg });
410 app.silent();
411 app._initPeers(app._peers, function(err) {
412 setTimeout(function() {
413 assert.equal(app._peerClients.length, 1);
414 done();
415 }, 100);
416 });
417 });
418 });
419
420 });
421
422 it('has the properties() function to add custom properties to the api.', function() {
423 var z = zetta({ registry: reg, peerRegistry: peerRegistry });
424 assert(typeof z.properties, 'function');
425 z.properties({ test: 'abc' });
426 });
427
428 it('.getProperties() returns properties.', function() {
429 var z = zetta({ registry: reg, peerRegistry: peerRegistry }).name('test');
430 z.properties({ someKey: 123 });
431 assert.deepEqual(z.getProperties(), { name: 'test', someKey: 123 });
432 });
433
434
435 describe('HTTP Server Websocket connect hooks', function() {
436 it('peer connect hook will fire when peer connects', function(done) {
437 var fired = false;
438 var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() });
439 z.silent();
440 z.use(function(server) {
441 server.httpServer.onPeerConnect(function(request, socket, head, next) {
442 fired = true;
443 next();
444 })
445 })
446 z.listen(0, function() {
447 var port = z.httpServer.server.address().port;
448 zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() })
449 .silent()
450 .use(function(server) {
451 server.pubsub.subscribe('_peer/connect', function(topic, data) {
452 assert.equal(fired, true);
453 done();
454 })
455 })
456 .link('http://localhost:' + port)
457 .listen(0);
458 })
459 })
460
461 it('websocket connect hook will fire when clients connects', function(done) {
462 var fired = false;
463 var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() });
464 z.silent();
465 z.use(function(server) {
466 server.httpServer.onEventWebsocketConnect(function(request, socket, head, next) {
467 fired = true;
468 next();
469 })
470 })
471 z.listen(0, function() {
472 var port = z.httpServer.server.address().port;
473 var ws = new WebSocket('ws://localhost:' + port + '/events');
474 ws.once('open', function() {
475 assert.equal(fired, true);
476 done();
477 })
478 });
479 })
480
481 it('multiple hooks will fire in order for peer connects', function(done) {
482 var fired = [];
483 var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() });
484 z.silent();
485 z.use(function(server) {
486 server.httpServer.onPeerConnect(function(request, socket, head, next) {
487 fired.push(1);
488 next();
489 })
490 server.httpServer.onPeerConnect(function(request, socket, head, next) {
491 fired.push(2);
492 next();
493 })
494 })
495 z.listen(0, function() {
496 var port = z.httpServer.server.address().port;
497 zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() })
498 .silent()
499 .use(function(server) {
500 server.pubsub.subscribe('_peer/connect', function(topic, data) {
501 assert.deepEqual(fired, [1, 2]);
502 done();
503 })
504 })
505 .link('http://localhost:' + port)
506 .listen(0);
507 })
508 })
509
510 it('multiple hooks will fire in order for websocket connects', function(done) {
511 var fired = [];
512 var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() });
513 z.silent();
514 z.use(function(server) {
515 server.httpServer.onEventWebsocketConnect(function(request, socket, head, next) {
516 fired.push(1);
517 next();
518 })
519 server.httpServer.onEventWebsocketConnect(function(request, socket, head, next) {
520 fired.push(2);
521 next();
522 })
523 })
524 z.listen(0, function() {
525 var port = z.httpServer.server.address().port;
526 var ws = new WebSocket('ws://localhost:' + port + '/events');
527 ws.once('open', function() {
528 assert.deepEqual(fired, [1, 2]);
529 done();
530 })
531 });
532 })
533
534 it('returning an error from hook will result in a 500 on peer connect', function(done) {
535 var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() });
536 z.silent();
537 z.use(function(server) {
538 server.httpServer.onPeerConnect(function(request, socket, head, next) {
539 next(new Error('Error 123'));
540 })
541 })
542 z.listen(0, function() {
543 var port = z.httpServer.server.address().port;
544 zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() })
545 .silent()
546 .use(function(server) {
547 server.onPeerResponse(function(req) {
548 return req.map(function(env) {
549 assert.equal(env.response.statusCode, 500);
550 done();
551 return env;
552 });
553 });
554 })
555 .link('http://localhost:' + port)
556 .listen(0);
557 })
558 })
559
560 it('returning an error from hook will result in a 500 on websocket connect', function(done) {
561 var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() });
562 z.silent();
563 z.use(function(server) {
564 server.httpServer.onEventWebsocketConnect(function(request, socket, head, next) {
565 next(new Error('test error'));
566 })
567 })
568 z.listen(0, function() {
569 var port = z.httpServer.server.address().port;
570 var ws = new WebSocket('ws://localhost:' + port + '/events');
571 ws.once('error', function(err) {
572 assert.equal(err.message, 'unexpected server response (500)');
573 done();
574 })
575 });
576 })
577 });
578
579});