1 | var assert = require('assert');
|
2 | var http = require('http');
|
3 | var os = require('os');
|
4 | var request = require('supertest');
|
5 | var spdy = require('spdy');
|
6 | var zetta = require('../');
|
7 | var Query = require('calypso').Query;
|
8 | var rels = require('zetta-rels');
|
9 | var zettacluster = require('zetta-cluster');
|
10 | var Scout = require('./fixture/example_scout');
|
11 | var Driver = require('./fixture/example_driver');
|
12 | var HttpDriver = require('./fixture/example_http_driver');
|
13 | var Registry = require('./fixture/mem_registry');
|
14 | var PeerRegistry = require('./fixture/mem_peer_registry');
|
15 |
|
16 | function getHttpServer(app) {
|
17 | return app.httpServer.server;
|
18 | }
|
19 |
|
20 | function getBody(fn) {
|
21 | return function(res) {
|
22 | try {
|
23 | if(res.text) {
|
24 | var body = JSON.parse(res.text);
|
25 | } else {
|
26 | var body = '';
|
27 | }
|
28 | } catch(err) {
|
29 | throw new Error('Failed to parse json body');
|
30 | }
|
31 |
|
32 | fn(res, body);
|
33 | }
|
34 | }
|
35 |
|
36 | function checkDeviceOnRootUri(entity) {
|
37 | assert(entity.class.indexOf('device') >= 0);
|
38 | assert(entity.class.indexOf(entity.properties.type) >= 0);
|
39 | assert.deepEqual(entity.rel, ["http://rels.zettajs.io/device"]);
|
40 |
|
41 | assert(entity.properties.name);
|
42 | assert(entity.properties.type);
|
43 | assert(entity.properties.state);
|
44 | assert(!entity.actions);
|
45 |
|
46 | assert(entity.links);
|
47 | hasLinkRel(entity.links, rels.self);
|
48 | hasLinkRel(entity.links, rels.server);
|
49 | hasLinkRel(entity.links, rels.type);
|
50 | hasLinkRel(entity.links, rels.edit);
|
51 | }
|
52 |
|
53 | function hasLinkRel(links, rel, title, href) {
|
54 | var found = false;
|
55 |
|
56 | links.forEach(function(link) {
|
57 | if(link.rel.indexOf(rel) != -1) {
|
58 | found = true;
|
59 |
|
60 | if(title !== undefined && link.title !== title) {
|
61 | throw new Error('link title does not match');
|
62 | }
|
63 |
|
64 | if(href !== undefined && link.href !== href) {
|
65 | throw new Error('link href does not match');
|
66 | }
|
67 | }
|
68 | });
|
69 |
|
70 | if(!found) {
|
71 | throw new Error('Link rel:'+rel+' not found in links');
|
72 | }
|
73 | }
|
74 |
|
75 |
|
76 | describe('Zetta Api', function() {
|
77 | var reg = null;
|
78 | var peerRegistry = null;
|
79 |
|
80 | beforeEach(function() {
|
81 | reg = new Registry();
|
82 | peerRegistry = new PeerRegistry();
|
83 | });
|
84 |
|
85 |
|
86 | describe('/servers/<peer id> ', function() {
|
87 | var app = null;
|
88 | var url = null;
|
89 |
|
90 | beforeEach(function(done) {
|
91 | app = zetta({ registry: reg, peerRegistry: peerRegistry })
|
92 | .silent()
|
93 | .properties({ custom: 123 })
|
94 | .use(Scout)
|
95 | .use(HttpDriver)
|
96 | .name('local')
|
97 | .expose('*')
|
98 | ._run(done);
|
99 |
|
100 | url = '/servers/'+app._name;
|
101 | });
|
102 |
|
103 | it('should have content type application/vnd.siren+json', function(done) {
|
104 | request(getHttpServer(app))
|
105 | .get(url)
|
106 | .expect('Content-Type', 'application/vnd.siren+json', done);
|
107 | });
|
108 |
|
109 | it('should return status code 200', function(done) {
|
110 | request(getHttpServer(app))
|
111 | .get(url)
|
112 | .expect(200, done);
|
113 | });
|
114 |
|
115 | it('should have class ["server"]', function(done) {
|
116 | request(getHttpServer(app))
|
117 | .get(url)
|
118 | .expect(getBody(function(res, body) {
|
119 | assert.deepEqual(body.class, ['server']);
|
120 | }))
|
121 | .end(done);
|
122 | });
|
123 |
|
124 | it('should have proper name and id property', function(done) {
|
125 | request(getHttpServer(app))
|
126 | .get(url)
|
127 | .expect(getBody(function(res, body) {
|
128 | assert.equal(body.properties.name, 'local');
|
129 | }))
|
130 | .end(done);
|
131 | });
|
132 |
|
133 | it('should have custom properties in resp', function(done) {
|
134 | request(getHttpServer(app))
|
135 | .get(url)
|
136 | .expect(getBody(function(res, body) {
|
137 | assert.equal(body.properties.name, 'local');
|
138 | assert.equal(body.properties.custom, 123);
|
139 | }))
|
140 | .end(done);
|
141 | });
|
142 |
|
143 | it('should have self link and log link', function(done) {
|
144 | request(getHttpServer(app))
|
145 | .get(url)
|
146 | .expect(getBody(function(res, body) {
|
147 | assert(body.links);
|
148 | hasLinkRel(body.links, 'self');
|
149 | hasLinkRel(body.links, 'monitor');
|
150 | }))
|
151 | .end(done);
|
152 | });
|
153 |
|
154 | it('should have a metadata link', function(done) {
|
155 | request(getHttpServer(app))
|
156 | .get(url)
|
157 | .expect(getBody(function(res, body) {
|
158 | assert(body.links);
|
159 | hasLinkRel(body.links, rels.metadata);
|
160 | }))
|
161 | .end(done);
|
162 | });
|
163 |
|
164 | it('should have monitor log link formatted correctly for HTTP requests', function(done) {
|
165 | request(getHttpServer(app))
|
166 | .get(url)
|
167 | .expect(getBody(function(res, body) {
|
168 | var link = body.links.filter(function(l) {
|
169 | return l.rel.indexOf('monitor') > -1;
|
170 | })[0];
|
171 | var obj = require('url').parse(link.href, true);
|
172 | assert.equal(obj.protocol, 'ws:');
|
173 | assert(obj.query.topic);
|
174 | }))
|
175 | .end(done);
|
176 | });
|
177 |
|
178 | it('should have monitor log link formatted correctly for SPDY requests', function(done) {
|
179 | var a = getHttpServer(app);
|
180 |
|
181 | if (!a.address()) a.listen(0);
|
182 |
|
183 | var agent = spdy.createAgent({
|
184 | host: '127.0.0.1',
|
185 | port: a.address().port,
|
186 | spdy: {
|
187 | plain: true,
|
188 | ssl: false
|
189 | }
|
190 | });
|
191 |
|
192 | var request = http.get({
|
193 | host: '127.0.0.1',
|
194 | port: a.address().port,
|
195 | path: url,
|
196 | agent: agent
|
197 | }, function(response) {
|
198 |
|
199 | var buffers = [];
|
200 | response.on('readable', function() {
|
201 | var data;
|
202 | while ((data = response.read()) !== null) {
|
203 | buffers.push(data);
|
204 | }
|
205 | });
|
206 |
|
207 | response.on('end', function() {
|
208 | var body = JSON.parse(Buffer.concat(buffers));
|
209 | var link = body.links.filter(function(l) {
|
210 | return l.rel.indexOf('monitor') > -1;
|
211 | })[0];
|
212 | var obj = require('url').parse(link.href, true);
|
213 | assert.equal(obj.protocol, 'http:');
|
214 | assert(obj.query.topic);
|
215 | agent.close();
|
216 | });
|
217 |
|
218 | response.on('end', done);
|
219 | }).end();
|
220 | });
|
221 |
|
222 | it('should have valid entities', function(done) {
|
223 | request(getHttpServer(app))
|
224 | .get(url)
|
225 | .expect(getBody(function(res, body) {
|
226 | assert(body.entities);
|
227 | assert.equal(body.entities.length, 1);
|
228 | checkDeviceOnRootUri(body.entities[0]);
|
229 | }))
|
230 | .end(done);
|
231 | });
|
232 |
|
233 | it('should have one action', function(done) {
|
234 | request(getHttpServer(app))
|
235 | .get(url)
|
236 | .expect(getBody(function(res, body) {
|
237 | assert(body.actions);
|
238 | assert.equal(body.actions.length, 1);
|
239 | }))
|
240 | .end(done);
|
241 | });
|
242 |
|
243 | it('should accept remote devices of type testdriver', function(done) {
|
244 | request(getHttpServer(app))
|
245 | .post(url + '/devices')
|
246 | .send('type=testdriver')
|
247 | .end(function(err, res) {
|
248 | getBody(function(res, body) {
|
249 | assert.equal(res.statusCode, 201);
|
250 | var query = Query.of('devices');
|
251 | reg.find(query, function(err, machines) {
|
252 | assert.equal(machines.length, 2);
|
253 | assert.equal(machines[1].type, 'testdriver');
|
254 | done();
|
255 | });
|
256 | })(res);
|
257 | });
|
258 | });
|
259 |
|
260 | it('should not accept a remote device of type foo', function(done) {
|
261 | request(getHttpServer(app))
|
262 | .post(url + '/devices')
|
263 | .send('type=foo')
|
264 | .expect(getBody(function(res, body) {
|
265 | assert.equal(res.statusCode, 404);
|
266 | }))
|
267 | .end(done);
|
268 | });
|
269 |
|
270 | it('should accept remote devices of type testdriver, and allow them to set their own id properties', function(done) {
|
271 | request(getHttpServer(app))
|
272 | .post(url + '/devices')
|
273 | .send('type=testdriver&id=12345&name=test')
|
274 | .end(function(err, res) {
|
275 | getBody(function(res, body) {
|
276 | assert.equal(res.statusCode, 201);
|
277 | var query = Query.of('devices').where('id', { eq: '12345'});
|
278 | reg.find(query, function(err, machines) {
|
279 | assert.equal(machines.length, 1);
|
280 | assert.equal(machines[0].type, 'testdriver');
|
281 | assert.equal(machines[0].id, '12345');
|
282 | done();
|
283 | });
|
284 | })(res);
|
285 | });
|
286 | });
|
287 |
|
288 | it('query for device should respond with properly formated api response', function(done) {
|
289 | request(getHttpServer(app))
|
290 | .get(url+'?server=local&ql=where%20type="testdriver"')
|
291 | .expect(getBody(function(res, body) {
|
292 | assert(body.entities);
|
293 | assert.equal(body.entities.length, 1);
|
294 | checkDeviceOnRootUri(body.entities[0]);
|
295 | }))
|
296 | .end(done);
|
297 | });
|
298 |
|
299 |
|
300 |
|
301 | });
|
302 |
|
303 | describe('/', function() {
|
304 | var app = null;
|
305 |
|
306 | beforeEach(function() {
|
307 | app = zetta({ registry: reg, peerRegistry: peerRegistry })
|
308 | .silent()
|
309 | .use(Scout)
|
310 | .name('local')
|
311 | .expose('*')
|
312 | ._run();
|
313 | });
|
314 |
|
315 | it('should have content type application/vnd.siren+json', function(done) {
|
316 | request(getHttpServer(app))
|
317 | .get('/')
|
318 | .expect('Content-Type', 'application/vnd.siren+json', done);
|
319 | });
|
320 |
|
321 | it('should have status code 200', function(done) {
|
322 | request(getHttpServer(app))
|
323 | .get('/')
|
324 | .expect(200, done);
|
325 | });
|
326 |
|
327 | it('body should contain class ["root"]', function(done) {
|
328 | request(getHttpServer(app))
|
329 | .get('/')
|
330 | .expect(getBody(function(res, body) {
|
331 | assert.deepEqual(body.class, ['root']);
|
332 | }))
|
333 | .end(done)
|
334 | });
|
335 |
|
336 |
|
337 | it('body should contain links property', function(done) {
|
338 | request(getHttpServer(app))
|
339 | .get('/')
|
340 | .expect(getBody(function(res, body) {
|
341 | assert.equal(body.links.length, 3);
|
342 | hasLinkRel(body.links, 'self');
|
343 | }))
|
344 | .end(done)
|
345 | });
|
346 |
|
347 | it('links should contain rel to server', function(done) {
|
348 | request(getHttpServer(app))
|
349 | .get('/')
|
350 | .expect(getBody(function(res, body) {
|
351 | hasLinkRel(body.links, rels.server);
|
352 | }))
|
353 | .end(done)
|
354 | });
|
355 |
|
356 | it('should use a default server name if none has been provided', function(done) {
|
357 | var app = zetta({ registry: reg, peerRegistry: peerRegistry }).silent()._run();
|
358 |
|
359 | request(getHttpServer(app))
|
360 | .get('/')
|
361 | .expect(getBody(function(res, body) {
|
362 | var self = body.links.filter(function(link) {
|
363 | return link.rel.indexOf(rels.server) !== -1;
|
364 | })[0];
|
365 |
|
366 | assert.equal(self.title, os.hostname());
|
367 | }))
|
368 | .end(done);
|
369 | });
|
370 | });
|
371 |
|
372 | describe('/peer-management', function() {
|
373 | var app = null;
|
374 |
|
375 | before(function(done) {
|
376 | peerRegistry.save({
|
377 | id: '12341234',
|
378 | name: 'test-peer'
|
379 | }, done);
|
380 | });
|
381 |
|
382 | beforeEach(function(done) {
|
383 | app = zetta({ registry: reg, peerRegistry: peerRegistry })
|
384 | .silent()
|
385 | .use(Scout)
|
386 | .name('local')
|
387 | .expose('*')
|
388 | ._run(done);
|
389 | });
|
390 |
|
391 | it('should have content type application/vnd.siren+json', function(done) {
|
392 | request(getHttpServer(app))
|
393 | .get('/peer-management')
|
394 | .expect('Content-Type', 'application/vnd.siren+json', done);
|
395 | });
|
396 |
|
397 | it('should return status code 200', function(done) {
|
398 | request(getHttpServer(app))
|
399 | .get('/peer-management')
|
400 | .expect(200, done);
|
401 | });
|
402 |
|
403 | it('should have class ["peer-management"]', function(done) {
|
404 | request(getHttpServer(app))
|
405 | .get('/peer-management')
|
406 | .expect(getBody(function(err, body) {
|
407 | assert.deepEqual(body.class, ['peer-management']);
|
408 | }))
|
409 | .end(done);
|
410 | });
|
411 |
|
412 | it('should list saved peers', function(done) {
|
413 | peerRegistry.save({ id: '0' }, function() {
|
414 | request(getHttpServer(app))
|
415 | .get('/peer-management')
|
416 | .expect(getBody(function(err, body) {
|
417 | assert.equal(body.entities.length, 1);
|
418 | }))
|
419 | .end(done);
|
420 | });
|
421 | });
|
422 |
|
423 | it('should allow the querying of peers with the ql parameter', function(done) {
|
424 | peerRegistry.save({ id: '1', type: 'initiator'}, function() {
|
425 | request(getHttpServer(app))
|
426 | .get('/peer-management?ql=where%20type%3D%22initiator%22')
|
427 | .expect(getBody(function(err, body) {
|
428 | assert.equal(body.entities.length, 1);
|
429 | var entity = body.entities[0];
|
430 | assert.equal(entity.properties.id, '1');
|
431 | }))
|
432 | .end(done);
|
433 | });
|
434 | });
|
435 |
|
436 | describe('#link', function() {
|
437 | it('should return status code 202', function(done) {
|
438 | request(getHttpServer(app))
|
439 | .post('/peer-management')
|
440 | .send('url=http://testurl')
|
441 | .expect(202, done);
|
442 | });
|
443 |
|
444 | it('should return a Location header', function(done) {
|
445 | request(getHttpServer(app))
|
446 | .post('/peer-management')
|
447 | .send('url=http://testurl')
|
448 | .expect('Location', /^http.+/)
|
449 | .end(done);
|
450 | });
|
451 | });
|
452 |
|
453 | describe('#show', function() {
|
454 | it('should return the peer item representation', function(done) {
|
455 | var id = '1234-5678-9ABCD';
|
456 | peerRegistry.save({ id: id }, function() {
|
457 | request(getHttpServer(app))
|
458 | .get('/peer-management/' + id)
|
459 | .expect(200, done);
|
460 | });
|
461 | });
|
462 | });
|
463 | });
|
464 |
|
465 | describe('/devices of server', function() {
|
466 | var app = null;
|
467 |
|
468 | beforeEach(function(done) {
|
469 | app = zetta({ registry: reg, peerRegistry: peerRegistry })
|
470 | .silent()
|
471 | .use(Scout)
|
472 | .name('local')
|
473 | .expose('*')
|
474 | ._run(done);
|
475 | });
|
476 |
|
477 | it('should have content type application/vnd.siren+json', function(done) {
|
478 | request(getHttpServer(app))
|
479 | .get('/devices')
|
480 | .expect('Content-Type', 'application/vnd.siren+json', done);
|
481 | });
|
482 |
|
483 | it('should return status code 200', function(done) {
|
484 | request(getHttpServer(app))
|
485 | .get('/devices')
|
486 | .expect(200, done);
|
487 | });
|
488 |
|
489 | it('should have class ["devices"]', function(done) {
|
490 | request(getHttpServer(app))
|
491 | .get('/devices')
|
492 | .expect(getBody(function(res, body) {
|
493 | assert.deepEqual(body.class, ['devices']);
|
494 | }))
|
495 | .end(done);
|
496 | });
|
497 |
|
498 | it('should have one valid entity', function(done) {
|
499 | request(getHttpServer(app))
|
500 | .get('/devices')
|
501 | .expect(getBody(function(res, body) {
|
502 | assert(body.entities);
|
503 | assert.equal(body.entities.length, 1);
|
504 | checkDeviceOnRootUri(body.entities[0]);
|
505 | hasLinkRel(body.links, 'self');
|
506 | }))
|
507 | .end(done);
|
508 | });
|
509 | });
|
510 |
|
511 |
|
512 |
|
513 |
|
514 | describe('/servers/:id/devices/:id', function() {
|
515 | var app = null;
|
516 | var url = null;
|
517 | var device = null;
|
518 |
|
519 | beforeEach(function(done) {
|
520 | app = zetta({ registry: reg, peerRegistry: peerRegistry })
|
521 | .silent()
|
522 | .use(Scout)
|
523 | .name('local')
|
524 | .expose('*')
|
525 | ._run(function() {
|
526 | device = app.runtime._jsDevices[Object.keys(app.runtime._jsDevices)[0]];
|
527 | url = '/servers/' + app._name + '/devices/' + device.id;
|
528 | done();
|
529 | });
|
530 | });
|
531 |
|
532 | it('should have content type application/vnd.siren+json', function(done) {
|
533 | request(getHttpServer(app))
|
534 | .get(url)
|
535 | .expect('Content-Type', 'application/vnd.siren+json', done);
|
536 | });
|
537 |
|
538 | it('class should be ["device", ":type"]', function(done) {
|
539 | request(getHttpServer(app))
|
540 | .get(url)
|
541 | .expect(getBody(function(res, body) {
|
542 | assert(body.class.indexOf('device') >= 0);
|
543 | assert(body.class.indexOf(body.properties.type) >= 0);
|
544 | }))
|
545 | .end(done);
|
546 | });
|
547 |
|
548 | |
549 |
|
550 |
|
551 |
|
552 |
|
553 |
|
554 | it('properties should match expected', function(done) {
|
555 | request(getHttpServer(app))
|
556 | .get(url)
|
557 | .expect(getBody(function(res, body) {
|
558 | assert(body.properties);
|
559 | assert.equal(body.properties.name, device.name);
|
560 | assert.equal(body.properties.type, device.type);
|
561 | assert.equal(body.properties.id, device.id);
|
562 | assert.equal(body.properties.state, device.state);
|
563 | }))
|
564 | .end(done);
|
565 | });
|
566 |
|
567 | it('device should have action change', function(done) {
|
568 | request(getHttpServer(app))
|
569 | .get(url)
|
570 | .expect(getBody(function(res, body) {
|
571 | assert.equal(body.actions.length, 3);
|
572 | var action = body.actions[0];
|
573 | assert.equal(action.name, 'change');
|
574 | assert.equal(action.method, 'POST');
|
575 | assert(action.href);
|
576 | assert.deepEqual(action.fields[0], { name: 'action', type: 'hidden', value: 'change' });
|
577 | }))
|
578 | .end(done);
|
579 | });
|
580 |
|
581 | it('device actions should have class "transition"', function(done) {
|
582 | request(getHttpServer(app))
|
583 | .get(url)
|
584 | .expect(getBody(function(res, body) {
|
585 | assert.equal(body.actions.length, 3);
|
586 | body.actions.forEach(function(action) {
|
587 | assert(action.class.indexOf('transition') >= 0);
|
588 | })
|
589 | }))
|
590 | .end(done);
|
591 | });
|
592 |
|
593 |
|
594 | it('device should have self link', function(done) {
|
595 | request(getHttpServer(app))
|
596 | .get(url)
|
597 | .expect(getBody(function(res, body) {
|
598 | hasLinkRel(body.links, 'self');
|
599 | }))
|
600 | .end(done);
|
601 | });
|
602 |
|
603 | it('device should have edit link', function(done) {
|
604 | request(getHttpServer(app))
|
605 | .get(url)
|
606 | .expect(getBody(function(res, body) {
|
607 | hasLinkRel(body.links, 'edit');
|
608 | }))
|
609 | .end(done);
|
610 | });
|
611 |
|
612 | it('device should have up link to server', function(done) {
|
613 | request(getHttpServer(app))
|
614 | .get(url)
|
615 | .expect(getBody(function(res, body) {
|
616 | hasLinkRel(body.links, 'up', 'local');
|
617 | }))
|
618 | .end(done);
|
619 | });
|
620 |
|
621 | it('device should have monitor link for bar', function(done) {
|
622 | request(getHttpServer(app))
|
623 | .get(url)
|
624 | .expect(getBody(function(res, body) {
|
625 | hasLinkRel(body.links, 'monitor');
|
626 | }))
|
627 | .end(done);
|
628 | });
|
629 |
|
630 | it('device should have monitor link for bar formatted correctly for HTTP requests', function(done) {
|
631 | request(getHttpServer(app))
|
632 | .get(url)
|
633 | .expect(getBody(function(res, body) {
|
634 | var fooBar = body.links.filter(function(link) {
|
635 | return link.title === 'foobar';
|
636 | });
|
637 |
|
638 | hasLinkRel(fooBar, rels.binaryStream);
|
639 | var parsed = require('url').parse(fooBar[0].href);
|
640 | assert.equal(parsed.protocol, 'ws:');
|
641 | }))
|
642 | .end(done);
|
643 | });
|
644 |
|
645 | it('should have a monitor link for bar formatted correctly for SPDY requests', function(done) {
|
646 | var a = getHttpServer(app);
|
647 |
|
648 | if (!a.address()) a.listen(0);
|
649 |
|
650 | var agent = spdy.createAgent({
|
651 | host: '127.0.0.1',
|
652 | port: a.address().port,
|
653 | spdy: {
|
654 | plain: true,
|
655 | ssl: false
|
656 | }
|
657 | });
|
658 |
|
659 | var request = http.get({
|
660 | host: '127.0.0.1',
|
661 | port: a.address().port,
|
662 | path: url,
|
663 | agent: agent
|
664 | }, function(response) {
|
665 |
|
666 | var buffers = [];
|
667 | response.on('readable', function() {
|
668 | var data;
|
669 | while ((data = response.read()) !== null) {
|
670 | buffers.push(data);
|
671 | }
|
672 | });
|
673 |
|
674 | response.on('end', function() {
|
675 | var body = JSON.parse(Buffer.concat(buffers));
|
676 | var fooBar = body.links.filter(function(link) {
|
677 | return link.title === 'foobar';
|
678 | });
|
679 |
|
680 | hasLinkRel(fooBar, rels.binaryStream);
|
681 | var parsed = require('url').parse(fooBar[0].href);
|
682 | assert.equal(parsed.protocol, 'http:');
|
683 | agent.close();
|
684 | });
|
685 |
|
686 | response.on('end', done);
|
687 | }).end();
|
688 | });
|
689 |
|
690 | it('device action should return a 400 status code on a missing request body', function(done) {
|
691 | request(getHttpServer(app))
|
692 | .post(url)
|
693 | .send()
|
694 | .expect(getBody(function(res, body) {
|
695 | assert.equal(res.statusCode, 400);
|
696 | }))
|
697 | .end(done);
|
698 | });
|
699 |
|
700 | it('device action should return a 400 status code on an invalid request body', function(done) {
|
701 | request(getHttpServer(app))
|
702 | .post(url)
|
703 | .type('form')
|
704 | .send('{ "what": "invalid" }')
|
705 | .expect(getBody(function(res, body) {
|
706 | assert.equal(res.statusCode, 400);
|
707 | }))
|
708 | .end(done);
|
709 | });
|
710 |
|
711 | it('device action should work', function(done) {
|
712 | request(getHttpServer(app))
|
713 | .post(url)
|
714 | .type('form')
|
715 | .send({ action: 'test', value: 123 })
|
716 | .expect(getBody(function(res, body) {
|
717 | assert.equal(body.properties.value, 123);
|
718 | hasLinkRel(body.links, 'monitor');
|
719 | }))
|
720 | .end(done);
|
721 | });
|
722 |
|
723 | it('device action should return 400 when not available.', function(done) {
|
724 | request(getHttpServer(app))
|
725 | .post(url)
|
726 | .type('form')
|
727 | .send({ action: 'prepare' })
|
728 | .expect(getBody(function(res, body) {
|
729 | assert.equal(res.statusCode, 400);
|
730 | }))
|
731 | .end(done);
|
732 | });
|
733 |
|
734 | it('should return 500 when a error is passed in a callback of device driver', function(done) {
|
735 | request(getHttpServer(app))
|
736 | .post(url)
|
737 | .type('form')
|
738 | .send({ action: 'error', value: 'some error' })
|
739 | .expect(getBody(function(res, body) {
|
740 | assert.equal(res.statusCode, 500);
|
741 | }))
|
742 | .end(done);
|
743 | });
|
744 |
|
745 | it('should support device updates using PUT', function(done) {
|
746 | request(getHttpServer(app))
|
747 | .put(url)
|
748 | .type('json')
|
749 | .send({ bar: 2, value: 3 })
|
750 | .expect(getBody(function(res, body) {
|
751 | assert.equal(res.statusCode, 200);
|
752 | assert.equal(body.properties.bar, 2);
|
753 | assert.equal(body.properties.value, 3);
|
754 | }))
|
755 | .end(done);
|
756 | });
|
757 |
|
758 | it('should not overwrite monitor properties using PUT', function(done) {
|
759 | request(getHttpServer(app))
|
760 | .put(url)
|
761 | .type('json')
|
762 | .send({ foo: 1 })
|
763 | .expect(getBody(function(res, body) {
|
764 | assert.equal(res.statusCode, 200);
|
765 | assert.equal(body.properties.foo, 0);
|
766 | }))
|
767 | .end(done);
|
768 | });
|
769 |
|
770 | it('should return a 404 when updating a non-existent device', function(done) {
|
771 | request(getHttpServer(app))
|
772 | .put(url + '1234567890')
|
773 | .type('json')
|
774 | .send({ foo: 1, bar: 2, value: 3 })
|
775 | .expect(function(res) {
|
776 | assert.equal(res.statusCode, 404);
|
777 | })
|
778 | .end(done);
|
779 | });
|
780 |
|
781 | it('should return a 400 when updating with a Content-Range header', function(done) {
|
782 | request(getHttpServer(app))
|
783 | .put(url)
|
784 | .set('Content-Range', 'bytes 0-499/1234')
|
785 | .type('json')
|
786 | .send({ foo: 1, bar: 2, value: 3 })
|
787 | .expect(function(res) {
|
788 | assert.equal(res.statusCode, 400);
|
789 | })
|
790 | .end(done);
|
791 | });
|
792 |
|
793 | it('should return a 400 when receiving invalid JSON input', function(done) {
|
794 | request(getHttpServer(app))
|
795 | .put(url)
|
796 | .type('json')
|
797 | .send('{"name":}')
|
798 | .expect(function(res) {
|
799 | assert.equal(res.statusCode, 400);
|
800 | })
|
801 | .end(done);
|
802 | });
|
803 |
|
804 | it('should not include reserved fields on device updates', function(done) {
|
805 | var input = { foo: 1, bar: 2, value: 3, id: 'abcdef',
|
806 | _x: 4, type: 'h', state: 'yo', streams: 's' };
|
807 |
|
808 | request(getHttpServer(app))
|
809 | .put(url)
|
810 | .type('json')
|
811 | .send(input)
|
812 | .expect(getBody(function(res, body) {
|
813 | assert.equal(res.statusCode, 200);
|
814 | assert.notEqual(body.properties.id, 'abcdef');
|
815 | assert.notEqual(body.properties._x, 4);
|
816 | assert.notEqual(body.properties.streams, 's');
|
817 | assert.notEqual(body.properties.state, 'yo');
|
818 | assert.notEqual(body.properties.type, 'h');
|
819 | }))
|
820 | .end(done);
|
821 | });
|
822 | });
|
823 |
|
824 | describe('Proxied requests', function() {
|
825 | var base = null;
|
826 | var cluster = null;
|
827 |
|
828 | beforeEach(function(done) {
|
829 | cluster = zettacluster({ zetta: zetta })
|
830 | .server('cloud')
|
831 | .server('detroit', [Scout], ['cloud'])
|
832 | .on('ready', function(){
|
833 | base = 'localhost:' + cluster.servers['cloud']._testPort + '/servers/' + cluster.servers['cloud'].locatePeer('detroit');
|
834 | setTimeout(done, 300);
|
835 | })
|
836 | .run(function(err) {
|
837 | console.log(err)
|
838 | if (err) {
|
839 | done(err);
|
840 | }
|
841 | });
|
842 | });
|
843 |
|
844 | afterEach(function(done) {
|
845 | cluster.stop();
|
846 | setTimeout(done, 10);
|
847 | });
|
848 |
|
849 | it('zetta should not crash when req to hub is pending and hub disconnects', function(done) {
|
850 | http.get('http://' + base, function(res) {
|
851 | assert.equal(res.statusCode, 502);
|
852 | done();
|
853 | }).on('socket', function(socket) {
|
854 | socket.on('connect', function() {
|
855 | cluster.servers['cloud'].httpServer.peers['detroit'].close();
|
856 | });
|
857 | })
|
858 | })
|
859 |
|
860 | })
|
861 |
|
862 | });
|