diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 989d7880e..353820b12 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -1,3 +1,5 @@ +- Add: openmetrics-compatible `/metrics` endpoint in nortbound API (#1627) +- Remove: push-based stats (including stats section in config file) - Fix: service header to use uppercase in case of update and delete (#1528) - Fix: Allow to send to CB batch update for multimeasures for NGSI-LD (#1623) - Add: new JEXL transformations for including into an array keys that have a certain value: valuePicker and valuePickerMulti diff --git a/doc/admin.md b/doc/admin.md index 644e3ace7..41758343b 100644 --- a/doc/admin.md +++ b/doc/admin.md @@ -5,7 +5,6 @@ - [loglevel](#loglevel) - [contextBroker](#contextbroker) - [server](#server) - - [stats](#stats) - [authentication](#authentication) - [deviceRegistry](#deviceregistry) - [mongodb](#mongodb) @@ -159,17 +158,6 @@ support nulls or multi-attribute requests if they are encountered. } ``` -#### `stats` - -It configures the periodic collection of statistics. Use `interval` in milliseconds to set the time between stats -writings. - -```javascript -stats: { - interval: 100; -} -``` - #### `authentication` Stores the authentication data, for use in retrieving tokens for devices with a trust token (required in scenarios with diff --git a/doc/api.md b/doc/api.md index d3aa87aa7..29e5fdf84 100644 --- a/doc/api.md +++ b/doc/api.md @@ -69,6 +69,8 @@ - [Retrieve log level `GET /admin/log`](#retrieve-log-level-get-adminlog) - [About operations](#about-operations) - [List IoTA Information `GET /iot/about`](#list-iota-information-get-iotabout) + - [Metrics](#metrics) + - [Retrieve metrics `GET /metrics`](#retrieve-metrics-get-metrics) @@ -2302,6 +2304,57 @@ Example: } ``` +### Metrics + +The IoT Agent Library exposes a [openmetrics-compatible](https://github.com/OpenObservability/OpenMetrics) endpoint for +telemetry collectors to gather application statistics. + +#### Retrieve metrics `GET /metrics` + +_**Response code**_ + +- `200` `OK` if successful. +- `406` `Wrong Accept Header` If accept format is not supported. +- `500` `SERVER ERROR` if there was any error not contemplated above. + +_**Response body**_ + +Returns the current value of the server stats, + +- If `Accept` header contains `application/openmetrics-text`, the response has content-type + `application/openmetrics-text; version=1.0.0; charset=utf-8` +- Else, If `Accept` header is missing or supports `text/plain` (explicitly or by `*/*`) , the response has + content-type `text/plain; version=0.0.4; charset=utf-8` (legacy format for [prometheus](https://prometheus.io)) +- In any other case, returns an error message with `406` status. + +For the kind of metrics exposed by the application, the actual payload itself is completely the same for both +content-types, and follows the openmetrics specification, e.g: + +``` +# HELP deviceCreationRequests global metric for deviceCreationRequests +# TYPE deviceCreationRequests counter +deviceCreationRequests 0 +# HELP deviceRemovalRequests global metric for deviceRemovalRequests +# TYPE deviceRemovalRequests counter +deviceRemovalRequests 0 +# HELP measureRequests global metric for measureRequests +# TYPE measureRequests counter +measureRequests 0 +# HELP raiseAlarm global metric for raiseAlarm +# TYPE raiseAlarm counter +raiseAlarm 0 +# HELP releaseAlarm global metric for releaseAlarm +# TYPE releaseAlarm counter +releaseAlarm 0 +# HELP updateEntityRequestsOk global metric for updateEntityRequestsOk +# TYPE updateEntityRequestsOk counter +updateEntityRequestsOk 2 +# HELP updateEntityRequestsError global metric for updateEntityRequestsError +# TYPE updateEntityRequestsError counter +updateEntityRequestsError 5 +# EOF +``` + [1]: https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22longitude%22%3A%205%2C%0A%20%20%22latitude%22%3A%2037%2C%0A%20%20%22level%22%3A223%0A%7D&input=%7Bcoordinates%3A%20%5Blongitude%2Clatitude%5D%2C%20type%3A%20'Point'%7D&transforms=%7B%0A%7D [2]: diff --git a/doc/deprecated.md b/doc/deprecated.md index 2a7ccf476..754bf9914 100644 --- a/doc/deprecated.md +++ b/doc/deprecated.md @@ -25,6 +25,7 @@ A list of deprecated features and the version in which they were deprecated foll - Support to legacy expressions (finally removed in 3.2.0) - Bidirectinal pluging (finally removed in 3.4.0) - appendMode configuration (`IOTA_APPEND_MODE` env var) (finally removed in 3.4.0) +- `config.stats` section, and push-mode statistics. The use of Node.js v14 is highly recommended. @@ -57,3 +58,4 @@ The following table provides information about the last iotagent-node-lib versio | Support to Legacy Expressions | 3.1.0 | April 25th, 2023 | | bidirectional plugin | 3.3.0 | August 24th, 2023 | | appendMode configuration (`IOTA_APPEND_MODE` env var) | 3.3.0 | August 24th, 2023 | +| push-mode stats | 4.5.0 | June 11th, 2024 | diff --git a/doc/devel/development.md b/doc/devel/development.md index 1ee5c082e..92ee51344 100644 --- a/doc/devel/development.md +++ b/doc/devel/development.md @@ -227,18 +227,20 @@ npm run prettier:text ### Stats Registry -The library provides a mechanism for the periodic reporting of stats related to the library's work. In order to activate -the use of the periodic stats, it must be configured in the config file, as described in the -[Configuration](../admin.md#configuration) section. - -The Stats Registry holds two dictionaries, with the same set of stats. For each stat, one of the dictionaries holds the -historical global value and the other one stores the value since the last value reporting (or current value). +The library provides a mechanism for the collection of stats related to the library's work. The Stats Registry holds a +dictionary with the historical global value of each stat. The stats library currently stores only the following values: - **deviceCreationRequests**: number of Device Creation Requests that arrived to the API (no matter the result). - **deviceRemovalRequests**: number of Removal Device Requests that arrived to the API (no matter the result). - **measureRequests**: number of times the ngsiService.update() function has been invoked (no matter the result). +- **raiseAlarm**: number of times the alarmManagement.raise() function has been invoked. +- **releaseAlarm**: number of times the alarmManagement.release() function has been invoked. +- **updateEntityRequestsOk**: number of times the ngsiService.sendUpdateValue() function has been invoked + successfully. +- **updateEntityRequestsError**: number of times the ngsiService.sendUpdateValue() function has been invoked and + failed. More values will be added in the future to the library. The applications using the library can add values to the Stats Registry just by using the following function: @@ -248,7 +250,7 @@ iotagentLib.statsRegistry.add('statName', statIncrementalValue, callback); ``` The first time this function is invoked, it will add the new stat to the registry. Subsequent calls will add the value -to the specified stat both to the current and global measures. The stat will be cleared in each interval as usual. +to the specified stat. ### Alarm module diff --git a/lib/fiware-iotagent-lib.js b/lib/fiware-iotagent-lib.js index 90a847313..1c057e819 100644 --- a/lib/fiware-iotagent-lib.js +++ b/lib/fiware-iotagent-lib.js @@ -45,22 +45,19 @@ const context = { op: 'IoTAgentNGSI.Global' }; +/* eslint-disable-next-line no-unused-vars */ function activateStatLogs(newConfig, callback) { - if (newConfig.stats && newConfig.stats.interval) { - async.series( - [ - apply(statsRegistry.globalLoad, { - deviceCreationRequests: 0, - deviceRemovalRequests: 0, - measureRequests: 0 - }), - apply(statsRegistry.addTimerAction, statsRegistry.logStats) - ], - callback - ); - } else { - callback(); - } + async.series([ + apply(statsRegistry.globalLoad, { + deviceCreationRequests: 0, + deviceRemovalRequests: 0, + measureRequests: 0, + raiseAlarm: 0, + releaseAlarm: 0, + updateEntityRequestsOk: 0, + updateEntityRequestsError: 0 + }) + ], callback); } /** diff --git a/lib/model/dbConn.js b/lib/model/dbConn.js index 310318b4c..b58198909 100644 --- a/lib/model/dbConn.js +++ b/lib/model/dbConn.js @@ -190,10 +190,7 @@ function configureDb(callback) { /*jshint camelcase:false, validthis:true */ const currentConfig = config.getConfig(); - if ( - (currentConfig.deviceRegistry && currentConfig.deviceRegistry.type === 'mongodb') || - (currentConfig.stats && currentConfig.stats.persistence === true) - ) { + if (currentConfig.deviceRegistry && currentConfig.deviceRegistry.type === 'mongodb') { if (!currentConfig.mongodb || !currentConfig.mongodb.host) { logger.fatal(context, 'MONGODB-003: No host found for MongoDB driver.'); callback(new errors.BadConfiguration('No host found for MongoDB driver')); diff --git a/lib/services/common/alarmManagement.js b/lib/services/common/alarmManagement.js index fa6e18beb..fad0cc147 100644 --- a/lib/services/common/alarmManagement.js +++ b/lib/services/common/alarmManagement.js @@ -22,6 +22,7 @@ */ let alarmRepository = {}; +const statsRegistry = require('../stats/statsRegistry'); const logger = require('logops'); const context = { op: 'IoTAgentNGSI.Alarms' @@ -41,6 +42,7 @@ function raise(alarmName, description) { }; logger.error(context, 'Raising [%s]: %j', alarmName, description); + statsRegistry.add('raiseAlarm', 1, function () {}); } } @@ -53,6 +55,7 @@ function release(alarmName) { if (alarmRepository[alarmName]) { delete alarmRepository[alarmName]; logger.error(context, 'Releasing [%s]', alarmName); + statsRegistry.add('releaseAlarm', 1, function () {}); } } diff --git a/lib/services/ngsi/ngsiService.js b/lib/services/ngsi/ngsiService.js index e91799886..4b57c53c1 100644 --- a/lib/services/ngsi/ngsiService.js +++ b/lib/services/ngsi/ngsiService.js @@ -26,6 +26,7 @@ const async = require('async'); const apply = async.apply; +const statsRegistry = require('../stats/statsRegistry'); const intoTrans = require('../common/domain').intoTrans; const fillService = require('./../common/domain').fillService; const errors = require('../../errors'); @@ -67,7 +68,8 @@ function init() { * @param {String} token User token to identify against the PEP Proxies (optional). */ function sendUpdateValue(entityName, attributes, typeInformation, token, callback) { - entityHandler.sendUpdateValue(entityName, attributes, typeInformation, token, callback); + const newCallback = statsRegistry.withStats('updateEntityRequestsOk', 'updateEntityRequestsError', callback); + entityHandler.sendUpdateValue(entityName, attributes, typeInformation, token, newCallback); } /** diff --git a/lib/services/northBound/northboundServer.js b/lib/services/northBound/northboundServer.js index bd22879d2..c5f2d8b7d 100644 --- a/lib/services/northBound/northboundServer.js +++ b/lib/services/northBound/northboundServer.js @@ -33,6 +33,7 @@ const intoTrans = domainUtils.intoTrans; const deviceProvisioning = require('./deviceProvisioningServer'); const deviceUpdating = require('./deviceProvisioningServer'); const groupProvisioning = require('./deviceGroupAdministrationServer'); +const statsRegistry = require('../stats/statsRegistry'); const logger = require('logops'); const context = { op: 'IoTAgentNGSI.NorthboundServer' @@ -83,6 +84,7 @@ function start(config, callback) { northboundServer.router.get('/version', middlewares.retrieveVersion); northboundServer.router.put('/admin/log', middlewares.changeLogLevel); northboundServer.router.get('/admin/log', middlewares.getLogLevel); + northboundServer.router.get('/metrics', statsRegistry.openmetricsHandler); northboundServer.app.use(baseRoot, northboundServer.router); contextServer.loadContextRoutes(northboundServer.router); diff --git a/lib/services/stats/statsRegistry.js b/lib/services/stats/statsRegistry.js index 6900c9940..9bf1923d6 100644 --- a/lib/services/stats/statsRegistry.js +++ b/lib/services/stats/statsRegistry.js @@ -23,16 +23,9 @@ /* eslint-disable no-prototype-builtins */ -const async = require('async'); const _ = require('underscore'); -const apply = async.apply; const logger = require('logops'); -const config = require('../../commonConfig'); -const dbService = require('../../model/dbConn'); let globalStats = {}; -let currentStats = {}; -let timerActions = []; -let timerHandler; const statsContext = { op: 'IoTAgentNGSI.TimedStats' }; @@ -45,30 +38,14 @@ const statsContext = { * @param {Number} value Value to be added to the total. */ function add(key, value, callback) { - if (currentStats[key]) { - currentStats[key] += value; - } else { - currentStats[key] = value; - } - if (globalStats[key]) { globalStats[key] += value; } else { globalStats[key] = value; } - callback(null); } -/** - * Get the current value of a particular stat. - * - * @param {String} key Name of the stat to retrive. - */ -function getCurrent(key, callback) { - callback(null, currentStats[key]); -} - /** * Get the global value of the selected attribute. * @@ -85,112 +62,121 @@ function getAllGlobal(callback) { callback(null, globalStats); } -/** - * Get all the current stats currently stored in the repository. - */ -function getAllCurrent(callback) { - callback(null, currentStats); -} - /** * Loads the values passed as parameters into the global statistics repository. * * @param {Object} values Key-value map with the values to be load. */ function globalLoad(values, callback) { - globalStats = values; - currentStats = {}; - - for (const i in values) { - if (values.hasOwnProperty(i)) { - currentStats[i] = 0; - } - } - + globalStats = _.clone(values); callback(null); } /** - * Reset each of the current stats to value zero. + * Predefined http handler that returns current openmetrics data */ -function resetCurrent(callback) { - for (const i in currentStats) { - if (currentStats.hasOwnProperty(i)) { - currentStats[i] = 0; +/* eslint-disable-next-line no-unused-vars */ +function openmetricsHandler(req, res) { + // Content-Type: + // - For openmetrics collectors, it MUST BE 'application/openmetrics-text; version=1.0.0; charset=utf-8'. See: + // https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#overall-structure + // - For prometheus compatible collectors, it SHOULD BE 'text/plain; version=0.0.4; charset=utf-8'. See: + // https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md + let contentType = 'application/openmetrics-text; version=1.0.0; charset=utf-8'; + let version = null; + let charset = null; + // To identify openmetrics collectors, we need to parse the `Accept` header. + // An openmetrics-based collectors SHOULD use an `Accept` header such as: + // `Accept: application/openmetrics-text; version=1.0.0; charset=utf-8' + // See: https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/18913 + // + // WORKAROUND: express version 4 does not parse properly the openmetrics Accept header, + // it won't match the regular expressions supported by `express.accepts`. + // So we must parse these key-value pairs ourselves, and remove them from the + // header before handling it to `requests.accept`. + if (req.headers.accept) { + const parts = req.headers.accept.split(';'); + let unparsed = []; + for (let i = 0; i < parts.length; i++) { + const current = parts[i]; + const trimmed = current.trim(); + if (trimmed.startsWith('version=')) { + version = trimmed.substring(8); + } else if (trimmed.startsWith('charset=')) { + charset = trimmed.substring(8); + } else { + unparsed.push(current); + } + } + if (unparsed.length < parts.length) { + delete req.headers['accept']; + req.headers['accept'] = unparsed.join(';'); } } - - callback(); -} - -/** - * Executes all the stored timer actions when a timer click is received. - */ -function tickHandler() { - process.nextTick(apply(async.series, timerActions)); -} - -/** - * Adds a new timer action to the timerActions Array, activating the timer if it was not previously activated. - * - * @param {Function} handler Action to be executed. Should take two statistics objects and a callback. - */ -function addTimerAction(handler, callback) { - if (!timerHandler && config.getConfig().stats.interval) { - timerHandler = setInterval(tickHandler, config.getConfig().stats.interval); + // charset MUST BE utf-8 + if (charset && charset !== 'utf-8') { + logger.error(statsContext, 'Unsupported charset: %s', charset); + res.status(406).send('Unsupported charset'); + return; } - - timerActions.push(apply(handler, currentStats, globalStats)); - callback(); -} - -/** - * Clear the actions array and stop the timers. - */ -function clearTimers(callback) { - if (timerHandler) { - clearInterval(timerHandler); - timerHandler = undefined; + switch (req.accepts(['text/plain', 'application/openmetrics-text'])) { + case 'application/openmetrics-text': + // Version MUST BE 1.0.0 for openmetrics + if (version && version !== '1.0.0') { + logger.error(statsContext, 'Unsupported openmetrics version: %s', version); + res.status(406).send('Unsupported openmetrics version'); + return; + } + break; + case 'text/plain': + contentType = 'text/plain; version=0.0.4; charset=utf-8'; + break; + default: + logger.error(statsContext, 'Unsupported accept header: %s', req.headers.accept); + res.status(406).send('Unsupported accept header'); + return; } - - timerActions = []; - callback(); + const metrics = []; + for (const key in globalStats) { + if (globalStats.hasOwnProperty(key)) { + metrics.push('# HELP ' + key + ' global metric for ' + key); + metrics.push('# TYPE ' + key + ' counter'); + metrics.push(key + ' ' + globalStats[key]); + } + } + // Expositions MUST END WITH '#EOF' + // See https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md + metrics.push('# EOF'); + res.set('Content-Type', contentType); + res.status(200).send(metrics.join('\n')); } /** - * Predefined stats action that logs the stats to the standard log. + * Wraps a callback with stats, incrementing the given counters + * depending on the parameters passed to the callback: * - * @param {Object} currentValues Current stat values. - * @param {Object} globalValues Global stat values. - */ -function logStats(currentValues, globalValues, callback) { - logger.info(statsContext, 'Global stat values:\n%s\n', JSON.stringify(globalValues, null, 4)); - logger.info(statsContext, 'Current stat values:\n%s\n', JSON.stringify(currentValues, null, 4)); - - resetCurrent(callback); -} - -/** - * Predefined action that persists the current value of the stats in the MongoDb instance. + * - If the callback receives an error, the errCounter is incremented. + * - If the callback receives no error, the okCounter is incremented. * - * @param {Object} currentValues Current stat values. - * @param {Object} globalValues Global stat values. + * @param {String} okCounter Name of the counter to increment on success. + * @param {String} errCounter Name of the counter to increment on error. + * @param {Function} callback Callback to wrap. It must be a function that can + * expect any number of parameters, but the first one must + * be an indication of the error occured, if any. */ -function mongodbPersistence(currentValues, globalValues, callback) { - const statStamp = _.clone(globalValues); - - statStamp.timestamp = new Date().toISOString(); - dbService.db.collection('kpis').insertOne(statStamp, callback); +function withStats(okCounter, errCounter, callback) { + function accounting(...args) { + const counter = args.length > 0 && args[0] ? errCounter : okCounter; + add(counter, 1, function () { + callback(...args); + }); + } + return accounting; } exports.add = add; -exports.getCurrent = getCurrent; exports.getGlobal = getGlobal; exports.getAllGlobal = getAllGlobal; -exports.getAllCurrent = getAllCurrent; exports.globalLoad = globalLoad; -exports.resetCurrent = resetCurrent; -exports.clearTimers = clearTimers; -exports.addTimerAction = addTimerAction; -exports.logStats = logStats; -exports.mongodbPersistence = mongodbPersistence; +exports.withStats = withStats; +exports.openmetricsHandler = openmetricsHandler; diff --git a/test/unit/general/statistics-persistence_test.js b/test/unit/general/statistics-persistence_test.js deleted file mode 100644 index 9e47b8d97..000000000 --- a/test/unit/general/statistics-persistence_test.js +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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 statsService = require('../../../lib/services/stats/statsRegistry'); -const commonConfig = require('../../../lib/commonConfig'); -const iotAgentLib = require('../../../lib/fiware-iotagent-lib'); -const should = require('should'); -const async = require('async'); -const mongoUtils = require('../mongodb/mongoDBUtils'); -const iotAgentConfig = { - logLevel: 'FATAL', - contextBroker: { - host: '192.168.1.1', - port: '1026' - }, - server: { - port: 4041, - host: 'localhost', - baseRoot: '/' - }, - stats: { - interval: 50, - persistence: true - }, - mongodb: { - host: 'localhost', - port: '27017', - db: 'iotagent' - }, - types: {}, - service: 'smartgondor', - subservice: 'gardens', - providerUrl: 'http://smartgondor.com', - deviceRegistrationDuration: 'P1M' -}; -let iotAgentDb; -let oldConfig; - -describe('Statistics persistence service', function () { - function insertDummy(n, callback) { - iotAgentDb.collection('tests').insertOne({ test: 'test' }, function () { - callback(); - }); - } - - beforeEach(function (done) { - oldConfig = commonConfig.getConfig(); - - iotAgentLib.activate(iotAgentConfig, function (error) { - statsService.globalLoad({}, function () { - iotAgentDb = require('../../../lib/model/dbConn').db; - - async.times(10, insertDummy, function () { - done(); - }); - }); - }); - }); - - afterEach(function (done) { - iotAgentLib.deactivate(function (error) { - commonConfig.setConfig(oldConfig); - statsService.globalLoad({}, function () { - mongoUtils.cleanDbs(done); - }); - }); - }); - - describe('When a periodic persitence action is set', function () { - beforeEach(function (done) { - statsService.globalLoad( - { - stat1: 10 - }, - function () { - statsService.add('stat1', 5, done); - } - ); - }); - - it('should store all the records in the database', function (done) { - statsService.addTimerAction(statsService.mongodbPersistence, function () { - setTimeout(function () { - statsService.clearTimers(function () { - iotAgentDb - .collection('kpis') - .find({}) - .toArray(function (err, docs) { - should.not.exist(err); - should.exist(docs); - docs.length.should.be.above(2); - done(); - }); - }); - }, 200); - }); - }); - }); -}); diff --git a/test/unit/general/statistics-service_test.js b/test/unit/general/statistics-service_test.js index 8d594a6da..cf3ae351c 100644 --- a/test/unit/general/statistics-service_test.js +++ b/test/unit/general/statistics-service_test.js @@ -35,9 +35,6 @@ const iotAgentConfig = { host: 'localhost', baseRoot: '/' }, - stats: { - interval: 100 - }, types: {}, service: 'smartgondor', subservice: 'gardens', @@ -51,9 +48,7 @@ describe('Statistics service', function () { oldConfig = commonConfig.getConfig(); commonConfig.setConfig(iotAgentConfig); - statsService.globalLoad({}, function () { - statsService.clearTimers(done); - }); + statsService.globalLoad({}, done); }); afterEach(function (done) { @@ -74,16 +69,6 @@ describe('Statistics service', function () { ); }); - it('should appear the modified value in the getCurrent() statistics', function (done) { - statsService.add(statName, statValue, function () { - statsService.getCurrent(statName, function (error, value) { - should.not.exist(error); - should.exist(value); - value.should.equal(statValue); - done(); - }); - }); - }); it('should add the value to the global values', function (done) { statsService.add(statName, statValue, function () { statsService.getGlobal(statName, function (error, value) { @@ -119,62 +104,4 @@ describe('Statistics service', function () { }); }); }); - describe('When the current statistics are reset', function () { - beforeEach(function (done) { - statsService.add('statA', 42, function () { - statsService.add('statB', 52, done); - }); - }); - - it('should return a value of zero for any of the individual statistics', function (done) { - statsService.resetCurrent(function (error) { - should.not.exist(error); - - statsService.getAllCurrent(function (error, data) { - should.exist(data); - should.exist(data.statA); - should.exist(data.statB); - data.statA.should.equal(0); - data.statB.should.equal(0); - done(); - }); - }); - }); - }); - describe('When a new periodic stats action is set', function () { - let valueCurrent = 0; - let valueGlobal = 0; - let times = 0; - - beforeEach(function (done) { - statsService.globalLoad( - { - stat1: 10 - }, - function () { - statsService.add('stat1', 5, done); - } - ); - }); - - function mockedAction(current, global, callback) { - valueCurrent = current.stat1; - valueGlobal = global.stat1; - times++; - callback(); - } - - it('should be triggered with the periodicity stated in the config.stats.interval parameter', function (done) { - statsService.addTimerAction(mockedAction, function () { - setTimeout(function () { - statsService.clearTimers(function () { - valueCurrent.should.equal(5); - valueGlobal.should.equal(15); - times.should.equal(4); - done(); - }); - }, 480); - }); - }); - }); }); diff --git a/test/unit/mongodb/mongodb-connectionoptions-test.js b/test/unit/mongodb/mongodb-connectionoptions-test.js index 9fd056a29..439a8c9a9 100644 --- a/test/unit/mongodb/mongodb-connectionoptions-test.js +++ b/test/unit/mongodb/mongodb-connectionoptions-test.js @@ -41,9 +41,8 @@ const iotAgentConfig = { host: 'localhost', baseRoot: '/' }, - stats: { - interval: 50, - persistence: true + deviceRegistry: { + type: 'mongodb' }, types: {}, service: 'smartgondor',