UNPKG

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