1 | var assert = require('assert');
|
2 | var http = require('http');
|
3 | var WebSocket = require('ws');
|
4 | var zetta = require('../');
|
5 | var zettacluster = require('zetta-cluster');
|
6 | var Scout = require('./fixture/example_scout');
|
7 | var VirtualDevice = require('../lib/virtual_device');
|
8 | var LedJSON = require('./fixture/virtual_device.json');
|
9 |
|
10 | var mockSocket = {
|
11 | on: function(){},
|
12 | subscribe: function(topic, cb){
|
13 | if(cb) {
|
14 | cb();
|
15 | }
|
16 | },
|
17 | unsubscribe: function(){}
|
18 | };
|
19 |
|
20 | describe('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);
|
78 | });
|
79 |
|
80 | describe('.call method', function() {
|
81 |
|
82 | it('call should work without arguments', function(done) {
|
83 | vdevice.call('change', function(err) {
|
84 | assert.equal(err, null);
|
85 | });
|
86 | var timer = setTimeout(function() {
|
87 | done(new Error('Faied to recv transition call on detroit device'));
|
88 | }, 100);
|
89 |
|
90 | device.on('change', function() {
|
91 | clearTimeout(timer);
|
92 | done();
|
93 | });
|
94 | });
|
95 |
|
96 | it('call should work with arguments', function(done) {
|
97 | vdevice.call('test', 'hello', function(err) {
|
98 | assert.equal(err, null);
|
99 | });
|
100 | var timer = setTimeout(function() {
|
101 | done(new Error('Faied to recv transition call on detroit device'));
|
102 | }, 100);
|
103 |
|
104 | device.on('test', function() {
|
105 | clearTimeout(timer);
|
106 | assert.equal(device.value, 'hello');
|
107 | done();
|
108 | });
|
109 | });
|
110 |
|
111 | it('call should work with arguments, after peer reconnects', function(done) {
|
112 |
|
113 | var timer = setTimeout(function() {
|
114 | done(new Error('Faied to recv transition call on detroit device'));
|
115 | }, 1500);
|
116 |
|
117 | vdevice.call('test', 'hello', function(err) {
|
118 | assert.equal(err, null);
|
119 |
|
120 | clearTimeout(timer);
|
121 | assert.equal(device.value, 'hello');
|
122 |
|
123 | var socket = cluster.servers['cloud'].httpServer.peers['detroit1'];
|
124 | socket.close();
|
125 |
|
126 | setTimeout(function() {
|
127 | vdevice.call('test', 'hello1', function(err) {
|
128 | assert.equal(err, null);
|
129 | });
|
130 | var timer = setTimeout(function() {
|
131 | done(new Error('Faied to recv transition call on detroit device'));
|
132 | }, 1500);
|
133 |
|
134 | device.on('test', function() {
|
135 | clearTimeout(timer);
|
136 | assert.equal(device.value, 'hello1');
|
137 | done();
|
138 | });
|
139 | }, 1500);
|
140 | });
|
141 |
|
142 | });
|
143 | });
|
144 |
|
145 | describe('Device log monitor stream', function() {
|
146 |
|
147 | it('should update virtual devices state when detroit device updates', function(done) {
|
148 | assert.equal(vdevice.state, 'ready');
|
149 | device.call('change', function() {
|
150 | assert.equal(device.state, 'changed');
|
151 | setTimeout(function() {
|
152 | assert.equal(vdevice.state, 'changed');
|
153 | done();
|
154 | }, 100);
|
155 | });
|
156 | });
|
157 |
|
158 | it('should update virtual devices state when virtual device calls transition', function(done) {
|
159 | assert.equal(vdevice.state, 'ready');
|
160 | vdevice.call('change', function() {
|
161 | assert.equal(device.state, 'changed');
|
162 | setTimeout(function() {
|
163 | assert.equal(vdevice.state, 'changed');
|
164 | done();
|
165 | }, 100);
|
166 | });
|
167 | });
|
168 |
|
169 | });
|
170 |
|
171 |
|
172 |
|
173 | describe('Device monitor streams on properties', function() {
|
174 |
|
175 | it('should update virtual device when value increments locally', function(done) {
|
176 | assert.equal(vdevice.bar, 0);
|
177 | assert.equal(device.bar, 0);
|
178 | device.incrementStreamValue();
|
179 | assert.equal(device.bar, 1);
|
180 | setTimeout(function() {
|
181 | assert.equal(vdevice.bar, 1);
|
182 | done();
|
183 | }, 100);
|
184 | });
|
185 |
|
186 | it('should implement .createReadStream() for object stream', function(done) {
|
187 | vdevice.createReadStream('bar').on('data', function(msg) {
|
188 | assert.equal(msg.data, 1);
|
189 | done();
|
190 | });
|
191 |
|
192 | setTimeout(function(){
|
193 | device.incrementStreamValue();
|
194 | }, 10);
|
195 | })
|
196 |
|
197 | it('should implement .createReadStream() for binary stream', function(done) {
|
198 | vdevice.createReadStream('foobar').on('data', function(buf) {
|
199 | assert.deepEqual(buf, new Buffer([1]));
|
200 | done();
|
201 | });
|
202 | setTimeout(function(){
|
203 | device.incrementFooBar();
|
204 | }, 10);
|
205 | })
|
206 |
|
207 | it('should recv data event after a client ws disconnected on the same topic', function(done) {
|
208 |
|
209 | var url = 'ws://localhost:' + cluster.servers['cloud']._testPort + '/servers/detroit1/events?topic=testdriver%2F' + device.id + '%2Fbar';
|
210 |
|
211 | var recv = 0;
|
212 | var wsRecv = 0;
|
213 | vdevice.streams.bar.on('data', function(data) {
|
214 | recv++;
|
215 | });
|
216 |
|
217 | device.incrementStreamValue();
|
218 |
|
219 | setTimeout(function() {
|
220 | assert.equal(recv, 1);
|
221 | var socket = new WebSocket(url);
|
222 | socket.on('message', function() {
|
223 | wsRecv++;
|
224 | });
|
225 | socket.on('open', function() {
|
226 | device.incrementStreamValue();
|
227 | setTimeout(function() {
|
228 | assert.equal(recv, 2);
|
229 | assert.equal(wsRecv, 1);
|
230 |
|
231 | socket.close();
|
232 | device.incrementStreamValue();
|
233 | setTimeout(function() {
|
234 | assert.equal(recv, 3);
|
235 | assert.equal(wsRecv, 1);
|
236 | done();
|
237 | }, 300);
|
238 | },300);
|
239 | });
|
240 | socket.on('error', done);
|
241 |
|
242 | }, 300);
|
243 | });
|
244 |
|
245 | });
|
246 |
|
247 | describe('Device binary streams', function() {
|
248 |
|
249 | it('should only subscribe to a binary stream if used', function(done) {
|
250 | var topic = device.type + '/' + device.id + '/foobar';
|
251 | assert.equal(cluster.servers['detroit1'].pubsub._listeners[topic], undefined);
|
252 | vdevice.streams.foobar.on('data', function() {});
|
253 | setTimeout(function() {
|
254 | assert.notEqual(cluster.servers['detroit1'].pubsub._listeners[topic], undefined);
|
255 | done();
|
256 | }, 100);
|
257 | });
|
258 |
|
259 | it('should pass binary data from local device to virtual', function(done) {
|
260 | var recv = 0;
|
261 | vdevice.streams.foobar.on('data', function(data) {
|
262 | recv++;
|
263 | assert.deepEqual(data, new Buffer([recv]));
|
264 | });
|
265 |
|
266 | setTimeout(function() {
|
267 | device.incrementFooBar();
|
268 | device.incrementFooBar();
|
269 | device.incrementFooBar();
|
270 |
|
271 | setTimeout(function() {
|
272 | assert.equal(recv, 3);
|
273 | done();
|
274 | }, 100);
|
275 | }, 100);
|
276 | });
|
277 |
|
278 | });
|
279 |
|
280 |
|
281 |
|
282 | describe('basic unit tests', function() {
|
283 |
|
284 | var device = null;
|
285 | beforeEach(function() {
|
286 | device = new VirtualDevice(LedJSON , mockSocket);
|
287 | });
|
288 |
|
289 | it('wires up logs, properties, and actions', function() {
|
290 | assert.equal(device.state, 'off');
|
291 | assert.equal(Object.keys(device.streams).length, 2);
|
292 | });
|
293 |
|
294 | it('will change properties with update.', function() {
|
295 | device._update({ properties: {state: 'on'}});
|
296 | assert.equal(device.state, 'on');
|
297 | });
|
298 |
|
299 | it('will return the proper action given a name', function() {
|
300 | var action = device._getAction('turn-on');
|
301 | assert.ok(action);
|
302 | assert.equal(action.name, 'turn-on');
|
303 | assert.equal(action.fields.length, 1);
|
304 | });
|
305 |
|
306 | it('will return link given a title', function() {
|
307 | var link = device._getLinkWithTitle('state');
|
308 | assert.ok(link);
|
309 | assert.equal(link.title, 'state');
|
310 | assert.equal(link.rel[0], 'monitor');
|
311 | assert.equal(link.rel[1], 'http://rels.zettajs.io/object-stream');
|
312 | });
|
313 |
|
314 | it('will return an array of links if searched for by rel', function() {
|
315 | var links = device._getLinksWithRel('http://rels.zettajs.io/object-stream');
|
316 | assert.ok(links);
|
317 | assert.equal(links.length, 2);
|
318 | assert.ok(Array.isArray(links));
|
319 | });
|
320 |
|
321 | it('will parse out a topic for a particular link', function() {
|
322 | var link = device._getLinkWithTitle('state');
|
323 | var topic = device._getTopic(link);
|
324 | assert.equal(topic, 'led/0eaf8607-5b8c-45ee-afae-9a5f9e1f34e2/state');
|
325 | });
|
326 |
|
327 | it('will encode transition arguments into an object', function() {
|
328 | var action = device._getAction('turn-on');
|
329 | var data = device._encodeData(action, {});
|
330 | assert.ok(data);
|
331 | assert.equal(Object.keys(data)[0], 'action');
|
332 | assert.equal(data.action, 'turn-on');
|
333 | });
|
334 | });
|
335 |
|
336 | });
|
337 |
|