mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-24 07:17:18 +00:00
Merge pull request #2029 from resin-io/remove-unused-robot
refactor(lib): Remove unused robot protocol
This commit is contained in:
commit
2e1764af82
@ -96,10 +96,6 @@ contains certain features to ease communication:
|
||||
|
||||
- [Well-documented exit codes.][exit-codes]
|
||||
|
||||
- An environment variable called `ETCHER_CLI_ROBOT` option, which when set
|
||||
causes the Etcher CLI to output state in a way that can be easily
|
||||
parsed by a machine.
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
|
@ -26,7 +26,6 @@ const propTypes = require('prop-types')
|
||||
const { react2angular } = require('react2angular')
|
||||
const analytics = require('../modules/analytics')
|
||||
const packageJSON = require('../../../../package.json')
|
||||
const robot = require('../../../shared/robot')
|
||||
|
||||
const MODULE_NAME = 'Etcher.Components.SafeWebview'
|
||||
const angularSafeWebview = angular.module(MODULE_NAME, [])
|
||||
@ -221,27 +220,24 @@ class SafeWebview extends react.PureComponent {
|
||||
* @example
|
||||
*
|
||||
* // In the webview
|
||||
* console.log(JSON.stringify({
|
||||
* command: 'error',
|
||||
* data: 'Good night!'
|
||||
* }));
|
||||
*
|
||||
* console.log(JSON.stringify({
|
||||
* command: 'log',
|
||||
* data: 'Hello, Mars!'
|
||||
* }));
|
||||
* console.log('Good night!')
|
||||
*/
|
||||
static consoleMessage (event) {
|
||||
if (!robot.isMessage(event.message)) {
|
||||
if (_.isNil(event.message)) {
|
||||
return
|
||||
}
|
||||
|
||||
const message = robot.parseMessage(event.message)
|
||||
let message = event.message
|
||||
try {
|
||||
message = JSON.parse(event.message)
|
||||
} catch (error) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
if (robot.getCommand(message) === robot.COMMAND.LOG) {
|
||||
analytics.logEvent(robot.getData(message))
|
||||
} else if (robot.getCommand(message) === robot.COMMAND.ERROR) {
|
||||
analytics.logException(robot.getData(message))
|
||||
if (message.command === 'error') {
|
||||
analytics.logException(message.data)
|
||||
} else {
|
||||
analytics.logEvent(message.data || message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,132 +0,0 @@
|
||||
The "robot" mechanism
|
||||
=====================
|
||||
|
||||
The "robot" module is an entity that implements a text-based protocol to share
|
||||
objects between processes.
|
||||
|
||||
The contents and structure of these messages is what the "robot" module is
|
||||
mainly concerned with. Each "message" consists of a type (a "command" in robot
|
||||
parlance) and an arbitrary data object:
|
||||
|
||||
- `String command`: the message command name
|
||||
- `Object data`: the message data
|
||||
|
||||
For example:
|
||||
|
||||
*Child process:*
|
||||
|
||||
```js
|
||||
robot.printMessage('my-message-type', {
|
||||
my: {
|
||||
message: 'data'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
*Parent process:*
|
||||
|
||||
```js
|
||||
const message = robot.parseMessage(line);
|
||||
|
||||
console.log(robot.getCommand(message));
|
||||
> 'my-message-type'
|
||||
|
||||
console.log(robot.getData(message));
|
||||
> {
|
||||
> my: {
|
||||
> message: 'data'
|
||||
> }
|
||||
> }
|
||||
```
|
||||
|
||||
**Logging debug data to the console:**
|
||||
|
||||
*Child process:*
|
||||
|
||||
```js
|
||||
// This will log the passed data to parent's console,
|
||||
// as `console.log()`ing in the child will cause errors
|
||||
robot.log({ debugging: 'things' })
|
||||
```
|
||||
|
||||
The codename "robot" is inspired by [xz][xz-man], which provides a `--robot`
|
||||
option that makes the tool print machine-parseable output:
|
||||
|
||||
```
|
||||
--robot
|
||||
Print messages in a machine-parsable format. This is intended
|
||||
to ease writing frontends that want to use xz instead of
|
||||
liblzma, which may be the case with various scripts. The output
|
||||
with this option enabled is meant to be stable across xz
|
||||
releases. See the section ROBOT MODE for details.
|
||||
```
|
||||
|
||||
To enable the "robot" option, we standardised the presence of an
|
||||
`ETCHER_CLI_ROBOT` environment variable. You can check if the mode is enabled
|
||||
by using the `.isEnabled()` static function that the robot module provides:
|
||||
|
||||
```js
|
||||
if (robot.isEnabled()) {
|
||||
console.log('The robot option is enabled');
|
||||
}
|
||||
```
|
||||
|
||||
The current protocol that we use is based on JSON. The writer process
|
||||
stringifies a JSON object, and prints it. The client then gets the line, parses
|
||||
it as JSON, and accesses the object.
|
||||
|
||||
For example, the writer process may have a fictitious internal object that
|
||||
looks like this:
|
||||
|
||||
```js
|
||||
{
|
||||
percentage: 50,
|
||||
stage: 'validation'
|
||||
}
|
||||
```
|
||||
|
||||
That object can be stringified as `{"percentage":50,"stage":"validation"}` and
|
||||
printed to `stdout`.
|
||||
|
||||
This is what a valid robot message looks like:
|
||||
|
||||
```json
|
||||
{
|
||||
"command": "progress",
|
||||
"data": {
|
||||
"percentage": 50
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The command content and the data associated with it are application specific,
|
||||
however the robot module defines a single command called "error", which is used
|
||||
to transmit a JavaScript Error object, along with its metadata (stacktrace,
|
||||
code, description, etc) as a string.
|
||||
|
||||
You don't have to worry about the internal details of how an Error object is
|
||||
encoded/decoded, given that the robot module exposes two high level utility
|
||||
functions: `.printError()` and `.recomposeErrorMessage()`.
|
||||
|
||||
Here's an example of these functions in action:
|
||||
|
||||
```javascript
|
||||
const error = errors.createError({
|
||||
title: 'This is an error',
|
||||
description: 'My description'
|
||||
});
|
||||
|
||||
robot.printError(error);
|
||||
```
|
||||
|
||||
The client can then fetch the line, and recompose it back:
|
||||
|
||||
```javascript
|
||||
const error = robot.recomposeErrorMessage(line);
|
||||
```
|
||||
|
||||
The resulting `error` inherits the stacktrace and other metadata from the
|
||||
original error, even if this was created in another process. This is how the
|
||||
writer process propagates informational errors to the GUI.
|
||||
|
||||
[xz-man]: https://www.freebsd.org/cgi/man.cgi?query=xz&sektion=1&manpath=FreeBSD+8.3-RELEASE
|
@ -1,294 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 resin.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const _ = require('lodash')
|
||||
const errors = require('../errors')
|
||||
|
||||
/**
|
||||
* @summary Robot commands
|
||||
* @namespace COMMAND
|
||||
* @public
|
||||
*/
|
||||
exports.COMMAND = {
|
||||
|
||||
/**
|
||||
* @property {String} ERROR
|
||||
* @memberof COMMAND
|
||||
*
|
||||
* @description
|
||||
* This command is used to represent an error message.
|
||||
*/
|
||||
ERROR: 'error',
|
||||
|
||||
/**
|
||||
* @property {String} LOG
|
||||
* @memberof COMMAND
|
||||
*
|
||||
* @description
|
||||
* This command is used to represent a log message.
|
||||
*/
|
||||
LOG: 'log'
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Check whether we should emit parseable output
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {Object} environment - environment
|
||||
* @returns {Boolean} whether we should emit parseable output
|
||||
*
|
||||
* @example
|
||||
* if (robot.isEnabled(process.env)) {
|
||||
* console.log('We should emit parseable output');
|
||||
* }
|
||||
*/
|
||||
exports.isEnabled = (environment) => {
|
||||
const value = _.get(environment, [ 'ETCHER_CLI_ROBOT' ], false)
|
||||
return Boolean(value === 'false' ? false : value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Build a machine-parseable message
|
||||
* @function
|
||||
* @private
|
||||
*
|
||||
* @param {String} title - message title
|
||||
* @param {Object} [data] - message data
|
||||
* @returns {String} parseable message
|
||||
*
|
||||
* @example
|
||||
* const message = robot.buildMessage('progress', {
|
||||
* percentage: 50
|
||||
* });
|
||||
*
|
||||
* console.log(message);
|
||||
* > '{"command":"progress","data":{"percentage":50}}'
|
||||
*/
|
||||
exports.buildMessage = (title, data = {}) => {
|
||||
if (!_.isPlainObject(data)) {
|
||||
throw errors.createError({
|
||||
title: `Invalid data: ${data}`
|
||||
})
|
||||
}
|
||||
|
||||
return JSON.stringify({
|
||||
command: title,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Check whether a string is a robot message
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @description
|
||||
* Note that this function doesn't check if the robot message
|
||||
* is valid, but just that it is a robot message that we should
|
||||
* attempt to parse.
|
||||
*
|
||||
* @param {String} string - string
|
||||
* @returns {Boolean} whether the string is a robot message
|
||||
*
|
||||
* @example
|
||||
* if (robot.isMessage(robot.buildMessage('foo', {
|
||||
* message: 'bar'
|
||||
* }))) {
|
||||
* console.log('This is a robot message');
|
||||
* }
|
||||
*/
|
||||
exports.isMessage = (string) => {
|
||||
try {
|
||||
return _.isPlainObject(JSON.parse(string))
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Parse a machine-parseable message
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {String} string - message string
|
||||
* @returns {Object} parsed message
|
||||
*
|
||||
* @example
|
||||
* const result = robot.parseMessage('{"command":"progress","data":{"foo":50}}');
|
||||
* console.log(result);
|
||||
* > {
|
||||
* > command: 'progress',
|
||||
* > data: {
|
||||
* > foo: 50
|
||||
* > }
|
||||
* > }
|
||||
*/
|
||||
exports.parseMessage = (string) => {
|
||||
let output = null
|
||||
|
||||
try {
|
||||
output = JSON.parse(string)
|
||||
} catch (error) {
|
||||
throw errors.createError({
|
||||
title: 'Invalid message',
|
||||
description: `${string}, ${error.message}`
|
||||
})
|
||||
}
|
||||
|
||||
if (!output.command || !output.data) {
|
||||
throw errors.createError({
|
||||
title: 'Invalid message',
|
||||
description: `No command or data: ${string}`
|
||||
})
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Build a machine-parseable error message
|
||||
* @function
|
||||
* @private
|
||||
*
|
||||
* @param {Error} error - error
|
||||
* @returns {String} parseable error message
|
||||
*
|
||||
* @example
|
||||
* const error = new Error('foo');
|
||||
* const errorMessage = robot.buildErrorMessage(error);
|
||||
*
|
||||
* console.log(error.command);
|
||||
* > 'error'
|
||||
*
|
||||
* console.log(error.data.message);
|
||||
* > 'foo'
|
||||
*/
|
||||
exports.buildErrorMessage = (error) => {
|
||||
return exports.buildMessage(exports.COMMAND.ERROR, errors.toJSON(error))
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Recompose an error message
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {String} message - error message
|
||||
* @returns {Error} error object
|
||||
*
|
||||
* @example
|
||||
* const message = robot.buildErrorMessage(new Error('foo'));
|
||||
* const error = robot.recomposeErrorMessage(robot.parseMessage(message));
|
||||
*
|
||||
* error instanceof Error;
|
||||
* > true
|
||||
*
|
||||
* console.log(error.message);
|
||||
* > 'foo'
|
||||
*/
|
||||
exports.recomposeErrorMessage = (message) => {
|
||||
return errors.fromJSON(message.data)
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get message command
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {Object} message - message
|
||||
* @returns {String} command
|
||||
*
|
||||
* @example
|
||||
* const command = robot.getCommand({
|
||||
* command: 'foo',
|
||||
* data: {}
|
||||
* });
|
||||
*
|
||||
* console.log(command);
|
||||
* > 'foo'
|
||||
*/
|
||||
exports.getCommand = (message) => {
|
||||
return _.get(message, [ 'command' ])
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get message data
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {Object} message - message
|
||||
* @returns {Object} data
|
||||
*
|
||||
* @example
|
||||
* const data = robot.getData({
|
||||
* command: 'foo',
|
||||
* data: {
|
||||
* foo: 1
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* console.log(data);
|
||||
* > { foo: 1 }
|
||||
*/
|
||||
exports.getData = (message) => {
|
||||
return _.get(message, [ 'data' ], {})
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Print an error in a machine-friendly way
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {Error} error - error
|
||||
*
|
||||
* @example
|
||||
* robot.printError(new Error('This is an error'));
|
||||
*/
|
||||
exports.printError = (error) => {
|
||||
console.error(exports.buildErrorMessage(error))
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Print a message in a machine-friendly way
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {String} message - message
|
||||
* @param {Object} [data] - data
|
||||
*
|
||||
* @example
|
||||
* robot.printMessage('progress', { percentage: 50 });
|
||||
*/
|
||||
exports.printMessage = (message, data) => {
|
||||
console.log(exports.buildMessage(message, data))
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Log a message to the host's console
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {Object} [data] - data
|
||||
*
|
||||
* @example
|
||||
* robot.log({ example: 'data' });
|
||||
*/
|
||||
exports.log = (data) => {
|
||||
exports.printMessage(exports.COMMAND.LOG, data)
|
||||
}
|
@ -1,348 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 resin.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const m = require('mochainon')
|
||||
const _ = require('lodash')
|
||||
const robot = require('../../lib/shared/robot')
|
||||
|
||||
describe('Shared: Robot', function () {
|
||||
describe('.COMMAND', function () {
|
||||
it('should be a plain object', function () {
|
||||
m.chai.expect(_.isPlainObject(robot.COMMAND)).to.be.true
|
||||
})
|
||||
|
||||
it('should only contain string values', function () {
|
||||
m.chai.expect(_.every(_.values(robot.COMMAND), _.isString)).to.be.true
|
||||
})
|
||||
|
||||
it('should contain only unique values', function () {
|
||||
const numberOfKeys = _.size(_.keys(robot.COMMAND))
|
||||
m.chai.expect(_.size(_.uniq(_.values(robot.COMMAND)))).to.equal(numberOfKeys)
|
||||
})
|
||||
})
|
||||
|
||||
describe('.isEnabled()', function () {
|
||||
it('should return false if ETCHER_CLI_ROBOT is not set', function () {
|
||||
m.chai.expect(robot.isEnabled({})).to.be.false
|
||||
})
|
||||
|
||||
it('should return true if ETCHER_CLI_ROBOT=1', function () {
|
||||
m.chai.expect(robot.isEnabled({
|
||||
ETCHER_CLI_ROBOT: 1
|
||||
})).to.be.true
|
||||
})
|
||||
|
||||
it('should return false if ETCHER_CLI_ROBOT=0', function () {
|
||||
m.chai.expect(robot.isEnabled({
|
||||
ETCHER_CLI_ROBOT: 0
|
||||
})).to.be.false
|
||||
})
|
||||
|
||||
it('should return true if ETCHER_CLI_ROBOT="true"', function () {
|
||||
m.chai.expect(robot.isEnabled({
|
||||
ETCHER_CLI_ROBOT: 'true'
|
||||
})).to.be.true
|
||||
})
|
||||
|
||||
it('should return false if ETCHER_CLI_ROBOT="false"', function () {
|
||||
m.chai.expect(robot.isEnabled({
|
||||
ETCHER_CLI_ROBOT: 'false'
|
||||
})).to.be.false
|
||||
})
|
||||
|
||||
it('should return true if ETCHER_CLI_ROBOT=true', function () {
|
||||
m.chai.expect(robot.isEnabled({
|
||||
ETCHER_CLI_ROBOT: true
|
||||
})).to.be.true
|
||||
})
|
||||
|
||||
it('should return false if ETCHER_CLI_ROBOT=false', function () {
|
||||
m.chai.expect(robot.isEnabled({
|
||||
ETCHER_CLI_ROBOT: false
|
||||
})).to.be.false
|
||||
})
|
||||
})
|
||||
|
||||
describe('.buildMessage()', function () {
|
||||
it('should build a message without data', function () {
|
||||
const message = robot.buildMessage('hello')
|
||||
const result = '{"command":"hello","data":{}}'
|
||||
m.chai.expect(message).to.equal(result)
|
||||
})
|
||||
|
||||
it('should build a message with data', function () {
|
||||
const message = robot.buildMessage('hello', {
|
||||
foo: 1,
|
||||
bar: 2
|
||||
})
|
||||
const result = '{"command":"hello","data":{"foo":1,"bar":2}}'
|
||||
m.chai.expect(message).to.equal(result)
|
||||
})
|
||||
|
||||
it('should throw if data is defined but it not an object', function () {
|
||||
m.chai.expect(() => {
|
||||
robot.buildMessage('hello', 'world')
|
||||
}).to.throw('Invalid data: world')
|
||||
})
|
||||
})
|
||||
|
||||
describe('.isMessage()', function () {
|
||||
it('should return true if message is an empty object', function () {
|
||||
m.chai.expect(robot.isMessage('{}')).to.be.true
|
||||
})
|
||||
|
||||
it('should return true if message is an object', function () {
|
||||
m.chai.expect(robot.isMessage('{"command":"foo"}')).to.be.true
|
||||
})
|
||||
|
||||
it('should return false if message is an invalid object', function () {
|
||||
m.chai.expect(robot.isMessage('{"command":\\foo"}')).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is an unquoted string', function () {
|
||||
m.chai.expect(robot.isMessage('foo')).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is an quoted string', function () {
|
||||
m.chai.expect(robot.isMessage('"foo"')).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is an empty string', function () {
|
||||
m.chai.expect(robot.isMessage('')).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is undefined', function () {
|
||||
m.chai.expect(robot.isMessage(undefined)).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is null', function () {
|
||||
m.chai.expect(robot.isMessage(null)).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is a positive integer string', function () {
|
||||
m.chai.expect(robot.isMessage('5')).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is a negative integer string', function () {
|
||||
m.chai.expect(robot.isMessage('-3')).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is a zero string', function () {
|
||||
m.chai.expect(robot.isMessage('0')).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is a positive float string', function () {
|
||||
m.chai.expect(robot.isMessage('5.3')).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is a negative float string', function () {
|
||||
m.chai.expect(robot.isMessage('-2.1')).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is a positive integer', function () {
|
||||
m.chai.expect(robot.isMessage(5)).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is a negative integer', function () {
|
||||
m.chai.expect(robot.isMessage(-3)).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is zero', function () {
|
||||
m.chai.expect(robot.isMessage(0)).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is a positive float', function () {
|
||||
m.chai.expect(robot.isMessage(5.3)).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is a negative float', function () {
|
||||
m.chai.expect(robot.isMessage(-2.1)).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is an array', function () {
|
||||
m.chai.expect(robot.isMessage([ 'foo' ])).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if message is an array string', function () {
|
||||
m.chai.expect(robot.isMessage('["foo"]')).to.be.false
|
||||
})
|
||||
|
||||
it('should return true for a message built with .buildMessage()', function () {
|
||||
m.chai.expect(robot.isMessage(robot.buildMessage('foo', {
|
||||
message: 'bar'
|
||||
}))).to.be.true
|
||||
})
|
||||
|
||||
it('should return true for a message built with .buildErrorMessage()', function () {
|
||||
const error = new Error('foo')
|
||||
m.chai.expect(robot.isMessage(robot.buildErrorMessage(error))).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
describe('.buildErrorMessage()', function () {
|
||||
it('should build a message from a simple error', function () {
|
||||
const error = new Error('foo')
|
||||
const message = robot.buildErrorMessage(error)
|
||||
|
||||
m.chai.expect(JSON.parse(message)).to.deep.equal({
|
||||
command: robot.COMMAND.ERROR,
|
||||
data: {
|
||||
message: 'foo',
|
||||
stack: error.stack
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should save the error description', function () {
|
||||
const error = new Error('foo')
|
||||
error.description = 'error description'
|
||||
const message = robot.buildErrorMessage(error)
|
||||
|
||||
m.chai.expect(JSON.parse(message)).to.deep.equal({
|
||||
command: robot.COMMAND.ERROR,
|
||||
data: {
|
||||
message: 'foo',
|
||||
description: 'error description',
|
||||
stack: error.stack
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should save the error code', function () {
|
||||
const error = new Error('foo')
|
||||
error.code = 'MYERROR'
|
||||
const message = robot.buildErrorMessage(error)
|
||||
|
||||
m.chai.expect(JSON.parse(message)).to.deep.equal({
|
||||
command: robot.COMMAND.ERROR,
|
||||
data: {
|
||||
message: 'foo',
|
||||
code: 'MYERROR',
|
||||
stack: error.stack
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle a string error', function () {
|
||||
const message = JSON.parse(robot.buildErrorMessage('foo'))
|
||||
m.chai.expect(message.data.message).to.equal('foo')
|
||||
m.chai.expect(message.data.stack).to.be.a.string
|
||||
m.chai.expect(_.isEmpty(message.data.stack)).to.be.false
|
||||
})
|
||||
})
|
||||
|
||||
describe('.parseMessage()', function () {
|
||||
it('should parse a valid message', function () {
|
||||
const message = robot.buildMessage('foo', {
|
||||
bar: 1
|
||||
})
|
||||
|
||||
m.chai.expect(robot.parseMessage(message)).to.deep.equal({
|
||||
command: 'foo',
|
||||
data: {
|
||||
bar: 1
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should parse a valid without data', function () {
|
||||
const message = robot.buildMessage('foo')
|
||||
m.chai.expect(robot.parseMessage(message)).to.deep.equal({
|
||||
command: 'foo',
|
||||
data: {}
|
||||
})
|
||||
})
|
||||
|
||||
it('should throw if input is not valid JSON', function () {
|
||||
m.chai.expect(() => {
|
||||
robot.parseMessage('Hello world\nFoo Bar')
|
||||
}).to.throw('Invalid message')
|
||||
})
|
||||
|
||||
it('should throw if input has no command', function () {
|
||||
m.chai.expect(() => {
|
||||
robot.parseMessage('{"data":{"foo":"bar"}}')
|
||||
}).to.throw('Invalid message')
|
||||
})
|
||||
|
||||
it('should throw if input has no data', function () {
|
||||
m.chai.expect(() => {
|
||||
robot.parseMessage('{"command":"foo"}')
|
||||
}).to.throw('Invalid message')
|
||||
})
|
||||
})
|
||||
|
||||
describe('.getCommand()', function () {
|
||||
it('should get the command of a message', function () {
|
||||
const message = robot.parseMessage(robot.buildMessage('hello', {
|
||||
foo: 1,
|
||||
bar: 2
|
||||
}))
|
||||
|
||||
m.chai.expect(robot.getCommand(message)).to.equal('hello')
|
||||
})
|
||||
})
|
||||
|
||||
describe('.getData()', function () {
|
||||
it('should get the data of a message', function () {
|
||||
const message = robot.parseMessage(robot.buildMessage('hello', {
|
||||
foo: 1,
|
||||
bar: 2
|
||||
}))
|
||||
|
||||
m.chai.expect(robot.getData(message)).to.deep.equal({
|
||||
foo: 1,
|
||||
bar: 2
|
||||
})
|
||||
})
|
||||
|
||||
it('should return an empty object if the message has no data', function () {
|
||||
m.chai.expect(robot.getData({
|
||||
command: 'foo'
|
||||
})).to.deep.equal({})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.recomposeErrorMessage()', function () {
|
||||
it('should return an instance of Error', function () {
|
||||
const error = new Error('Foo bar')
|
||||
const message = robot.parseMessage(robot.buildErrorMessage(error))
|
||||
m.chai.expect(robot.recomposeErrorMessage(message)).to.be.an.instanceof(Error)
|
||||
})
|
||||
|
||||
it('should be able to recompose an error object', function () {
|
||||
const error = new Error('Foo bar')
|
||||
const message = robot.parseMessage(robot.buildErrorMessage(error))
|
||||
m.chai.expect(robot.recomposeErrorMessage(message)).to.deep.equal(error)
|
||||
})
|
||||
|
||||
it('should be able to recompose an error object with a code', function () {
|
||||
const error = new Error('Foo bar')
|
||||
error.code = 'FOO'
|
||||
const message = robot.parseMessage(robot.buildErrorMessage(error))
|
||||
m.chai.expect(robot.recomposeErrorMessage(message)).to.deep.equal(error)
|
||||
})
|
||||
|
||||
it('should be able to recompose an error object with a description', function () {
|
||||
const error = new Error('Foo bar')
|
||||
error.description = 'My description'
|
||||
const message = robot.parseMessage(robot.buildErrorMessage(error))
|
||||
m.chai.expect(robot.recomposeErrorMessage(message)).to.deep.equal(error)
|
||||
})
|
||||
})
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user