Skip to content

Commit

Permalink
Merge pull request #1391 from BrendenHaskins/development
Browse files Browse the repository at this point in the history
Obivus Modes Testing, logfile uploads
  • Loading branch information
huss authored Dec 10, 2024
2 parents b65b7b5 + 3c8d2a5 commit bfe936c
Show file tree
Hide file tree
Showing 2 changed files with 238 additions and 5 deletions.
19 changes: 15 additions & 4 deletions src/server/routes/obvius.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ function handleStatus(req, res) {
/**
* Logs the Obvius request and sets the req.IP field to be the ip address.
*/
function obviusLog(req, res, next){
function obviusLog(req, res, next) {
// Log the IP of the requester
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
req.IP = ip;
Expand All @@ -111,7 +111,7 @@ function obviusLog(req, res, next){
/**
* Verifies an Obvius request via username and password.
*/
function verifyObviusUser(req, res, next){
function verifyObviusUser(req, res, next) {
// First we ensure that the password and username parameters are provided.
const password = req.param('password');
// TODO This is allowing for backwards compatibility if previous obvius meters are using the'email' parameter
Expand Down Expand Up @@ -157,6 +157,7 @@ router.all('/', obviusLog, verifyObviusUser, async (req, res) => {
return;
}
const conn = getConnection();
const loadLogfilePromises = [];
for (const fx of req.files) {
log.info(`Received ${fx.fieldname}: ${fx.originalname}`);
// Logfiles are always gzipped.
Expand All @@ -168,9 +169,19 @@ router.all('/', obviusLog, verifyObviusUser, async (req, res) => {
failure(req, res, `Unable to gunzip incoming buffer: ${err}`);
return;
}
loadLogfileToReadings(req.param('serialnumber'), ip, data, conn);
// The original code did not await for the Promise to finish. The new version
// allows the files to run in parallel (as before) but then wait for them all
// to finish before returning.
loadLogfilePromises.push(loadLogfileToReadings(req.param('serialnumber'), ip, data, conn));
}
success(req, res, 'Logfile Upload IS PROVISIONAL');
// TODO This version returns an error. Should check all usage to be sure it is properly handled.
Promise.all(loadLogfilePromises).then(() => {
success(req, res, 'Logfile Upload IS PROVISIONAL');
}).catch((err) => {
log.warn(`Logfile Upload had issues from ip: ${ip}`, err)
failure(req, res, 'Logfile Upload had issues');
});
// This return may not be needed.
return;
}

Expand Down
224 changes: 223 additions & 1 deletion src/server/test/web/obviusTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,24 @@

const { chai, mocha, expect, app, testDB, testUser } = require('../common');
const User = require('../../models/User');
const Configfile = require('../../models/obvius/Configfile');
const bcrypt = require('bcryptjs');
const { insertUnits } = require('../../util/insertData');
const Unit = require('../../models/Unit');
const Meter = require('../../models/Meter.js');
const { Console } = require('console');

//expected names and ids for obvius meters.
const expMeterNames = [
'mb-001.0', 'mb-001.1', 'mb-001.2', 'mb-001.3', 'mb-001.4', 'mb-001.5', 'mb-001.6', 'mb-001.7',
'mb-001.8', 'mb-001.9', 'mb-001.10', 'mb-001.11', 'mb-001.12', 'mb-001.13', 'mb-001.14', 'mb-001.15',
'mb-001.16', 'mb-001.17', 'mb-001.18', 'mb-001.19', 'mb-001.20', 'mb-001.21', 'mb-001.22', 'mb-001.23',
'mb-001.24', 'mb-001.25', 'mb-001.26', 'mb-001.27'
];

const expMeterIDs = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28
];

mocha.describe('Obvius API', () => {
mocha.describe('upload: ', () => {
Expand Down Expand Up @@ -58,5 +75,210 @@ mocha.describe('Obvius API', () => {
}
}
});
mocha.describe('obvius request modes', async () => {
mocha.beforeEach(async () => {
const conn = testDB.getConnection();
// The kWh unit is not used in all tests but easier to just put in.
const unitData = [
{
name: 'kWh',
identifier: '',
unitRepresent: Unit.unitRepresentType.QUANTITY,
secInRate: 3600,
typeOfUnit: Unit.unitType.UNIT,
suffix: '',
displayable: Unit.displayableType.ALL,
preferredDisplay: true,
note: 'OED created standard unit'
}
];
await insertUnits(unitData, false, conn);
});
mocha.it('should reject requests without a mode', async () => {
const password = 'password';
const hashedPassword = await bcrypt.hash(password, 10);
const obviusUser = new User(undefined, '[email protected]', hashedPassword, User.role.OBVIUS);
await obviusUser.insert(conn);
obviusUser.password = password;
const res = await chai.request(app).post('/api/obvius').send({ username: obviusUser.username, password: obviusUser.password });
//should respond with 406, not acceptable
expect(res).to.have.status(406);
//should also return expected message
expect(res.text).equals(`<pre>\nRequest must include mode parameter.\n</pre>\n`);
});
mocha.it('should accept status requests', async () => {
const password = 'password';
const hashedPassword = await bcrypt.hash(password, 10);
const obviusUser = new User(undefined, '[email protected]', hashedPassword, User.role.OBVIUS);
await obviusUser.insert(conn);
obviusUser.password = password;
const requestMode = 'STATUS';
const res = await chai.request(app).post('/api/obvius').send({ username: obviusUser.username, password: obviusUser.password, mode: requestMode });
//should respond with 200, success
expect(res).to.have.status(200);
//should also return expected message
expect(res.text).equals("<pre>\nSUCCESS\n</pre>\n");
});
mocha.it('should accept valid logfile uploads', async () => {
const password = 'password';
const hashedPassword = await bcrypt.hash(password, 10);
const obviusUser = new User(undefined, '[email protected]', hashedPassword, User.role.OBVIUS);
await obviusUser.insert(conn);
obviusUser.password = password;
const logfileRequestMode = 'LOGFILEUPLOAD';

// Adapted from ../obvius/README.md
const logfilePath = 'src/server/test/web/obvius/mb-001.log.gz';

//the upload of a logfile is the subject of the test
const res = await chai.request(app)
.post('/api/obvius')
.field('username', obviusUser.username)
.field('password', obviusUser.password)
.field('mode', logfileRequestMode)
.field('serialnumber', 'mb-001')
.attach('files', logfilePath);
//should respond with 200, success
expect(res).to.have.status(200);
//should also return expected message
expect(res.text).equals("<pre>\nSUCCESS\nLogfile Upload IS PROVISIONAL</pre>\n");

});
mocha.it('should accept valid config file uploads', async () => {
const password = 'password';
const hashedPassword = await bcrypt.hash(password, 10);
const obviusUser = new User(undefined, '[email protected]', hashedPassword, User.role.OBVIUS);
await obviusUser.insert(conn);
obviusUser.password = password;
const requestMode = 'CONFIGFILEUPLOAD';

// Adapted from ../obvius/README.md
const configFilePath = 'src/server/test/web/obvius/mb-001.ini.gz';

const res = await chai.request(app)
.post('/api/obvius')
.field('username', obviusUser.username)
.field('password', obviusUser.password)
.field('mode', requestMode)
.field('serialnumber', 'mb-001')
.field('modbusdevice', '1234')
.attach('files', configFilePath);

//should respond with 200, success
expect(res).to.have.status(200);
//should also return expected message
expect(res.text).equals("<pre>\nSUCCESS\nAcquired config log with (pseudo)filename mb-001-mb-1234.ini.</pre>\n");
});
mocha.it('should return accurate config file manifests', async () => {
const password = 'password';
const hashedPassword = await bcrypt.hash(password, 10);
const obviusUser = new User(undefined, '[email protected]', hashedPassword, User.role.OBVIUS);
await obviusUser.insert(conn);
obviusUser.password = password;
const uploadRequestMode = 'CONFIGFILEUPLOAD';
const manifestRequestMode = 'CONFIGFILEMANIFEST';
const serialStart = 'mb-';
const serialNumber = serialStart + '001';
const modbusDevice = '1234'

// Adapted from ../obvius/README.md
const configFilePath = 'src/server/test/web/obvius/mb-001.ini.gz';
const upload = await chai.request(app)
.post('/api/obvius')
.field('username', obviusUser.username)
.field('password', obviusUser.password)
.field('mode', uploadRequestMode)
.field('serialnumber', serialNumber)
.field('modbusdevice', modbusDevice)
.attach('files', configFilePath);

//logfile upload should respond with 200, success
expect(upload).to.have.status(200);

const res = await chai.request(app)
.post('/api/obvius')
.field('username', obviusUser.username)
.field('password', obviusUser.password)
.field('mode', manifestRequestMode);

//logfile request should respond with 200, success
expect(res).to.have.status(200);

//get "all" config files to compare to response
const allConfigfiles = await Configfile.getAll(conn);
let response = '';
for (f of allConfigfiles) {
response += `CONFIGFILE,${serialNumber}-${serialStart}${modbusDevice}.ini,${f.hash},${f.created.format('YYYY-MM-DD hh:mm:ss')}`;
}

//the third line of the response should be the config file
expect(res.text.split("\n")[2]).equals(response);

//config file uploads should create accurate meter objects
const allMeters = await Meter.getAll(conn);

//mb-001.ini should make meters equal to expMeterNames.length
expect(allMeters.length).to.equal(expMeterNames.length);

//these arrays should vary for different submeters
const meterNames = [];
const meterIDs = [];

//flags for meter fields (.type, .displayable, .enabled)
const allMetersAreObvius = true;
const allMetersAreNotDisplayable = true;
const allMetersAreNotEnabled = true;

for (const meter of allMeters) {
//populate arrays with varying values in ascending order
let currentName = meter.name;
let idx = currentName.split('.')[1];
meterNames[parseInt(idx)] = meter.name;
meterIDs[meter.id - 1] = meter.id;
//ensure each meter is obvius, not displayable, and not enabled
if (meter.type != 'obvius') {
allMetersAreObvius = false;
}
if (meter.displayable != false) {
allMetersAreNotDisplayable = false;
}
if (meter.enabled != false) {
allMetersAreNotEnabled = false;
}
}

//flags for comparison between expected arrays and actual arrays
let expectedNamesAreEqual = true;
let expectedIDsAreEqual = true;

//error message for more descriptive failures
let allErrorMessagesNames = "";
let allErrorMessagesIDs = "";


//both arrays should be contain the same sequence of values
for (let i = 0; i < expMeterNames.length; i++) {
if (expMeterNames[i] != meterNames[i]) {
expectedNamesAreEqual = false;
allErrorMessagesNames += "Meter failed name comparison, Expected: " + expMeterNames[i] + " Actual: " + meterNames[i] + "\n";
}

if (expMeterIDs[i] != meterIDs[i]) {
expectedIDsAreEqual = false;
allErrorMessagesIDs += "Meter failed ID comparison. Expected: " + expMeterIDs[i] + " Actual: " + meterIDs[i] + "\n";

}
}

//assertion for type, displayable, and enabled
expect(allMetersAreObvius).to.equal(true);
expect(allMetersAreNotDisplayable).to.equal(true);
expect(allMetersAreNotEnabled).to.equal(true);

//expected arrays should equal actual arrays
expect(expectedNamesAreEqual).to.equal(true, allErrorMessagesNames);
expect(expectedIDsAreEqual).to.equal(true, allErrorMessagesIDs);
});
});
});
});
});

0 comments on commit bfe936c

Please sign in to comment.