skip to Main Content

I try to use chai to make a request to my API on the /login route, but it cannot complete, I can use the same structure to do it for another route but it is not working in the best way.

const sinon = require('sinon');
const chai = require('chai');
const chaiHttp = require('chai-http');
const sinonChai = require('sinon-chai');
const app = require('../../src/app');
const { User } = require('../../src/models');
const { userMock } = require('../mocks');

const { expect } = chai;
chai.use(sinonChai);
chai.use(chaiHttp);

describe('login test', function () {
  describe('POST /login', function () {
    beforeEach(function () { sinon.restore(); });
    it('should return 200 and a token when the user is found', async function () {
      // arrange
      sinon.stub(User, 'findOne').resolves(userMock.users[0]);

      const bodyReq = {
        email: userMock.userId1.email,
        password: userMock.userId1.password,
      };

      // act

      const { status, body } = await chai.request(app)
        .post('/login')
        .send(bodyReq)

      // assert
  
      expect(status).to.equal(200);
      expect(body).to.have.property('token');
    });
  });
});
❯ npm run test:mocha

> [email protected] test:mocha
> mocha

(node:90966) [SEQUELIZE0002] DeprecationWarning: The logging-option should be either a function or false. Default: console.log
(Use `node --trace-deprecation ...` to show where the warning was created)


  login test
    POST /login
      1) should return 200 and a token when the user is found


  0 passing (2s)
  1 failing

  1) login test
       POST /login
         should return 200 and a token when the user is found:
     Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/home/julio/Trybe/modulo-back/2-periodo/sd-035-project-blogs-api/tests/integrations/Login.test.js)
      at listOnTimeout (node:internal/timers:569:17)
      at process.processTimers (node:internal/timers:512:7)

the warning is because of the configuration of the .mocharc.josn file:

{
  "spec": ["tests/**/*.test.js", "tests/*.test.js"],
  "exit": true
}

I already removed it "tests/*.test.js".

The project folders look like this:

/src/
--congig
--controllers
--middlewares
--migrations
--models
--routes
--seeds
--services
--utils
--app.js
--server.js
/tests/
--integrations/
----Login.test.js
--mocks/

If you want to investigate by looking at the code in more depth, here is the pullrequest:
https://github.com/juliomatiastrybe/blogs-api-sequelize/pull/4/commits/7501936be512f06600d970fc8ced895f69a0acf7

I used debug to try to see the error but I couldn’t, and even when making a request for another route, the middleware doesn’t work as it should, for example on routes that have token validation, I can’t validate it, even though the API is functional and can do the same process via thunderClient.

3

Answers


  1. Chosen as BEST ANSWER

    I did another test for another route but it doesn't work either, I passed a valid token, tested via ThunderClient, but it's as if the middleware didn't work correctly to validate the data.

    folder and file structure:

    /tests/
    --integrations
    ----Login.test.js
    ----Categories.test.js
    --mocks
    
    const sinon = require('sinon');
    const chai = require('chai');
    const chaiHttp = require('chai-http');
    const sinonChai = require('sinon-chai');
    const app = require('../../src/app');
    const { Category } = require('../../src/models');
    const { categoriesMock } = require('../mocks');
    
    const { expect } = chai;
    chai.use(sinonChai);
    chai.use(chaiHttp);
    
    describe.only('categories test', function () {
      describe('GET /categories', function () {
        beforeEach(function () { sinon.restore(); });
        it('should return 200 and a array by categories', async function () {
          // arrange
          sinon.stub(Category, 'findAll').resolves(categoriesMock);
    
          // act
          const tokenJWT = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZGlzcGxheU5hbWUiOiJMZXdpcyBIYW1pbHRvbiIsImVtYWlsIjoibGV3aXNoYW1pbHRvbkBnbWFpbC5jb20iLCJpbWFnZSI6Imh0dHBzOi8vdXBsb2FkLndpa2ltZWRpYS5vcmcvd2lraXBlZGlhL2NvbW1vbnMvMS8xOC9MZXdpc19IYW1pbHRvbl8yMDE2X01hbGF5c2lhXzIuanBnIiwiaWF0IjoxNzE0MTA3MDUxLCJleHAiOjE3MTQ3MTE4NTF9.3sfZcm_9lZZ6D4gzreIh7kaBYV-AfA0YAxhrFsiNYeU';
    
          const { status, body } = await chai.request(app)
            .get('/categories')
            .set('Authorization', `Bearer ${tokenJWT}`);
          
            console.log('status', status);
            console.log('body', body);
    
          // assert
      
          expect(status).to.equal(200);
          expect(body).to.be.an('array');
          expect(body).to.be.equal(categoriesMock);
        });
      });
    });
    

    terminal return:

    ❯ npm run test:mocha
    
    > [email protected] test:mocha
    > mocha
    
    (node:164327) [SEQUELIZE0002] DeprecationWarning: The logging-option should be either a function or false. Default: console.log
    (Use `node --trace-deprecation ...` to show where the warning was created)
    
    
      categories test
        GET /categories
    status 401
    body { message: 'Expired or invalid token' }
          1) should return 200 and a array by categories
    
    
      0 passing (38ms)
      1 failing
    
      1) categories test
           GET /categories
             should return 200 and a array by categories:
    
          AssertionError: expected 401 to equal 200
          + expected - actual
    
          -401
          +200
          
          at Context.<anonymous> (tests/integrations/Catergories.test.js:32:25)
          at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    

  2. It could be that there is a file with tests that Mocha is trying to use which it cannot find that lives in the folder called tests and is called SOMETHING dot (.) test dot js hence why it says there is no file with matching the pattern:

    tests/*.test.js
    

    So can you show us:

    1. Your code
    2. Your file structure and folders
    3. Also code for the other route which it does work

    It could be that a (THE) file has been renamed to a DIFFERENT pattern or simply does not exist anymore.

    Login or Signup to reply.
  3. I managed to resolve it.
    There were several issues to be resolved.

    The first thing is that when creating the JWT token, there was no default for secret when not provided an environment variable, so I needed to add it.

    With the auth.js file looking like this:

    const jwt = require('jsonwebtoken');
    
    const SECRET = process.env.JWT_SECRET || 'secret';
    
    const createToken = (payload) => {
      const token = jwt.sign(payload, SECRET, {
        algorithm: 'HS256',
        expiresIn: '7d',
      });
    
      return token;
    };
    
    const verify = (token) => {
      const payload = jwt.verify(token, SECRET);
    
      return payload;
    };
    
    module.exports = {
      createToken,
      verify,
    };
    

    In the loginSevice file, I would get both the properties returned from User sequelize and also the dataValues.

    const { User } = require('../../models');
    const httpName = require('../../utils/httpStatusName');
    const { createToken } = require('../../utils/auth');
    
    const login = async (email, password) => {
      const user = await User.findOne({ where: { email, password } });
    
      if (!user || user.password !== password) {
        return {
          status: httpName.BAD_REQUEST,
          data: {
            message: 'Invalid fields',
          },
        };
      }
    
      const { password: userPassword, ...userWithoutPassword } = user.dataValues;
    
      const token = createToken(userWithoutPassword);
    
      return { status: httpName.SUCCESSFUL, data: { token } };
    };
    
    module.exports = login;
    

    However, in my mock I was not passing this behavior, returning only the user properties, without the dataValues, so in the mock I left it like this:

    const userId1 = {
      id: 1,
      displayName: 'Lewis Hamilton',
      email: '[email protected]',
      password: '123456',
      image: 'https://upload.wikimedia.org/wikipedia/commons/1/18/Lewis_Hamilton_2016_Malaysia_2.jpg',
    };
    const userModelSequelize = {
      dataValues: userId1,
      ...userId1,
    }
    
    module.exports = {
      userId1,
      userModelSequelize,
    };
    

    And the request was not complete precisely because of these errors that were not captured, this reveals the impotence of always worrying about capturing these error.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search