UNPKG

11.2 kBJavaScriptView Raw
1var assert = require('assert');
2var http = require('http');
3var WebSocket = require('ws');
4var zetta = require('../');
5var zettacluster = require('zetta-cluster');
6var Scout = require('./fixture/example_scout');
7var VirtualDevice = require('../lib/virtual_device');
8var LedJSON = require('./fixture/virtual_device.json');
9
10var mockSocket = {
11 on: function(){},
12 subscribe: function(topic, cb){
13 if(cb) {
14 cb();
15 }
16 },
17 unsubscribe: function(){}
18};
19
20describe('Virtual Device', function() {
21 var base = null;
22 var cluster = null;
23 var device = null;
24 var socket = null;
25 var deviceJson = null;
26 var vdevice = null;
27
28 var startPort = 2600;
29
30 beforeEach(function(done) {
31 cluster = zettacluster({ zetta: zetta })
32 .server('cloud')
33 .server('detroit1', [Scout], ['cloud'])
34 .on('ready', function() {
35 socket = cluster.servers['cloud'].httpServer.peers['detroit1'];
36 if (!socket) {
37 done(new Error('socket not found'));
38 }
39
40 var did = Object.keys(cluster.servers['detroit1'].runtime._jsDevices)[0];
41 device = cluster.servers['detroit1'].runtime._jsDevices[did];
42 var id = cluster.servers['detroit1'].id;
43 base = 'localhost:' + cluster.servers['cloud']._testPort + '/servers/' + cluster.servers['cloud'].locatePeer(id) + '/devices/' + did;
44
45 http.get('http://' + base, function(res) {
46 var buffer = [];
47 var len = 0;
48 res.on('readable', function() {
49 var data;
50 while (data = res.read()) {
51 buffer.push(data);
52 len += data.length;
53 }
54 });
55 res.on('end', function() {
56 var buf = Buffer.concat(buffer, len);
57 deviceJson = JSON.parse(buf.toString());
58 vdevice = new VirtualDevice(deviceJson, socket);
59 vdevice.on('ready', function() {
60 setTimeout(done, 100);
61 });
62 });
63 res.on('error', function(err) {
64 done(err);
65 });
66 })
67 })
68 .run(function(err){
69 if (err) {
70 return done(err);
71 }
72 });
73 });
74
75 afterEach(function(done) {
76 cluster.stop();
77 setTimeout(done, 10); // fix issues with server not being closed before a new one starts
78 });
79
80 describe('.call method', function() {
81
82 it('call should work without a callback function', function(done) {
83 vdevice.call('change')
84 var timer = setTimeout(function() {
85 done(new Error('Faied to recv transition call on detroit device'));
86 }, 100);
87
88 device.on('change', function() {
89 clearTimeout(timer);
90 done();
91 });
92 });
93
94 it('_update should always be called with data.actions in proper format', function(done) {
95 var called = 0;
96 var orig = vdevice._update;
97 vdevice._update = function(data) {
98 called++;
99 assert(Array.isArray(data.actions));
100 data.actions.forEach(function(action) {
101 assert(action.class);
102 assert(action.name);
103 assert(action.method);
104 assert(action.href);
105 assert(action.fields);
106 });
107 orig.apply(vdevice, arguments);
108
109 // _update is called twice on transitions. Once for the return of the transition http POST and again
110 // for the log topic update.
111 if (called === 2) {
112 done();
113 }
114 };
115
116 vdevice.call('change');
117 });
118
119 it('call should work without arguments', function(done) {
120 vdevice.call('change', function(err) {
121 assert.equal(err, null);
122 });
123 var timer = setTimeout(function() {
124 done(new Error('Faied to recv transition call on detroit device'));
125 }, 100);
126
127 device.on('change', function() {
128 clearTimeout(timer);
129 done();
130 });
131 });
132
133 it('call should work with arguments', function(done) {
134 vdevice.call('test', 321, function(err) {
135 assert.equal(err, null);
136 });
137 var timer = setTimeout(function() {
138 done(new Error('Faied to recv transition call on detroit device'));
139 }, 100);
140
141 device.on('test', function() {
142 clearTimeout(timer);
143 assert.equal(device.value, 321);
144 done();
145 });
146 });
147
148 it('call should work with arguments, after peer reconnects', function(done) {
149
150 var timer = setTimeout(function() {
151 done(new Error('Faied to recv transition call on detroit device'));
152 }, 1500);
153
154 vdevice.call('test', 999, function(err) {
155 assert.equal(err, null);
156
157 clearTimeout(timer);
158 assert.equal(device.value, 999);
159
160 var socket = cluster.servers['cloud'].httpServer.peers['detroit1'];
161 socket.close();
162
163 setTimeout(function() {
164 vdevice.call('test', 222, function(err) {
165 assert.equal(err, null);
166 });
167 var timer = setTimeout(function() {
168 done(new Error('Faied to recv transition call on detroit device'));
169 }, 1500);
170
171 device.on('test', function() {
172 clearTimeout(timer);
173 assert.equal(device.value, 222);
174 done();
175 });
176 }, 1500);
177 });
178
179 });
180 });
181
182 describe('Device log monitor stream', function() {
183
184 it('should update virtual devices state when detroit device updates', function(done) {
185 assert.equal(vdevice.state, 'ready');
186 device.call('change', function() {
187 assert.equal(device.state, 'changed');
188 setTimeout(function() {
189 assert.equal(vdevice.state, 'changed');
190 done();
191 }, 100);
192 });
193 });
194
195 it('should update virtual devices state when virtual device calls transition', function(done) {
196 assert.equal(vdevice.state, 'ready');
197 vdevice.call('change', function() {
198 assert.equal(device.state, 'changed');
199 setTimeout(function() {
200 assert.equal(vdevice.state, 'changed');
201 done();
202 }, 100);
203 });
204 });
205
206 });
207
208
209
210 describe('Device monitor streams on properties', function() {
211
212 it('should update virtual device when value increments locally', function(done) {
213 assert.equal(vdevice.bar, 0);
214 assert.equal(device.bar, 0);
215 device.incrementStreamValue();
216 assert.equal(device.bar, 1);
217 setTimeout(function() {
218 assert.equal(vdevice.bar, 1);
219 done();
220 }, 100);
221 });
222
223 it('should implement .createReadStream() for object stream', function(done) {
224 vdevice.createReadStream('bar').on('data', function(msg) {
225 assert.equal(msg.data, 1);
226 done();
227 });
228
229 setTimeout(function(){
230 device.incrementStreamValue();
231 }, 10);
232 })
233
234 it('should implement .createReadStream() for binary stream', function(done) {
235 vdevice.createReadStream('foobar').on('data', function(buf) {
236 assert.deepEqual(buf, new Buffer([1]));
237 done();
238 });
239 setTimeout(function(){
240 device.incrementFooBar();
241 }, 10);
242 })
243
244 it('should recv data event after a client ws disconnected on the same topic', function(done) {
245
246 var url = 'ws://localhost:' + cluster.servers['cloud']._testPort + '/servers/detroit1/events?topic=testdriver%2F' + device.id + '%2Fbar';
247
248 var recv = 0;
249 var wsRecv = 0;
250 vdevice.streams.bar.on('data', function(data) {
251 recv++;
252 });
253
254 device.incrementStreamValue();
255
256 setTimeout(function() {
257 assert.equal(recv, 1);
258 var socket = new WebSocket(url);
259 socket.on('message', function() {
260 wsRecv++;
261 });
262 socket.on('open', function() {
263 device.incrementStreamValue();
264 setTimeout(function() {
265 assert.equal(recv, 2);
266 assert.equal(wsRecv, 1);
267
268 socket.close();
269 device.incrementStreamValue();
270 setTimeout(function() {
271 assert.equal(recv, 3);
272 assert.equal(wsRecv, 1);
273 done();
274 }, 300);
275 },300);
276 });
277 socket.on('error', done);
278
279 }, 300);
280 });
281
282 });
283
284 describe('Device binary streams', function() {
285
286 it('should only subscribe to a binary stream if used', function(done) {
287 var topic = device.type + '/' + device.id + '/foobar';
288 assert.equal(cluster.servers['detroit1'].pubsub._listeners[topic], undefined);
289 vdevice.streams.foobar.on('data', function() {});
290 setTimeout(function() {
291 assert.notEqual(cluster.servers['detroit1'].pubsub._listeners[topic], undefined);
292 done();
293 }, 100);
294 });
295
296 it('should pass binary data from local device to virtual', function(done) {
297 var recv = 0;
298 vdevice.streams.foobar.on('data', function(data) {
299 recv++;
300 assert.deepEqual(data, new Buffer([recv]));
301 });
302
303 setTimeout(function() {
304 device.incrementFooBar();
305 device.incrementFooBar();
306 device.incrementFooBar();
307
308 setTimeout(function() {
309 assert.equal(recv, 3);
310 done();
311 }, 100);
312 }, 100);
313 });
314
315 });
316
317
318
319 describe('basic unit tests', function() {
320
321 var device = null;
322 beforeEach(function() {
323 device = new VirtualDevice(LedJSON , mockSocket);
324 });
325
326 it('wires up logs, properties, and actions', function() {
327 assert.equal(device.state, 'off');
328 assert.equal(Object.keys(device.streams).length, 2);
329 });
330
331 it('will change properties with update.', function() {
332 device._update({ properties: {state: 'on'}});
333 assert.equal(device.state, 'on');
334 });
335
336 it('will return the proper action given a name', function() {
337 var action = device._getAction('turn-on');
338 assert.ok(action);
339 assert.equal(action.name, 'turn-on');
340 assert.equal(action.fields.length, 1);
341 });
342
343 it('will return link given a title', function() {
344 var link = device._getLinkWithTitle('state');
345 assert.ok(link);
346 assert.equal(link.title, 'state');
347 assert.equal(link.rel[0], 'monitor');
348 assert.equal(link.rel[1], 'http://rels.zettajs.io/object-stream');
349 });
350
351 it('will return an array of links if searched for by rel', function() {
352 var links = device._getLinksWithRel('http://rels.zettajs.io/object-stream');
353 assert.ok(links);
354 assert.equal(links.length, 2);
355 assert.ok(Array.isArray(links));
356 });
357
358 it('will parse out a topic for a particular link', function() {
359 var link = device._getLinkWithTitle('state');
360 var topic = device._getTopic(link);
361 assert.equal(topic, 'led/0eaf8607-5b8c-45ee-afae-9a5f9e1f34e2/state');
362 });
363
364 it('will encode transition arguments into an object', function() {
365 var action = device._getAction('turn-on');
366 var data = device._encodeData(action, {});
367 assert.ok(data);
368 assert.equal(Object.keys(data)[0], 'action');
369 assert.equal(data.action, 'turn-on');
370 });
371
372 it('exposes .available() method', function() {
373 assert.equal(typeof device.available, 'function');
374 assert.equal(device.available('turn-on'), true);
375 assert.equal(device.available('turn-off'), false);
376 });
377 });
378
379});
380