mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-23 03:06:38 +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]
|
- [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
|
Summary
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ const propTypes = require('prop-types')
|
|||||||
const { react2angular } = require('react2angular')
|
const { react2angular } = require('react2angular')
|
||||||
const analytics = require('../modules/analytics')
|
const analytics = require('../modules/analytics')
|
||||||
const packageJSON = require('../../../../package.json')
|
const packageJSON = require('../../../../package.json')
|
||||||
const robot = require('../../../shared/robot')
|
|
||||||
|
|
||||||
const MODULE_NAME = 'Etcher.Components.SafeWebview'
|
const MODULE_NAME = 'Etcher.Components.SafeWebview'
|
||||||
const angularSafeWebview = angular.module(MODULE_NAME, [])
|
const angularSafeWebview = angular.module(MODULE_NAME, [])
|
||||||
@ -221,27 +220,24 @@ class SafeWebview extends react.PureComponent {
|
|||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* // In the webview
|
* // In the webview
|
||||||
* console.log(JSON.stringify({
|
* console.log('Good night!')
|
||||||
* command: 'error',
|
|
||||||
* data: 'Good night!'
|
|
||||||
* }));
|
|
||||||
*
|
|
||||||
* console.log(JSON.stringify({
|
|
||||||
* command: 'log',
|
|
||||||
* data: 'Hello, Mars!'
|
|
||||||
* }));
|
|
||||||
*/
|
*/
|
||||||
static consoleMessage (event) {
|
static consoleMessage (event) {
|
||||||
if (!robot.isMessage(event.message)) {
|
if (_.isNil(event.message)) {
|
||||||
return
|
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) {
|
if (message.command === 'error') {
|
||||||
analytics.logEvent(robot.getData(message))
|
analytics.logException(message.data)
|
||||||
} else if (robot.getCommand(message) === robot.COMMAND.ERROR) {
|
} else {
|
||||||
analytics.logException(robot.getData(message))
|
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