Archived
2

implemented code coverage

This commit is contained in:
VLE2FE 2020-05-28 11:47:51 +02:00
parent d004a01b69
commit 0ea28fa50a
12 changed files with 79 additions and 18 deletions

View File

@ -10,7 +10,7 @@
"start": "tsc && node dist/index.js || exit 1", "start": "tsc && node dist/index.js || exit 1",
"dev": "nodemon -e ts,yaml --exec \"npm run start\"", "dev": "nodemon -e ts,yaml --exec \"npm run start\"",
"loadDev": "node dist/test/loadDev.js", "loadDev": "node dist/test/loadDev.js",
"coverage": "nyc --reporter=html --reporter=tex mocha dist/**/**.spec.js" "coverage": "tsc && nyc --reporter=html --reporter=text mocha dist/**/**.spec.js --timeout 5000"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",

View File

@ -42,15 +42,18 @@ export default class db {
}); });
mongoose.connection.on('error', console.error.bind(console, 'connection error:')); mongoose.connection.on('error', console.error.bind(console, 'connection error:'));
mongoose.connection.on('disconnected', () => { // reset state on disconnect mongoose.connection.on('disconnected', () => { // reset state on disconnect
console.info('Database disconnected'); if (process.env.NODE_ENV !== 'test') { // Do not interfere with testing
this.state.db = 0; console.info('Database disconnected');
done(); this.state.db = 0;
}
}); });
process.on('SIGINT', () => { // close connection when app is terminated process.on('SIGINT', () => { // close connection when app is terminated
mongoose.connection.close(() => { if (!this.state.db) { // database still connected
console.info('Mongoose default connection disconnected through app termination'); mongoose.connection.close(() => {
process.exit(0); console.info('Mongoose default connection disconnected through app termination');
}); process.exit(0);
});
}
}); });
mongoose.connection.once('open', () => { mongoose.connection.once('open', () => {
mongoose.set('useFindAndModify', false); mongoose.set('useFindAndModify', false);
@ -60,6 +63,14 @@ export default class db {
}); });
} }
static disconnect (done) {
mongoose.connection.close(() => {
console.info(process.env.NODE_ENV === 'test' ? '' : `Disconnected from database`);
this.state.db = 0;
done();
});
}
static getState () { static getState () {
return this.state; return this.state;
} }

View File

@ -13,6 +13,7 @@ import db from './db';
// TODO: add multiple samples at once // TODO: add multiple samples at once
// TODO: coverage // TODO: coverage
// TODO: think about the display of deleted/new samples and validation in data and UI // TODO: think about the display of deleted/new samples and validation in data and UI
// TODO: improve error coverage
// tell if server is running in debug or production environment // tell if server is running in debug or production environment
console.info(process.env.NODE_ENV === 'production' ? '===== PRODUCTION =====' : process.env.NODE_ENV === 'test' ? '' :'===== DEVELOPMENT ====='); console.info(process.env.NODE_ENV === 'production' ? '===== PRODUCTION =====' : process.env.NODE_ENV === 'test' ? '' :'===== DEVELOPMENT =====');

View File

@ -12,6 +12,7 @@ describe('/material', () => {
before(done => TestHelper.before(done)); before(done => TestHelper.before(done));
beforeEach(done => server = TestHelper.beforeEach(server, done)); beforeEach(done => server = TestHelper.beforeEach(server, done));
afterEach(done => TestHelper.afterEach(server, done)); afterEach(done => TestHelper.afterEach(server, done));
after(done => TestHelper.after(done));
describe('GET /materials', () => { describe('GET /materials', () => {
it('returns all materials', done => { it('returns all materials', done => {
@ -146,7 +147,6 @@ describe('/material', () => {
} }
}); });
}); });
done();
}); });
}); });
it('rejects requests from a write user', done => { it('rejects requests from a write user', done => {

View File

@ -5,14 +5,17 @@ import globals from '../globals';
// TODO: restore measurements for m/a // TODO: restore measurements for m/a
// TODO: coverage!!!
describe('/measurement', () => { describe('/measurement', () => {
let server; let server;
before(done => TestHelper.before(done)); before(done => TestHelper.before(done));
beforeEach(done => server = TestHelper.beforeEach(server, done)); beforeEach(done => server = TestHelper.beforeEach(server, done));
afterEach(done => TestHelper.afterEach(server, done)); afterEach(done => TestHelper.afterEach(server, done));
after(done => TestHelper.after(done));
describe('GET /mesurement/{id}', () => { describe('GET /measurement/{id}', () => {
it('returns the right measurement', done => { it('returns the right measurement', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'get', method: 'get',

View File

@ -36,7 +36,7 @@ router.put('/measurement/' + IdValidate.parameter(), async (req, res, next) => {
const data = await MeasurementModel.findById(req.params.id).lean().exec().catch(err => {next(err);}) as any; const data = await MeasurementModel.findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
if (data instanceof Error) return; if (data instanceof Error) return;
if (!data) { if (!data) {
res.status(404).json({status: 'Not found'}); return res.status(404).json({status: 'Not found'});
} }
// add properties needed for sampleIdCheck // add properties needed for sampleIdCheck
@ -55,7 +55,6 @@ router.put('/measurement/' + IdValidate.parameter(), async (req, res, next) => {
if (!await templateCheck(measurement, 'change', res, next)) return; if (!await templateCheck(measurement, 'change', res, next)) return;
await MeasurementModel.findByIdAndUpdate(req.params.id, measurement, {new: true}).lean().exec((err, data) => { await MeasurementModel.findByIdAndUpdate(req.params.id, measurement, {new: true}).lean().exec((err, data) => {
if (err) return next(err); if (err) return next(err);
console.log(data);
res.json(MeasurementValidate.output(data)); res.json(MeasurementValidate.output(data));
}); });
}); });
@ -66,12 +65,12 @@ router.delete('/measurement/' + IdValidate.parameter(), (req, res, next) => {
MeasurementModel.findById(req.params.id).lean().exec(async (err, data) => { MeasurementModel.findById(req.params.id).lean().exec(async (err, data) => {
if (err) return next(err); if (err) return next(err);
if (!data) { if (!data) {
res.status(404).json({status: 'Not found'}); return res.status(404).json({status: 'Not found'});
} }
if (!await sampleIdCheck(data, req, res, next)) return; if (!await sampleIdCheck(data, req, res, next)) return;
await MeasurementModel.findByIdAndUpdate(req.params.id, {status:globals.status.deleted}).lean().exec(err => { await MeasurementModel.findByIdAndUpdate(req.params.id, {status:globals.status.deleted}).lean().exec(err => {
if (err) return next(err); if (err) return next(err);
res.json({status: 'OK'}); return res.json({status: 'OK'});
}); });
}); });
}); });
@ -89,7 +88,6 @@ router.post('/measurement/new', async (req, res, next) => {
measurement.status = 0; measurement.status = 0;
await new MeasurementModel(measurement).save((err, data) => { await new MeasurementModel(measurement).save((err, data) => {
if (err) return next(err); if (err) return next(err);
console.log(data);
res.json(MeasurementValidate.output(data.toObject())); res.json(MeasurementValidate.output(data.toObject()));
}); });
}); });

View File

@ -1,4 +1,5 @@
import TestHelper from "../test/helper"; import TestHelper from "../test/helper";
import db from '../db';
describe('/', () => { describe('/', () => {
@ -6,6 +7,7 @@ describe('/', () => {
before(done => TestHelper.before(done)); before(done => TestHelper.before(done));
beforeEach(done => server = TestHelper.beforeEach(server, done)); beforeEach(done => server = TestHelper.beforeEach(server, done));
afterEach(done => TestHelper.afterEach(server, done)); afterEach(done => TestHelper.afterEach(server, done));
after(done => TestHelper.after(done));
describe('GET /', () => { describe('GET /', () => {
it('returns the root message', done => { it('returns the root message', done => {
@ -40,7 +42,15 @@ describe('/', () => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'get', method: 'get',
url: '/authorized', url: '/authorized',
auth: {name: 'admin', pass: 'Abc123!!'}, auth: {basic: {name: 'admin', pass: 'Abc123!!'}},
httpStatus: 401
});
});
it('does not work with incorrect username', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/authorized',
auth: {basic: {name: 'adminxx', pass: 'Abc123!!'}},
httpStatus: 401 httpStatus: 401
}); });
}); });
@ -66,4 +76,32 @@ describe('/', () => {
}); });
}); });
}); });
describe('An invalid JSON body', () => {
it('is rejected', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/',
httpStatus: 400,
reqType: 'json',
req: '{"xxx"}',
res: {status: 'Invalid JSON body'}
});
});
});
describe('A not connected database', () => {
it('resolves to an 500 error', done => {
db.disconnect(() => {
TestHelper.request(server, done, {
method: 'get',
url: '/',
httpStatus: 500
});
});
});
});
// describe('API') // TODO not in production
}); });

View File

@ -19,6 +19,7 @@ describe('/sample', () => {
before(done => TestHelper.before(done)); before(done => TestHelper.before(done));
beforeEach(done => server = TestHelper.beforeEach(server, done)); beforeEach(done => server = TestHelper.beforeEach(server, done));
afterEach(done => TestHelper.afterEach(server, done)); afterEach(done => TestHelper.afterEach(server, done));
after(done => TestHelper.after(done));
describe('GET /samples', () => { describe('GET /samples', () => {
it('returns all samples', done => { it('returns all samples', done => {
@ -197,7 +198,7 @@ describe('/sample', () => {
}); });
}); });
it('returns a deleted sample for a maintain/admin user', done => { // TODO: make tests work it('returns a deleted sample for a maintain/admin user', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'get', method: 'get',
url: '/sample/400000000000000000000005', url: '/sample/400000000000000000000005',

View File

@ -13,6 +13,7 @@ describe('/template', () => {
before(done => TestHelper.before(done)); before(done => TestHelper.before(done));
beforeEach(done => server = TestHelper.beforeEach(server, done)); beforeEach(done => server = TestHelper.beforeEach(server, done));
afterEach(done => TestHelper.afterEach(server, done)); afterEach(done => TestHelper.afterEach(server, done));
after(done => TestHelper.after(done));
describe('/template/condition', () => { describe('/template/condition', () => {
describe('GET /template/conditions', () => { describe('GET /template/conditions', () => {

View File

@ -44,7 +44,7 @@ router.put('/template/:collection(measurement|condition)/' + IdValidate.paramete
const templateData = await model(req).findById(req.params.id).lean().exec().catch(err => {next(err);}) as any; const templateData = await model(req).findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
if (templateData instanceof Error) return; if (templateData instanceof Error) return;
if (!templateData) { if (!templateData) {
res.status(404).json({status: 'Not found'}); return res.status(404).json({status: 'Not found'});
} }
if (!_.isEqual(_.pick(templateData, _.keys(template)), template)) { // data was changed if (!_.isEqual(_.pick(templateData, _.keys(template)), template)) { // data was changed

View File

@ -9,6 +9,7 @@ describe('/user', () => {
before(done => TestHelper.before(done)); before(done => TestHelper.before(done));
beforeEach(done => server = TestHelper.beforeEach(server, done)); beforeEach(done => server = TestHelper.beforeEach(server, done));
afterEach(done => TestHelper.afterEach(server, done)); afterEach(done => TestHelper.afterEach(server, done));
after(done => TestHelper.after(done));
describe('GET /users', () => { describe('GET /users', () => {
it('returns all users', done => { it('returns all users', done => {

View File

@ -39,6 +39,10 @@ export default class TestHelper {
server.close(done); server.close(done);
} }
static after(done) {
db.disconnect(done);
}
static request (server, done, options) { // options in form: {method, url, auth: {key/basic: 'name' or 'key'/{name, pass}}, httpStatus, req, res} static request (server, done, options) { // options in form: {method, url, auth: {key/basic: 'name' or 'key'/{name, pass}}, httpStatus, req, res}
let st = supertest(server); let st = supertest(server);
if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('key')) { // resolve API key if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('key')) { // resolve API key
@ -58,6 +62,9 @@ export default class TestHelper {
st = st.delete(options.url) st = st.delete(options.url)
break; break;
} }
if (options.hasOwnProperty('reqType')) { // request body
st = st.type(options.reqType);
}
if (options.hasOwnProperty('req')) { // request body if (options.hasOwnProperty('req')) { // request body
st = st.send(options.req); st = st.send(options.req);
} }