@studiolabs/strong-remoting
Version:
StrongLoop Remoting Module
250 lines (211 loc) • 6.81 kB
JavaScript
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: strong-remoting
// This file is licensed under the Artistic License 2.0.
// License text available at https://opensource.org/licenses/Artistic-2.0
;
var assert = require('assert');
var RemoteObjects = require('../');
var expect = require('chai').expect;
var SharedClass = require('../lib/shared-class');
var express = require('express');
var request = require('supertest');
var fs = require('fs');
var es = require('event-stream');
var EventSource = require('eventsource');
describe('strong-remoting', function() {
var app, remotes, objects;
beforeEach(function() {
objects = RemoteObjects.create();
remotes = objects.exports;
app = express();
app.disable('x-powered-by');
app.use(function(req, res, next) {
objects.handler('rest').apply(objects, arguments);
});
});
function json(method, url) {
return request(app)[method](url)
.set('Accept', 'application/json')
.set('Content-Type', 'application/json')
.expect(200)
.expect('Content-Type', /json/);
}
function createSteam() {
remotes.fs = fs;
fs.createReadStream.shared = true;
fs.createReadStream.accepts = [
{arg: 'path', type: 'string'},
{arg: 'encoding', type: 'string'},
];
fs.createReadStream.returns = {arg: 'res', type: 'stream'};
fs.createReadStream.http = {
verb: 'get',
pipe: {
dest: 'res',
},
};
}
it('should stream the file output', function(done) {
createSteam();
json('get', '/fs/createReadStream?path=' + __dirname + '/data/foo.json&encoding=utf8')
.expect({bar: 'baz'}, done);
});
it('should stream the file output with no compression', function(done) {
createSteam();
request(app)['get']('/fs/createReadStream')
.expect('Content-Encoding', 'x-no-compression');
done();
});
});
describe('a function returning a ReadableStream', function() {
var Readable = require('stream').Readable;
var remotes = RemoteObjects.create();
var streamClass, server, app, streamClosed;
before(function(done) {
var test = this;
// NOTE: Date is intentionally excluded as it is not supported yet
var data = test.data = [{foo: 'bar'}, 'bat', false, 0, null];
app = express();
server = app.listen(0, '127.0.0.1', done);
function StreamClass() {
}
StreamClass.createStream = function createStream(cb) {
cb(null, es.readArray(test.data));
};
StreamClass.createStreamWithError = function createStreamWithError(cb) {
var rs = new Readable({objectMode: true});
rs._read = function() {
// required method
};
process.nextTick(function() {
rs.emit('error', new Error('test error'));
});
cb(null, rs);
};
StreamClass.createInfiniteStream = function createStream(cb) {
streamClosed = new Promise(resolve => {
const rs = new Readable({
objectMode: true,
read: function(size) {
setTimeout(() => this.push({foo: 'bar'}), 50);
},
destroy: function(size) {
resolve(true);
},
});
cb(null, rs);
});
};
streamClass = new SharedClass('StreamClass', StreamClass);
this.createStreamMethod = streamClass.defineMethod('createStream', {
isStatic: true,
fn: StreamClass.createStream,
returns: [{
arg: 'result',
type: 'ReadableStream',
json: true,
}],
});
streamClass.defineMethod('createStreamWithError', {
isStatic: true,
fn: StreamClass.createStreamWithError,
returns: [{
arg: 'result',
type: 'ReadableStream',
json: true,
}],
});
streamClass.defineMethod('createInfiniteStream', {
isStatic: true,
fn: StreamClass.createInfiniteStream,
returns: [{
arg: 'result',
type: 'ReadableStream',
json: true,
}],
});
remotes.addClass(streamClass);
app.use(remotes.handler('rest'));
});
before(function() {
remotes.connect('http://127.0.0.1:' + server.address().port, 'rest');
});
it('should return a ReadableStream', function(done) {
var testData = this.data;
remotes.invoke('StreamClass.createStream', [], function(err, stream) {
assert(stream.readable, 'must be a ReadableStream');
var out = es.writeArray(function(err, result) {
if (err) return done(err);
expect(testData).to.eql(result);
done();
});
stream.pipe(out);
});
});
it('should include errors', function(done) {
remotes.invoke('StreamClass.createStreamWithError', [], function(err, stream) {
stream.on('error', function(err) {
expect(err).instanceof(Error);
expect(err.message).to.equal('test error');
});
stream.on('end', done);
});
});
describe('an http client requesting a stream as an event source', function() {
before(function(done) {
var server = this.server = app.listen(done);
this.port = server.address().port;
});
before(function() {
var test = this;
this.url = 'http://localhost:' + this.port;
});
it('should respond with an event stream', function(done) {
var es = new EventSource(this.url + '/StreamClass/createStream');
var testData = this.data;
var result = [];
es.on('data', function(e) {
result.push(JSON.parse(e.data));
});
es.on('end', function() {
expect(testData).to.eql(result);
done();
});
});
it('should respond with an event stream with errors', function(done) {
var es = new EventSource(this.url + '/StreamClass/createStreamWithError');
es.on('error', function(e) {
var err;
if (e && e.data) {
err = JSON.parse(e.data);
} else {
return done(new Error('no error data!'));
}
expect(err.message).to.equal('test error');
done();
});
});
if ('destroy' in new Readable()) {
it('should close server stream on client disconnect',
function(done) {
const es = new EventSource(this.url + '/StreamClass/createInfiniteStream');
es.on('data', function(e) {
es.close();
streamClosed.then(() => done());
});
});
} else {
it('supports legacy ReadableStreams with no destroy() method',
function(done) {
const es = new EventSource(this.url + '/StreamClass/createInfiniteStream');
es.on('data', function(e) {
es.close();
process.nextTick(done);
});
});
}
after(function() {
this.server.close();
});
});
});