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();
});