implemented first tests and basic functionality
This commit is contained in:
		@@ -1,19 +1,58 @@
 | 
			
		||||
import supertest from 'supertest';
 | 
			
		||||
import should from 'should/as-function';
 | 
			
		||||
import db from '../db';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
let server = supertest.agent('http://localhost:3000');
 | 
			
		||||
describe('/', () => {
 | 
			
		||||
  let server;
 | 
			
		||||
 | 
			
		||||
describe('Testing /', () => {
 | 
			
		||||
  it('returns the message object', done => {
 | 
			
		||||
    server
 | 
			
		||||
  before(done => {
 | 
			
		||||
    process.env.port = '2999';
 | 
			
		||||
    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 root message', done => {
 | 
			
		||||
    supertest(server)
 | 
			
		||||
      .get('/')
 | 
			
		||||
      .expect('Content-type', /json/)
 | 
			
		||||
      .expect(200)
 | 
			
		||||
      .end(function(err, res) {
 | 
			
		||||
        should(res.statusCode).equal(200);
 | 
			
		||||
      .expect(200, (err, res) => {
 | 
			
		||||
        should(res.body).be.eql({message: 'API server up and running!'});
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe('Testing 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('returns a 404 message', done => {
 | 
			
		||||
    supertest(server)
 | 
			
		||||
      .get('/unknownroute')
 | 
			
		||||
      .expect(404);
 | 
			
		||||
    done();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										75
									
								
								src/routes/user.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/routes/user.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
import supertest from 'supertest';
 | 
			
		||||
import should from 'should/as-function';
 | 
			
		||||
import db from '../db';
 | 
			
		||||
import userModel from '../models/user';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe('/user/new', () => {
 | 
			
		||||
  let server;
 | 
			
		||||
 | 
			
		||||
  before(done => {
 | 
			
		||||
    process.env.port = '2999';
 | 
			
		||||
    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 added user data', done => {
 | 
			
		||||
    supertest(server)
 | 
			
		||||
      .post('/user/new')
 | 
			
		||||
      .send({email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'})
 | 
			
		||||
      .expect('Content-type', /json/)
 | 
			
		||||
      .expect(200, (err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        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')
 | 
			
		||||
      .send({email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'})
 | 
			
		||||
      .expect(200, 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 => {
 | 
			
		||||
    supertest(server)
 | 
			
		||||
      .post('/user/new')
 | 
			
		||||
      .send({email: 'j.doe@bosch.com', name: 'janedoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'})
 | 
			
		||||
      .expect(400, 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);
 | 
			
		||||
          done();
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
  });  // TODO: authentication
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										31
									
								
								src/routes/user.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/routes/user.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
import express from 'express';
 | 
			
		||||
import mongoose from 'mongoose';
 | 
			
		||||
import bcrypt from 'bcryptjs';
 | 
			
		||||
import UserValidate from './validate/user';
 | 
			
		||||
import UserModel from '../models/user';
 | 
			
		||||
 | 
			
		||||
const router = express.Router();
 | 
			
		||||
 | 
			
		||||
router.get('/users', (req, res) => {
 | 
			
		||||
  res.json({message: 'users up and running!'});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
router.post('/user/new', (req, res, next) => {
 | 
			
		||||
  // validate input
 | 
			
		||||
  const {error, value: user} = UserValidate.input(req.body);
 | 
			
		||||
  if(error !== undefined) {
 | 
			
		||||
    res.status(400).json({status: 'Invalid body format'});
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  user.key = mongoose.Types.ObjectId();  // use object id as unique API key
 | 
			
		||||
  bcrypt.hash(user.pass, 10, (err, hash) => {  // password hashing
 | 
			
		||||
    user.pass = hash;
 | 
			
		||||
    new UserModel(user).save((err, data) => {  // store user
 | 
			
		||||
      if (err) next(err);
 | 
			
		||||
      res.json(UserValidate.output(data.toObject()));
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
module.exports = router;
 | 
			
		||||
							
								
								
									
										44
									
								
								src/routes/validate/user.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/routes/validate/user.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
import joi from '@hapi/joi';
 | 
			
		||||
import globals from "../../globals";
 | 
			
		||||
 | 
			
		||||
export default class UserValidate {  // validate input for user
 | 
			
		||||
  static input (data) {
 | 
			
		||||
    return joi.object({
 | 
			
		||||
      name: joi.string()
 | 
			
		||||
        .alphanum()
 | 
			
		||||
        .lowercase()
 | 
			
		||||
        .required(),
 | 
			
		||||
 | 
			
		||||
      email: joi.string()
 | 
			
		||||
        .email({minDomainSegments: 2})
 | 
			
		||||
        .lowercase()
 | 
			
		||||
        .required(),
 | 
			
		||||
 | 
			
		||||
      pass: joi.string()
 | 
			
		||||
        .pattern(new RegExp('^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!"#$%&\'()*+,-.\\/:;<=>?@[\\]^_`{|}~])(?=\\S+$).{8,}$'))
 | 
			
		||||
        .required(),
 | 
			
		||||
 | 
			
		||||
      level: joi.string()
 | 
			
		||||
        .valid(...globals.levels)
 | 
			
		||||
        .required(),
 | 
			
		||||
 | 
			
		||||
      location: joi.string()
 | 
			
		||||
        .alphanum()
 | 
			
		||||
        .required(),
 | 
			
		||||
 | 
			
		||||
      device_name: joi.string()
 | 
			
		||||
        .required()
 | 
			
		||||
    }).validate(data);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static output (data) {  // validate output from database for needed properties, strip everything else
 | 
			
		||||
    return joi.object({
 | 
			
		||||
      _id: joi.any(),
 | 
			
		||||
      name: joi.string(),
 | 
			
		||||
      email: joi.string(),
 | 
			
		||||
      level: joi.string(),
 | 
			
		||||
      location: joi.string(),
 | 
			
		||||
      device_name: joi.string()
 | 
			
		||||
    }).validate(data, {stripUnknown: true}).value;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user