vendredi 22 février 2019

Mocha / Chia testing not waiting for method chains to complete before pass / failing the test

So I've been working on this project of mine for some time now, but I can never really figure out how to get my testing to work. I've tried testing my API in a variety of different ways, however each time I changed things it just seemed to just make more problems. At this point I am stumped, I need some fresh eyes to check it out.

FYI the API actually works fine, I can call all of my routes and get the proper results with no issue, I've been using httpie to sort of "test" things without actually writing any tests.

So anyways, here is some of the code I've been running:

const chai = require('chai');
const expect = chai.expect;
const superagent = require('superagent');

const User = require('../model/User.js');

const url = process.env.API_URL;

const testUser = {
  email: 'exampleuser@test.com',
  password: '123'
};

const newUser = {
  email: 'newuser@test.com',
  password: '321'
};

describe('POST: /api/signup', () => {
  describe('with valid credentials', () => {
    afterEach(() => User.deleteOne({'email': testUser.email}));

    console.log(`${url}/api/signup`);
    it('should return a status code of 200', () => {
      superagent.post(`${url}/api/signup`)
        .send(testUser)
        .then((res) => {
          expect(res).to.be.an('object');
          expect(res.status).to.equal(200);
        })
        .catch((err) => console.error(err));
    });
  });
});

I've tried using done as well, it looked something like this:

describe('POST: /api/signup', () => {
  describe('with valid credentials', () => {
    afterEach((done) => {
      User.deleteOne({'email': testUser.email})
        .then(() => done())
        .catch((err) => done(err));
    });

    console.log(`${url}/api/signup`);
    it('should return a status code of 200', (done) => {
      superagent.post(`${url}/api/signup`)
        .send(testUser)
        .end((res, err) => {
          if(err) return done(err);
          expect(res).to.be.an('object');
          expect(res.status).to.equal(200);
        })
        .then(() => done())
        .catch((err) => console.error(err));
    });
  });
});

but every time I run it, you can see it finishes the test and moves on to the next case before everything is called and returned:

Backend-Portfolio:server.js running on port: 8000 +0ms

  USER_ROUTES
    POST: /api/signup
      with valid credentials
        1) should return a status code of 200
  Backend-Portfolio:user-router.js POST: /api/signup +0ms
        2) "after each" hook for "should return a status code of 200"
    GET: /api/login
      with valid credentials
  Backend-Portfolio:user-router.js POST: /api/signup +2s
  Backend-Portfolio:user-router.js setting up new user +50ms
  Backend-Portfolio:User.js generatePasswordHash:normal +0ms
  Backend-Portfolio:User.js generateToken +158ms
  Backend-Portfolio:User.js generateFindHash +1ms
  Backend-Portfolio:user-router.js setting up new user +462ms
  Backend-Portfolio:User.js generatePasswordHash:normal +295ms
  Backend-Portfolio:User.js generateToken +101ms
  Backend-Portfolio:User.js generateFindHash +0ms

Here is the route and the methods that are called if anyone is curious:

You can see here where the method chain is called, I turned this route into a promise just to try a different way of testing.

userRouter.post('/api/signup', jsonParser, (req, res) => {
  debug('POST: /api/signup');

  let password = req.body.password;
  delete req.body.password;

  return new Promise((resolve, reject) => {
    User.findOne({'email': req.body.email})
      .then((user) => {
        if(user){
          if(user.authenticated){
            // NOTE: maybe update all error codes and texts to be very specific
            reject(createError(400, 'this email is already used, please log in with your password'));
          }else{
            user.generatePasswordHash('normal', password)
              .then((user) => user.generateToken())
              .then((token) => resolve(res.json(token)))
              .catch((err) => reject(console.error(err)));
          }
        }
        else{
          debug('setting up new user');
          let user = new User({
            googlePermissions: {authenticated: false, password: null},
            facebookPermissions: {authenticated: false, password: null},
            twitterPermissions: {authenticated: false, password: null},
            authenticated: true,
            email: req.body.email
          });

          user.generatePasswordHash('normal', password)
            .then((user) => user.generateToken())
            .then((token) => resolve(res.json(token)))
            .catch((err) => reject(console.error(err)));
        }
      })
      .catch((err) => reject(console.error(err)));
  });
});

The rest of this is probably a bit unnecessary, but here ya go:

generatePasswordHash:

userSchema.methods.generatePasswordHash = function(type, password){
  debug(`generatePasswordHash:${type}`);

  return new Promise((resolve, reject) => {
    if(!password) reject(createError(400, 'no password was provided'));

    bcrypt.hash(password, 10, (err, hash) => {
      if(err) return reject(err);
      switch(type){
      case 'normal':
        this.password = hash;
        resolve(this);
        break;
      case 'googlePermissions':
        this.googlePermissions.password = hash;
        resolve(this);
        break;
      case 'facebookPermissions':
        this.facebookPermissions.password = hash;
        resolve(this);
        break;
      case 'twitterPermissions':
        this.twitterPermissions.password = hash;
        resolve(this);
        break;
      default:
        return reject(createError(400, `unrecognized password type: ${type}`));
      }
    });
  });
};

generateToken:

userSchema.methods.generateToken = function(){
  debug('generateToken');

  return new Promise((resolve, reject) => {
    this.generateFindHash()
      .then((findHash) => resolve(jsonwebtoken.sign({token: findHash}, process.env.APP_SECRET)))
      .catch((err) => reject(err));
  });
};

generateFindHash:

userSchema.methods.generateFindHash = function(){
  debug('generateFindHash');

  return new Promise((resolve, reject) => {
    this.findHash = crypto.randomBytes(32).toString('hex');
    this.save()
      .then(() => resolve(this.findHash))
      .catch((err) => reject(err));
  });
};

Thanks to anyone who's made it this far.

Aucun commentaire:

Enregistrer un commentaire