UNPKG

13.7 kBJavaScriptView Raw
1var util = require('util');
2var PubSub = require('../lib/pubsub_service');
3var Logger = require('../lib/logger');
4var Runtime = require('../zetta_runtime');
5var Device = Runtime.Device;
6var Scientist = require('zetta-scientist');
7var assert = require('assert');
8var SensorDriver = require('./fixture/sensor_driver');
9var TestDriver = require('./fixture/example_driver');
10var MemRegistry = require('./fixture/mem_registry');
11
12describe('Driver', function() {
13 var machine = null;
14 var pubsub = null;
15 var log = null;
16 var reg = null;
17
18 beforeEach(function(){
19 reg = new MemRegistry();
20 pubsub = new PubSub();
21 log = new Logger({pubsub: pubsub});
22 log.pubsub = pubsub;
23 // create machine
24 machine = Scientist.create(TestDriver);
25 machine._pubsub = pubsub; // setup pubsub, log, registry
26 machine._log = log;
27 machine._registry = reg;
28
29 // init machine
30 machine = Scientist.init(machine);
31 });
32
33 it('should be attached to the zetta runtime', function() {
34 assert.ok(Runtime.Device);
35 });
36
37 it('should expose an enableStream function', function() {
38 assert.ok(Device.prototype.enableStream);
39 });
40
41 it('should expose a disableStream function', function() {
42 assert.ok(Device.prototype.disableStream);
43 });
44
45 describe('Configuration', function() {
46 it('should be configured by Scientist#configure', function() {
47 assert.ok(machine.call);
48 assert.equal(machine.type, 'testdriver');
49 assert.equal(machine.state, 'ready');
50 assert.equal(machine.name, 'Matt\'s Test Device');
51 });
52
53 it('should have an id automatically generated for it', function(){
54 assert.ok(machine.id);
55 });
56
57 it('should have properties function', function() {
58 assert.equal(typeof machine.properties, 'function');
59 });
60
61 it('properties function should return filtered property list', function() {
62 machine._foo = 123;
63 var p = machine.properties();
64 assert.equal(p._foo, undefined);
65 });
66
67 });
68
69 describe('Logging', function() {
70 it('should expose a .log method', function() {
71 assert.equal(typeof machine.log, 'function');
72 });
73
74 it('should have log level functions', function() {
75 assert.ok(machine.log);
76 assert.ok(machine.info);
77 assert.ok(machine.warn);
78 assert.ok(machine.error);
79 });
80 });
81
82
83 describe('Transitions', function() {
84
85 it('should not throw when calling an invalid transition name.', function(done) {
86 machine.call('not-a-transition', function(err) {
87 assert(err);
88 done();
89 });
90 });
91
92 it('should not throw when calling a transition when destroyed.', function(done) {
93 machine.state = 'zetta-device-destroy';
94 machine.call('prepare', function(err) {
95 assert(err);
96 assert.equal(err.message, 'Machine destroyed. Cannot use transition prepare');
97 done();
98 });
99 });
100
101 it('should not throw when calling a transition not allowed in invalid state.', function(done) {
102 machine.state = 'not-a-state';
103 machine.call('prepare', function(err) {
104 assert(err);
105 done();
106 });
107 });
108
109 it('avialable transitions should not throw when in invalid state.', function(done) {
110 machine.state = 'not-a-state';
111 machine.transitionsAvailable();
112 done();
113 });
114
115 it('should change the state from ready to changed when calling change.', function(done) {
116 machine.call('change', function() {
117 assert.equal(machine.state, 'changed');
118 done();
119 });
120 });
121
122 it('should be able to call transiton afterchange after change was called', function(done) {
123 machine.call('change', function() {
124 assert.equal(machine.state, 'changed');
125 machine.call('prepare', function(err) {
126 assert.equal(machine.state, 'ready');
127 done();
128 });
129 });
130 });
131
132 it('should not throw an error when a disallowed transition tries to happen.', function(done) {
133 assert.doesNotThrow(function(){
134 machine.call('change', function() {
135 machine.call('change');
136 done();
137 });
138 });
139 });
140
141 it('should return error in callback.', function(done) {
142 machine.call('error', 'some error', function(err) {
143 assert(err instanceof Error);
144 done();
145 });
146 });
147
148 it('should have transitions emitted like events.', function(done) {
149 machine.on('change', function() {
150 done();
151 });
152
153 machine.call('change');
154 });
155
156 it('should publish transitions to pubsub', function(done) {
157 var topic = machine.type + '/' + machine.id + '/logs';
158
159 var recv = 0;
160 pubsub.subscribe(topic, function(topic, msg) {
161 assert.ok(msg.timestamp);
162 assert.ok(msg.topic);
163 assert.ok(!msg.data);
164 assert.ok(msg.properties);
165 assert.ok(msg.input);
166 assert.ok(msg.transition);
167 recv++;
168 });
169 machine.call('change');
170 setImmediate(function() {
171 assert.equal(recv, 1);
172 done();
173 });
174 });
175
176 it('should publish transitions to logs', function(done) {
177 var recv = 0;
178 pubsub.subscribe('logs', function(topic, msg) {
179 assert.ok(msg.timestamp);
180 assert.ok(msg.topic);
181 assert.ok(!msg.data);
182 assert.ok(msg.properties);
183 assert.ok(msg.input);
184 assert.ok(msg.transition);
185 recv++;
186 });
187 machine.call('change');
188 setImmediate(function() {
189 assert.equal(recv, 1);
190 done();
191 });
192 });
193
194 it('transitionsAvailable should return proper transitions', function() {
195 //.when('ready', { allow: ['change', 'test'] })
196 //.when('changed', { allow: ['prepare', 'test'] })
197
198 machine.state = 'ready';
199 var transitions = machine.transitionsAvailable();
200 assert(Object.keys(transitions).indexOf('change') > -1);
201 assert(Object.keys(transitions).indexOf('test') > -1);
202
203 machine.state = 'changed';
204 var transitions = machine.transitionsAvailable();
205 assert(Object.keys(transitions).indexOf('prepare') > -1);
206 assert(Object.keys(transitions).indexOf('test') > -1);
207
208 });
209 });
210
211 describe('Monitors', function(){
212
213 it('should be able to read state property', function() {
214 assert.equal(machine.state, 'ready');
215 });
216
217 it('should be able to read monitors properties', function() {
218 assert.equal(machine.foo, 0);
219 machine.foo = 1;
220 assert.equal(machine.foo, 1);
221 });
222
223 it('should be able to pass disable option to monitor', function() {
224 assert.equal(machine._streams['disabledMonitor'].enabled, false);
225 assert.equal(machine._streams['enabledMonitor'].enabled, true);
226 });
227 });
228
229 describe('Streams', function(){
230
231 function wireUpPubSub(stream, done){
232 pubsub.publish = function(name, data){
233 assert.ok(name);
234 assert.ok(data);
235 assert.ok(name.indexOf(stream) > -1);
236 done();
237 }
238 }
239
240 it('should stream values of foo once configured', function(done){
241 assert.ok(machine.streams.foo);
242 wireUpPubSub('foo', done);
243 machine.foo++;
244 });
245
246 it('should be able to pass disable option to stream', function() {
247 assert.equal(machine._streams['disabledStream'].enabled, false);
248 assert.equal(machine._streams['enabledStream'].enabled, true);
249 });
250
251 it('should have createReadSteam on device', function(){
252 assert.ok(machine.createReadStream);
253 assert.ok(machine.createReadStream('foo'));
254 });
255
256 it('createReadStream should return values from stream', function(done){
257 var s = machine.createReadStream('foo');
258 s.on('data', function() {
259 done();
260 });
261 machine.foo++;
262 });
263
264 it('createReadStream stream when paused shoud not recieve any updates', function(done){
265 var s = machine.createReadStream('foo');
266 var recv = 0;
267 s.on('data', function() {
268 recv++;
269 if (recv === 1) {
270 s.pause();
271 machine.foo++;
272 setTimeout(done, 10);
273 } else {
274 throw new Error('Should not recieve more than one data event');
275 }
276 });
277 machine.foo++;
278 });
279
280 it('should stream values of bar once configured', function(done){
281 assert.ok(machine.streams.bar);
282 wireUpPubSub('bar', done);
283 machine.incrementStreamValue();
284 });
285
286 it('should create a state stream when transitions are present', function() {
287 assert.ok(machine.streams.state);
288 });
289
290 it('should not create a state stream when no transitions are present', function() {
291 var machine = Scientist.init(Scientist.create(SensorDriver));
292 assert(!machine.streams.state);
293 });
294 });
295
296 describe('Device.save', function() {
297
298 it('save should be implemented on device', function() {
299 assert.equal(typeof machine.save, 'function');
300 });
301
302 it('save should update the registry with new property values', function(cb) {
303
304 reg.get(machine.id, function(err, result) {
305 assert(err);
306
307 machine.someval = 123;
308 machine._hidden = 'some-string';
309 machine.save(function(err) {
310 assert(!err);
311
312 reg.get(machine.id, function(err, result) {
313 assert.equal(err, null);
314 assert.equal(result.id, machine.id);
315 assert.equal(result.someval, 123);
316 assert.equal(typeof result._hidden, 'undefined');
317 cb();
318 });
319 });
320 });
321 });
322
323 });
324
325 describe('Remote Update and Fetch Hooks', function() {
326
327 it('can pass config a remoteFetch function to be called when .properties() is called', function() {
328 var Device = Runtime.Device;
329 var SomeDevice = function() {
330 this.hidden = 'hidden prop';
331 Device.call(this);
332 };
333 util.inherits(SomeDevice, Device);
334 SomeDevice.prototype.init = function(config) {
335 config
336 .type('some-device')
337 .name('device-1')
338 .remoteFetch(function() {
339 assert.equal(this.hidden, 'hidden prop');
340 return { prop: 123 };
341 })
342 };
343
344 var machine = Scientist.init(Scientist.create(SomeDevice));
345 assert.deepEqual(machine.properties(), {
346 name: 'device-1',
347 prop: 123,
348 type: 'some-device',
349 id: machine.id
350 });
351 })
352
353 it('handle remote update method, will update non reserved properties and remove old properties', function(done) {
354 var Device = Runtime.Device;
355 var SomeDevice = function() {
356 this.ip = '1.2.3.4';
357 this.mutable = 'abc';
358 this.deleted = 'gone after update';
359 Device.call(this);
360 };
361 util.inherits(SomeDevice, Device);
362 SomeDevice.prototype.init = function(config) {
363 config
364 .type('some-device')
365 .name('device-1');
366 };
367
368 var machine = Scientist.init(Scientist.create(SomeDevice));
369 machine._registry = reg;
370 machine._pubsub = pubsub;
371 machine._log = log;
372 machine._handleRemoteUpdate({ mutable: 123 }, function(err) {
373 assert.equal(err, null);
374 assert.equal(machine.ip, undefined);
375 assert.equal(machine.mutable, 123);
376 assert.equal(machine.deleted, undefined);
377 done();
378 });
379 })
380
381
382 it('can pass config a remoteUpdate function to be called when remoteUpdates are called', function(done) {
383 var Device = Runtime.Device;
384 var SomeDevice = function() {
385 this.ip = '1.2.3.4';
386 this.mutable = 'abc';
387 this.deleted = 'gone after update';
388 Device.call(this);
389 };
390 util.inherits(SomeDevice, Device);
391 SomeDevice.prototype.init = function(config) {
392 config
393 .type('some-device')
394 .name('device-1')
395 .remoteUpdate(function(properties, cb) {
396 var self = this;
397 // make sure ip cant be updated
398 delete properties.ip;
399
400 Object.keys(properties).forEach(function(key) {
401 self[key] = properties[key];
402 });
403
404 this.save(cb);
405 })
406 };
407
408 var machine = Scientist.init(Scientist.create(SomeDevice));
409 machine._registry = reg;
410 machine._pubsub = pubsub;
411 machine._log = log;
412 machine._handleRemoteUpdate({ mutable: 123 }, function(err) {
413 assert.equal(err, null);
414 assert.equal(machine.ip, '1.2.3.4');
415 assert.equal(machine.mutable, 123);
416 done();
417 });
418 });
419 });
420
421 describe('Deletion', function() {
422 it('should have a destroy function', function() {
423 assert.ok(machine.destroy);
424 });
425
426 it('should emit a destroy event when destroy is called.', function(done) {
427 machine.on('destroy', function(m) {
428 assert.ok(m);
429 done();
430 });
431 machine.destroy();
432 });
433
434 it('handle remote destroy method, will return true by default', function(done) {
435 var Device = Runtime.Device;
436 var SomeDevice = function() {
437 this.ip = '1.2.3.4';
438 this.mutable = 'abc';
439 this.deleted = 'gone after update';
440 Device.call(this);
441 };
442 util.inherits(SomeDevice, Device);
443 SomeDevice.prototype.init = function(config) {
444 config
445 .type('some-device')
446 .name('device-1');
447 };
448
449 var machine = Scientist.init(Scientist.create(SomeDevice));
450 machine._registry = reg;
451 machine._pubsub = pubsub;
452 machine._log = log;
453 machine._handleRemoteDestroy(function(err, destroyFlag) {
454 assert.equal(err, null);
455 assert.equal(destroyFlag, true);
456 done();
457 });
458 });
459 });
460});