test: adding some API tests

This commit is contained in:
Ravinou 2025-04-06 20:48:50 +02:00
commit e1f234d54b
No known key found for this signature in database
GPG key ID: EEEE670C40F6A4D7
8 changed files with 611 additions and 110 deletions

View file

@ -1,5 +1,4 @@
// This function is used to hash user passwords and to verify them with the bcryptjs library
//Lib
import { hash, compare } from 'bcryptjs';
export async function hashPassword(password: string) {

View file

@ -3,6 +3,5 @@ export * from './isSshPubKeyDuplicate';
export * from './lanCommandOption';
export * from './nodemailerSMTP';
export * from './repoHistory';
export * from './timestampConverter';
export * from './tokenController';
export * from './shell.utils';

View file

@ -1,12 +0,0 @@
// This function is used to parse the date and time into a human readable format from the timestamp.
export function timestampConverter(UNIX_timestamp) {
const a = new Date(UNIX_timestamp * 1000);
const year = a.getFullYear();
const month = a.getMonth() + 1;
const date = a.getDate();
const hour = a.getHours();
const min = (a.getMinutes() < 10 ? '0' : '') + a.getMinutes();
//const sec = a.getSeconds();
const time = year + '/' + month + '/' + date + ' ' + hour + ':' + min;
return time;
}

View file

@ -1,137 +1,158 @@
import { describe, it, expect, vi } from 'vitest';
import handler from './updateEmail';
import { getUsersList, updateUsersList } from '~/services';
import { createMocks } from 'node-mocks-http';
import handler from '~/pages/api/account/updateEmail';
import { getServerSession } from 'next-auth/next';
import { NextApiRequest, NextApiResponse } from 'next';
import { getUsersList, updateUsersList } from '~/services';
vi.mock('next-auth/next');
vi.mock('~/services', () => ({
getUsersList: vi.fn(),
updateUsersList: vi.fn(),
}));
vi.mock('next-auth/next', () => ({
getServerSession: vi.fn(),
}));
describe('updateEmail API handler', () => {
const mockReq = {
method: 'PUT',
body: { email: 'newemail@example.com' },
} as unknown as NextApiRequest;
const mockRes = {
status: vi.fn().mockReturnThis(),
json: vi.fn(),
} as unknown as NextApiResponse;
it('should return 405 if method is not PUT', async () => {
mockReq.method = 'GET';
await handler(mockReq, mockRes);
expect(mockRes.status).toHaveBeenCalledWith(405);
describe('PUT /api/account/updateEmail', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, 'log').mockImplementation(() => {});
});
it('should return 401 if session is not found', async () => {
vi.mocked(getServerSession).mockResolvedValueOnce(null);
await handler(mockReq, mockRes);
expect(mockRes.status).toHaveBeenCalledWith(401);
it('should return 401 if not authenticated', async () => {
vi.mocked(getServerSession).mockResolvedValue(null);
const { req, res } = createMocks({ method: 'PUT' });
await handler(req, res);
expect(res._getStatusCode()).toBe(401);
});
it('should return 405 if method is not PUT', async () => {
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);
});
it('should return 422 if email is not provided', async () => {
mockReq.body = {};
vi.mocked(getServerSession).mockResolvedValueOnce({ user: { name: 'testuser' } });
await handler(mockReq, mockRes);
expect(mockRes.status).toHaveBeenCalledWith(422);
expect(mockRes.json).toHaveBeenCalledWith({ message: 'Unexpected data' });
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
const { req, res } = createMocks({
method: 'PUT',
body: {},
});
await handler(req, res);
expect(res._getStatusCode()).toBe(422);
expect(res._getJSONData()).toEqual({ message: 'Unexpected data' });
});
it('should return 400 if user is not found in users list', async () => {
vi.mocked(getServerSession).mockResolvedValueOnce({ user: { name: 'testuser' } });
vi.mocked(getUsersList).mockResolvedValueOnce([]);
await handler(mockReq, mockRes);
expect(mockRes.status).toHaveBeenCalledWith(400);
expect(mockRes.json).toHaveBeenCalledWith({
it('should return 400 if user is not found in the users list', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockResolvedValue([
{ id: 1, username: 'Ada', email: 'ada@example.com', password: '', roles: [] },
]);
const { req, res } = createMocks({
method: 'PUT',
body: { email: 'new@example.com' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toEqual({
message: 'User is incorrect. Please, logout to update your session.',
});
});
it('should return 400 if email already exists', async () => {
vi.mocked(getServerSession).mockResolvedValueOnce({ user: { name: 'testuser' } });
vi.mocked(getUsersList).mockResolvedValueOnce([
{
username: 'testuser',
email: 'oldemail@example.com',
id: 1,
password: 'hashedpassword',
roles: ['user'],
},
{
username: 'otheruser',
email: 'newemail@example.com',
id: 2,
password: 'hashedpassword',
roles: ['user'],
},
]);
await handler(mockReq, mockRes);
expect(mockRes.status).toHaveBeenCalledWith(400);
expect(mockRes.json).toHaveBeenCalledWith({ message: 'Email already exists' });
});
it('should update email and return 200 on success', async () => {
vi.mocked(getServerSession).mockResolvedValueOnce({ user: { name: 'testuser' } });
vi.mocked(getUsersList).mockResolvedValueOnce([
{
username: 'testuser',
email: 'oldemail@example.com',
id: 1,
password: 'hashedpassword',
roles: ['user'],
},
{
username: 'otheruser',
email: 'otheremail@example.com',
id: 2,
password: 'hashedpassword',
roles: ['user'],
},
]);
vi.mocked(updateUsersList).mockResolvedValueOnce(undefined);
await handler(mockReq, mockRes);
expect(mockRes.status).toHaveBeenCalledWith(200);
expect(mockRes.json).toHaveBeenCalledWith({ message: 'Successful API send' });
expect(updateUsersList).toHaveBeenCalledWith([
{ username: 'testuser', email: 'newemail@example.com' },
{ username: 'otheruser', email: 'otheremail@example.com' },
]);
});
it('should return 500 if an unexpected error occurs', async () => {
vi.mocked(getServerSession).mockResolvedValueOnce({ user: { name: 'testuser' } });
vi.mocked(getUsersList).mockRejectedValueOnce(new Error('Unexpected error'));
await handler(mockReq, mockRes);
expect(mockRes.status).toHaveBeenCalledWith(500);
expect(mockRes.json).toHaveBeenCalledWith({
status: 500,
message: 'API error, contact the administrator',
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockResolvedValue([
{ id: 1, username: 'Lovelace', email: 'lovelace@example.com', password: '', roles: [] },
{ id: 2, username: 'Ada', email: 'new@example.com', password: '', roles: [] },
]);
const { req, res } = createMocks({
method: 'PUT',
body: { email: 'new@example.com' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toEqual({ message: 'Email already exists' });
});
it('should return 500 with specific message if ENOENT error occurs', async () => {
vi.mocked(getServerSession).mockResolvedValueOnce({ user: { name: 'testuser' } });
const enoentError = new Error('No such file or directory');
(enoentError as any).code = 'ENOENT';
vi.mocked(getUsersList).mockRejectedValueOnce(enoentError);
it('should update the email and return 200 on success', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
await handler(mockReq, mockRes);
const users = [
{ id: 1, username: 'Lovelace', email: 'lovelace@example.com', password: '', roles: [] },
];
expect(mockRes.status).toHaveBeenCalledWith(500);
expect(mockRes.json).toHaveBeenCalledWith({
vi.mocked(getUsersList).mockResolvedValue(users);
vi.mocked(updateUsersList).mockResolvedValue();
const { req, res } = createMocks({
method: 'PUT',
body: { email: 'new@example.com' },
});
await handler(req, res);
expect(updateUsersList).toHaveBeenCalledWith([{ ...users[0], email: 'new@example.com' }]);
expect(res._getStatusCode()).toBe(200);
expect(res._getJSONData()).toEqual({ message: 'Successful API send' });
});
it('should return 500 if there is a file system error', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockRejectedValue({ code: 'ENOENT' });
const { req, res } = createMocks({
method: 'PUT',
body: { email: 'new@example.com' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(500);
expect(res._getJSONData()).toEqual({
status: 500,
message: 'No such file or directory',
});
});
it('should return 500 on unknown error', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockRejectedValue({ code: 'UNKNOWN_ERROR' });
const { req, res } = createMocks({
method: 'PUT',
body: { email: 'new@example.com' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(500);
expect(res._getJSONData()).toEqual({
status: 500,
message: 'API error, contact the administrator',
});
});
});

View file

@ -0,0 +1,149 @@
import { createMocks } from 'node-mocks-http';
import handler from '~/pages/api/account/updateEmailAlert';
import { getServerSession } from 'next-auth/next';
import { getUsersList, updateUsersList } from '~/services';
vi.mock('next-auth/next');
vi.mock('~/services', () => ({
getUsersList: vi.fn(),
updateUsersList: vi.fn(),
}));
describe('PUT /api/account/updateEmailAlert', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, 'log').mockImplementation(() => {});
});
it('should return 401 if not authenticated', async () => {
vi.mocked(getServerSession).mockResolvedValue(null);
const { req, res } = createMocks({ method: 'PUT' });
await handler(req, res);
expect(res._getStatusCode()).toBe(401);
});
it('should return 405 if method is not PUT', async () => {
const { req, res } = createMocks({ method: 'POST' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);
});
it('should return 422 if emailAlert is not a boolean', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
const { req, res } = createMocks({
method: 'PUT',
body: { emailAlert: 'yes' }, // incorrect type
});
await handler(req, res);
expect(res._getStatusCode()).toBe(422);
expect(res._getJSONData()).toEqual({ message: 'Unexpected data' });
});
it('should return 400 if user is not found in the users list', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockResolvedValue([
{
id: 2,
username: 'Ada',
email: 'ada@example.com',
emailAlert: false,
password: '',
roles: [],
},
]);
const { req, res } = createMocks({
method: 'PUT',
body: { emailAlert: true },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toEqual({
message: 'User is incorrect. Please, logout to update your session.',
});
});
it('should update emailAlert and return 200 on success', async () => {
const user = {
id: 1,
username: 'Lovelace',
email: 'lovelace@example.com',
emailAlert: false,
password: '',
roles: [],
};
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockResolvedValue([user]);
vi.mocked(updateUsersList).mockResolvedValue();
const { req, res } = createMocks({
method: 'PUT',
body: { emailAlert: true },
});
await handler(req, res);
expect(updateUsersList).toHaveBeenCalledWith([{ ...user, emailAlert: true }]);
expect(res._getStatusCode()).toBe(200);
expect(res._getJSONData()).toEqual({ message: 'Successful API send' });
});
it('should return 500 if there is a file system error (ENOENT)', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockRejectedValue({ code: 'ENOENT' });
const { req, res } = createMocks({
method: 'PUT',
body: { emailAlert: true },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(500);
expect(res._getJSONData()).toEqual({
status: 500,
message: 'No such file or directory',
});
});
it('should return 500 on unknown error', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockRejectedValue({ code: 'UNKNOWN' });
const { req, res } = createMocks({
method: 'PUT',
body: { emailAlert: true },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(500);
expect(res._getJSONData()).toEqual({
status: 500,
message: 'API error, contact the administrator',
});
});
});

View file

@ -0,0 +1,173 @@
import { createMocks } from 'node-mocks-http';
import handler from '~/pages/api/account/updatePassword';
import { getServerSession } from 'next-auth/next';
import { getUsersList, updateUsersList } from '~/services';
import { verifyPassword, hashPassword } from '~/helpers/functions';
vi.mock('next-auth/next');
vi.mock('~/services', () => ({
getUsersList: vi.fn(),
updateUsersList: vi.fn(),
}));
vi.mock('~/helpers/functions', () => ({
verifyPassword: vi.fn(),
hashPassword: vi.fn(),
}));
describe('PUT /api/account/updatePassword', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, 'log').mockImplementation(() => {});
});
it('should return 401 if not authenticated', async () => {
vi.mocked(getServerSession).mockResolvedValue(null);
const { req, res } = createMocks({ method: 'PUT' });
await handler(req, res);
expect(res._getStatusCode()).toBe(401);
});
it('should return 405 if method is not PUT', async () => {
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);
});
it('should return 422 if oldPassword or newPassword are missing or not strings', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
const { req, res } = createMocks({
method: 'PUT',
body: { oldPassword: 1234, newPassword: true },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(422);
expect(res._getJSONData()).toEqual({ message: 'Unexpected data' });
});
it('should return 400 if user is not found', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockResolvedValue([
{ id: 1, username: 'Ada', password: 'hashedpass', roles: [], email: 'ada@example.com' },
]);
const { req, res } = createMocks({
method: 'PUT',
body: { oldPassword: 'test', newPassword: 'newpass' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toEqual({
message: 'User is incorrect. Please, logout to update your session.',
});
});
it('should return 400 if old password is incorrect', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockResolvedValue([
{ id: 1, username: 'Lovelace', password: 'hashedpass', roles: [], email: 'love@example.com' },
]);
vi.mocked(verifyPassword).mockResolvedValue(false);
const { req, res } = createMocks({
method: 'PUT',
body: { oldPassword: 'wrongpass', newPassword: 'newpass' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toEqual({ message: 'Old password is incorrect.' });
});
it('should update password and return 200 on success', async () => {
const oldUser = {
id: 1,
username: 'Lovelace',
password: 'hashedpass',
roles: [],
email: 'love@example.com',
};
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockResolvedValue([oldUser]);
vi.mocked(verifyPassword).mockResolvedValue(true);
vi.mocked(hashPassword).mockResolvedValue('newHashedPassword');
vi.mocked(updateUsersList).mockResolvedValue();
const { req, res } = createMocks({
method: 'PUT',
body: { oldPassword: 'oldpass', newPassword: 'newpass' },
});
await handler(req, res);
expect(verifyPassword).toHaveBeenCalledWith('oldpass', 'hashedpass');
expect(hashPassword).toHaveBeenCalledWith('newpass');
expect(updateUsersList).toHaveBeenCalledWith([{ ...oldUser, password: 'newHashedPassword' }]);
expect(res._getStatusCode()).toBe(200);
expect(res._getJSONData()).toEqual({ message: 'Successful API send' });
});
it('should return 500 if there is a file system error (ENOENT)', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockRejectedValue({ code: 'ENOENT' });
const { req, res } = createMocks({
method: 'PUT',
body: { oldPassword: 'test', newPassword: 'new' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(500);
expect(res._getJSONData()).toEqual({
status: 500,
message: 'No such file or directory',
});
});
it('should return 500 on unknown error', async () => {
vi.mocked(getServerSession).mockResolvedValue({
user: { name: 'Lovelace' },
});
vi.mocked(getUsersList).mockRejectedValue({ code: 'SOMETHING_ELSE' });
const { req, res } = createMocks({
method: 'PUT',
body: { oldPassword: 'test', newPassword: 'new' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(500);
expect(res._getJSONData()).toEqual({
status: 500,
message: 'API error, contact the administrator',
});
});
});

View file

@ -0,0 +1,172 @@
import { createMocks } from 'node-mocks-http';
import handler from '~/pages/api/account/updateUsername'; // adapte le chemin si besoin
import { getServerSession } from 'next-auth/next';
import { getUsersList, updateUsersList } from '~/services';
vi.mock('next-auth/next');
vi.mock('~/services', () => ({
getUsersList: vi.fn(),
updateUsersList: vi.fn(),
}));
describe('PUT /api/account/updateUsername', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.resetModules();
vi.resetAllMocks();
vi.spyOn(console, 'log').mockImplementation(() => {});
});
it('should return 401 if not authenticated', async () => {
vi.mocked(getServerSession).mockResolvedValue(null);
const { req, res } = createMocks({ method: 'PUT' });
await handler(req, res);
expect(res._getStatusCode()).toBe(401);
});
it('should return 405 if method is not PUT', async () => {
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(405);
});
it('should return 422 if username is not a string', async () => {
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'Lovelace' } });
const { req, res } = createMocks({
method: 'PUT',
body: { username: 12345 },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(422);
expect(res._getJSONData()).toEqual({ message: 'Unexpected data' });
});
it('should return 422 if username format is invalid', async () => {
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'Lovelace' } });
const { req, res } = createMocks({
method: 'PUT',
body: { username: 'Too$hort!' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(422);
expect(res._getJSONData()).toEqual({
message: 'Only a-z characters are allowed (5 to 15 char.)',
});
});
it('should return 400 if user is not found', async () => {
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'Lovelace' } });
vi.mocked(getUsersList).mockResolvedValue([
{ username: 'Ada', email: 'ada@example.com', password: 'xxx', id: 1, roles: ['user'] },
]);
const { req, res } = createMocks({
method: 'PUT',
body: { username: 'newname' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toEqual({
message: 'User is incorrect. Please, logout to update your session.',
});
});
it('should return 400 if new username already exists', async () => {
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'Lovelace' } });
vi.mocked(getUsersList).mockResolvedValue([
{ username: 'Lovelace', email: 'love@example.com', password: 'xxx', id: 1, roles: ['user'] },
{
username: 'newname',
email: 'someone@example.com',
password: 'xxx',
id: 2,
roles: ['user'],
},
]);
const { req, res } = createMocks({
method: 'PUT',
body: { username: 'newname' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toEqual({ message: 'Username already exists' });
});
it('should return 200 and update the username', async () => {
const originalUser = {
username: 'Lovelace',
email: 'love@example.com',
password: 'xxx',
id: 1,
roles: ['user'],
};
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'Lovelace' } });
vi.mocked(getUsersList).mockResolvedValue([originalUser]);
vi.mocked(updateUsersList).mockResolvedValue();
const { req, res } = createMocks({
method: 'PUT',
body: { username: 'newusername' },
});
await handler(req, res);
expect(updateUsersList).toHaveBeenCalledWith([{ ...originalUser, username: 'newusername' }]);
expect(res._getStatusCode()).toBe(200);
expect(res._getJSONData()).toEqual({ message: 'Successful API send' });
});
it('should return 500 if file not found (ENOENT)', async () => {
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'Lovelace' } });
vi.mocked(getUsersList).mockRejectedValue({ code: 'ENOENT' });
const { req, res } = createMocks({
method: 'PUT',
body: { username: 'newname' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(500);
expect(res._getJSONData()).toEqual({
status: 500,
message: 'No such file or directory',
});
});
it('should return 500 on unknown error', async () => {
vi.mocked(getServerSession).mockResolvedValue({ user: { name: 'Lovelace' } });
vi.mocked(getUsersList).mockRejectedValue({ code: 'SOMETHING_ELSE' });
const { req, res } = createMocks({
method: 'PUT',
body: { username: 'newname' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(500);
expect(res._getJSONData()).toEqual({
status: 500,
message: 'API error, contact the administrator',
});
});
});