iotagent-node-lib
Version:
IoT Agent library to interface with NGSI Context Broker
1,579 lines (1,497 loc) • 82.2 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, seehttp://www.gnu.org/licenses/.
*
* For those usages not covered by the GNU Affero General Public License
* please contact with::[contacto@tid.es]
*
* Developed by: Federico M. Facca - Martel Innovate
*/
/* jshint camelcase: false */
const iotAgentLib = require('../../../../lib/fiware-iotagent-lib');
const utils = require('../../../tools/utils');
const should = require('should');
const logger = require('logops');
const nock = require('nock');
const timekeeper = require('timekeeper');
let contextBrokerMock;
const iotAgentConfig = {
contextBroker: {
host: '192.168.1.1',
port: '1026',
ngsiVersion: 'v2'
},
server: {
port: 4041,
host: 'localhost'
},
types: {
Light: {
commands: [],
type: 'Light',
lazy: [],
active: [
{
object_id: 'p',
name: 'pressure',
type: 'Number'
},
{
object_id: 'e',
name: 'consumption',
type: 'Number'
},
{
object_id: 'a',
name: 'alive',
type: 'None',
skipValue: 'null passes'
},
{
object_id: 'u',
name: 'updated',
type: 'Boolean'
},
{
object_id: 'm',
name: 'manufacturer',
type: 'Object'
},
{
object_id: 'r',
name: 'revisions',
type: 'Array'
},
{
object_id: 'x',
name: 'consumption_x',
type: 'Number',
expression: 'pressure * 20'
}
]
},
LightError: {
commands: [],
type: 'Light',
lazy: [],
active: [
{
object_id: 'p',
name: 'pressure',
type: 'Number',
expression: 'pressure * / 20',
skipValue: 'null passes'
}
]
},
WeatherStation: {
commands: [],
type: 'WeatherStation',
lazy: [],
active: [
{
object_id: 'p',
name: 'pressure',
type: 'Number',
expression: 'pressure * 20'
},
{
object_id: 'e',
name: 'consumption',
type: 'Number',
expression: 'consumption * 20'
},
{
object_id: 'h',
name: 'humidity',
type: 'Percentage'
},
{
name: 'weather',
type: 'Summary',
expression: '"Humidity " + (humidity / 2) + " and pressure " + (p * 20)'
},
{
object_id: 'a',
name: 'alive',
type: 'None',
expression: 'alive * 20'
},
{
object_id: 'u',
name: 'updated',
type: 'Boolean',
expression: 'updated * 20'
}
]
},
WeatherStationWithIdNumber: {
commands: [],
type: 'WeatherStation',
entityNameExp: 'id',
lazy: [],
active: [
{
object_id: 'p',
name: 'pressure',
type: 'Number',
expression: 'pressure * 20'
}
]
},
WeatherStationUndef: {
commands: [],
type: 'WeatherStation',
lazy: [],
active: [
{
object_id: 'u',
name: 'undef',
type: 'json',
expression: 'u["no"]'
},
{
object_id: 'n',
name: 'nil',
type: 'json',
expression: 'u["no"]?u["no"]:null'
},
{
object_id: 'f',
name: 'falsy',
type: 'Boolean',
expression: 'u["no"]?u["no"]:false'
},
{
object_id: 'z',
name: 'zero',
type: 'Number',
expression: 'u["no"]?u["no"]:0'
}
]
},
WeatherStationMultiple: {
commands: [],
type: 'WeatherStation',
lazy: [],
active: [
{
object_id: 'p',
name: 'pressure',
type: 'Number',
expression: 'pressure|trim'
},
{
object_id: 'p25',
name: 'pressure25',
type: 'Number'
},
{
object_id: 'e',
name: 'consumption',
type: 'Number',
expression: 'consumption|trim'
},
{
object_id: 'h',
name: 'humidity12',
type: 'Percentage'
},
{
name: 'weather',
type: 'Summary',
expression: '"Humidity " + (humidity12 / 2) + " and pressure " + (pressure25 * 20)'
},
{
object_id: 'a',
name: 'alive',
type: 'None',
expression: 'alive|trim'
},
{
object_id: 'u',
name: 'updated',
type: 'Boolean',
expression: 'updated|trim'
}
]
},
GPS: {
commands: [],
type: 'GPS',
lazy: [],
active: [
{
name: 'location',
type: 'geo:json',
expression: "{coordinates: [lon,lat], type: 'Point'}"
},
{
name: 'TimeInstant',
type: 'DateTime',
expression: 'ts|toisodate'
}
],
explicitAttrs: true
},
GPS2: {
commands: [],
type: 'GPS',
lazy: [],
active: [
{
name: 'location',
type: 'geo:json',
expression: "{coordinates: [lon,lat], type: 'Point'}"
}
],
explicitAttrs: true
},
GPS2b: {
commands: [],
type: 'GPS',
lazy: [],
active: [
{
name: 'location',
type: 'geo:json',
expression: "{coordinates: [lon,lat], type: 'Point'}"
},
{
name: 'temperature',
type: 'Number',
expression: 't * 10'
}
],
explicitAttrs: true
},
GPS3: {
commands: [],
type: 'GPS',
lazy: [],
staticAttributes: [
{
name: 'color',
type: 'string',
value: 'blue'
}
],
active: [
{
name: 'price',
type: 'number'
},
{
name: 'location',
type: 'geo:json',
expression: "{coordinates: [lon,lat], type: 'Point'}"
}
],
explicitAttrs: ' location&&price ? [ "location", "price" ] : location ? [ "location" ] : []'
},
GPS4: {
commands: [],
type: 'GPS',
lazy: [],
staticAttributes: [
{
name: 'color',
type: 'string',
value: 'blue'
}
],
active: [
{
name: 'price',
type: 'number'
},
{
name: 'location',
type: 'geo:json',
expression: "{coordinates: [lon,lat], type: 'Point'}"
}
],
explicitAttrs: '[ "loca" + "tion" ]'
},
GPS5: {
commands: [],
type: 'GPS',
lazy: [],
staticAttributes: [
{
name: 'color',
type: 'string',
value: 'blue'
}
],
active: [
{
name: 'price',
type: 'number'
},
{
object_id: 'theLocation',
name: 'mylocation',
type: 'geo:json'
}
],
explicitAttrs: "theLocation ? ['mylocation'] : []"
},
GPS5b: {
commands: [],
type: 'GPS',
lazy: [],
staticAttributes: [
{
name: 'lat',
type: 'string',
value: 52
}
],
active: [
{
name: 'price',
type: 'number'
},
{
object_id: 'theLocation',
name: 'mylocation',
type: 'geo:json',
expression: "{coordinates: [lon,lat], type: 'Point'}"
}
],
explicitAttrs: "mylocation ? [{object_id: 'theLocation'}] : []"
},
GPS6: {
commands: [],
type: 'GPS',
lazy: [],
staticAttributes: [
{
name: 'lat',
type: 'Number',
value: 52
},
{
name: 'lon',
type: 'Number',
value: 13
}
],
active: [
{
name: 'location',
type: 'geo:json',
expression: "{coordinates: [lon,lat], type: 'Point'}"
}
],
explicitAttrs: 'TimeInstant == null ? [] : true' // which is true
},
GPS7: {
commands: [],
type: 'GPS',
lazy: [],
staticAttributes: [
{
name: 'color',
type: 'string',
value: 'blue'
}
],
active: [
{
name: 'price',
type: 'number'
},
{
name: 'location',
type: 'geo:json',
expression: "{coordinates: [lon,lat], type: 'Point'}"
}
],
explicitAttrs: '[ ]'
},
skipvalue: {
commands: [],
type: 'skipvalue',
lazy: [],
active: [
{
name: 'alwaysSkip',
type: 'Number',
skipValue: true,
expression: 'true'
},
{
name: 'neverSkip',
type: 'Number',
skipValue: true,
expression: 'false'
},
{
name: 'skip',
type: 'Number',
skipValue: 33,
expression: 'condition'
},
{
object_id: 'condition',
name: 'condition',
type: 'Number'
},
{
object_id: 'nonProgressAtt1',
name: 'nonProgressatt1',
type: 'Number',
expression: 'nonexistent * 2'
},
{
object_id: 'nonProgressAtt2',
name: 'nonProgressatt2',
type: 'Number',
expression: 'nonexistent * 2',
skipValue: null
}
]
},
nestedExpressionsObj: {
commands: [],
type: 'nestedExpressionsObj',
lazy: [],
active: [
{
name: 'value3',
object_id: 'v3',
type: 'Number',
expression: 'v*2'
},
{
name: 'value2',
object_id: 'v2',
type: 'Number',
expression: 'v3*2'
},
{
name: 'value1',
object_id: 'v1',
type: 'Number',
expression: 'v2*2'
}
]
},
nestedExpressionsName: {
commands: [],
type: 'nestedExpressionsName',
lazy: [],
active: [
{
name: 'prefix',
object_id: 't1',
type: 'text',
expression: '"pre_"+t'
},
{
name: 'postfix',
object_id: 't2',
type: 'text',
expression: 'prefix+"_post"'
}
]
},
nestedExpressionsSkip: {
commands: [],
type: 'nestedExpressionsSkip',
lazy: [],
active: [
{
name: 'prefix',
object_id: 't1',
type: 'text',
expression: '"pre_"+t'
},
{
name: 'postfix',
object_id: 't2',
type: 'text',
expression: 'prefix+"_post"'
},
{
name: 't',
object_id: 't',
type: 'text',
expression: 'null'
}
]
},
nestedExpressionDirect: {
commands: [],
type: 'nestedExpressionsDirect',
lazy: [],
active: [
{
name: 'correctedLevel',
type: 'Number',
expression: 'level * 0.897'
},
{
name: 'normalizedLevel',
type: 'Number',
expression: 'correctedLevel / 100'
}
]
},
nestedExpressionReverse: {
commands: [],
type: 'nestedExpressionsReverse',
lazy: [],
active: [
{
name: 'normalizedLevel',
type: 'Number',
expression: 'correctedLevel / 100'
},
{
name: 'correctedLevel',
type: 'Number',
expression: 'level * 0.897'
}
]
},
nestedExpressionsAnti: {
commands: [],
type: 'nestedExpressionsAnti',
lazy: [],
active: [
{
name: 'a',
type: 'Number',
expression: 'b*10'
},
{
name: 'b',
type: 'Number',
expression: 'a*10'
}
]
},
testNull: {
commands: [],
type: 'testNull',
lazy: [],
active: [
{
name: 'a',
type: 'Number',
expression: 'v'
},
{
name: 'b',
type: 'Number',
expression: 'v*3'
},
{
name: 'c',
type: 'Boolean',
expression: 'v==null'
},
{
name: 'd',
type: 'Text',
expression: "v?'no soy null':'soy null'"
},
{
name: 'e',
type: 'Text',
expression: "v==null?'soy null':'no soy null'"
},
{
name: 'f',
type: 'Text',
expression: "(v*3)==null?'soy null':'no soy null'"
},
{
name: 'g',
type: 'Boolean',
expression: 'v == undefined'
}
]
},
testNullSkip: {
commands: [],
type: 'testNullSkip',
lazy: [],
active: [
{
name: 'a',
type: 'Number',
expression: 'v',
skipValue: 'avoidNull'
},
{
name: 'b',
type: 'Number',
expression: 'v*3',
skipValue: 'avoidNull'
},
{
name: 'c',
type: 'Boolean',
expression: 'v==null',
skipValue: 'avoidNull'
},
{
name: 'd',
type: 'Text',
expression: "v?'no soy null':'soy null'",
skipValue: 'avoidNull'
},
{
name: 'e',
type: 'Text',
expression: "v==null?'soy null':'no soy null'",
skipValue: 'avoidNull'
},
{
name: 'f',
type: 'Text',
expression: "(v*3)==null?'soy null':'no soy null'",
skipValue: 'avoidNull'
},
{
name: 'g',
type: 'Boolean',
expression: 'v == undefined',
skipValue: 'avoidNull'
}
]
},
testNullExplicit: {
type: 'testNullExplicit',
explicitAttrs: true,
commands: [],
lazy: [],
active: [
{
name: 'a',
type: 'Number',
expression: 'v'
},
{
name: 'b',
type: 'Number',
expression: 'v*3'
},
{
name: 'c',
type: 'Boolean',
expression: 'v==null'
},
{
name: 'd',
type: 'Text',
expression: "v?'no soy null':'soy null'"
},
{
name: 'e',
type: 'Text',
expression: "v==null?'soy null':'no soy null'"
},
{
name: 'f',
type: 'Text',
expression: "(v*3)==null?'soy null':'no soy null'"
},
{
name: 'g',
type: 'Boolean',
expression: 'v == undefined'
}
]
}
},
service: 'smartgondor',
subservice: 'gardens',
providerUrl: 'http://smartgondor.com',
deviceRegistrationDuration: 'P1M',
useCBflowControl: true
};
const iotAgentConfigTS = {
contextBroker: {
host: '192.168.1.1',
port: '1026',
ngsiVersion: 'v2'
},
server: {
port: 4041,
host: 'localhost'
},
types: {
GPS: {
commands: [],
type: 'GPS',
lazy: [],
active: [
{
name: 'location',
type: 'geo:json',
expression: "{coordinates: [lon,lat], type: 'Point'}"
}
],
explicitAttrs: true
},
WaterTank: {
commands: [],
type: 'WaterTank',
lazy: [],
active: [
{
object_id: 'cnt2',
name: 'contA',
type: 'Number'
},
{
object_id: 'cnt3',
name: 'contB',
type: 'Number'
},
{
object_id: 'false2',
name: 'waterLeavingTanks',
type: 'Number',
expression: 'cnt2*0.1',
entity_name: 'PA_A_0001',
entity_type: 'WaterTank'
},
{
object_id: 'false3',
name: 'waterLeavingTanks',
type: 'Number',
expression: 'cnt3*0.1',
entity_name: 'PA_B_0001',
entity_type: 'WaterTank'
},
{
object_id: 'foostatus2',
name: 'status',
type: 'Text',
expression: 'status',
entity_name: 'PA_A_0001',
entity_type: 'WaterTank'
},
{
object_id: 'foostatus3',
name: 'status',
type: 'Text',
expression: 'status',
entity_name: 'PA_B_0001',
entity_type: 'WaterTank'
}
],
explicitAttrs:
"contA&&contB?['TimeInstant','contA',{object_id:'false2'},'contB',{object_id:'false3'},'status']:contA?['TimeInstant','contA',{object_id:'false2'},{object_id:'foostatus2'}]:contB?['TimeInstant','contB',{object_id:'false3'},{object_id:'foostatus3'}]:[]"
}
},
timestamp: true,
service: 'smartgondor',
subservice: 'gardens',
providerUrl: 'http://smartgondor.com',
useCBflowControl: true
};
describe('Java expression language (JEXL) based transformations plugin', function () {
beforeEach(function (done) {
logger.setLevel('FATAL');
iotAgentLib.activate(iotAgentConfig, function () {
iotAgentLib.clearAll(function () {
done();
});
});
});
afterEach(function (done) {
iotAgentLib.clearAll(function () {
iotAgentLib.deactivate(done);
});
});
describe('When an update comes for expressions with syntax errors', function () {
// Case: Update for an attribute with bad expression
const values = [
{
name: 'p',
type: 'Number',
value: 1040
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/entities?options=upsert,flowControl',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin30.json'
)
)
.reply(204);
});
it('should ignore the expression and send the values', function (done) {
iotAgentLib.update('light1', 'LightError', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When applying expressions with null values', function () {
// Case: Update for an attribute with bad expression
const values = [
{
name: 'v',
type: 'Number',
value: null
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post('/v2/entities?options=upsert,flowControl', {
id: 'testNull1',
type: 'testNull',
v: {
value: null,
type: 'Number'
},
c: {
value: true,
type: 'Boolean'
},
d: {
value: 'soy null',
type: 'Text'
},
e: {
value: 'soy null',
type: 'Text'
},
f: {
value: 'no soy null',
type: 'Text'
},
g: {
value: true,
type: 'Boolean'
}
})
.reply(204);
});
it('it should be handled properly', function (done) {
iotAgentLib.update('testNull1', 'testNull', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When applying expressions without values (NaN)', function () {
// Case: Update for an attribute with bad expression
const values = [
{
name: 'z',
type: 'Number',
value: null
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post('/v2/entities?options=upsert,flowControl', {
id: 'testNull2',
type: 'testNull',
z: {
value: null,
type: 'Number'
},
c: {
value: true,
type: 'Boolean'
},
d: {
value: 'soy null',
type: 'Text'
},
e: {
value: 'soy null',
type: 'Text'
},
f: {
value: 'no soy null',
type: 'Text'
},
g: {
value: true,
type: 'Boolean'
}
})
.reply(204);
});
it('it should be handled properly', function (done) {
iotAgentLib.update('testNull2', 'testNull', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When applying expressions with null values - Skip values disabled', function () {
// Case: Update for an attribute with bad expression
const values = [
{
name: 'v',
type: 'Number',
value: null
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post('/v2/entities?options=upsert,flowControl', {
id: 'testNullSkip1',
type: 'testNullSkip',
v: {
value: null,
type: 'Number'
},
a: {
value: null,
type: 'Number'
},
b: {
value: null,
type: 'Number'
},
c: {
value: true,
type: 'Boolean'
},
d: {
value: 'soy null',
type: 'Text'
},
e: {
value: 'soy null',
type: 'Text'
},
f: {
value: 'no soy null',
type: 'Text'
},
g: {
value: true,
type: 'Boolean'
}
})
.reply(204);
});
it('it should be handled properly', function (done) {
iotAgentLib.update('testNullSkip1', 'testNullSkip', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When applying expressions without values (NaN) - Skip values disabled', function () {
// Case: Update for an attribute with bad expression
const values = [
{
name: 'z',
type: 'Number',
value: null
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post('/v2/entities?options=upsert,flowControl', {
id: 'testNullSkip2',
type: 'testNullSkip',
z: {
value: null,
type: 'Number'
},
a: {
value: null,
type: 'Number'
},
b: {
value: null,
type: 'Number'
},
c: {
value: true,
type: 'Boolean'
},
d: {
value: 'soy null',
type: 'Text'
},
e: {
value: 'soy null',
type: 'Text'
},
f: {
value: 'no soy null',
type: 'Text'
},
g: {
value: true,
type: 'Boolean'
}
})
.reply(204);
});
it('it should be handled properly', function (done) {
iotAgentLib.update('testNullSkip2', 'testNullSkip', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When applying expressions with not explicit measures - explicitAttrs = true', function () {
// Case: Update for an attribute with bad expression
const values = [
{
name: 'v',
type: 'Number',
value: null
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post('/v2/entities?options=upsert,flowControl', {
id: 'testNullExplicit1',
type: 'testNullExplicit',
c: {
value: true,
type: 'Boolean'
},
d: {
value: 'soy null',
type: 'Text'
},
e: {
value: 'soy null',
type: 'Text'
},
f: {
value: 'no soy null',
type: 'Text'
},
g: {
value: true,
type: 'Boolean'
}
})
.reply(204);
});
it('it should be handled properly', function (done) {
iotAgentLib.update('testNullExplicit1', 'testNullExplicit', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When there are expression attributes that are just calculated (not sent by the device)', function () {
// Case: Expression which results is sent as a new attribute
const values = [
{
name: 'p',
type: 'Number',
value: 52
},
{
name: 'h',
type: 'Percentage',
value: '12'
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/entities?options=upsert,flowControl',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin2.json'
)
)
.reply(204);
});
it('should calculate them and add them to the payload', function (done) {
iotAgentLib.update('ws1', 'WeatherStation', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When an expression with multiple variables with numbers arrive', function () {
// Case: Update for integer and string attributes with expression
const values = [
{
name: 'p25',
type: 'Number',
value: 52
},
{
name: 'h',
type: 'percentage',
value: '12'
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/entities?options=upsert,flowControl',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin4.json'
)
)
.reply(204);
});
it('should calculate it and add it to the payload', function (done) {
iotAgentLib.update('ws1', 'WeatherStationMultiple', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When an update comes for attributes without expressions and type integer', function () {
// Case: Update for an integer attribute without expression
const values = [
{
name: 'e',
type: 'Number',
value: 52
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/entities?options=upsert,flowControl',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin11.json'
)
)
.reply(204);
});
it('should apply the expression before sending the values', function (done) {
iotAgentLib.update('light1', 'Light', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When an update comes for attributes with numeric expressions and type integer', function () {
// Case: Update for an integer attribute with arithmetic expression
const values = [
{
name: 'p',
type: 'Number',
value: 52
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/entities?options=upsert,flowControl',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin1.json'
)
)
.reply(204);
});
it('should apply the expression before sending the values', function (done) {
iotAgentLib.update('ws1', 'WeatherStation', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When an update comes for attributes without expressions and type float', function () {
// Case: Update for a Float attribute without expressions
const values = [
{
name: 'e',
type: 'Number',
value: 0.44
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/entities?options=upsert,flowControl',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin3.json'
)
)
.reply(204);
});
it('should apply the expression before sending the values', function (done) {
iotAgentLib.update('light1', 'Light', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When an update comes for attributes with numeric expressions and type float', function () {
// Case: Update for a Float attribute with arithmetic expression
const values = [
{
name: 'e',
type: 'Number',
value: 0.44
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/entities?options=upsert,flowControl',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin8.json'
)
)
.reply(204);
});
it('should apply the expression before sending the values', function (done) {
iotAgentLib.update('ws1', 'WeatherStation', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When an update comes for attributes without expressions and NULL type', function () {
// Case: Update for a Null attribute without expression
const values = [
{
name: 'a',
type: 'None',
value: null
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/entities?options=upsert,flowControl',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin5.json'
)
)
.reply(204);
});
it('should apply the expression before sending the values', function (done) {
iotAgentLib.update('light1', 'Light', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When an update comes for attributes without expressions and Boolean type', function () {
// Case: Update for a Boolean attribute without expression
const values = [
{
name: 'u',
type: 'Boolean',
value: true
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/entities?options=upsert,flowControl',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin9.json'
)
)
.reply(204);
});
it('should apply the expression before sending the values', function (done) {
iotAgentLib.update('light1', 'Light', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When an update comes for attributes without expressions and Object type', function () {
// Case: Update for a JSON document attribute without expression
const values = [
{
name: 'm',
type: 'Object',
value: { name: 'Manufacturer1', VAT: 'U12345678' }
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/entities?options=upsert,flowControl',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin6.json'
)
)
.reply(204);
});
it('should apply the expression before sending the values', function (done) {
iotAgentLib.update('light1', 'Light', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When an update comes for attributes without expressions and Object type', function () {
// Case: Update for a JSON array attribute without expression
const values = [
{
name: 'r',
type: 'Object',
value: ['v0.1', 'v0.2', 'v0.3']
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/entities?options=upsert,flowControl',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin7.json'
)
)
.reply(204);
});
it('should apply the expression before sending the values', function (done) {
iotAgentLib.update('light1', 'Light', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When there are expressions including other attributes and they are not updated', function () {
const values = [
{
name: 'x',
type: 'Number',
value: 0.44
}
];
beforeEach(function () {
nock.cleanAll();
});
it('should apply the expression before sending the values', function (done) {
iotAgentLib.update('light1', 'Light', '', values, function (error) {
should.not.exist(error);
done();
});
});
});
describe('When there are expressions including other attributes and they are updated', function () {
const values = [
{
name: 'p',
type: 'Number',
value: 10
}
];
beforeEach(function () {
nock.cleanAll();
contextBrokerMock = nock('http://192.168.1.1:1026')
.matchHeader('fiware-service', 'smartgondor')
.matchHeader('fiware-servicepath', 'gardens')
.post(
'/v2/entities?options=upsert,flowControl',
utils.readExampleFile(
'./test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin13.json'
)
)
.reply(204);
});
it('should apply the expression before sending the values', function (done) {
iotAgentLib.update('light1', 'Light', '', values, function (error) {
should.not.exist(error);
contextBrokerMock.done();
done();
});
});
});
describe('When there are expressions including other attributes and they are updated (overriding situation)', function () {
const values = [
{
name: 'x',
type: 'Number',
value: 0.44
},
{
name: 'p',
type: 'Numbe