code review feedback
This commit is contained in:
Родитель
168b4ac087
Коммит
cc6462125b
Двоичные данные
common/core/devdoc/message_requirements.docm
Двоичные данные
common/core/devdoc/message_requirements.docm
Двоичный файл не отображается.
|
@ -0,0 +1,55 @@
|
|||
# azure-iot-common.Message Requirements
|
||||
========================================
|
||||
|
||||
## Overview
|
||||
azure-iot-common.Message provides a container for message content and properties travelling to and from an IoT Hub.
|
||||
|
||||
## Example usage
|
||||
```
|
||||
'use strict';
|
||||
var Message = require('azure-iot-common').Message;
|
||||
|
||||
var message = new Message('data to send');
|
||||
message.properties.add('myProperty', 'my property value');
|
||||
console.log('message content: ' + message.getData());
|
||||
```
|
||||
|
||||
## Public Interface
|
||||
|
||||
| **Name** | **Type** | **Description** |
|
||||
|-----------------------|-------------------------|-----------------|
|
||||
| message.properties | Object<string, string> | A container of custom message properties |
|
||||
| message.messageId | String | Used to correlate two-way communication. Format: A case-sensitive string ( up to 128 char long) of ASCII 7-bit alphanumeric chars + {'-', ':',’.', '+', '%', '_', '#', '*', '?', '!', '(', ')', ',', '=', '@', ';', '$', '''}. |
|
||||
| message.to | | Destination of the message |
|
||||
| message.expiryTime | Date | Expiry time in UTC. Interpreted by hub on C2D messages. Ignored in other cases |
|
||||
| message.lockToken | String | Used by receiver to Abandon, Reject or Complete the message |
|
||||
| message.correlationId | String | Used in message responses and feedback |
|
||||
| message.userId | String | Used to specify the entity creating the message |
|
||||
|
||||
|
||||
### Message(data)
|
||||
Construct a Message with the given data
|
||||
**SRS_NODE_IOTHUB_MESSAGE_07_004: [**The `Message` constructor shall accept a variable message that will be transmitted.**]**
|
||||
|
||||
### getData()
|
||||
Return the data passed to the constructor
|
||||
|
||||
**SRS_NODE_IOTHUB_MESSAGE_07_003: [**The `getData` function shall return a representation of the body of the message as the type that was presented during construction.**]**
|
||||
|
||||
### getBytes()
|
||||
Return the data passed to the constructor as a Buffer of bytes
|
||||
|
||||
**SRS_NODE_IOTHUB_MESSAGE_07_001: [**If the data message that is store is of type `Buffer` then the data object will get returned unaltered.**]**
|
||||
|
||||
**SRS_NODE_IOTHUB_MESSAGE_07_002: [**If the data message is of any other type then the data will be converted to a `Buffer` object and returned.**]**
|
||||
|
||||
## static isBufferConvertible(obj: any): boolean;
|
||||
Returns true if the given object is of type `BufferConvertible`
|
||||
|
||||
**SRS_NODE_IOTHUB_MESSAGE_18_001: [** `isBufferConvertible` shall return `true` if `obj` is a `Buffer`, a `string`, an `Array`, or an `ArrayBuffer`. **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_MESSAGE_18_002: [** `isBufferConvertible` shall return `false` if `obj` is any other type. **]**
|
||||
|
||||
## properties
|
||||
A collection of Message properties.
|
||||
|
|
@ -107,6 +107,28 @@ export class Message {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the given object is of type {@link Message.BufferConvertible}. Objects of type {@link Message.BufferConvertible} can be passed into the {@link Message} constructor.
|
||||
*
|
||||
* @param obj object instance to check
|
||||
*
|
||||
* @returns True if the object is of type {@link Message.BufferConvertible}
|
||||
*/
|
||||
static isBufferConvertible(obj: any): boolean {
|
||||
/*Codes_SRS_NODE_IOTHUB_MESSAGE_18_001: [`isBufferConvertible` shall return `true` if `obj` is a `Buffer`, a `string`, an `Array`, or an `ArrayBuffer`.]*/
|
||||
if (Buffer.isBuffer(obj)) {
|
||||
return true;
|
||||
} else if (typeof obj === 'string') {
|
||||
return true;
|
||||
} else if (obj instanceof Array) {
|
||||
return true;
|
||||
} else if (obj instanceof ArrayBuffer) {
|
||||
return true;
|
||||
} else {
|
||||
/*Codes_SRS_NODE_IOTHUB_MESSAGE_18_002: [`isBufferConvertible` shall return `false` if `obj` is any other type.]*/
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Message {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
var assert = require('chai').assert;
|
||||
var Message = require('../lib/message.js').Message;
|
||||
|
||||
var stringTestMsg = "message";
|
||||
var stringTestMsg = 'message';
|
||||
|
||||
describe('message', function () {
|
||||
|
||||
|
@ -41,4 +41,33 @@ describe('message', function () {
|
|||
|
||||
});
|
||||
|
||||
describe('#isBufferConvertible', function() {
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_MESSAGE_18_001: [`isBufferConvertible` shall return `true` if `obj` is a `Buffer`, a `string`, an `Array`, or an `ArrayBuffer`.]*/
|
||||
[
|
||||
{ obj: new Buffer('foo'), name: 'Buffer' },
|
||||
{ obj: 'foo', name: 'string' },
|
||||
{ obj: [], name: 'Array' },
|
||||
{ obj: new ArrayBuffer(), name: 'ArrayBuffer' }
|
||||
].forEach(function(testConfig) {
|
||||
it('returns true if object is of type ' + testConfig.name, function() {
|
||||
assert.isTrue(Message.isBufferConvertible(testConfig.obj));
|
||||
});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_MESSAGE_18_002: [`isBufferConvertible` shall return `false` if `obj` is any other type.]*/
|
||||
[
|
||||
{ obj: 1, name: 'number' },
|
||||
{ obj: true, name: 'boolean' },
|
||||
{ obj: {}, name: 'object' },
|
||||
{ obj: new Message(), name: 'Message' }
|
||||
].forEach(function(testConfig) {
|
||||
it('returns false if object is of type ' + testConfig.name, function() {
|
||||
assert.isFalse(Message.isBufferConvertible(testConfig.obj));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -15,7 +15,7 @@ There are two kinds of incoming messages that can be received by an IoT device:
|
|||
|
||||
The following table shows which clients support which events & messages:
|
||||
|
||||
| client type | `sendEvent` | `sendOutputEvent` | Messages | InputMessages |
|
||||
| client type | `sendEvent` | `sendOutputEvent` | `on('messages')` | `on('inputMessages')` |
|
||||
|------------ | --------- | --------------- | -------- | ------------- |
|
||||
| device | yes | no | yes | no |
|
||||
| module | no | yes | no | yes|
|
||||
|
|
|
@ -286,5 +286,5 @@ This method is deprecated. The `AmqpReceiver` object and pattern is going away a
|
|||
|
||||
### inputMessage events
|
||||
|
||||
**SRS_NODE_DEVICE_AMQP_18_014: [** If `amqp` receives a message on the C2D link with an annotation named "x-opt-input-name", it shall emit a "message" event with the "x-opt-input-name" annotation as the first parameter and the message as the second parameter. **]**
|
||||
**SRS_NODE_DEVICE_AMQP_18_014: [** If `amqp` receives a message on the C2D link with an annotation named "x-opt-input-name", it shall emit an "inputMessage" event with the "x-opt-input-name" annotation as the first parameter and the message as the second parameter. **]**
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ export class Amqp extends EventEmitter implements Client.Transport {
|
|||
inputName = msg.messageAnnotations['x-opt-input-name'];
|
||||
}
|
||||
if (inputName) {
|
||||
/*Codes_SRS_NODE_DEVICE_AMQP_18_014: [If `amqp` receives a message on the C2D link with an annotation named "x-opt-input-name", it shall emit a "message" event with the "x-opt-input-name" annotation as the first parameter and the message as the second parameter.]*/
|
||||
/*Codes_SRS_NODE_DEVICE_AMQP_18_014: [If `amqp` receives a message on the C2D link with an annotation named "x-opt-input-name", it shall emit an "inputMessage" event with the "x-opt-input-name" annotation as the first parameter and the message as the second parameter.]*/
|
||||
this.emit('inputMessage', inputName, AmqpMessage.toMessage(msg));
|
||||
} else {
|
||||
/*Codes_SRS_NODE_DEVICE_AMQP_18_013: [If `amqp` receives a message on the C2D link without an annotation named "x-opt-input-name", it shall emit a "message" event with the message as the event parameter.]*/
|
||||
|
|
|
@ -1479,7 +1479,7 @@ describe('Amqp', function () {
|
|||
});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_DEVICE_AMQP_18_014: [If `amqp` receives a message on the C2D link with an annotation named "x-opt-input-name", it shall emit a "message" event with the "x-opt-input-name" annotation as the first parameter and the message as the second parameter.]*/
|
||||
/*Tests_SRS_NODE_DEVICE_AMQP_18_014: [If `amqp` receives a message on the C2D link with an annotation named "x-opt-input-name", it shall emit an "inputMessage" event with the "x-opt-input-name" annotation as the first parameter and the message as the second parameter.]*/
|
||||
describe('on(\'inputMessage\')', function () {
|
||||
it('calls the message handler when message received', function (testCallback) {
|
||||
var testText = '__TEST_TEXT__';
|
||||
|
|
|
@ -54,9 +54,10 @@
|
|||
"module_messaging": "mocha --reporter spec test/module_messaging.js",
|
||||
"module_twin": "mocha --reporter spec test/module_twin.js",
|
||||
"module_methods": "mocha --reporter spec test/module_methods.js",
|
||||
"phase1_fast": "npm-run-all -p -l twin_disconnect service registry device_acknowledge_tests upload_disconnect authentication job_client module_crud module_messaging module_twin module_methods",
|
||||
"phase0_modules": "npm-run-all -p -l module_crud module_messaging module_twin module_methods",
|
||||
"phase1_fast": "npm-run-all -p -l twin_disconnect service registry device_acknowledge_tests upload_disconnect authentication job_client",
|
||||
"phase2_slow": "npm-run-all -p -l device_method twin_e2e_tests sas_token_tests device_service provisioning",
|
||||
"alltest": "npm run phase1_fast && npm run phase2_slow",
|
||||
"alltest": "npm run phase0_modules && npm run phase1_fast && npm run phase2_slow",
|
||||
"ci": "npm -s run lint && npm -s run alltest",
|
||||
"test": "npm -s run lint && npm -s run alltest"
|
||||
},
|
||||
|
|
|
@ -11,6 +11,7 @@ var closeDeviceEventHubClients = require('./testUtils.js').closeDeviceEventHubCl
|
|||
var hubConnectionString = process.env.IOTHUB_CONNECTION_STRING;
|
||||
|
||||
var EventHubReceiverHelper = function() {
|
||||
EventEmitter.call(this);
|
||||
};
|
||||
util.inherits(EventHubReceiverHelper, EventEmitter);
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var ModuleIdentityHelper = require('./module_identity_helper.js');
|
||||
var ModuleTestHelper = require('./module_test_helper.js');
|
||||
var EventHubReceiverHelper = require('./eventhub_receiver_helper');
|
||||
var Message = require('azure-iot-common').Message;
|
||||
var assert = require('chai').assert;
|
||||
|
@ -23,11 +23,11 @@ describe('module messaging', function() {
|
|||
var testModule = {};
|
||||
|
||||
before(function(done) {
|
||||
ModuleIdentityHelper.createModule(testModule, Transport, done);
|
||||
ModuleTestHelper.createModule(testModule, Transport, done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
ModuleIdentityHelper.cleanUpAfterTest(testModule, done);
|
||||
ModuleTestHelper.cleanUpAfterTest(testModule, done);
|
||||
});
|
||||
|
||||
it ('Can send from service to module input', function(done) {
|
||||
|
@ -42,8 +42,8 @@ describe('module messaging', function() {
|
|||
|
||||
var messageToSend = new Message(testMessageText);
|
||||
debug('sending message to input named ' + testInputName);
|
||||
testModule.serviceClient.sendToModule(testModule.deviceId, testModule.moduleId, testInputName, messageToSend, function(err) {
|
||||
debug('sendToModule returned ' + (err ? err : 'success'));
|
||||
testModule.serviceClient.sendToModuleInput(testModule.deviceId, testModule.moduleId, testInputName, messageToSend, function(err) {
|
||||
debug('sendToModuleInput returned ' + (err ? err : 'success'));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var ModuleIdentityHelper = require('./module_identity_helper.js');
|
||||
var ModuleTestHelper = require('./module_test_helper.js');
|
||||
var assert = require('chai').assert;
|
||||
var debug = require('debug')('e2etests:module-methods');
|
||||
var Amqp = require('azure-iot-device-amqp').Amqp;
|
||||
|
@ -21,11 +21,11 @@ describe('module methods', function() {
|
|||
var testModule = {};
|
||||
|
||||
before(function(done) {
|
||||
ModuleIdentityHelper.createModule(testModule, Transport, done);
|
||||
ModuleTestHelper.createModule(testModule, Transport, done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
ModuleIdentityHelper.cleanUpAfterTest(testModule, done);
|
||||
ModuleTestHelper.cleanUpAfterTest(testModule, done);
|
||||
});
|
||||
|
||||
it ('can receive a method call', function(done) {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
'use strict';
|
||||
|
||||
var uuid = require('uuid');
|
||||
var debug = require('debug')('e2etests:module-identity-helper');
|
||||
var debug = require('debug')('e2etests:module-test-helper');
|
||||
var async = require('async');
|
||||
|
||||
var Registry = require('azure-iothub').Registry;
|
||||
|
@ -72,7 +72,7 @@ module.exports.createModule = function(testModule, Transport, done) {
|
|||
], done);
|
||||
};
|
||||
|
||||
module.exports.createModuleTwinObjects = function(testModule, done) {
|
||||
module.exports.getTwinObjects = function(testModule, done) {
|
||||
async.series([
|
||||
function createServiceTwin(done) {
|
||||
debug('creating service twin');
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var ModuleIdentityHelper = require('./module_identity_helper.js');
|
||||
var ModuleTestHelper = require('./module_test_helper.js');
|
||||
var assert = require('chai').assert;
|
||||
var debug = require('debug')('e2etests:module-twin');
|
||||
var Amqp = require('azure-iot-device-amqp').Amqp;
|
||||
|
@ -21,17 +21,17 @@ describe('module twin', function() {
|
|||
var testModule = {};
|
||||
|
||||
before(function(done) {
|
||||
ModuleIdentityHelper.createModule(testModule, Transport, function(err) {
|
||||
ModuleTestHelper.createModule(testModule, Transport, function(err) {
|
||||
if (err) {
|
||||
done(err);
|
||||
} else {
|
||||
ModuleIdentityHelper.createModuleTwinObjects(testModule, done);
|
||||
ModuleTestHelper.getTwinObjects(testModule, done);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
ModuleIdentityHelper.cleanUpAfterTest(testModule, done);
|
||||
ModuleTestHelper.cleanUpAfterTest(testModule, done);
|
||||
});
|
||||
|
||||
it ('can receive desired property changes', function(done) {
|
||||
|
|
|
@ -89,7 +89,9 @@ The `send` method sends a cloud-to-device message to the service, intended for d
|
|||
|
||||
**SRS_NODE_IOTHUB_CLIENT_05_013: [**The `send` method shall throw `ReferenceError` if the deviceId or message arguments are falsy.**]**
|
||||
|
||||
**SRS_NODE_IOTHUB_CLIENT_05_014: [**The `send` method shall convert the message object to type azure-iot-common.Message if necessary.**]**
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_016: [** The `send` method shall throw an `ArgumentError` if the `message` argument is not of type `azure-iot-common.Message` or `azure-iot-common.Message.BufferConvertible`. **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_CLIENT_05_014: [**The `send` method shall convert the `message` object to type `azure-iot-common.Message` if it is not already of type `azure-iot-common.Message`. **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_CLIENT_05_016: [**When the `send` method completes, the callback function (indicated by the done - argument) shall be invoked with the following arguments:
|
||||
- `err` - standard JavaScript Error object (or subclass)
|
||||
|
@ -107,14 +109,16 @@ The `send` method sends a cloud-to-device message to the service, intended for d
|
|||
|
||||
**SRS_NODE_IOTHUB_CLIENT_16_030: [** The `send` method shall not throw if the `done` callback is falsy. **]**
|
||||
|
||||
sendToModule(deviceId: string, moduleId: string, inputName: string, message: Message | Message.BufferConvertible, done?: Callback<results.MessageEnqueued>): void;
|
||||
The `sendToModule` method sends a message to the service, intended for delivery to the given input on the given module on the given device
|
||||
sendToModuleInput(deviceId: string, moduleId: string, inputName: string, message: Message | Message.BufferConvertible, done?: Callback<results.MessageEnqueued>): void;
|
||||
The `sendToModuleInput` method sends a message to the service, intended for delivery to the given input on the given module on the given device
|
||||
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_007: [** The `sendToModule` method shall throw `ReferenceError` if the deviceId, moduleId, inputName, or message arguments are falsy. **]**
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_007: [** The `sendToModuleInput` method shall throw `ReferenceError` if the deviceId, moduleId, inputName, or message arguments are falsy. **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_008: [** The `sendToModule` method shall convert the message object to type azure-iot-common.Message if necessary. **]**
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_017: [** The `sendToModuleInput` method shall throw an `ArgumentError` if the `message` argument is not of type `azure-iot-common.Message` or `azure-iot-common.Message.BufferConvertible`. **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_009: [** When the `sendToModule` method completes, the callback function (indicated by the done - argument) shall be invoked with the following arguments:
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_008: [** The `sendToModuleInput` method shall convert the message object to type `azure-iot-common.Message` if it is not already of type `azure-iot-common.Message`. **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_009: [** When the `sendToModuleInput` method completes, the callback function (indicated by the done - argument) shall be invoked with the following arguments:
|
||||
- `err` - standard JavaScript Error object (or subclass)
|
||||
- `result` - an implementation-specific response object returned by the underlying protocol, useful for logging and troubleshooting **]**
|
||||
|
||||
|
@ -122,13 +126,15 @@ The `sendToModule` method sends a message to the service, intended for delivery
|
|||
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_011: [** Otherwise the argument `err` shall have an `amqpError` property containing implementation-specific response information for use in logging and troubleshooting. **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_012: [** If the `deviceId` has not been registered with the IoT Hub, `sendToModule` shall call the `done` callback with a `DeviceNotFoundError`. **]**
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_012: [** If the `deviceId` has not been registered with the IoT Hub, `sendToModuleInput` shall call the `done` callback with a `DeviceNotFoundError`. **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_013: [** If the queue which receives messages on behalf of the device is full, `sendToModule` shall call the `done` callback with a `DeviceMaximumQueueDepthExceededError`. **]**
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_018: [** If the `moduleId` has not been added to the device named `deviceId` on the IoT Hub, `sendToModuleInput` shall call the `done` callback with a `DeviceNotFoundError`. **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_014: [** The `sendToModule` method shall use the retry policy defined either by default or by a call to `setRetryPolicy` if necessary to send the message. **]**
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_013: [** If the queue which receives messages on behalf of the device is full, `sendToModuleInput` shall call the `done` callback with a `DeviceMaximumQueueDepthExceededError`. **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_015: [** The `sendToModule` method shall not throw if the `done` callback is falsy. . **]**
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_014: [** The `sendToModuleInput` method shall use the retry policy defined either by default or by a call to `setRetryPolicy` if necessary to send the message. **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_CLIENT_18_015: [** The `sendToModuleInput` method shall not throw if the `done` callback is falsy. **]**
|
||||
|
||||
|
||||
###getFeedbackReceiver(done)
|
||||
|
|
|
@ -74,7 +74,7 @@ The `send` method sends an event to the device passed as argument, using the IoT
|
|||
|
||||
**SRS_NODE_IOTHUB_SERVICE_AMQP_16_002: [** The `send` method shall construct an AMQP request using the message passed in argument as the body of the message.**]**
|
||||
|
||||
**SRS_NODE_IOTHUB_SERVICE_AMQP_16_003: [** The message generated by the `send` method should have its “to” field set to the device ID passed as an argument.**]**
|
||||
**SRS_NODE_IOTHUB_SERVICE_AMQP_16_003: [** The message generated by the `send` method should have its “to” field set to "/devices/(uriEncode<deviceId>)/messages/devicebound".**]**
|
||||
|
||||
**SRS_NODE_IOTHUB_SERVICE_AMQP_16_025: [** The `send` method shall connect and authenticate the transport if it is disconnected. **]**
|
||||
|
||||
|
@ -92,7 +92,7 @@ The `send` method sends an event to the device passed as argument, using the IoT
|
|||
|
||||
**SRS_NODE_IOTHUB_SERVICE_AMQP_18_001: [** The `sendToModule` method shall construct an AMQP request using the message passed in argument as the body of the message. **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_SERVICE_AMQP_18_002: [** The message generated by the `sendToModule` method should have its “to” field set to the `deviceId` & `moduleId` passed as arguments. **]**
|
||||
**SRS_NODE_IOTHUB_SERVICE_AMQP_18_002: [** The message generated by the `sendToModule` method should have its “to” field set to "/devices/(uriEncode<deviceId>)/modules/(uriEncode<moduleId>)/messages/devicebound" **]**
|
||||
|
||||
**SRS_NODE_IOTHUB_SERVICE_AMQP_18_009: [** The message generated by the `sendToModule` method should have its “x-opt-input-name” annotation set to the `inputName` passed a an argument. **]**
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
"alltest-min": "istanbul cover --report none node_modules/mocha/bin/_mocha -- --reporter dot test/_*_test*.js",
|
||||
"unittest": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter spec test/_*_test.js",
|
||||
"alltest": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter spec test/_*_test*.js",
|
||||
"ci": "npm run typings && npm -s run lint && npm -s run build && npm -s run alltest-min && npm -s run check-cover",
|
||||
"ci": "npm -s run lint && npm -s run build && npm -s run alltest-min && npm -s run check-cover",
|
||||
"test": "npm -s run lint && npm -s run build && npm -s run unittest",
|
||||
"check-cover": "istanbul check-coverage --statements 97 --branches 92 --lines 97 --functions 93",
|
||||
"cover": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter spec test/_*_test*.js"
|
||||
|
|
|
@ -122,11 +122,11 @@ export class Amqp extends EventEmitter implements Client.Transport {
|
|||
disconnect: (callback) => callback(),
|
||||
send: (amqpMessage, deviceEndpoint, callback) => {
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_025: [The `send` method shall connect and authenticate the transport if it is disconnected.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_003: [The `sendToModule` method shall connect and authenticate the transport if it is disconnected.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_003: [The `sendToModuleInput` method shall connect and authenticate the transport if it is disconnected.]*/
|
||||
this._fsm.handle('connect', (err) => {
|
||||
if (err) {
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_026: [The `send` method shall call its callback with an error if connecting and/or authenticating the transport fails.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_004: [The `sendToModule` method shall call its callback with an error if connecting and/or authenticating the transport fails.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_004: [The `sendToModuleInput` method shall call its callback with an error if connecting and/or authenticating the transport fails.]*/
|
||||
callback(err);
|
||||
} else {
|
||||
this._fsm.handle('send', amqpMessage, deviceEndpoint, callback);
|
||||
|
@ -236,27 +236,27 @@ export class Amqp extends EventEmitter implements Client.Transport {
|
|||
disconnect: (callback) => this._fsm.transition('disconnecting', null, callback),
|
||||
send: (amqpMessage, deviceEndpoint, callback) => {
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_003: [The message generated by the `send` method should have its “to” field set to the device ID passed as an argument.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_002: [The message generated by the `sendToModule` method should have its “to” field set to the `deviceId` & `moduleId` passed as arguments.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_002: [The message generated by the `sendToModuleInput` method should have its “to” field set to the `deviceId` & `moduleId` passed as arguments.]*/
|
||||
amqpMessage.properties.to = deviceEndpoint;
|
||||
if (!this._c2dLink) {
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_027: [The `send` method shall attach the C2D link if necessary.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_005: [The `sendToModule` method shall attach the C2D link if necessary.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_005: [The `sendToModuleInput` method shall attach the C2D link if necessary.]*/
|
||||
this._amqp.attachSenderLink(this._c2dEndpoint, null, (err, link) => {
|
||||
if (err) {
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_029: [The `send` method shall call its callback with an error if it fails to attach the C2D link.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_007: [The `sendToModule` method shall call its callback with an error if it fails to attach the C2D link.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_007: [The `sendToModuleInput` method shall call its callback with an error if it fails to attach the C2D link.]*/
|
||||
callback(err);
|
||||
} else {
|
||||
this._c2dLink = link;
|
||||
this._c2dLink.on('error', this._c2dErrorListener);
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_030: [The `send` method shall call the `send` method of the C2D link and pass it the Amqp request that it created.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_008: [The `sendToModule` method shall call the `send` method of the C2D link and pass it the Amqp request that it created.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_008: [The `sendToModuleInput` method shall call the `send` method of the C2D link and pass it the Amqp request that it created.]*/
|
||||
this._c2dLink.send(amqpMessage, callback);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_028: [The `send` method shall reuse the C2D link if it is already attached.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_006: [The `sendToModule` method shall reuse the C2D link if it is already attached.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_006: [The `sendToModuleInput` method shall reuse the C2D link if it is already attached.]*/
|
||||
this._c2dLink.send(amqpMessage, callback);
|
||||
}
|
||||
},
|
||||
|
@ -457,7 +457,7 @@ export class Amqp extends EventEmitter implements Client.Transport {
|
|||
* completes execution.
|
||||
*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_002: [The send method shall construct an AMQP request using the message passed in argument as the body of the message.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_003: [The message generated by the send method should have its “to” field set to the device ID passed as an argument.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_003: [The message generated by the send method should have its “to” field set to "/devices/(uriEncode<deviceId>)/messages/devicebound".]*/
|
||||
send(deviceId: string, message: Message, done: Client.Callback<results.MessageEnqueued>): void {
|
||||
const deviceEndpoint = endpoint.messagePath(encodeURIComponent(deviceId));
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_002: [The `send` method shall construct an AMQP request using the message passed in argument as the body of the message.]*/
|
||||
|
@ -468,15 +468,15 @@ export class Amqp extends EventEmitter implements Client.Transport {
|
|||
/**
|
||||
* @private
|
||||
*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_001: [The `sendToModule` method shall construct an AMQP request using the message passed in argument as the body of the message.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_002: [The message generated by the `sendToModule` method should have its “to” field set to the `deviceId` & `moduleId` passed as arguments.]*/
|
||||
sendToModule(deviceId: string, moduleId: string, inputName: string, message: Message, done?: Callback<results.MessageEnqueued>): void {
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_001: [The `sendToModuleInput` method shall construct an AMQP request using the message passed in argument as the body of the message.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_002: [The message generated by the `sendToModuleInput` method should have its “to” field set to "/devices/(uriEncode<deviceId>)/modules/(uriEncode<moduleId>)/messages/devicebound".]*/
|
||||
sendToModuleInput(deviceId: string, moduleId: string, inputName: string, message: Message, done?: Callback<results.MessageEnqueued>): void {
|
||||
const deviceEndpoint = endpoint.messagePath(encodeURIComponent(deviceId), encodeURIComponent(moduleId));
|
||||
let amqpMessage = AmqpMessage.fromMessage(message);
|
||||
if (!amqpMessage.messageAnnotations) {
|
||||
amqpMessage.messageAnnotations = {};
|
||||
}
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_009: [The message generated by the `sendToModule` method should have its “x-opt-input-name” annotation set to the `inputName` passed a an argument.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_18_009: [The message generated by the `sendToModuleInput` method should have its “x-opt-input-name” annotation set to the `inputName` passed a an argument.]*/
|
||||
amqpMessage.messageAnnotations['x-opt-input-name'] = inputName;
|
||||
this._fsm.handle('send', amqpMessage, deviceEndpoint, handleResult('AMQP Transport: Could not send message', done));
|
||||
}
|
||||
|
|
|
@ -137,8 +137,13 @@ export class Client extends EventEmitter {
|
|||
if (!message) {
|
||||
throw new ReferenceError('message is \'' + message + '\'');
|
||||
}
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_014: [The send method shall convert the message object to type azure-iot-common.Message if necessary.]*/
|
||||
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_014: [The `send` method shall convert the `message` object to type `azure-iot-common.Message` if it is not already of type `azure-iot-common.Message`.]*/
|
||||
if ((<any>message.constructor).name !== 'Message') {
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_016: [The `send` method shall throw an `ArgumentError` if the `message` argument is not of type `azure-iot-common.Message` or `azure-iot-common.Message.BufferConvertible`.]*/
|
||||
if (!Message.isBufferConvertible(message)) {
|
||||
throw new errors.ArgumentError('message is not of type Message or Message.BufferConvertible');
|
||||
}
|
||||
message = new Message(message as Message.BufferConvertible);
|
||||
}
|
||||
|
||||
|
@ -168,7 +173,7 @@ export class Client extends EventEmitter {
|
|||
|
||||
|
||||
/**
|
||||
* @method module:azure-iothub.Client#sendToModule
|
||||
* @method module:azure-iothub.Client#sendToModuleInput
|
||||
* @description Sends a message to a device.
|
||||
* @param {String} deviceId The identifier of an existing device identity.
|
||||
* @param {String} moduleId The identifier of an existing module.
|
||||
|
@ -185,8 +190,8 @@ export class Client extends EventEmitter {
|
|||
*
|
||||
* @throws {ReferenceError} If `deviceId`, `moduleId`, `inputName`, or `message` is null, undefined or empty.
|
||||
*/
|
||||
sendToModule(deviceId: string, moduleId: string, inputName: string, message: Message | Message.BufferConvertible, done?: Callback<results.MessageEnqueued>): void {
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_007: [The `sendToModule` method shall throw `ReferenceError` if the deviceId, moduleId, inputName, or message arguments are falsy.]*/
|
||||
sendToModuleInput(deviceId: string, moduleId: string, inputName: string, message: Message | Message.BufferConvertible, done?: Callback<results.MessageEnqueued>): void {
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_007: [The `sendToModuleInput` method shall throw `ReferenceError` if the deviceId, moduleId, inputName, or message arguments are falsy.]*/
|
||||
if (!deviceId) {
|
||||
throw new ReferenceError('deviceId is \'' + deviceId + '\'');
|
||||
}
|
||||
|
@ -199,24 +204,29 @@ export class Client extends EventEmitter {
|
|||
if (!message) {
|
||||
throw new ReferenceError('message is \'' + message + '\'');
|
||||
}
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_008: [The `sendToModule` method shall convert the message object to type azure-iot-common.Message if necessary.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_008: [The `sendToModuleInput` method shall convert the message object to type `azure-iot-common.Message` if it is not already of type `azure-iot-common.Message`.]*/
|
||||
if ((<any>message.constructor).name !== 'Message') {
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_017: [The `sendToModuleInput` method shall throw an `ArgumentError` if the `message` argument is not of type `azure-iot-common.Message` or `azure-iot-common.Message.BufferConvertible`.]*/
|
||||
if (!Message.isBufferConvertible(message)) {
|
||||
throw new errors.ArgumentError('message is not of type Message or Message.BufferConvertible');
|
||||
}
|
||||
message = new Message(message as Message.BufferConvertible);
|
||||
}
|
||||
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_009: [When the `sendToModule` method completes, the callback function (indicated by the done - argument) shall be invoked with the following arguments:
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_009: [When the `sendToModuleInput` method completes, the callback function (indicated by the done - argument) shall be invoked with the following arguments:
|
||||
- `err` - standard JavaScript Error object (or subclass)
|
||||
- `result` - an implementation-specific response object returned by the underlying protocol, useful for logging and troubleshooting]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_010: [The argument `err` passed to the callback `done` shall be `null` if the protocol operation was successful.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_011: [Otherwise the argument `err` shall have an `amqpError` property containing implementation-specific response information for use in logging and troubleshooting.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_012: [If the `deviceId` has not been registered with the IoT Hub, `sendToModule` shall call the `done` callback with a `DeviceNotFoundError`.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_013: [If the queue which receives messages on behalf of the device is full, `sendToModule` shall call the `done` callback with a `DeviceMaximumQueueDepthExceededError`.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_014: [The `sendToModule` method shall use the retry policy defined either by default or by a call to `setRetryPolicy` if necessary to send the message.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_012: [If the `deviceId` has not been registered with the IoT Hub, `sendToModuleInput` shall call the `done` callback with a `DeviceNotFoundError`.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_018: [If the `moduleId` has not been added to the device named `deviceId` on the IoT Hub, `sendToModuleInput` shall call the `done` callback with a `DeviceNotFoundError`.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_013: [If the queue which receives messages on behalf of the device is full, `sendToModuleInput` shall call the `done` callback with a `DeviceMaximumQueueDepthExceededError`.]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_014: [The `sendToModuleInput` method shall use the retry policy defined either by default or by a call to `setRetryPolicy` if necessary to send the message.]*/
|
||||
const retryOp = new RetryOperation(this._retryPolicy, MAX_RETRY_TIMEOUT);
|
||||
retryOp.retry((retryCallback) => {
|
||||
this._transport.sendToModule(deviceId, moduleId, inputName, message as Message, retryCallback);
|
||||
this._transport.sendToModuleInput(deviceId, moduleId, inputName, message as Message, retryCallback);
|
||||
}, (err, result) => {
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_015: [The `sendToModule` method shall not throw if the `done` callback is falsy. .]*/
|
||||
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_015: [The `sendToModuleInput` method shall not throw if the `done` callback is falsy.]*/
|
||||
if (done) {
|
||||
if (err) {
|
||||
done(err);
|
||||
|
@ -507,7 +517,7 @@ export namespace Client {
|
|||
connect(done?: Callback<results.Connected>): void;
|
||||
disconnect(done: Callback<results.Disconnected>): void;
|
||||
send(deviceId: string, message: Message, done?: Callback<results.MessageEnqueued>): void;
|
||||
sendToModule(deviceId: string, moduleId: string, inputName: string, message: Message, done?: Callback<results.MessageEnqueued>): void;
|
||||
sendToModuleInput(deviceId: string, moduleId: string, inputName: string, message: Message, done?: Callback<results.MessageEnqueued>): void;
|
||||
getFeedbackReceiver(done: Callback<ServiceReceiver>): void;
|
||||
getFileNotificationReceiver(done: Callback<ServiceReceiver>): void;
|
||||
}
|
||||
|
|
|
@ -537,15 +537,15 @@ describe('Amqp', function() {
|
|||
expectedInputName: null
|
||||
},
|
||||
{
|
||||
functionUnderTest: 'sendToModule',
|
||||
invokeFunctionUnderTest: function(amqp, msg, callback) { amqp.sendToModule('fakeDeviceId', 'fakeModuleId', 'fakeInputName', msg, callback); },
|
||||
functionUnderTest: 'sendToModuleInput',
|
||||
invokeFunctionUnderTest: function(amqp, msg, callback) { amqp.sendToModuleInput('fakeDeviceId', 'fakeModuleId', 'fakeInputName', msg, callback); },
|
||||
expectedDestination: '/devices/fakeDeviceId/modules/fakeModuleId/messages/devicebound',
|
||||
expectedInputName: 'fakeInputName'
|
||||
}
|
||||
].forEach(function(testConfig) {
|
||||
describe('#' + testConfig.functionUnderTest, function () {
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_025: [The `send` method shall connect and authenticate the transport if it is disconnected.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_003: [The `sendToModule` method shall connect and authenticate the transport if it is disconnected.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_003: [The `sendToModuleInput` method shall connect and authenticate the transport if it is disconnected.]*/
|
||||
it('connects the transport if necessary', function (testCallback) {
|
||||
var fakeSender = new EventEmitter();
|
||||
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
|
||||
|
@ -561,7 +561,7 @@ describe('Amqp', function() {
|
|||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_026: [The `send` method shall call its callback with an error if connecting and/or authenticating the transport fails.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_004: [The `sendToModule` method shall call its callback with an error if connecting and/or authenticating the transport fails.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_004: [The `sendToModuleInput` method shall call its callback with an error if connecting and/or authenticating the transport fails.]*/
|
||||
it('calls its callback with an error if connecting the transport fails', function (testCallback) {
|
||||
var amqp = new Amqp(fakeConfig, fakeAmqpBase);
|
||||
fakeAmqpBase.connect = sinon.stub().callsArgWith(2, new Error('fakeError'));
|
||||
|
@ -574,7 +574,7 @@ describe('Amqp', function() {
|
|||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_027: [The `send` method shall attach the C2D link if necessary.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_005: [The `sendToModule` method shall attach the C2D link if necessary.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_005: [The `sendToModuleInput` method shall attach the C2D link if necessary.]*/
|
||||
it('attaches the link if necessary', function (testCallback) {
|
||||
var fakeSender = new EventEmitter();
|
||||
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
|
||||
|
@ -591,7 +591,7 @@ describe('Amqp', function() {
|
|||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_029: [The `send` method shall call its callback with an error if it fails to attach the C2D link.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_007: [The `sendToModule` method shall call its callback with an error if it fails to attach the C2D link.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_007: [The `sendToModuleInput` method shall call its callback with an error if it fails to attach the C2D link.]*/
|
||||
it('calls its callback with an error if the link fails to attach', function (testCallback) {
|
||||
var fakeSender = new EventEmitter();
|
||||
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
|
||||
|
@ -608,7 +608,7 @@ describe('Amqp', function() {
|
|||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_030: [The `send` method shall call the `send` method of the C2D link and pass it the Amqp request that it created.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_008: [The `sendToModule` method shall call the `send` method of the C2D link and pass it the Amqp request that it created.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_008: [The `sendToModuleInput` method shall call the `send` method of the C2D link and pass it the Amqp request that it created.]*/
|
||||
it('calls send on the c2d link object', function (testCallback) {
|
||||
var fakeSender = new EventEmitter();
|
||||
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
|
||||
|
@ -619,10 +619,10 @@ describe('Amqp', function() {
|
|||
testConfig.invokeFunctionUnderTest(amqp, new Message('foo'), function () {
|
||||
assert.isTrue(fakeSender.send.calledOnce);
|
||||
assert.instanceOf(fakeSender.send.firstCall.args[0], AmqpMessage);
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_003: [The message generated by the `send` method should have its “to” field set to the device ID passed as an argument.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_002: [The message generated by the `sendToModule` method should have its “to” field set to the `deviceId` & `moduleId` passed as arguments.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_003: [The message generated by the `send` method should have its “to” field set to "/devices/(uriEncode<deviceId>)/messages/devicebound".]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_002: [The message generated by the `sendToModuleInput` method should have its “to” field set to "/devices/(uriEncode<deviceId>)/modules/(uriEncode<moduleId>)/messages/devicebound".]*/
|
||||
assert.strictEqual(fakeSender.send.firstCall.args[0].properties.to, testConfig.expectedDestination);
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_009: [The message generated by the `sendToModule` method should have its “x-opt-input-name” annotation set to the `inputName` passed a an argument.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_009: [The message generated by the `sendToModuleInput` method should have its “x-opt-input-name” annotation set to the `inputName` passed a an argument.]*/
|
||||
if (testConfig.expectedInputName) {
|
||||
assert.strictEqual(fakeSender.send.firstCall.args[0].messageAnnotations['x-opt-input-name'], testConfig.expectedInputName);
|
||||
}
|
||||
|
@ -648,7 +648,7 @@ describe('Amqp', function() {
|
|||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_028: [The `send` method shall reuse the C2D link if it is already attached.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_006: [The `sendToModule` method shall reuse the C2D link if it is already attached.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_18_006: [The `sendToModuleInput` method shall reuse the C2D link if it is already attached.]*/
|
||||
it('Reuses the c2d link object', function (testCallback) {
|
||||
var fakeSender = new EventEmitter();
|
||||
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
|
||||
|
|
|
@ -7,11 +7,14 @@ var assert = require('chai').assert;
|
|||
var Client = require('../lib/client.js').Client;
|
||||
var errors = require('azure-iot-common').errors;
|
||||
var Message = require('azure-iot-common').Message;
|
||||
var debug = require('debug')('azure-iothub:_client_common_testrun');
|
||||
|
||||
function transportSpecificTests(opts) {
|
||||
describe('Client', function () {
|
||||
var testSubject;
|
||||
var deviceId = 'testDevice-node-' + Math.random();
|
||||
var moduleId = 'testModule-' + Math.random();
|
||||
var inputName = 'inputName';
|
||||
|
||||
before('prepare test subject', function (done) {
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_008: [The open method shall open a connection to the IoT Hub that was identified when the Client object was created (e.g., in Client.fromConnectionString).]*/
|
||||
|
@ -19,11 +22,23 @@ function transportSpecificTests(opts) {
|
|||
err - standard JavaScript Error object (or subclass)]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_010: [The argument err passed to the callback done shall be null if the protocol operation was successful.]*/
|
||||
opts.registry.create({ deviceId: deviceId, status: "enabled" }, function(err) {
|
||||
debug('create returned ' + (err ? err : 'success'));
|
||||
if (err) {
|
||||
done(err);
|
||||
} else {
|
||||
testSubject = Client.fromConnectionString(opts.connectionString, opts.transport);
|
||||
testSubject.open(function (err) { done(err); });
|
||||
var module = {
|
||||
deviceId: deviceId,
|
||||
moduleId: moduleId
|
||||
};
|
||||
opts.registry.addModule(module, function(err) {
|
||||
debug('addModule returned ' + (err ? err : 'success'));
|
||||
if (err) {
|
||||
done(err);
|
||||
} else {
|
||||
testSubject = Client.fromConnectionString(opts.connectionString, opts.transport);
|
||||
testSubject.open(function (err) { done(err); });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -67,19 +82,11 @@ function transportSpecificTests(opts) {
|
|||
});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_014: [The send method shall convert the message object to type azure-iot-common.Message if necessary.]*/
|
||||
it('accepts any message that is convertible to type Message', function (done) {
|
||||
var message = 'msg';
|
||||
testSubject.send(deviceId, message, function (err, state) {
|
||||
if (!err) {
|
||||
assert.equal(state.constructor.name, "MessageEnqueued");
|
||||
}
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_019: [If the deviceId has not been registered with the IoT Hub, send shall return an instance of DeviceNotFoundError.]*/
|
||||
it('returns DeviceNotFoundError when sending to an unregistered deviceId', function (done) {
|
||||
// DeviceNotFound returned because
|
||||
// 1) amqp_simulated has special-case code for devices with 'no-device' in the name.
|
||||
// 2) real hubs don't have this device.
|
||||
var unregisteredDeviceId = 'no-device' + Math.random();
|
||||
testSubject.send(unregisteredDeviceId, new Message('msg'), function (err) {
|
||||
assert.instanceOf(err, errors.DeviceNotFoundError);
|
||||
|
@ -88,19 +95,19 @@ function transportSpecificTests(opts) {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#sendToModule', function () {
|
||||
describe('#sendToModuleInput', function () {
|
||||
function createTestMessage() {
|
||||
var msg = new Message('msg');
|
||||
msg.expiryTimeUtc = Date.now() + 5000; // Expire 5s from now, to reduce the chance of us hitting the 50-message limit on the IoT Hub
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_009: [When the `sendToModule` method completes, the callback function (indicated by the done - argument) shall be invoked with the following arguments:
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_009: [When the `sendToModuleInput` method completes, the callback function (indicated by the done - argument) shall be invoked with the following arguments:
|
||||
- `err` - standard JavaScript Error object (or subclass)
|
||||
- `result` - an implementation-specific response object returned by the underlying protocol, useful for logging and troubleshooting]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_010: [The argument `err` passed to the callback `done` shall be `null` if the protocol operation was successful.]*/
|
||||
it('sends the message', function (done) {
|
||||
testSubject.send(deviceId, createTestMessage(), function (err, state) {
|
||||
testSubject.sendToModuleInput(deviceId, moduleId, inputName, createTestMessage(), function (err, state) {
|
||||
if (!err) {
|
||||
assert.equal(state.constructor.name, "MessageEnqueued");
|
||||
}
|
||||
|
@ -108,21 +115,27 @@ function transportSpecificTests(opts) {
|
|||
});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_008: [The `sendToModule` method shall convert the message object to type azure-iot-common.Message if necessary.]*/
|
||||
it('accepts any message that is convertible to type Message', function (done) {
|
||||
var message = 'msg';
|
||||
testSubject.send(deviceId, message, function (err, state) {
|
||||
if (!err) {
|
||||
assert.equal(state.constructor.name, "MessageEnqueued");
|
||||
}
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_012: [If the `deviceId` has not been registered with the IoT Hub, `sendToModule` shall call the `done` callback with a `DeviceNotFoundError`.]*/
|
||||
it('returns DeviceNotFoundError when sending to an unregistered deviceId', function (done) {
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_012: [If the `deviceId` has not been registered with the IoT Hub, `sendToModuleInput` shall call the `done` callback with a `DeviceNotFoundError`.]*/
|
||||
// commented out because IoT hub is currently returning amqp:internal-error in this case
|
||||
it.skip('returns DeviceNotFoundError when sending to an unregistered deviceId', function (done) {
|
||||
// DeviceNotFound returned because
|
||||
// 1) amqp_simulated has special-case code for devices with 'no-device' in the name.
|
||||
// 2) real hubs don't have this device.
|
||||
var unregisteredDeviceId = 'no-device' + Math.random();
|
||||
testSubject.send(unregisteredDeviceId, new Message('msg'), function (err) {
|
||||
testSubject.sendToModuleInput(unregisteredDeviceId, moduleId, inputName, new Message('msg'), function (err) {
|
||||
assert.instanceOf(err, errors.DeviceNotFoundError);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_018: [If the `moduleId` has not been added to the device named `deviceId` on the IoT Hub, `sendToModuleInput` shall call the `done` callback with a `DeviceNotFoundError`.]*/
|
||||
// commented out because IoT hub is currently returning amqp:internal-error in this case
|
||||
it.skip('returns DeviceNotFoundError when sending to an unregistered moduleId', function (done) {
|
||||
// DeviceNotFound returned because
|
||||
// 1) amqp_simulated has special-case code for devices with 'no-module' in the name.
|
||||
// 2) real hubs don't have this module.
|
||||
var unregisteredModuleId = 'no-module' + Math.random();
|
||||
testSubject.sendToModuleInput(deviceId, unregisteredModuleId, inputName, new Message('msg'), function (err) {
|
||||
assert.instanceOf(err, errors.DeviceNotFoundError);
|
||||
done();
|
||||
});
|
||||
|
|
|
@ -99,6 +99,18 @@ describe('Client', function () {
|
|||
});
|
||||
});
|
||||
|
||||
var goodSendParameters = [
|
||||
{ obj: new Buffer('foo'), name: 'Buffer' },
|
||||
{ obj: 'foo', name: 'string' },
|
||||
{ obj: [], name: 'Array' },
|
||||
{ obj: new ArrayBuffer(), name: 'ArrayBuffer' }
|
||||
];
|
||||
var badSendParameters = [
|
||||
{ obj: 1, name: 'number' },
|
||||
{ obj: true, name: 'boolean' },
|
||||
{ obj: {}, name: 'object' }
|
||||
];
|
||||
|
||||
describe('#send', function () {
|
||||
var testSubject;
|
||||
|
||||
|
@ -128,16 +140,43 @@ describe('Client', function () {
|
|||
client.send('id', new Message('msg'));
|
||||
});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_016: [The `send` method shall throw an `ArgumentError` if the `message` argument is not of type `azure-iot-common.Message` or `azure-iot-common.Message.BufferConvertible`.]*/
|
||||
badSendParameters.forEach(function(testConfig) {
|
||||
it('throws if message is of type ' + testConfig.name, function() {
|
||||
assert.throws(function () {
|
||||
testSubject.send('id', testConfig.obj);
|
||||
}, errors.ArgumentError);
|
||||
});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_014: [The `send` method shall convert the `message` object to type `azure-iot-common.Message` if it is not already of type `azure-iot-common.Message`.]*/
|
||||
goodSendParameters.forEach(function(testConfig) {
|
||||
it('Converts to message if message is of type ' + testConfig.name, function(testCallback) {
|
||||
var simulatedAmqp = new SimulatedAmqp();
|
||||
var client = new Client(simulatedAmqp);
|
||||
sinon.spy(simulatedAmqp, 'send');
|
||||
client.send('id', testConfig.obj, function(err, state) {
|
||||
assert(!err);
|
||||
assert.equal(state.constructor.name, "MessageEnqueued");
|
||||
var sentMessage = simulatedAmqp.send.firstCall.args[1];
|
||||
assert.deepEqual(sentMessage, new Message(testConfig.obj));
|
||||
testCallback();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('#sendToModule', function () {
|
||||
describe('#sendToModuleInput', function () {
|
||||
var testSubject;
|
||||
|
||||
beforeEach('prepare test subject', function () {
|
||||
testSubject = new Client({}, {});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_007: [The `sendToModule` method shall throw `ReferenceError` if the deviceId, moduleId, inputName, or message arguments are falsy.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_007: [The `sendToModuleInput` method shall throw `ReferenceError` if the deviceId, moduleId, inputName, or message arguments are falsy.]*/
|
||||
['deviceId', 'moduleId', 'inputName', 'message'].forEach(function(paramToTest) {
|
||||
[ null, '', undefined, ].forEach(function(falsyValue) {
|
||||
it('throws if ' + paramToTest + ' is ' + falsyValue, function() {
|
||||
|
@ -149,18 +188,44 @@ describe('Client', function () {
|
|||
};
|
||||
params[paramToTest] = falsyValue;
|
||||
assert.throws(function() {
|
||||
testSubject.sendToModule(params.deviceId, params.moduleId, params.inputName, params.messageName);
|
||||
testSubject.sendToModuleInput(params.deviceId, params.moduleId, params.inputName, params.messageName);
|
||||
}, ReferenceError);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_015: [The `sendToModule` method shall not throw if the `done` callback is falsy. .]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_015: [The `sendToModuleInput` method shall not throw if the `done` callback is falsy.git stat]*/
|
||||
it('does not throw if done is falsy', function () {
|
||||
var simulatedAmqp = new SimulatedAmqp();
|
||||
var client = new Client(simulatedAmqp);
|
||||
assert.doesNotThrow(function () {
|
||||
client.sendToModule('deviceId', 'moduleId', 'inputName', new Message('msg'));
|
||||
client.sendToModuleInput('deviceId', 'moduleId', 'inputName', new Message('msg'));
|
||||
});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_017: [The `sendToModuleInput` method shall throw an `ArgumentError` if the `message` argument is not of type `azure-iot-common.Message` or `azure-iot-common.Message.BufferConvertible`.]*/
|
||||
badSendParameters.forEach(function(testConfig) {
|
||||
it('throws if message is of type ' + testConfig.name, function() {
|
||||
var client = new Client({}, {});
|
||||
assert.throws(function () {
|
||||
client.sendToModuleInput('id', 'id2', 'input', testConfig.obj);
|
||||
}, errors.ArgumentError);
|
||||
});
|
||||
});
|
||||
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_008: [The `sendToModuleInput` method shall convert the message object to type `azure-iot-common.Message` if it is not already of type `azure-iot-common.Message`.]*/
|
||||
goodSendParameters.forEach(function(testConfig) {
|
||||
it('Converts to message if message is of type ' + testConfig.name, function(testCallback) {
|
||||
var simulatedAmqp = new SimulatedAmqp();
|
||||
var client = new Client(simulatedAmqp);
|
||||
sinon.spy(simulatedAmqp, 'sendToModuleInput');
|
||||
client.sendToModuleInput('id', 'id2', 'input', testConfig.obj, function(err, state) {
|
||||
assert(!err);
|
||||
assert.equal(state.constructor.name, "MessageEnqueued");
|
||||
var sentMessage = simulatedAmqp.sendToModuleInput.firstCall.args[3];
|
||||
assert.deepEqual(sentMessage, new Message(testConfig.obj));
|
||||
testCallback();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -226,7 +291,7 @@ describe('Client', function () {
|
|||
describe('#' + testConfig.name, function() {
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_009: [The `invokeDeviceMethod` method shall initialize a new instance of `DeviceMethod` with the `methodName` and `timeout` values passed in the arguments.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_010: [The `invokeDeviceMethod` method shall use the newly created instance of `DeviceMethod` to invoke the method with the `payload` argument on the device specified with the `deviceid` argument .]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_013: [The `invokeDeviceMethod` method sendthe `done` callback with a `null` first argument, the result of the method execution in the second argument, and the transport-specific response object as a third argument.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_013: [The `invokeDeviceMethod` method shall call the `done` callback with a `null` first argument, the result of the method execution in the second argument, and the transport-specific response object as a third argument.]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_002: [The `invokeModuleMethod` method shall initialize a new `DeviceMethod` instance with `methodParams` values passed in the arguments. ]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_003: [The `invokeModuleMethod` method shall call `invokeOnModule` on the new `DeviceMethod` instance. ]*/
|
||||
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_005: [The `invokeModuleMethod` method shall call the `done` callback with a `null` first argument, the result of the method execution in the second argument, and the transport-specific response object as a third argument. ]*/
|
||||
|
@ -452,6 +517,7 @@ describe('Client', function () {
|
|||
|
||||
var fakeRegistry = {
|
||||
create: function(device, done) { done(); },
|
||||
addModule: function(module, done) { done(); },
|
||||
delete: function(deviceId, done) { done(); }
|
||||
};
|
||||
|
||||
|
|
|
@ -47,12 +47,13 @@ SimulatedAmqp.prototype.send = function send(deviceId, message, done) {
|
|||
}
|
||||
};
|
||||
|
||||
SimulatedAmqp.prototype.sendToModule = function send(deviceId, moduleId, inputName, message, done) {
|
||||
SimulatedAmqp.prototype.sendToModuleInput = function send(deviceId, moduleId, inputName, message, done) {
|
||||
if (done) {
|
||||
if (deviceId.search(/^no-device/) !== -1) {
|
||||
done(new errors.DeviceNotFoundError());
|
||||
}
|
||||
else {
|
||||
} else if (moduleId.search(/^no-module/) !== -1) {
|
||||
done(new errors.DeviceNotFoundError());
|
||||
} else {
|
||||
done(null, new results.MessageEnqueued());
|
||||
if (message.ack === 'full') {
|
||||
this._receiver.emit('message', {
|
||||
|
|
Загрузка…
Ссылка в новой задаче