Home > database >  how to make jest wait for async code to be executed in my server code?
how to make jest wait for async code to be executed in my server code?

Time:07-16

i'm new to test driven development and i'm trying to test certain api's but the the async code in my server just throwing errors and it really works but i dont know what happens i have a code similar to this one exactly for my sequelize code , and this is my server code

require("dotenv").config();
const express = require("express");
const app = express();
const sequelize = require("./sequelize/index");
const cookieParser = require("cookie-parser");
const jwt = require("jsonwebtoken");

const taskRoutes = require("./routes/task");
const userRoutes = require("./routes/user");
const authRoutes = require("./routes/auth");

const {
  catchErrors,
  checkIfUserIsAuthenticated,
  getUserAuthorization,
} = require("./middleware/index");

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());

app.use("/api/v1/auth", authRoutes);
app.use(checkIfUserIsAuthenticated);
app.use(getUserAuthorization);
app.use("/api/v1/task", taskRoutes);
app.use("/api/v1/user", userRoutes);
app.use(catchErrors);

module.exports = app;

and it works like charm , and in the index.js i'm making sure that the database connected successfully , in the 2 methods below the error arises in the test i've written and i don't know why

index.js:
const Sequelize = require("sequelize");
const { applyAssociation } = require("./associations");
require("dotenv").config();

const sequelize = new Sequelize(
  process.env.DB_NAME,
  process.env.DB_USERNAME,
  process.env.DB_PASSWORD,
  {
    host: process.env.HOST,
    dialect: "mysql",
    logging: false,
  }
);

const modelDefiners = [
  require("./models/user"),
  require("./models/role"),
  require("./models/task"),
];

modelDefiners.map((modelDefiner) => {
  modelDefiner(sequelize);
});

(async () => { ====>>>>>>> this one
  try {
    await sequelize.authenticate();
    console.log("connected to the database successfully");
  } catch (error) {
    console.error(error);
  }
})();

applyAssociation(sequelize);

(async () => { =========>>>>>> and this one 
  try {
    await sequelize.sync({ alter: true });
    console.log("synced correctly with the table");
  } catch (error) {
    console.log(error);
  }
})();

module.exports = sequelize;

and this is my test : i know it's not well written but it's my first time writting it

const request = require("supertest");
const app = require("./server");

describe("user API", () => {
  it("GET /user --> array of users", async () => {
    await request(app)
      .get("/api/v1/user/")
      .expect("Content-Type", /json/)
      .expect(200)
      .then((response) => {
        expect(response.body).toEqual(
          expect.objectContaining({
            status: "success",
            response: expect.any(String),
          })
        );
      });
  });
  // it("GET /task/id --> certain task", () => {});
  // it("POST /task --> create task", () => {});
});

and this is the error that arises :

 FAIL  ./server.test.js
  user API
    ✕ GET /user --> array of users (47 ms)

  ● user API › GET /user --> array of users

    expected 200 "OK", got 403 "Forbidden"

       7 |       .get("/api/v1/user/")
       8 |       .expect("Content-Type", /json/)
    >  9 |       .expect(200)
         |        ^
      10 |       .then((response) => {
      11 |         expect(response.body).toEqual(
      12 |           expect.objectContaining({

      at Object.expect (server.test.js:9:8)
      ----
      at Test._assertStatus (node_modules/supertest/lib/test.js:252:14)
      at node_modules/supertest/lib/test.js:306:17
      at Test._assertFunction (node_modules/supertest/lib/test.js:285:13)
      at Test.assert (node_modules/supertest/lib/test.js:164:23)
      at Server.localAssert (node_modules/supertest/lib/test.js:120:14)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        0.94 s, estimated 1 s
Ran all test suites.

ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From server.test.js.

      at ClientHandshake.handshakeResult (node_modules/mysql2/lib/commands/client_handshake.js:150:26)
      at ClientHandshake.execute (node_modules/mysql2/lib/commands/command.js:45:22)
      at Connection.handlePacket (node_modules/mysql2/lib/connection.js:456:32)
      at PacketParser.onPacket (node_modules/mysql2/lib/connection.js:85:12)
      at PacketParser.executeStart (node_modules/mysql2/lib/packet_parser.js:75:16)
  console.error
    ConnectionError [SequelizeConnectionError]: authSwitch.authSwitchRequest is not a function
        at ConnectionManager.connect (/home/esraa/task/TodoApp/backend/node_modules/sequelize/src/dialects/mysql/connection-manager.js:126:17)
        at processTicksAndRejections (node:internal/process/task_queues:96:5)
        at ConnectionManager._connect (/home/esraa/task/TodoApp/backend/node_modules/sequelize/src/dialects/abstract/connection-manager.js:326:24)
        at /home/esraa/task/TodoApp/backend/node_modules/sequelize/src/dialects/abstract/connection-manager.js:250:32 {
      parent: TypeError: authSwitch.authSwitchRequest is not a function
          at ClientHandshake.handshakeResult (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/commands/client_handshake.js:155:22)
          at ClientHandshake.execute (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/commands/command.js:45:22)
          at Connection.handlePacket (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/connection.js:456:32)
          at PacketParser.onPacket (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/connection.js:85:12)
          at PacketParser.executeStart (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/packet_parser.js:75:16)
          at Socket.<anonymous> (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/connection.js:92:25)
          at Socket.emit (node:events:527:28)
          at addChunk (node:internal/streams/readable:315:12)
          at readableAddChunk (node:internal/streams/readable:289:9)
          at Socket.Readable.push (node:internal/streams/readable:228:10) {
        code: 'AUTH_SWITCH_PLUGIN_ERROR',
        fatal: true
      },
      original: TypeError: authSwitch.authSwitchRequest is not a function
          at ClientHandshake.handshakeResult (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/commands/client_handshake.js:155:22)
          at ClientHandshake.execute (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/commands/command.js:45:22)
          at Connection.handlePacket (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/connection.js:456:32)
          at PacketParser.onPacket (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/connection.js:85:12)
          at PacketParser.executeStart (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/packet_parser.js:75:16)
          at Socket.<anonymous> (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/connection.js:92:25)
          at Socket.emit (node:events:527:28)
          at addChunk (node:internal/streams/readable:315:12)
          at readableAddChunk (node:internal/streams/readable:289:9)
          at Socket.Readable.push (node:internal/streams/readable:228:10) {
        code: 'AUTH_SWITCH_PLUGIN_ERROR',
        fatal: true
      }
    }

      22 | modelDefiners.map((modelDefiner) => {
      23 |   modelDefiner(sequelize);
    > 24 | });
         |             ^
      25 |
      26 | (async () => {
      27 |   try {

      at sequelize/index.js:24:13


  ●  Cannot log after tests are done. Did you forget to wait for something async in your test?
    Attempted to log "ConnectionError [SequelizeConnectionError]: authSwitch.authSwitchRequest is not a function
        at ConnectionManager.connect (/home/esraa/task/TodoApp/backend/node_modules/sequelize/src/dialects/mysql/connection-manager.js:126:17)
        at processTicksAndRejections (node:internal/process/task_queues:96:5)
        at ConnectionManager._connect (/home/esraa/task/TodoApp/backend/node_modules/sequelize/src/dialects/abstract/connection-manager.js:326:24)
        at /home/esraa/task/TodoApp/backend/node_modules/sequelize/src/dialects/abstract/connection-manager.js:250:32 {
      parent: TypeError: authSwitch.authSwitchRequest is not a function
          at ClientHandshake.handshakeResult (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/commands/client_handshake.js:155:22)
          at ClientHandshake.execute (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/commands/command.js:45:22)
          at Connection.handlePacket (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/connection.js:456:32)
          at PacketParser.onPacket (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/connection.js:85:12)
          at PacketParser.executeStart (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/packet_parser.js:75:16)
          at Socket.<anonymous> (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/connection.js:92:25)
          at Socket.emit (node:events:527:28)
          at addChunk (node:internal/streams/readable:315:12)
          at readableAddChunk (node:internal/streams/readable:289:9)
          at Socket.Readable.push (node:internal/streams/readable:228:10) {
        code: 'AUTH_SWITCH_PLUGIN_ERROR',
        fatal: true
      },
      original: TypeError: authSwitch.authSwitchRequest is not a function
          at ClientHandshake.handshakeResult (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/commands/client_handshake.js:155:22)
          at ClientHandshake.execute (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/commands/command.js:45:22)
          at Connection.handlePacket (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/connection.js:456:32)
          at PacketParser.onPacket (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/connection.js:85:12)
          at PacketParser.executeStart (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/packet_parser.js:75:16)
          at Socket.<anonymous> (/home/esraa/task/TodoApp/backend/node_modules/mysql2/lib/connection.js:92:25)
          at Socket.emit (node:events:527:28)
          at addChunk (node:internal/streams/readable:315:12)
          at readableAddChunk (node:internal/streams/readable:289:9)
          at Socket.Readable.push (node:internal/streams/readable:228:10) {
        code: 'AUTH_SWITCH_PLUGIN_ERROR',
        fatal: true
      }
    }".

      35 | applyAssociation(sequelize);
      36 |
    > 37 | (async () => {
         |             ^
      38 |   try {
      39 |     await sequelize.sync({ alter: true });
      40 |     console.log("synced correctly with the table");

      at ConnectionManager.connect (node_modules/sequelize/src/dialects/mysql/connection-manager.js:126:17)
      at ConnectionManager._connect (node_modules/sequelize/src/dialects/abstract/connection-manager.js:326:24)
      at node_modules/sequelize/src/dialects/abstract/connection-manager.js:250:32 {
        parent: TypeError: authSwitch.authSwitchRequest is not a function
      at ClientHandshake.handshakeResult (node_modules/mysql2/lib/commands/client_handshake.js:155:22)
      at ClientHandshake.execute (node_modules/mysql2/lib/commands/command.js:45:22)
      at Connection.handlePacket (node_modules/mysql2/lib/connection.js:456:32)
      at PacketParser.onPacket (node_modules/mysql2/lib/connection.js:85:12)
      at PacketParser.executeStart (node_modules/mysql2/lib/packet_parser.js:75:16)
      at Socket.<anonymous> (node_modules/mysql2/lib/connection.js:92:25)
      at Socket.Readable.push (node:internal/streams/readable:228:10) {
          code: 'AUTH_SWITCH_PLUGIN_ERROR',
          fatal: true
        },
        original: TypeError: authSwitch.authSwitchRequest is not a function
      at ClientHandshake.handshakeResult (node_modules/mysql2/lib/commands/client_handshake.js:155:22)
      at ClientHandshake.execute (node_modules/mysql2/lib/commands/command.js:45:22)
      at Connection.handlePacket (node_modules/mysql2/lib/connection.js:456:32)
      at PacketParser.onPacket (node_modules/mysql2/lib/connection.js:85:12)
      at PacketParser.executeStart (node_modules/mysql2/lib/packet_parser.js:75:16)
      at Socket.<anonymous> (node_modules/mysql2/lib/connection.js:92:25)
      at Socket.Readable.push (node:internal/streams/readable:228:10) {
          code: 'AUTH_SWITCH_PLUGIN_ERROR',
          fatal: true
        }
      }".
      at console.log (node_modules/@jest/console/build/CustomConsole.js:172:10)
      at sequelize/index.js:37:13

CodePudding user response:

It's an antipattern to have unchained promise that can't be awaited or handled. It's acceptable to have async IIFE, but primarily in main entry point with errors being handled.

This results in application server being unable to handle database requests within some time after it's started, and also in failed tests.

It can be initialization function instead of asynchronous side effects in sequelize/index.js:

exports.init = async () => {
  // no catch, can run promises in parallel with Promise.all
  await sequelize.authenticate();
  ...
}

Then it propagates to server.js:

exports.app = app;

exports.init = async () => {
  await sequelize.init();
}

Then it's finally called in main entry point:

(async () => {
  try {
    await server.init();
    server.app.listen(...);
  } catch (error) {
    console.error(error);
  }
})()

And in tests:

beforeAll(async () => {
  await server.init();
});
  • Related