formies/__tests__/unit/services/jwtService.test.js
Mohamad.Elsena a3236ae9d5 Refactor environment configuration for PostgreSQL and enhance application structure
- Updated `.env` and `.env.test` files to include PostgreSQL connection settings and Redis configuration.
- Migrated database from SQLite to PostgreSQL, updating relevant queries and connection logic.
- Enhanced error handling and logging throughout the application.
- Added new test utilities for PostgreSQL integration and updated user model methods.
- Introduced new routes for user authentication and form management, ensuring compatibility with the new database structure.
- Created login and registration views in EJS for user interaction.
2025-05-28 16:16:33 +02:00

127 lines
4.2 KiB
JavaScript

// __tests__/unit/services/jwtService.test.js
const jwtService = require("../../../src/services/jwtService"); // Adjust path
const User = require("../../../src/models/User"); // Adjust path
const jwt = require("jsonwebtoken");
jest.mock("../../../src/models/User"); // Mock the User model
describe("JWT Service", () => {
const mockUser = { id: 1, email: "test@example.com", role: "user" };
const originalJwtSecret = process.env.JWT_SECRET;
beforeAll(() => {
process.env.JWT_SECRET = "test-secret-for-jwt-service"; // Use a fixed secret for tests
});
afterAll(() => {
process.env.JWT_SECRET = originalJwtSecret; // Restore original
});
beforeEach(() => {
User.saveSession.mockClear();
User.isTokenBlacklisted.mockClear();
User.revokeSession.mockClear();
});
describe("generateAccessToken", () => {
it("should generate a valid access token and save session", async () => {
User.saveSession.mockResolvedValue(true);
const { token, expiresAt, jti } =
jwtService.generateAccessToken(mockUser);
expect(token).toBeDefined();
expect(jti).toBeDefined();
const decoded = jwt.verify(token, process.env.JWT_SECRET);
expect(decoded.sub).toBe(mockUser.id);
expect(decoded.type).toBe("access");
expect(decoded.jti).toBe(jti);
expect(User.saveSession).toHaveBeenCalledWith(
mockUser.id,
jti,
expiresAt,
undefined,
undefined
);
});
});
describe("generateRefreshToken", () => {
it("should generate a valid refresh token and save session", async () => {
User.saveSession.mockResolvedValue(true);
const { token } = jwtService.generateRefreshToken(mockUser);
const decoded = jwt.verify(token, process.env.JWT_SECRET);
expect(decoded.sub).toBe(mockUser.id);
expect(decoded.type).toBe("refresh");
});
});
describe("verifyToken", () => {
it("should verify a valid token", () => {
const { token } = jwtService.generateAccessToken(mockUser);
const decoded = jwtService.verifyToken(token, "access");
expect(decoded.sub).toBe(mockUser.id);
});
it("should throw error for an expired token", () => {
// Generate token with 0s expiry (sign options need to be passed to jwt.sign)
const expiredToken = jwt.sign(
{ sub: mockUser.id, type: "access" },
process.env.JWT_SECRET,
{ expiresIn: "0s" }
);
// Wait a bit for it to actually expire
return new Promise((resolve) => {
setTimeout(() => {
expect(() => jwtService.verifyToken(expiredToken, "access")).toThrow(
"Token has expired"
);
resolve();
}, 10);
});
});
it("should throw error for an invalid token type", () => {
const { token } = jwtService.generateAccessToken(mockUser); // This is an 'access' token
expect(() => jwtService.verifyToken(token, "refresh")).toThrow(
"Invalid token type. Expected refresh"
);
});
});
describe("refreshAccessToken", () => {
it("should refresh access token with a valid refresh token", async () => {
const { token: rToken, jti: refreshJti } =
jwtService.generateRefreshToken(mockUser);
User.isTokenBlacklisted.mockResolvedValue(false); // Not blacklisted
User.findById.mockResolvedValue(mockUser); // User exists
User.saveSession.mockResolvedValue(true); // For the new access token
const { accessToken } = await jwtService.refreshAccessToken(rToken);
expect(accessToken).toBeDefined();
const decodedAccess = jwt.verify(accessToken, process.env.JWT_SECRET);
expect(decodedAccess.type).toBe("access");
expect(User.isTokenBlacklisted).toHaveBeenCalledWith(refreshJti);
});
it("should throw if refresh token is blacklisted", async () => {
const { token: rToken, jti: refreshJti } =
jwtService.generateRefreshToken(mockUser);
User.isTokenBlacklisted.mockResolvedValue(true); // Blacklisted
await expect(jwtService.refreshAccessToken(rToken)).rejects.toThrow(
"Refresh token has been revoked"
);
expect(User.isTokenBlacklisted).toHaveBeenCalledWith(refreshJti);
});
});
describe("revokeToken", () => {
it("should call User.revokeSession with JTI", async () => {
const { token, jti } = jwtService.generateAccessToken(mockUser);
User.revokeSession.mockResolvedValue(true);
await jwtService.revokeToken(token);
expect(User.revokeSession).toHaveBeenCalledWith(jti);
});
});
// ... more tests ...
});