/* * 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) }) }) })