From eaa6484dcab12947d15fcaa36bfdaae896577e9e Mon Sep 17 00:00:00 2001 From: VLE2FE Date: Mon, 27 Apr 2020 11:44:28 +0200 Subject: [PATCH] added test helper and rewrote tests --- .gitignore | 1 + src/helpers/test.ts | 88 ++++ src/routes/root.spec.ts | 180 ++----- src/routes/user.spec.ts | 1012 ++++++++++++++++----------------------- 4 files changed, 567 insertions(+), 714 deletions(-) create mode 100644 src/helpers/test.ts diff --git a/.gitignore b/.gitignore index 0a811ca..645d3a5 100644 --- a/.gitignore +++ b/.gitignore @@ -112,3 +112,4 @@ dist **/.idea/tasks.xml **/.idea/shelf **/.idea/*.iml +/tmp/ diff --git a/src/helpers/test.ts b/src/helpers/test.ts new file mode 100644 index 0000000..4a537aa --- /dev/null +++ b/src/helpers/test.ts @@ -0,0 +1,88 @@ +import supertest from 'supertest'; +import should from 'should/as-function'; +import db from "../db"; + + +export default class TestHelper { + public static auth = { + admin: {pass: 'Abc123!#', key: '5ea131671feb9c2ee0aafc9a'}, + janedoe: {pass: 'Xyz890*)', key: '5ea0450ed851c30a90e70899'} + } + public static res = { + 400: {status: 'Bad request'}, + 401: {status: 'Unauthorized'}, + 403: {status: 'Forbidden'}, + 404: {status: 'Not found'}, + } + + static before (done) { + process.env.port = '2999'; + process.env.NODE_ENV = 'test'; + db.connect('test', done); + } + + static beforeEach (server, done) { + delete require.cache[require.resolve('../index')]; // prevent loading from cache + server = require('../index'); + db.drop(err => { // reset database + if (err) return done(err); + db.loadJson(require('../test/db.json'), done); + }); + return server + } + + static afterEach (server, done) { + server.close(done); + } + + 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); + if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('key')) { + options.url += '?key=' + (this.auth.hasOwnProperty(options.auth.key)? this.auth[options.auth.key].key : options.auth.key); + } + switch (options.method) { + case 'get': + st = st.get(options.url) + break; + case 'post': + st = st.post(options.url) + break; + case 'put': + st = st.put(options.url) + break; + case 'delete': + st = st.delete(options.url) + break; + } + if (options.hasOwnProperty('req')) { + st = st.send(options.req); + } + if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('basic')) { + if (this.auth.hasOwnProperty(options.auth.basic)) { + st = st.auth(options.auth.basic, this.auth[options.auth.basic].pass) + } + else { + st = st.auth(options.auth.basic.name, options.auth.basic.pass) + } + } + st = st.expect('Content-type', /json/) + .expect(options.httpStatus); + if (options.hasOwnProperty('res')) { + return st.end((err, res) => { + if (err) return done (err); + should(res.body).be.eql(options.res); + done(); + }); + } + else if (this.res.hasOwnProperty(options.httpStatus) && options.default !== false) { + return st.end((err, res) => { + if (err) return done (err); + should(res.body).be.eql(this.res[options.httpStatus]); + done(); + }); + } + else { + return st; + } + } +} \ No newline at end of file diff --git a/src/routes/root.spec.ts b/src/routes/root.spec.ts index 9372259..25be1ba 100644 --- a/src/routes/root.spec.ts +++ b/src/routes/root.spec.ts @@ -1,145 +1,69 @@ -import supertest from 'supertest'; -import should from 'should/as-function'; -import db from '../db'; +import TestHelper from "../helpers/test"; -describe('GET /', () => { +describe('/', () => { let server; + before(done => TestHelper.before(done)); + beforeEach(done => server = TestHelper.beforeEach(server, done)); + afterEach(done => TestHelper.afterEach(server, done)); - before(done => { - process.env.port = '2999'; - process.env.NODE_ENV = 'test'; - db.connect('test', done); - }); - beforeEach(done => { - delete require.cache[require.resolve('../index')]; // prevent loading from cache - server = require('../index'); - db.drop(err => { // reset database - if (err) return done(err); - db.loadJson(require('../test/db.json'), done); + describe('GET /', () => { + it('returns the root message', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/', + httpStatus: 200, + res: {status: 'API server up and running!'} + }); }); }); - afterEach(done => { - server.close(done); - }); - it('returns the root message', done => { - supertest(server) - .get('/') - .expect('Content-type', /json/) - .expect(200) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'API server up and running!'}); - done(); + + describe('Unknown routes', () => { + it('return a 404 message', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/unknownroute', + httpStatus: 404 }); - }); -}); - -describe('Unknown routes', () => { - let server; - - before(done => { - db.connect('test', done); - }); - beforeEach(done => { - delete require.cache[require.resolve('../index')]; // prevent loading from cache - server = require('../index'); - db.drop(err => { // reset database - if (err) return done(err); - db.loadJson(require('../test/db.json'), done); }); }); - afterEach(done => { - server.close(done); - }); - it('return a 404 message', done => { - supertest(server) - .get('/unknownroute') - .expect(404) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Not found'}); - done(); + + describe('An unauthorized request', () => { + it('returns a 401 message', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/authorized', + httpStatus: 401 + }); + }); + it('does not work with correct username', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/authorized', + auth: {name: 'admin', pass: 'Abc123!!'}, + httpStatus: 401 }); - }); -}); - -describe('An unauthorized request', () => { - let server; - - before(done => { - db.connect('test', done); - }); - beforeEach(done => { - delete require.cache[require.resolve('../index')]; // prevent loading from cache - server = require('../index'); - db.drop(err => { // reset database - if (err) return done(err); - db.loadJson(require('../test/db.json'), done); }); }); - afterEach(done => { - server.close(done); - }); - it('returns a 401 message', done => { - supertest(server) - .get('/authorized') - .expect(401) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Unauthorized'}); - done(); - }); - }); - it('does not work with correct username', done => { - supertest(server) - .get('/authorized') - .auth('admin', 'Abc123!!') - .expect(401) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Unauthorized'}); - done(); - }); - }); -}); -describe('An authorized request', () => { - let server; - - before(done => { - db.connect('test', done); - }); - beforeEach(done => { - delete require.cache[require.resolve('../index')]; // prevent loading from cache - server = require('../index'); - db.drop(err => { // reset database - if (err) return done(err); - db.loadJson(require('../test/db.json'), done); + describe('An authorized request', () => { + it('works with an API key', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/authorized', + auth: {key: 'admin'}, + httpStatus: 200, + res: {status: 'Authorization successful', method: 'key'} + }); }); - }); - afterEach(done => { - server.close(done); - }); - it('works with an API key', done => { - supertest(server) - .get('/authorized?key=5ea131671feb9c2ee0aafc9a') - .expect(200) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Authorization successful', method: 'key'}); - done(); - }); - }); - it('works with basic auth', done => { - supertest(server) - .get('/authorized') - .auth('admin', 'Abc123!#') - .expect(200) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Authorization successful', method: 'basic'}); - done(); + it('works with basic auth', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/authorized', + auth: {basic: 'admin'}, + httpStatus: 200, + res: {status: 'Authorization successful', method: 'basic'} }); + }); }); }); \ No newline at end of file diff --git a/src/routes/user.spec.ts b/src/routes/user.spec.ts index 46860ac..8098d9c 100644 --- a/src/routes/user.spec.ts +++ b/src/routes/user.spec.ts @@ -1,36 +1,23 @@ -import supertest from 'supertest'; import should from 'should/as-function'; -import db from '../db'; import UserModel from '../models/user'; +import TestHelper from "../helpers/test"; -describe('GET /users', () => { +describe('/user', () => { let server; + before(done => TestHelper.before(done)); + beforeEach(done => server = TestHelper.beforeEach(server, done)); + afterEach(done => TestHelper.afterEach(server, done)); - before(done => { - process.env.port = '2999'; - process.env.NODE_ENV = 'test'; - db.connect('test', done); - }); - beforeEach(done => { - delete require.cache[require.resolve('../index')]; // prevent loading from cache - server = require('../index'); - db.drop(err => { // reset database - if (err) return done(err); - db.loadJson(require('../test/db.json'), done); - }); - }); - afterEach(done => { - server.close(done); - }); - it('returns all users', done => { - supertest(server) - .get('/users') - .auth('admin', 'Abc123!#') - .expect('Content-type', /json/) - .expect(200) - .end((err, res) => { - if (err) done (err); + describe('GET /users', () => { + it('returns all users', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/users', + auth: {basic: 'admin'}, + httpStatus: 200 + }).end((err, res) => { + if (err) return done(err); const json = require('../test/db.json'); should(res.body).have.lengthOf(json.collections.users.length); should(res.body).matchEach(user => { @@ -44,60 +31,34 @@ describe('GET /users', () => { }); done(); }); - }); - it('rejects requests from non-admins', done => { - supertest(server) - .get('/users') - .auth('janedoe', 'Xyz890*)') - .expect('Content-type', /json/) - .expect(403) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Forbidden'}); - done(); + }); + it('rejects requests from non-admins', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/users', + auth: {basic: 'janedoe'}, + httpStatus: 403 }); - }); - it('rejects requests from an admin API key', done => { - supertest(server) - .get('/users?key=5ea131671feb9c2ee0aafc9a') - .expect('Content-type', /json/) - .expect(401) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Unauthorized'}); - done(); + }); + it('rejects requests from an admin API key', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/users', + auth: {key: 'admin'}, + httpStatus: 401 }); - }); -}); - - -describe('GET /user/{name}', () => { - let server; - - before(done => { - process.env.port = '2999'; - process.env.NODE_ENV = 'test'; - db.connect('test', done); - }); - beforeEach(done => { - delete require.cache[require.resolve('../index')]; // prevent loading from cache - server = require('../index'); - db.drop(err => { // reset database - if (err) return done(err); - db.loadJson(require('../test/db.json'), done); }); }); - afterEach(done => { - server.close(done); - }); - it('returns own user details', done => { - supertest(server) - .get('/user') - .auth('janedoe', 'Xyz890*)') - .expect('Content-type', /json/) - .expect(200) - .end((err, res) => { - if (err) done (err); + + describe('GET /user/{name}', () => { + it('returns own user details', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/user', + auth: {basic: 'janedoe'}, + httpStatus: 200 + }).end((err, res) => { + if (err) return done (err); should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('email', 'jane.doe@bosch.com'); @@ -107,529 +68,408 @@ describe('GET /user/{name}', () => { should(res.body).have.property('device_name', 'Alpha I'); done(); }); - }); - it('returns other user details for admin', done => { - supertest(server) - .get('/user/janedoe') - .auth('admin', 'Abc123!#') - .expect('Content-type', /json/) - .expect(200) - .end((err, res) => { - if (err) done (err); - should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name'); - should(res.body).have.property('_id').be.type('string'); - should(res.body).have.property('email', 'jane.doe@bosch.com'); - should(res.body).have.property('name', 'janedoe'); - should(res.body).have.property('level', 'write'); - should(res.body).have.property('location', 'Rng'); - should(res.body).have.property('device_name', 'Alpha I'); - done(); + }); + it('returns other user details for admin', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/user/janedoe', + auth: {basic: 'admin'}, + httpStatus: 200 + }).end((err, res) => { + if (err) return done (err); + should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name'); + should(res.body).have.property('_id').be.type('string'); + should(res.body).have.property('email', 'jane.doe@bosch.com'); + should(res.body).have.property('name', 'janedoe'); + should(res.body).have.property('level', 'write'); + should(res.body).have.property('location', 'Rng'); + should(res.body).have.property('device_name', 'Alpha I'); + done(); + }); + }); + it('rejects requests from non-admins for another user', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/user/admin', + auth: {basic: 'janedoe'}, + httpStatus: 403 }); - }); - it('rejects requests from non-admins for another user', done => { - supertest(server) - .get('/user/admin') - .auth('janedoe', 'Xyz890*)') - .expect('Content-type', /json/) - .expect(403) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Forbidden'}); - done(); + }); + it('rejects requests from a user API key', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/user', + auth: {key: 'janedoe'}, + httpStatus: 401 }); - }); - it('rejects requests from a user API key', done => { - supertest(server) - .get('/user?key=5ea0450ed851c30a90e70899') - .expect('Content-type', /json/) - .expect(401) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Unauthorized'}); - done(); + }); + it('rejects requests from an admin API key', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/user/janedoe', + auth: {key: 'janedoe'}, + httpStatus: 401 }); - }); - it('rejects requests from an admin API key', done => { - supertest(server) - .get('/user/janedoe?key=5ea131671feb9c2ee0aafc9a') - .expect('Content-type', /json/) - .expect(401) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Unauthorized'}); - done(); + }); + it('returns 404 for an unknown user', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/user/unknown', + auth: {basic: 'admin'}, + httpStatus: 404 }); - }); - it('returns 404 for an unknown user', done => { - supertest(server) - .get('/user/unknown') - .auth('admin', 'Abc123!#') - .expect('Content-type', /json/) - .expect(404) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Not found'}); - done(); - }); - }); -}); - - -describe('PUT /user/{name}', () => { - let server; - - before(done => { - process.env.port = '2999'; - process.env.NODE_ENV = 'test'; - db.connect('test', done); - }); - beforeEach(done => { - delete require.cache[require.resolve('../index')]; // prevent loading from cache - server = require('../index'); - db.drop(err => { // reset database - if (err) return done(err); - db.loadJson(require('../test/db.json'), done); }); }); - afterEach(done => { - server.close(done); - }); - it('returns own user details', done => { - supertest(server) - .put('/user') - .send({}) - .auth('janedoe', 'Xyz890*)') - .expect('Content-type', /json/) - .expect(200) - .end((err, res) => { - if (err) done (err); - should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name'); - should(res.body).have.property('_id').be.type('string'); - should(res.body).have.property('email', 'jane.doe@bosch.com'); - should(res.body).have.property('name', 'janedoe'); - should(res.body).have.property('level', 'write'); - should(res.body).have.property('location', 'Rng'); - should(res.body).have.property('device_name', 'Alpha I'); - done(); - }); - }); - it('returns other user details for admin', done => { - supertest(server) - .put('/user/janedoe') - .send({}) - .auth('admin', 'Abc123!#') - .expect('Content-type', /json/) - .expect(200) - .end((err, res) => { - if (err) done (err); - should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name'); - should(res.body).have.property('_id').be.type('string'); - should(res.body).have.property('email', 'jane.doe@bosch.com'); - should(res.body).have.property('name', 'janedoe'); - should(res.body).have.property('level', 'write'); - should(res.body).have.property('location', 'Rng'); - should(res.body).have.property('device_name', 'Alpha I'); - done(); - }); - }); - it('changes user details as given', done => { - supertest(server) - .put('/user') - .auth('admin', 'Abc123!#') - .send({name: 'adminnew', email: 'adminnew@bosch.com', pass: 'Abc123##', location: 'Abt', device_name: 'test'}) - .expect(200) - .end(err => { - if (err) done (err); - UserModel.find({name: 'adminnew'}).lean().exec( 'find', (err, data) => { - if (err) return done(err); - should(data).have.lengthOf(1); - should(data[0]).have.only.keys('_id', 'name', 'pass', 'email', 'level', 'location', 'device_name', 'key', '__v'); - should(data[0]).have.property('_id'); - should(data[0]).have.property('name', 'adminnew'); - should(data[0]).have.property('email', 'adminnew@bosch.com'); - should(data[0]).have.property('pass').not.eql('Abc123##'); - should(data[0]).have.property('level', 'admin'); - should(data[0]).have.property('location', 'Abt'); - should(data[0]).have.property('device_name', 'test'); - done(); - }); - }); - }); - it('lets the admin change a user level', done => { - supertest(server) - .put('/user/janedoe') - .auth('admin', 'Abc123!#') - .send({level: 'read'}) - .expect(200) - .end(err => { - if (err) done (err); - UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => { - if (err) return done(err); - should(data).have.lengthOf(1); - should(data[0]).have.property('level', 'read'); - done(); - }); - }); - }); - it('does not change the level', done => { - supertest(server) - .put('/user') - .auth('janedoe', 'Xyz890*)') - .send({level: 'read'}) - .expect(400) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Invalid body format'}); - UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => { - if (err) return done(err); - should(data).have.lengthOf(1); - should(data[0]).have.property('level', 'write'); - done(); - }); - }); - }); - it('rejects a username already in use', done => { - supertest(server) - .put('/user') - .auth('admin', 'Abc123!#') - .send({name: 'janedoe'}) - .expect(400) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Username already taken'}); - UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => { - if (err) return done(err); - should(data).have.lengthOf(1); - done(); - }); - }); - }); - it('rejects invalid user details', done => { - supertest(server) - .put('/user') - .auth('admin', 'Abc123!#') - .send({email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', location: 44, device_name: 'Alpha II'}) - .expect(400) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Invalid body format'}); - done(); - }); - }); - it('rejects an invalid email address', done => { - supertest(server) - .put('/user') - .auth('admin', 'Abc123!#') - .send({email: 'john.doe'}) - .expect(400) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Invalid body format'}); - done(); - }); - }); - it('rejects an invalid password', done => { - supertest(server) - .put('/user') - .auth('admin', 'Abc123!#') - .send({pass: 'password'}) - .expect(400) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Invalid body format'}); - done(); - }); - }); - it('rejects requests from non-admins for another user', done => { - supertest(server) - .put('/user/admin') - .send({}) - .auth('janedoe', 'Xyz890*)') - .expect('Content-type', /json/) - .expect(403) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Forbidden'}); - done(); - }); - }); - it('rejects requests from a user API key', done => { - supertest(server) - .put('/user?key=5ea0450ed851c30a90e70899') - .send({}) - .expect('Content-type', /json/) - .expect(401) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Unauthorized'}); - done(); - }); - }); - it('rejects requests from an admin API key', done => { - supertest(server) - .put('/user/janedoe?key=5ea131671feb9c2ee0aafc9a') - .send({}) - .expect('Content-type', /json/) - .expect(401) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Unauthorized'}); - done(); - }); - }); - it('returns 404 for an unknown user', done => { - supertest(server) - .put('/user/unknown') - .auth('admin', 'Abc123!#') - .send({}) - .expect(404) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Not found'}); - done(); - }); - }); -}); - -describe('POST /user/new', () => { - let server; - - before(done => { - process.env.port = '2999'; - process.env.NODE_ENV = 'test'; - db.connect('test', done); - }); - beforeEach(done => { - delete require.cache[require.resolve('../index')]; // prevent loading from cache - server = require('../index'); - db.drop(err => { // reset database - if (err) return done(err); - db.loadJson(require('../test/db.json'), done); + describe('PUT /user/{name}', () => { + it('returns own user details', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user', + auth: {basic: 'janedoe'}, + httpStatus: 200, + req: {} + }).end((err, res) => { + if (err) return done (err); + should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name'); + should(res.body).have.property('_id').be.type('string'); + should(res.body).have.property('email', 'jane.doe@bosch.com'); + should(res.body).have.property('name', 'janedoe'); + should(res.body).have.property('level', 'write'); + should(res.body).have.property('location', 'Rng'); + should(res.body).have.property('device_name', 'Alpha I'); + done(); + }); }); - }); - afterEach(done => { - server.close(done); - }); - it('returns the added user data', done => { - supertest(server) - .post('/user/new') - .auth('admin', 'Abc123!#') - .send({email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'}) - .expect('Content-type', /json/) - .expect(200) - .end((err, res) => { - if (err) done (err); - should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name'); - should(res.body).have.property('_id').be.type('string'); - should(res.body).have.property('email', 'john.doe@bosch.com'); - should(res.body).have.property('name', 'johndoe'); - should(res.body).have.property('level', 'read'); - should(res.body).have.property('location', 'Rng'); - should(res.body).have.property('device_name', 'Alpha II'); - done(); - }); - }); - it('stores the data', done => { - supertest(server) - .post('/user/new') - .auth('admin', 'Abc123!#') - .send({email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'}) - .expect(200) - .end(err => { - if (err) done (err); - UserModel.find({name: 'johndoe'}).lean().exec( 'find', (err, data) => { - if (err) return done(err); - should(data).have.lengthOf(1); - should(data[0]).have.only.keys('_id', 'name', 'pass', 'email', 'level', 'location', 'device_name', 'key', '__v'); - should(data[0]).have.property('_id'); - should(data[0]).have.property('name', 'johndoe'); - should(data[0]).have.property('email', 'john.doe@bosch.com'); - should(data[0]).have.property('pass').not.eql('Abc123!#'); - should(data[0]).have.property('level', 'read'); - should(data[0]).have.property('location', 'Rng'); - should(data[0]).have.property('device_name', 'Alpha II'); + it('returns other user details for admin', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user/janedoe', + auth: {basic: 'admin'}, + httpStatus: 200, + req: {} + }).end((err, res) => { + if (err) return done (err); + should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name'); + should(res.body).have.property('_id').be.type('string'); + should(res.body).have.property('email', 'jane.doe@bosch.com'); + should(res.body).have.property('name', 'janedoe'); + should(res.body).have.property('level', 'write'); + should(res.body).have.property('location', 'Rng'); + should(res.body).have.property('device_name', 'Alpha I'); done(); }); - }); - }); - it('rejects a username already in use', done => { - supertest(server) - .post('/user/new') - .auth('admin', 'Abc123!#') - .send({email: 'j.doe@bosch.com', name: 'janedoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'}) - .expect(400) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Username already taken'}); - UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => { - if (err) return done(err); - should(data).have.lengthOf(1); - done(); - }); - }); - }); - it('rejects invalid user details', done => { - supertest(server) - .post('/user/new') - .auth('admin', 'Abc123!#') - .send({email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 44, device_name: 'Alpha II'}) - .expect(400) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Invalid body format'}); - done(); - }); - }); - it('rejects an invalid user level', done => { - supertest(server) - .post('/user/new') - .auth('admin', 'Abc123!#') - .send({email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'xxx', location: 'Rng', device_name: 'Alpha II'}) - .expect(400) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Invalid body format'}); - done(); - }); - }); - it('rejects an invalid email address', done => { - supertest(server) - .post('/user/new') - .auth('admin', 'Abc123!#') - .send({email: 'john.doe', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'}) - .expect(400) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Invalid body format'}); - done(); - }); - }); - it('rejects an invalid password', done => { - supertest(server) - .post('/user/new') - .auth('admin', 'Abc123!#') - .send({email: 'john.doe@bosch.com', name: 'johndoe', pass: 'password', level: 'read', location: 'Rng', device_name: 'Alpha II'}) - .expect(400) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Invalid body format'}); - done(); - }); - }); - it('rejects requests from non-admins', done => { - supertest(server) - .post('/user/new') - .auth('janedoe', 'Xyz890*)') - .send({email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'}) - .expect('Content-type', /json/) - .expect(403) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Forbidden'}); - done(); - }); - }); - it('rejects requests from an admin API key', done => { - supertest(server) - .post('/user/new?key=5ea131671feb9c2ee0aafc9a') - .send({email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'}) - .expect('Content-type', /json/) - .expect(401) - .end((err, res) => { - if (err) done (err); - should(res.body).be.eql({status: 'Unauthorized'}); - done(); - }); - }); -}); - - -describe('POST /user/passreset', () => { - let server; - - before(done => { - process.env.port = '2999'; - process.env.NODE_ENV = 'test'; - db.connect('test', done); - }); - beforeEach(done => { - delete require.cache[require.resolve('../index')]; // prevent loading from cache - server = require('../index'); - db.drop(err => { // reset database - if (err) return done(err); - db.loadJson(require('../test/db.json'), done); }); - }); - afterEach(done => { - server.close(done); - }); - it('returns the ok response', done => { - supertest(server) - .post('/user/passreset') - .send({ - email: 'jane.doe@bosch.com', - name: 'janedoe' - }) - .expect('Content-type', /json/) - .expect(200) - .end((err, res) => { - if (err) done(err); - should(res.body).be.eql({status: 'OK'}); - done(); - }); - }); - it('returns 404 for wrong username/email combo', done => { - supertest(server) - .post('/user/passreset') - .send({ - email: 'jane.doe@bosch.com', - name: 'admin' - }) - .expect('Content-type', /json/) - .expect(404) - .end((err, res) => { - if (err) done(err); - should(res.body).be.eql({status: 'Not found'}); - done(); - }); - }); - it('returns 404 for unknown username', done => { - supertest(server) - .post('/user/passreset') - .send({ - email: 'jane.doe@bosch.com', - name: 'admin' - }) - .expect('Content-type', /json/) - .expect(404) - .end((err, res) => { - if (err) done(err); - should(res.body).be.eql({status: 'Not found'}); - done(); - }); - }); - it('changes the user password', done => { - UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data: any) => { - if (err) return done(err); - const oldpass = data[0].pass; - supertest(server) - .post('/user/passreset') - .send({ - email: 'jane.doe@bosch.com', - name: 'janedoe' - }) - .expect('Content-type', /json/) - .expect(200) - .end((err, res) => { - if (err) done(err); - should(res.body).be.eql({status: 'OK'}); - UserModel.find({name: 'janedoe'}).lean().exec( (err, data: any) => { + it('changes user details as given', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user', + auth: {basic: 'admin'}, + httpStatus: 200, + req: {name: 'adminnew', email: 'adminnew@bosch.com', pass: 'Abc123##', location: 'Abt', device_name: 'test'} + }).end(err => { + if (err) return done (err); + UserModel.find({name: 'adminnew'}).lean().exec( 'find', (err, data) => { if (err) return done(err); - should(data[0].pass).not.eql(oldpass); + should(data).have.lengthOf(1); + should(data[0]).have.only.keys('_id', 'name', 'pass', 'email', 'level', 'location', 'device_name', 'key', '__v'); + should(data[0]).have.property('_id'); + should(data[0]).have.property('name', 'adminnew'); + should(data[0]).have.property('email', 'adminnew@bosch.com'); + should(data[0]).have.property('pass').not.eql('Abc123##'); + should(data[0]).have.property('level', 'admin'); + should(data[0]).have.property('location', 'Abt'); + should(data[0]).have.property('device_name', 'test'); done(); }); }); }); + it('lets the admin change a user level', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user/janedoe', + auth: {basic: 'admin'}, + httpStatus: 200, + req: {level: 'read'} + }).end(err => { + if (err) return done (err); + UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => { + if (err) return done(err); + should(data).have.lengthOf(1); + should(data[0]).have.property('level', 'read'); + done(); + }); + }); + }); + it('does not change the level', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user', + auth: {basic: 'janedoe'}, + httpStatus: 400, default: false, + req: {level: 'read'} + }).end((err, res) => { + if (err) return done (err); + should(res.body).be.eql({status: 'Invalid body format'}); + UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => { + if (err) return done(err); + should(data).have.lengthOf(1); + should(data[0]).have.property('level', 'write'); + done(); + }); + }); + }); + it('rejects a username already in use', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user', + auth: {basic: 'admin'}, + httpStatus: 400, default: false, + req: {name: 'janedoe'} + }).end((err, res) => { + if (err) return done (err); + should(res.body).be.eql({status: 'Username already taken'}); + UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => { + if (err) return done(err); + should(data).have.lengthOf(1); + done(); + }); + }); + }); + it('rejects invalid user details', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user', + auth: {basic: 'admin'}, + httpStatus: 400, + req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', location: 44, device_name: 'Alpha II'}, + res: {status: 'Invalid body format'} + }); + }); + it('rejects an invalid email address', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user', + auth: {basic: 'admin'}, + httpStatus: 400, + req: {email: 'john.doe'}, + res: {status: 'Invalid body format'} + }); + }); + it('rejects an invalid password', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user', + auth: {basic: 'admin'}, + httpStatus: 400, + req: {pass: 'password'}, + res: {status: 'Invalid body format'} + }); + }); + it('rejects requests from non-admins for another user', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user/admin', + auth: {basic: 'janedoe'}, + httpStatus: 403, + req: {} + }); + }); + it('rejects requests from a user API key', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user', + auth: {key: 'janedoe'}, + httpStatus: 401, + req: {} + }); + }); + it('rejects requests from an admin API key', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user/janedoe', + auth: {key: 'admin'}, + httpStatus: 401, + req: {} + }); + }); + it('returns 404 for an unknown user', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/user/unknown', + auth: {basic: 'admin'}, + httpStatus: 404, + req: {} + }); + }); }); -}); + + describe('POST /user/new', () => { + it('returns the added user data', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/user/new', + auth: {basic: 'admin'}, + httpStatus: 200, + req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'} + }).end((err, res) => { + if (err) return done (err); + should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name'); + should(res.body).have.property('_id').be.type('string'); + should(res.body).have.property('email', 'john.doe@bosch.com'); + should(res.body).have.property('name', 'johndoe'); + should(res.body).have.property('level', 'read'); + should(res.body).have.property('location', 'Rng'); + should(res.body).have.property('device_name', 'Alpha II'); + done(); + }); + }); + it('stores the data', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/user/new', + auth: {basic: 'admin'}, + httpStatus: 200, + req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'} + }).end(err => { + if (err) return done (err); + UserModel.find({name: 'johndoe'}).lean().exec( 'find', (err, data) => { + if (err) return done(err); + should(data).have.lengthOf(1); + should(data[0]).have.only.keys('_id', 'name', 'pass', 'email', 'level', 'location', 'device_name', 'key', '__v'); + should(data[0]).have.property('_id'); + should(data[0]).have.property('name', 'johndoe'); + should(data[0]).have.property('email', 'john.doe@bosch.com'); + should(data[0]).have.property('pass').not.eql('Abc123!#'); + should(data[0]).have.property('level', 'read'); + should(data[0]).have.property('location', 'Rng'); + should(data[0]).have.property('device_name', 'Alpha II'); + done(); + }); + }); + }); + it('rejects a username already in use', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/user/new', + auth: {basic: 'admin'}, + httpStatus: 400, default: false, + req: {email: 'j.doe@bosch.com', name: 'janedoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'} + }).end((err, res) => { + if (err) return done (err); + should(res.body).be.eql({status: 'Username already taken'}); + UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => { + if (err) return done(err); + should(data).have.lengthOf(1); + done(); + }); + }); + }); + it('rejects invalid user details', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/user/new', + auth: {basic: 'admin'}, + httpStatus: 400, + req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 44, device_name: 'Alpha II'}, + res: {status: 'Invalid body format'} + }); + }); + it('rejects an invalid user level', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/user/new', + auth: {basic: 'admin'}, + httpStatus: 400, + req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'xxx', location: 'Rng', device_name: 'Alpha II'}, + res: {status: 'Invalid body format'} + }); + }); + it('rejects an invalid email address', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/user/new', + auth: {basic: 'admin'}, + httpStatus: 400, + req: {email: 'john.doe', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'}, + res: {status: 'Invalid body format'} + }); + }); + it('rejects an invalid password', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/user/new', + auth: {basic: 'admin'}, + httpStatus: 400, + req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'password', level: 'read', location: 'Rng', device_name: 'Alpha II'}, + res: {status: 'Invalid body format'} + }); + }); + it('rejects requests from non-admins', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/user/new', + auth: {basic: 'janedoe'}, + httpStatus: 403, + req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'} + }); + }); + it('rejects requests from an admin API key', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/user/new', + auth: {key: 'admin'}, + httpStatus: 401, + req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'} + }); + }); + }); + + describe('POST /user/passreset', () => { + it('returns the ok response', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/user/passreset', + httpStatus: 200, + req: {email: 'jane.doe@bosch.com', name: 'janedoe'}, + res: {status: 'OK'} + }); + }); + it('returns 404 for wrong username/email combo', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/user/passreset', + httpStatus: 404, + req: {email: 'jane.doe@bosch.com', name: 'admin'} + }); + }); + it('returns 404 for unknown username', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/user/passreset', + httpStatus: 404, + req: {email: 'jane.doe@bosch.com', name: 'username'} + }); + }); + it('changes the user password', done => { + UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data: any) => { + if (err) return done(err); + const oldpass = data[0].pass; + TestHelper.request(server, done, { + method: 'post', + url: '/user/passreset', + httpStatus: 200, + req: {email: 'jane.doe@bosch.com', name: 'janedoe'} + }).end((err, res) => { + if (err) return done(err); + should(res.body).be.eql({status: 'OK'}); + UserModel.find({name: 'janedoe'}).lean().exec( (err, data: any) => { + if (err) return done(err); + should(data[0].pass).not.eql(oldpass); + done(); + }); + }); + }); + }); + }); +}); \ No newline at end of file