Skip to content

Commit

Permalink
TBR playground/client: add JSON stream test coverage
Browse files Browse the repository at this point in the history
* Add tests for error and edge cases for the JSONStream
* api.run() returns stream directly instead of having the extra callback.
* Add initial message to let the user know that the run has started.

Change-Id: I9056da42389c4b4e3dd62e00b9e0b2d69175800b
Related: vanadium/issues#38
  • Loading branch information
jxson committed May 6, 2015
1 parent 34cba87 commit 7e49e0a
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 50 deletions.
30 changes: 14 additions & 16 deletions client/browser/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ var url = require('url');
var extend = require('xtend');
var prr = require('prr');
var config = require('../config');
var JSONStream = require('./json-stream');
var split = require('split');
var jsonStream = require('./json-stream');
var defaults = {
// Timeout for HTTP requests, 5 secs in milliseconds.
timeout: 5 * 60 * 1000,
Expand Down Expand Up @@ -204,18 +203,20 @@ API.prototype.done = function(uuid) {
// immediately.
// TODO(jasoncampbell): stop pending xhr
// SEE: https://github.com/vanadium/issues/issues/39
API.prototype.run = function(data, callback) {
API.prototype.run = function(data) {
var api = this;
var uuid = data.uuid;
var uri = api.url({
action: 'run',
debug: true
});

// NOTE: The UI prevents the channel from being fired while the run is in
// progress so this should never happen.
if (api.isPending(uuid)) {
var message = format('%s is already running');
var err = new Error(message);
return callback(err);
throw err;
}

api.pending(uuid);
Expand All @@ -231,23 +232,20 @@ API.prototype.run = function(data, callback) {
// TODO(jasoncampbell): Consolidate http libraries.
// TODO(jasoncampbell): Verify XHR timeout logic and handle appropriately.
var req = hyperquest.post(uri, options);
var stream = jsonStream();

req.once('error', callback);

var stream = JSONStream(); // jshint ignore:line
req.on('error', function(err) {
stream.emit('error', err);
});

stream.on('end', function() {
req.on('end', function() {
api.done(uuid);
});

req
.pipe(split())
.pipe(stream);

callback(null, stream);
req.pipe(stream);

var string = JSON.stringify(data);

req.write(string);
req.write(JSON.stringify(data));
req.end();

return stream;
};
28 changes: 11 additions & 17 deletions client/browser/api/json-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,21 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var through2 = require('through2');
// Using jxson/split#ignore-trailing until it is merged upstream.
//
// SEE: https://github.com/dominictarr/split/pull/15
var split = require('split');

module.exports = create;
module.exports = JSONStream;

function create() {
return through2.obj(write);
function JSONStream() {
return split(parse, null, { trailing: false });
}

function write(buffer, enc, callback) {
function parse(buffer) {
if (buffer.length === 0) {
return callback();
return undefined;
} else {
return JSON.parse(buffer);
}

var json;
var err;

try {
json = JSON.parse(buffer);
} catch (err) {
err.data = buffer;
}

callback(err, json);
}
31 changes: 17 additions & 14 deletions client/browser/components/bundle/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,27 +115,30 @@ function run(state) {
debug('running');
state.running.set(true);

// Temporary message to provide feedback and show that the run is happening.
state.results.logs.push(log({
File: 'web client',
Message: 'Run request initiated.',
Stream: 'stdout'
}));

var data = {
uuid: state.uuid(),
files: toArray(state.files())
};

api.run(data, function onrun(err, stream) {
if (err) {
// TODO(jasoncampbell): handle error appropriately.
throw err;
}
var stream = api.run(data);

stream.on('error', function onerror(err) {
throw err;
});
stream.on('error', function(err) {
// TODO(jasoncampbell): handle errors appropriately.
throw err;
});

stream.on('data', function ondata(data) {
state.results.logs.push(log(data));
});
stream.on('data', function ondata(data) {
state.results.logs.push(log(data));
});

stream.on('end', function() {
state.running.set(false);
});
stream.on('end', function() {
state.running.set(false);
});
}
4 changes: 3 additions & 1 deletion client/browser/components/log/normalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var now = require('date-now');

module.exports = normalize;

// Takes data from API messages and converts them to appropriate objects
// (lowecase, correct values, etc.).
function normalize(data) {
// convert `data.Timestamp` nanosecond value to a float in milliseconds.
var oneMillion = 1e6;
var timestamp = data.Timestamp / oneMillion;
var timestamp = data.Timestamp ? (data.Timestamp / oneMillion) : now();

return {
message: data.Message,
Expand Down
4 changes: 3 additions & 1 deletion client/browser/exception-logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
var window = require('global/window');
var UA = window.navigator ? window.navigator.userAgent : '';

module.exports = init;
module.exports = {
init: init
};

function init() {
window.addEventListener('error', onerror);
Expand Down
3 changes: 2 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"main": "browser/index.js",
"dependencies": {
"brace": "^0.4.0",
"date-now": "^1.0.1",
"debug": "^2.1.3",
"domready": "^1.0.7",
"format": "^0.2.1",
Expand All @@ -20,7 +21,7 @@
"routes": "^2.0.0",
"run-parallel": "^1.1.0",
"single-page": "^1.0.0",
"split": "^0.3.3",
"split": "jxson/split#ignore-trailing",
"superagent": "^1.1.0",
"through2": "^0.6.3",
"xtend": "^4.0.0"
Expand Down
2 changes: 2 additions & 0 deletions client/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
require('./test-api-url');
require('./test-config');
require('./test-component-log');
require('./test-component-results');
require('./test-api-json-stream');
138 changes: 138 additions & 0 deletions client/test/test-api-json-stream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var test = require('tape');
var jsonStream = require('../browser/api/json-stream');
var Buffer = require('buffer').Buffer;

test('jsonStream() - single entity per write', function(t) {
t.plan(4);

var stream = jsonStream();

stream
.on('data', function(data) {
t.same(data, { foo: 'bar' });
});

iterate(4, function() {
stream.write(new Buffer('{ "foo": "bar" }\n'));
});

stream.end();
});

test('jsonStream() - empty strings', function(t) {
t.plan(1);

var stream = jsonStream();

stream
.on('data', function(data) {
t.same(data, { foo: 'bar' });
});

stream.write(new Buffer('{ "foo": "bar" }\n'));
stream.write(new Buffer(''));

stream.end();
});

test('jsonStream() - single entry, multiple writes', function(t) {
t.plan(1);

var stream = jsonStream();

stream
.on('data', function(data) {
t.same(data, { foo: 'bar', baz: 'qux' });
});

stream.write(new Buffer('{ "foo":'));
stream.write(new Buffer('"ba'));
stream.write(new Buffer('r", "ba'));
stream.write(new Buffer('z":'));
stream.write(new Buffer('"qux" }\n'));
stream.end();
});

test('jsonStream() - chunk with several entries, then pieces', function(t) {
t.plan(11);

var stream = jsonStream();
var chunk = '';

stream
.on('data', function(data) {
t.same(data, { a: 'b' });
});

iterate(10, function() {
chunk += '{ "a": "b" }\n';
});

chunk += '{ "a":';

stream.write(new Buffer(chunk));
stream.write(new Buffer(' "b" }\n'));
stream.end();
});

test('jsonStream() - end with a partial entry', function(t) {
t.plan(2);

var stream = jsonStream();

stream
.on('end', t.end)
.on('error', function(err) {
t.fail('should not error');
})
.on('data', function(data) {
t.same(data, { a: 'b' });
});

stream.write('{ "a": "b" }\n');
stream.write('{ "a": "b" }\n');
stream.write('{ "a": ');
stream.end();
});

test('jsonStream() - blank line entries', function(t) {
t.plan(1);

var stream = jsonStream();

stream
.on('end', t.end)
.on('error', function(err) {
t.fail('should not error');
})
.on('data', function(data) {
t.same(data, { a: 'b' });
});

stream.write('{ "a": "b" }\n');
stream.write('');
stream.end();
});

test('jsonStream() - bad json entry', function(t) {
var stream = jsonStream();
var chunk = '{ bad: "json"';

stream.on('error', function(err) {
t.ok(err instanceof Error, 'should error');
t.ok(err instanceof SyntaxError, 'should be a SyntaxError');
t.end();
});

stream.write(new Buffer(chunk + '\n'));
});

function iterate(times, iterator) {
for (var i = 0; i < times; i++) {
iterator(i);
}
}

0 comments on commit 7e49e0a

Please sign in to comment.