iotagent-node-lib
Version:
IoT Agent library to interface with NGSI Context Broker
570 lines (526 loc) • 19.4 kB
JavaScript
/*
* Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U
*
* This file is part of fiware-iotagent-lib
*
* fiware-iotagent-lib is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* fiware-iotagent-lib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with fiware-iotagent-lib.
* If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by the GNU Affero General Public License
* please contact with::[contacto@tid.es]
*/
/* eslint-disable no-unused-vars */
const iotAgentLib = require('../../../lib/fiware-iotagent-lib');
const utils = require('../../tools/utils');
const should = require('should');
const logger = require('logops');
const mongo = require('mongodb').MongoClient;
const nock = require('nock');
const async = require('async');
const mongoUtils = require('./mongoDBUtils');
let contextBrokerMock;
const iotAgentConfig = {
contextBroker: {
host: '192.168.1.1',
port: '1026'
},
server: {
port: 4041,
host: 'localhost'
},
types: {
Light: {
commands: [
{
name: 'position',
type: 'Array'
}
],
lazy: [
{
name: 'temperature',
type: 'centigrades'
}
],
attributes: [
{
name: 'pressure',
type: 'Hgmm'
}
],
staticAttributes: [
{
name: 'location',
type: 'Vector'
}
],
service: 'smartgondor',
subservice: 'gardens',
internalAttributes: {
customAttribute: 'customValue'
}
},
Termometer: {
commands: [],
lazy: [
{
name: 'temp',
type: 'kelvin'
}
],
attributes: [],
service: 'smartgondor',
subservice: 'gardens'
}
},
deviceRegistry: {
type: 'mongodb'
},
mongodb: {
host: 'localhost',
port: '27017',
db: 'iotagent'
},
service: 'smartgondor',
subservice: 'gardens',
providerUrl: 'http://smartgondor.com',
deviceRegistrationDuration: 'P1M'
};
const device1 = {
id: 'light1',
type: 'Light',
service: 'smartgondor',
subservice: 'gardens',
endpoint: 'http://testEndpoint.com',
transport: 'HTTP',
resource: '/test',
apikey: '2345678ikjhgfr678i',
protocol: 'GENERIC_PROTOCOL',
commands: [
{
name: 'position',
type: 'Array'
}
],
lazy: [
{
name: 'temperature',
type: 'centigrades'
}
],
active: [
{
name: 'pressure',
type: 'Hgmm'
}
],
staticAttributes: [
{
name: 'location',
type: 'Vector'
}
],
internalAttributes: {
customAttribute: 'customValue'
}
};
const device2 = {
id: 'term2',
type: 'Termometer',
service: 'smartgondor',
subservice: 'gardens',
resource: '/',
apikey: 'dsf8yy789iyushu786',
protocol: 'GENERIC_PROTOCOL'
};
const device3 = {
id: 'light1',
type: 'Light',
service: 'smartMordor',
subservice: 'electricity',
endpoint: 'http://testEndpoint.com',
transport: 'HTTP',
resource: '/test',
apikey: '2345678ikjhgfr678i',
protocol: 'GENERIC_PROTOCOL',
commands: [
{
name: 'position',
type: 'Array'
}
],
lazy: [
{
name: 'temperature',
type: 'centigrades'
}
],
active: [
{
name: 'pressure',
type: 'Hgmm'
}
],
staticAttributes: [
{
name: 'location',
type: 'Vector'
}
],
internalAttributes: {
customAttribute: 'customValue'
}
};
let iotAgentDb;
describe('NGSI-v2 - MongoDB Device Registry', function () {
beforeEach(function (done) {
logger.setLevel('FATAL');
mongoUtils.cleanDbs(function () {
mongo.connect('mongodb://localhost:27017/iotagent', function (err, db) {
iotAgentDb = db;
done();
});
});
});
afterEach(function (done) {
delete device1.registrationId;
iotAgentLib.deactivate(function (error) {
iotAgentDb
.db()
.collection('devices')
.deleteOne({}, function (error) {
iotAgentDb.close(function (error) {
mongoUtils.cleanDbs(done);
});
});
});
});
describe('When a new device is connected to the IoT Agent', function () {
beforeEach(function (done) {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/registrations',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextAvailabilityRequests/registerIoTAgent3.json'
)
)
.reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' });
contextBrokerMock.post('/v2/entities?options=upsert').reply(204);
iotAgentLib.activate(iotAgentConfig, function (error) {
done();
});
});
it('should be registered in mongodb with all its attributes', function (done) {
iotAgentLib.register(device1, function (error) {
should.not.exist(error);
iotAgentDb
.db()
.collection('devices')
.find({})
.toArray(function (err, docs) {
should.not.exist(err);
should.exist(docs);
should.exist(docs.length);
docs.length.should.equal(1);
should.exist(docs[0].internalAttributes);
should.exist(docs[0].staticAttributes);
should.exist(docs[0].internalAttributes.customAttribute);
should.exist(docs[0].active);
should.exist(docs[0].commands);
should.exist(docs[0].resource);
should.exist(docs[0].endpoint);
should.exist(docs[0].transport);
should.exist(docs[0].apikey);
should.exist(docs[0].protocol);
docs[0].active.length.should.equal(1);
docs[0].staticAttributes.length.should.equal(1);
docs[0].staticAttributes[0].name.should.equal('location');
docs[0].active[0].name.should.equal('pressure');
docs[0].commands[0].name.should.equal('position');
docs[0].internalAttributes.customAttribute.should.equal('customValue');
docs[0].resource.should.equal('/test');
docs[0].endpoint.should.equal('http://testEndpoint.com');
docs[0].transport.should.equal('HTTP');
docs[0].protocol.should.equal('GENERIC_PROTOCOL');
docs[0].apikey.should.equal('2345678ikjhgfr678i');
done();
});
});
});
});
describe('When a device with the same Device ID tries to register to the IOT Agent', function () {
beforeEach(function (done) {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/registrations',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextAvailabilityRequests/registerIoTAgent3.json'
)
)
.reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' });
contextBrokerMock.post('/v2/entities?options=upsert').reply(204);
contextBrokerMock
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/registrations',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextAvailabilityRequests/registerIoTAgent3.json'
)
)
.reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' });
contextBrokerMock.post('/v2/entities?options=upsert').reply(204);
iotAgentLib.activate(iotAgentConfig, function (error) {
done();
});
});
it('should be rejected with a DUPLICATE_DEVICE_ID', function (done) {
iotAgentLib.register(device1, function (error) {
iotAgentLib.register(device1, function (error) {
should.exist(error);
error.name.should.equal('DUPLICATE_DEVICE_ID');
done();
});
});
});
});
describe('When a device with the same Device ID but different service tries to be registered', function () {
beforeEach(function (done) {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.post(
'/v2/registrations',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextAvailabilityRequests/registerIoTAgent3.json'
)
)
.reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' });
contextBrokerMock.post('/v2/entities?options=upsert').reply(204);
contextBrokerMock
.post(
'/v2/registrations',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextAvailabilityRequests/registerIoTAgent3.json'
)
)
.reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' });
contextBrokerMock.post('/v2/entities?options=upsert').reply(204);
iotAgentLib.activate(iotAgentConfig, function (error) {
done();
});
});
it('should accept both devices', function (done) {
iotAgentLib.register(device1, function (error) {
iotAgentLib.register(device3, function (error) {
should.not.exist(error);
done();
});
});
});
});
describe('When a device is removed from the IoT Agent', function () {
beforeEach(function (done) {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.post('/v2/registrations')
.reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' });
contextBrokerMock.post('/v2/entities?options=upsert').reply(204);
contextBrokerMock
.post('/v2/registrations')
.reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' });
contextBrokerMock.post('/v2/entities?options=upsert').reply(204);
contextBrokerMock.delete('/v2/registrations/6319a7f5254b05844116584d', '').reply(204);
iotAgentLib.activate(iotAgentConfig, function (error) {
async.series(
[async.apply(iotAgentLib.register, device1), async.apply(iotAgentLib.register, device2)],
done
);
});
});
it('should be removed from MongoDB', function (done) {
iotAgentLib.unregister(device1.id, null, 'smartgondor', 'gardens', function (error) {
iotAgentDb
.db()
.collection('devices')
.find({})
.toArray(function (err, docs) {
should.not.exist(err);
should.exist(docs);
should.exist(docs.length);
docs.length.should.equal(1);
done();
});
});
});
});
describe('When the registry is queried for a device using an arbitrary attribute', function () {
beforeEach(function (done) {
contextBrokerMock = nock('http://192.168.1.1:1026')
.post('/v2/entities?options=upsert')
.times(10)
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.reply(204);
const devices = [];
for (let i = 0; i < 10; i++) {
devices.push({
id: 'id' + i,
type: 'Light' + i,
internalId: 'internal' + i,
service: 'smartgondor',
subservice: 'gardens',
active: [
{
id: 'attrId',
type: 'attrType' + i,
value: i
}
]
});
}
iotAgentLib.activate(iotAgentConfig, function (error) {
async.map(devices, iotAgentLib.register, function (error, results) {
done();
});
});
});
afterEach(function (done) {
iotAgentLib.clearRegistry(done);
});
it('should return the appropriate device', function (done) {
iotAgentLib.getDevicesByAttribute(
'internalId',
'internal3',
'smartgondor',
'gardens',
function (error, devices) {
should.not.exist(error);
should.exist(devices);
devices.length.should.equal(1);
devices[0].id.should.equal('id3');
done();
}
);
});
});
describe('When the list of devices is retrieved', function () {
beforeEach(function (done) {
contextBrokerMock = nock('http://192.168.1.1:1026')
.post('/v2/entities?options=upsert')
.times(10)
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.reply(204);
const devices = [];
for (let i = 0; i < 10; i++) {
devices.push({
id: 'id' + i,
type: 'Light' + i,
internalId: 'internal' + i,
service: 'smartgondor',
subservice: 'gardens',
active: [
{
id: 'attrId',
type: 'attrType' + i,
value: i
}
]
});
}
iotAgentLib.activate(iotAgentConfig, function (error) {
async.map(devices, iotAgentLib.register, function (error, results) {
done();
});
});
});
afterEach(function (done) {
iotAgentLib.clearRegistry(done);
});
it('should return the limited number of devices', function (done) {
iotAgentLib.listDevices('smartgondor', 'gardens', 3, 2, function (error, result) {
should.not.exist(error);
should.exist(result.devices);
result.devices.length.should.equal(3);
done();
});
});
it('should return the total number of devices', function (done) {
iotAgentLib.listDevices('smartgondor', 'gardens', 3, 2, function (error, result) {
should.not.exist(error);
should.exist(result.count);
result.count.should.equal(10);
done();
});
});
});
describe('When the device is queried with the name and type', function () {
beforeEach(function (done) {
contextBrokerMock = nock('http://192.168.1.1:1026')
.post('/v2/entities?options=upsert')
.times(10)
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.reply(204);
const devices = [];
for (let i = 0; i < 10; i++) {
devices.push({
id: 'id' + i,
type: 'Light' + i,
internalId: 'internal' + i,
service: 'smartgondor',
subservice: 'gardens',
active: [
{
id: 'attrId',
type: 'attrType' + i,
value: i
}
]
});
}
iotAgentLib.activate(iotAgentConfig, function (error) {
async.map(devices, iotAgentLib.register, function (error, results) {
done();
});
});
});
afterEach(function (done) {
iotAgentLib.clearRegistry(done);
});
it('should return the device with name and type', function (done) {
iotAgentLib.getDeviceByNameAndType(
'Light4:id4',
'Light4',
'smartgondor',
'gardens',
function (error, device) {
should.not.exist(error);
should.exist(device);
should.exist(device.name);
should.exist(device.type);
device.name.should.equal('Light4:id4');
device.type.should.equal('Light4');
done();
}
);
});
});
});