зеркало из
1
0
Форкнуть 0
This commit is contained in:
Bert Kleewein 2018-04-13 14:38:48 -07:00
Родитель 168b4ac087
Коммит cc6462125b
23 изменённых файлов: 312 добавлений и 108 удалений

Двоичные данные
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', {